diff --git a/.changes/next-release/Bug Fix-19fd655d-6194-4ec1-954a-1a0124a8de36.json b/.changes/next-release/Bug Fix-19fd655d-6194-4ec1-954a-1a0124a8de36.json deleted file mode 100644 index 2b6cb1f746c..00000000000 --- a/.changes/next-release/Bug Fix-19fd655d-6194-4ec1-954a-1a0124a8de36.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Bump amazon-states-language-service dependency to ^1.8.0 to allow state machines to be created with the new intrinsic functions and Map state" -} \ No newline at end of file diff --git a/.changes/next-release/Bug Fix-77137e24-593d-4859-a15f-e029edf843a7.json b/.changes/next-release/Bug Fix-77137e24-593d-4859-a15f-e029edf843a7.json new file mode 100644 index 00000000000..2af1555a690 --- /dev/null +++ b/.changes/next-release/Bug Fix-77137e24-593d-4859-a15f-e029edf843a7.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Resource Explorer: S3 tree view now shows bucket contents correctly, even when restricted to root prefix." +} diff --git a/.changes/next-release/Feature-3082756c-79f7-4341-88be-d2f583a93686.json b/.changes/next-release/Feature-3082756c-79f7-4341-88be-d2f583a93686.json deleted file mode 100644 index 65c3c3a9512..00000000000 --- a/.changes/next-release/Feature-3082756c-79f7-4341-88be-d2f583a93686.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Feature", - "description": "CloudWatch Logs: one click to list log streams for a log group" -} \ No newline at end of file diff --git a/.dependency-cruiser.js b/.dependency-cruiser.js new file mode 100644 index 00000000000..1976385c872 --- /dev/null +++ b/.dependency-cruiser.js @@ -0,0 +1,466 @@ +/** + * This file was generated using the `dependency-cruiser` npm package. + * + * The initial purpose for this file is to find transitive dependencies for Browser + * incompatible modules (eg `fs-extra`). See `browser.md` explaining how this is used. + */ + +/** @type {import('dependency-cruiser').IConfiguration} */ +module.exports = { + forbidden: [ + { + name: 'no-circular', + severity: 'warn', + comment: + 'This dependency is part of a circular relationship. You might want to revise ' + + 'your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ', + from: {}, + to: { + circular: true, + }, + }, + { + name: 'no-orphans', + comment: + "This is an orphan module - it's likely not used (anymore?). Either use it or " + + "remove it. If it's logical this module is an orphan (i.e. it's a config file), " + + 'add an exception for it in your dependency-cruiser configuration. By default ' + + 'this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration ' + + 'files (.d.ts), tsconfig.json and some of the babel and webpack configs.', + severity: 'warn', + from: { + orphan: true, + pathNot: [ + '(^|/)[.][^/]+[.](js|cjs|mjs|ts|json)$', // dot files + '[.]d[.]ts$', // TypeScript declaration files + '(^|/)tsconfig[.]json$', // TypeScript config + '(^|/)(babel|webpack)[.]config[.](js|cjs|mjs|ts|json)$', // other configs + ], + }, + to: {}, + }, + { + name: 'no-deprecated-core', + comment: + 'A module depends on a node core module that has been deprecated. Find an alternative - these are ' + + "bound to exist - node doesn't deprecate lightly.", + severity: 'warn', + from: {}, + to: { + dependencyTypes: ['core'], + path: [ + '^(v8/tools/codemap)$', + '^(v8/tools/consarray)$', + '^(v8/tools/csvparser)$', + '^(v8/tools/logreader)$', + '^(v8/tools/profile_view)$', + '^(v8/tools/profile)$', + '^(v8/tools/SourceMap)$', + '^(v8/tools/splaytree)$', + '^(v8/tools/tickprocessor-driver)$', + '^(v8/tools/tickprocessor)$', + '^(node-inspect/lib/_inspect)$', + '^(node-inspect/lib/internal/inspect_client)$', + '^(node-inspect/lib/internal/inspect_repl)$', + '^(async_hooks)$', + '^(punycode)$', + '^(domain)$', + '^(constants)$', + '^(sys)$', + '^(_linklist)$', + '^(_stream_wrap)$', + ], + }, + }, + { + name: 'not-to-deprecated', + comment: + 'This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later ' + + 'version of that module, or find an alternative. Deprecated modules are a security risk.', + severity: 'warn', + from: {}, + to: { + dependencyTypes: ['deprecated'], + }, + }, + { + name: 'no-non-package-json', + severity: 'error', + comment: + "This module depends on an npm package that isn't in the 'dependencies' section of your package.json. " + + "That's problematic as the package either (1) won't be available on live (2 - worse) will be " + + 'available on live with an non-guaranteed version. Fix it by adding the package to the dependencies ' + + 'in your package.json.', + from: {}, + to: { + dependencyTypes: ['npm-no-pkg', 'npm-unknown'], + }, + }, + { + name: 'not-to-unresolvable', + comment: + "This module depends on a module that cannot be found ('resolved to disk'). If it's an npm " + + 'module: add it to your package.json. In all other cases you likely already know what to do.', + severity: 'error', + from: {}, + to: { + couldNotResolve: true, + }, + }, + { + name: 'no-duplicate-dep-types', + comment: + "Likely this module depends on an external ('npm') package that occurs more than once " + + 'in your package.json i.e. bot as a devDependencies and in dependencies. This will cause ' + + 'maintenance problems later on.', + severity: 'warn', + from: {}, + to: { + moreThanOneDependencyType: true, + // as it's pretty common to have a type import be a type only import + // _and_ (e.g.) a devDependency - don't consider type-only dependency + // types for this rule + dependencyTypesNot: ['type-only'], + }, + }, + + /* rules you might want to tweak for your specific situation: */ + + { + name: 'not-to-spec', + comment: + 'This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. ' + + "If there's something in a spec that's of use to other modules, it doesn't have that single " + + 'responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.', + severity: 'error', + from: {}, + to: { + path: '[.](spec|test)[.](js|mjs|cjs|ts|ls|coffee|litcoffee|coffee[.]md)$', + }, + }, + { + name: 'not-to-dev-dep', + severity: 'error', + comment: + "This module depends on an npm package from the 'devDependencies' section of your " + + 'package.json. It looks like something that ships to production, though. To prevent problems ' + + "with npm packages that aren't there on production declare it (only!) in the 'dependencies'" + + 'section of your package.json. If this module is development only - add it to the ' + + 'from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration', + from: { + path: '^(src)', + pathNot: '[.](spec|test)[.](js|mjs|cjs|ts|ls|coffee|litcoffee|coffee[.]md)$', + }, + to: { + dependencyTypes: ['npm-dev'], + // type only dependencies are not a problem as they don't end up in the + // production code or are ignored by the runtime. + dependencyTypesNot: ['type-only'], + pathNot: ['node_modules/@types/'], + }, + }, + { + name: 'optional-deps-used', + severity: 'info', + comment: + 'This module depends on an npm package that is declared as an optional dependency ' + + "in your package.json. As this makes sense in limited situations only, it's flagged here. " + + "If you're using an optional dependency here by design - add an exception to your" + + 'dependency-cruiser configuration.', + from: {}, + to: { + dependencyTypes: ['npm-optional'], + }, + }, + { + name: 'peer-deps-used', + comment: + 'This module depends on an npm package that is declared as a peer dependency ' + + 'in your package.json. This makes sense if your package is e.g. a plugin, but in ' + + 'other cases - maybe not so much. If the use of a peer dependency is intentional ' + + 'add an exception to your dependency-cruiser configuration.', + severity: 'warn', + from: {}, + to: { + dependencyTypes: ['npm-peer'], + }, + }, + ], + options: { + /* conditions specifying which files not to follow further when encountered: + - path: a regular expression to match + - dependencyTypes: see https://github.com/sverweij/dependency-cruiser/blob/main/doc/rules-reference.md#dependencytypes-and-dependencytypesnot + for a complete list + */ + doNotFollow: { + path: 'node_modules', + }, + + /* conditions specifying which dependencies to exclude + - path: a regular expression to match + - dynamic: a boolean indicating whether to ignore dynamic (true) or static (false) dependencies. + leave out if you want to exclude neither (recommended!) + */ + // exclude : { + // path: '', + // dynamic: true + // }, + + /* pattern specifying which files to include (regular expression) + dependency-cruiser will skip everything not matching this pattern + */ + // includeOnly : '', + + /* dependency-cruiser will include modules matching against the focus + regular expression in its output, as well as their neighbours (direct + dependencies and dependents) + */ + // focus : '', + + /* List of module systems to cruise. + When left out dependency-cruiser will fall back to the list of _all_ + module systems it knows of. It's the default because it's the safe option + It might come at a performance penalty, though. + moduleSystems: ['amd', 'cjs', 'es6', 'tsd'] + + As in practice only commonjs ('cjs') and ecmascript modules ('es6') + are widely used, you can limit the moduleSystems to those. + */ + + // moduleSystems: ['cjs', 'es6'], + + /* prefix for links in html and svg output (e.g. 'https://github.com/you/yourrepo/blob/develop/' + to open it on your online repo or `vscode://file/${process.cwd()}/` to + open it in visual studio code), + */ + // prefix: '', + + /* false (the default): ignore dependencies that only exist before typescript-to-javascript compilation + true: also detect dependencies that only exist before typescript-to-javascript compilation + "specify": for each dependency identify whether it only exists before compilation or also after + */ + // tsPreCompilationDeps: false, + + /* + list of extensions to scan that aren't javascript or compile-to-javascript. + Empty by default. Only put extensions in here that you want to take into + account that are _not_ parsable. + */ + // extraExtensionsToScan: [".json", ".jpg", ".png", ".svg", ".webp"], + + /* if true combines the package.jsons found from the module up to the base + folder the cruise is initiated from. Useful for how (some) mono-repos + manage dependencies & dependency definitions. + */ + // combinedDependencies: false, + + /* if true leave symlinks untouched, otherwise use the realpath */ + // preserveSymlinks: false, + + /* TypeScript project file ('tsconfig.json') to use for + (1) compilation and + (2) resolution (e.g. with the paths property) + + The (optional) fileName attribute specifies which file to take (relative to + dependency-cruiser's current working directory). When not provided + defaults to './tsconfig.json'. + */ + tsConfig: { + fileName: 'tsconfig.json', + }, + + /* Webpack configuration to use to get resolve options from. + + The (optional) fileName attribute specifies which file to take (relative + to dependency-cruiser's current working directory. When not provided defaults + to './webpack.conf.js'. + + The (optional) `env` and `arguments` attributes contain the parameters to be passed if + your webpack config is a function and takes them (see webpack documentation + for details) + */ + // webpackConfig: { + // fileName: 'webpack.config.js', + // env: {}, + // arguments: {} + // }, + + /* Babel config ('.babelrc', '.babelrc.json', '.babelrc.json5', ...) to use + for compilation (and whatever other naughty things babel plugins do to + source code). This feature is well tested and usable, but might change + behavior a bit over time (e.g. more precise results for used module + systems) without dependency-cruiser getting a major version bump. + */ + // babelConfig: { + // fileName: '.babelrc', + // }, + + /* List of strings you have in use in addition to cjs/ es6 requires + & imports to declare module dependencies. Use this e.g. if you've + re-declared require, use a require-wrapper or use window.require as + a hack. + */ + // exoticRequireStrings: [], + /* options to pass on to enhanced-resolve, the package dependency-cruiser + uses to resolve module references to disk. You can set most of these + options in a webpack.conf.js - this section is here for those + projects that don't have a separate webpack config file. + + Note: settings in webpack.conf.js override the ones specified here. + */ + enhancedResolveOptions: { + /* List of strings to consider as 'exports' fields in package.json. Use + ['exports'] when you use packages that use such a field and your environment + supports it (e.g. node ^12.19 || >=14.7 or recent versions of webpack). + + If you have an `exportsFields` attribute in your webpack config, that one + will have precedence over the one specified here. + */ + exportsFields: ['exports'], + /* List of conditions to check for in the exports field. e.g. use ['imports'] + if you're only interested in exposed es6 modules, ['require'] for commonjs, + or all conditions at once `(['import', 'require', 'node', 'default']`) + if anything goes for you. Only works when the 'exportsFields' array is + non-empty. + + If you have a 'conditionNames' attribute in your webpack config, that one will + have precedence over the one specified here. + */ + conditionNames: ['import', 'require', 'node', 'default'], + /* + The extensions, by default are the same as the ones dependency-cruiser + can access (run `npx depcruise --info` to see which ones that are in + _your_ environment. If that list is larger than what you need (e.g. + it contains .js, .jsx, .ts, .tsx, .cts, .mts - but you don't use + TypeScript you can pass just the extensions you actually use (e.g. + [".js", ".jsx"]). This can speed up the most expensive step in + dependency cruising (module resolution) quite a bit. + */ + // extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"], + /* + If your TypeScript project makes use of types specified in 'types' + fields in package.jsons of external dependencies, specify "types" + in addition to "main" in here, so enhanced-resolve (the resolver + dependency-cruiser uses) knows to also look there. You can also do + this if you're not sure, but still use TypeScript. In a future version + of dependency-cruiser this will likely become the default. + */ + mainFields: ['main', 'types', 'typings'], + /* + A list of alias fields in manifests (package.jsons). + Specify a field, such as browser, to be parsed according to + [this specification](https://github.com/defunctzombie/package-browser-field-spec). + Also see [resolve.alias](https://webpack.js.org/configuration/resolve/#resolvealiasfields) + in the webpack docs. + + Defaults to an empty array (don't use any alias fields). + */ + // aliasFields: ["browser"], + }, + reporterOptions: { + dot: { + /* pattern of modules that can be consolidated in the detailed + graphical dependency graph. The default pattern in this configuration + collapses everything in node_modules to one folder deep so you see + the external modules, but not the innards your app depends upon. + */ + collapsePattern: 'node_modules/(@[^/]+/[^/]+|[^/]+)', + + /* Options to tweak the appearance of your graph.See + https://github.com/sverweij/dependency-cruiser/blob/main/doc/options-reference.md#reporteroptions + for details and some examples. If you don't specify a theme + don't worry - dependency-cruiser will fall back to the default one. + */ + // theme: { + // graph: { + // /* use splines: "ortho" for straight lines. Be aware though + // graphviz might take a long time calculating ortho(gonal) + // routings. + // */ + // splines: "true" + // }, + // modules: [ + // { + // criteria: { matchesFocus: true }, + // attributes: { + // fillcolor: "lime", + // penwidth: 2, + // }, + // }, + // { + // criteria: { matchesFocus: false }, + // attributes: { + // fillcolor: "lightgrey", + // }, + // }, + // { + // criteria: { matchesReaches: true }, + // attributes: { + // fillcolor: "lime", + // penwidth: 2, + // }, + // }, + // { + // criteria: { matchesReaches: false }, + // attributes: { + // fillcolor: "lightgrey", + // }, + // }, + // { + // criteria: { source: "^src/model" }, + // attributes: { fillcolor: "#ccccff" } + // }, + // { + // criteria: { source: "^src/view" }, + // attributes: { fillcolor: "#ccffcc" } + // }, + // ], + // dependencies: [ + // { + // criteria: { "rules[0].severity": "error" }, + // attributes: { fontcolor: "red", color: "red" } + // }, + // { + // criteria: { "rules[0].severity": "warn" }, + // attributes: { fontcolor: "orange", color: "orange" } + // }, + // { + // criteria: { "rules[0].severity": "info" }, + // attributes: { fontcolor: "blue", color: "blue" } + // }, + // { + // criteria: { resolved: "^src/model" }, + // attributes: { color: "#0000ff77" } + // }, + // { + // criteria: { resolved: "^src/view" }, + // attributes: { color: "#00770077" } + // } + // ] + // } + }, + archi: { + /* pattern of modules that can be consolidated in the high level + graphical dependency graph. If you use the high level graphical + dependency graph reporter (`archi`) you probably want to tweak + this collapsePattern to your situation. + */ + collapsePattern: + '^(packages|src|lib|app|bin|test(s?)|spec(s?))/[^/]+|node_modules/(@[^/]+/[^/]+|[^/]+)', + + /* Options to tweak the appearance of your graph.See + https://github.com/sverweij/dependency-cruiser/blob/main/doc/options-reference.md#reporteroptions + for details and some examples. If you don't specify a theme + for 'archi' dependency-cruiser will use the one specified in the + dot section (see above), if any, and otherwise use the default one. + */ + // theme: { + // }, + }, + text: { + highlightFocused: true, + }, + }, + }, +} +// generated: dependency-cruiser@16.0.0 on 2024-01-05T15:20:51.138Z diff --git a/.editorconfig b/.editorconfig index 5533ce9b3a5..c6cf2790c3e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,13 +8,10 @@ end_of_line = lf insert_final_newline = true indent_size = 4 indent_style = space +max_line_length = 100 -[*.{js,py}] +[*.{js,ts,py}] charset = utf-8 -# 4 space indentation -[*.py] -indent_style = space - [Makefile] indent_style = tab diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 133b72c995e..00000000000 --- a/.eslintignore +++ /dev/null @@ -1,9 +0,0 @@ -node_modules/** -**/*.json -src/shared/telemetry/clienttelemetry.d.ts -src/codewhisperer/client/codewhispererclient.d.ts -src/codewhisperer/client/codewhispereruserclient.d.ts -**/*.gen.ts -src/testFixtures/workspaceFolder/ts-plain-sam-app/ -dist/** -types/*.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index 370942c8aed..026366245b9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,20 +2,25 @@ module.exports = { root: true, parser: '@typescript-eslint/parser', parserOptions: { - project: './tsconfig.json', + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./packages/*/tsconfig.json', './plugins/*/tsconfig.json'], tsconfigRootDir: __dirname, }, env: { node: true, mocha: true, + es2024: true, }, - plugins: ['@typescript-eslint', 'header', 'no-null'], + plugins: ['@typescript-eslint', '@stylistic', 'unicorn', 'header', 'security-node', 'aws-toolkits'], extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended-requiring-type-checking', 'plugin:@typescript-eslint/recommended', - 'prettier', + // "Add this as the _last_ item in the extends array, so that eslint-config-prettier has the + // opportunity to override other configs." https://github.com/prettier/eslint-plugin-prettier + 'plugin:prettier/recommended', ], rules: { curly: 2, // Enforce braces on "if"/"for"/etc. @@ -50,6 +55,8 @@ module.exports = { // modifiers: ['requiresQuotes'], }, ], + // Avoid accidental use of "==" instead of "===". + eqeqeq: 'error', // TODO reenable this rule (by removing this off) 'no-async-promise-executor': 'off', // TODO reenable this rule (by removing this off) @@ -85,37 +92,130 @@ module.exports = { // TODO reenable this rule, tests mostly break this one (by changing off to error) // This currently produces 700 non fixable by --fix errors 'sort-imports': 'off', - // TODO rennable this rule (by removing this off) - // namespaces are not great and we should stop using them - '@typescript-eslint/no-namespace': 'off', - // Turn this on by removing off when we fix namespaces - 'no-inner-declarations': 'off', + '@typescript-eslint/no-namespace': 'error', // This is off because prettier takes care of it 'no-extra-semi': 'off', - 'no-null/no-null': 'error', '@typescript-eslint/no-empty-function': 'off', + // Disallows returning e.g. Promise<…|never> which signals that an exception may be thrown. + // https://stackoverflow.com/q/64230626/152142 + '@typescript-eslint/no-redundant-type-constituents': 'off', '@typescript-eslint/no-unused-vars': 'off', - // New rules --> New TODOs + '@typescript-eslint/no-floating-promises': 'error', // Promises must catch errors or be awaited. '@typescript-eslint/no-var-requires': 'off', // Should be able to remove with the full migration of SDK v3 '@typescript-eslint/no-unsafe-member-access': 'off', // use typeguard before accessing a member '@typescript-eslint/no-unsafe-assignment': 'off', // 112 errors, similar to above '@typescript-eslint/no-unsafe-return': 'off', // 26 errors, similar to above '@typescript-eslint/no-unsafe-call': 'off', // 24 errors, need types for imported constructors '@typescript-eslint/restrict-template-expressions': 'off', // 294 errors, forces template literals to be a certain type - '@typescript-eslint/no-floating-promises': 'off', // 274 errors, promises should catch errors or be awaited '@typescript-eslint/ban-ts-comment': 'off', // 27 errors, bans compiler error exceptions '@typescript-eslint/explicit-module-boundary-types': 'off', // Remove this once 'explicit-function-return-type' is on // Do not check loops so while(true) works. Potentially reevalute this. 'no-constant-condition': ['error', { checkLoops: false }], 'no-empty': 'off', + + // https://eslint.style/rules/default/spaced-comment + // Require space after // comment. + '@stylistic/spaced-comment': [ + 'error', + 'always', + { + block: { + markers: ['!'], // Allow the /*!…*/ license header. + // exceptions: ['*'], + // balanced: true + }, + }, + ], + + // Rules from https://github.com/sindresorhus/eslint-plugin-unicorn + // TODO: 'unicorn/no-useless-promise-resolve-reject': 'error', + // TODO: 'unicorn/prefer-at': 'error', + // TODO: 'unicorn/prefer-event-target': 'error', + // TODO: 'unicorn/prefer-negative-index': 'error', + // TODO: 'unicorn/prefer-string-slice': 'error', + // TODO: 'unicorn/prefer-regexp-test': 'error', + // TODO: 'unicorn/prefer-ternary': 'error', + // TODO(?): 'unicorn/custom-error-definition': 'error', + // TODO(?): 'unicorn/prefer-json-parse-buffer': 'error', + // TODO: ESM modules https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-module.md + // 'unicorn/prefer-module': 'error', + 'unicorn/no-abusive-eslint-disable': 'error', + 'unicorn/no-null': 'error', + 'unicorn/no-unnecessary-polyfills': 'error', + 'unicorn/no-useless-spread': 'error', + 'unicorn/prefer-array-some': 'error', + 'unicorn/prefer-blob-reading-methods': 'error', + 'unicorn/prefer-code-point': 'error', + 'unicorn/prefer-date-now': 'error', + 'unicorn/prefer-dom-node-text-content': 'error', + 'unicorn/prefer-includes': 'error', + 'unicorn/prefer-keyboard-event-key': 'error', + 'unicorn/prefer-modern-dom-apis': 'error', + 'unicorn/prefer-modern-math-apis': 'error', + 'unicorn/prefer-native-coercion-functions': 'error', + // 'unicorn/prefer-node-protocol': 'error', + // 'unicorn/prefer-object-from-entries': 'error', + 'unicorn/prefer-reflect-apply': 'error', + 'unicorn/prefer-string-trim-start-end': 'error', + 'unicorn/prefer-type-error': 'error', + // Discourage `.forEach` because it can lead to accidental, incorrect use of async callbacks. + 'unicorn/no-array-for-each': 'error', + 'security-node/detect-child-process': 'error', + 'header/header': [ 'error', 'block', { pattern: - 'Copyright ([0-9]{4}[-,]{0,1}[ ]{0,1}){1,} Amazon.com, Inc. or its affiliates. All Rights Reserved.\\r?\\n \\* SPDX-License-Identifier: Apache-2.0', + 'Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\\r?\\n \\* SPDX-License-Identifier: Apache-2.0', }, { lineEndings: 'unix' }, ], + + 'aws-toolkits/no-only-in-tests': 'error', + 'aws-toolkits/no-await-on-vscode-msg': 'error', + 'aws-toolkits/no-banned-usages': 'error', + 'aws-toolkits/no-incorrect-once-usage': 'error', + 'aws-toolkits/no-string-exec-for-child-process': 'error', + 'aws-toolkits/no-console-log': 'error', + 'aws-toolkits/no-json-stringify-in-log': 'error', + 'aws-toolkits/no-printf-mismatch': 'error', + 'aws-toolkits/no-index-import': 'error', + 'no-restricted-imports': [ + 'error', + { + patterns: [ + { + group: ['**/core/dist/*'], + message: + "Avoid importing from the core lib's dist/ folders; please use directly from the core lib defined exports.", + }, + ], + // The following will place an error on the `fs-extra` import since we do not want it to be used for browser compatibility reasons. + paths: [ + { + name: 'fs-extra', + message: + 'Avoid fs-extra, use shared/fs/fs.ts. Notify the Toolkit team if your required functionality is not available.', + }, + { + name: 'fs', + message: 'Avoid node:fs and use shared/fs/fs.ts when possible.', + }, + { + name: 'child_process', + message: + 'Avoid child_process, use ChildProcess from `shared/utilities/processUtils.ts` instead.', + }, + { + name: '..', + message: + 'Avoid importing from index.ts files as it can lead to circular dependencies. Import from the module directly instead.', + }, + ], + }, + ], + + 'prettier/prettier': ['error', { endOfLine: 'auto' }], }, } diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f34129fb4d4..68e055e6886 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,4 @@ * @aws/aws-ides-team -src/codewhisperer/ @aws/codewhisperer-team +packages/core/src/codewhisperer/ @aws/codewhisperer-team +packages/core/src/amazonqFeatureDev/ @aws/earlybird +packages/core/src/awsService/accessanalyzer/ @aws/access-analyzer diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 0105f354bcf..8854d20a194 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -16,8 +16,9 @@ labels: bug ## Expected behavior -## System details (run the `AWS: About Toolkit` command) +## System details (run `AWS: About` and/or `Amazon Q: About`) - OS: - Visual Studio Code version: - AWS Toolkit version: +- Amazon Q version: diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 7e740fa52a1..fd8bb78fb8c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,6 +1,6 @@ --- name: Feature request -about: Suggest an idea for AWS Toolkit +about: Suggest an idea for AWS Toolkit or Amazon Q labels: feature-request --- diff --git a/.github/ISSUE_TEMPLATE/guidance_request.md b/.github/ISSUE_TEMPLATE/guidance_request.md index acbc67fa384..24eae4b2ac8 100644 --- a/.github/ISSUE_TEMPLATE/guidance_request.md +++ b/.github/ISSUE_TEMPLATE/guidance_request.md @@ -4,11 +4,12 @@ about: Ask for guidance, "how to", or other questions labels: guidance --- -## System details (run the `AWS: About Toolkit` command) +## System details (run `AWS: About` and/or `Amazon Q: About`) - OS: - Visual Studio Code version: - AWS Toolkit version: +- Amazon Q version: ## Question diff --git a/.github/ISSUE_TEMPLATE/unreliable_test_report.md b/.github/ISSUE_TEMPLATE/unreliable_test_report.md new file mode 100644 index 00000000000..a1981703b69 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/unreliable_test_report.md @@ -0,0 +1,17 @@ +--- +name: unreliable test +about: Help us monitor our test suite by reporting flaky tests. +labels: tests-ci-cd +--- + +## Test Details + +- Name of test: +- OS of failure: +- VSCode version of failure (minimum/insider/stable): +- Link to failing run: +- Link to failing test: + +## Log of Test Failure + +## Additional Information diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 30277038e28..3b891b24003 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,17 +1,11 @@ ## Problem + ## Solution - -## License +--- -By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. +- 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. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index af36f718161..c9ffbf03153 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,45 @@ # Documentation for configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file +# +# Troubleshooting: Insights tab > Dependency Graph > Dependabot +# https://github.com/aws/aws-toolkit-vscode/network/updates version: 2 updates: - package-ecosystem: 'npm' - directory: '/' # Location of package manifests. + directory: './src.gen' + target-branch: 'master' + schedule: + interval: 'monthly' + ignore: + - dependency-name: '*' # roundabout way to ignore this entire directory, see https://github.com/dependabot/dependabot-core/issues/4364#issuecomment-2002406602 + - package-ecosystem: 'npm' + directory: './' # Location of package manifests. + target-branch: 'master' # Avoid updates to "staging". + versioning-strategy: 'increase' + commit-message: + prefix: 'deps' + schedule: + interval: 'daily' + groups: + aws-sdk: + patterns: + - '@aws-sdk/*' + vscode-lsp: + patterns: + - 'vscode-lang*' + smithy: + patterns: + - '@smithy*' + - 'smithy*' + - package-ecosystem: 'github-actions' + directory: './' target-branch: 'master' # Avoid updates to "staging". + commit-message: + prefix: 'deps' schedule: interval: 'daily' + groups: + github-actions: + patterns: + - '*' diff --git a/.github/workflows/filterDuplicates.js b/.github/workflows/filterDuplicates.js new file mode 100644 index 00000000000..148c8cf0ac0 --- /dev/null +++ b/.github/workflows/filterDuplicates.js @@ -0,0 +1,297 @@ +/** + * Filters the report produced by jscpd to only include clones that involve changes from the given git diff. + * If the filtered report is non-empty, i.e. there exists a clone in the changes, + * the program exits with an error and logs the filtered report to console. + * + * Usage: + * node filterDuplicates.js run [path_to_git_diff] [path_to_jscpd_report] [commit_hash] [repo_name] + * + * Tests: + * node filterDuplicates.js test + */ + +const fs = require('fs/promises') +const path = require('path') + +function parseDiffFilePath(filePathLine) { + return filePathLine.split(' ')[2].split('/').slice(1).join('/') +} + +function parseDiffRange(rangeLine) { + const [_fromRange, toRange] = rangeLine.split(' ').slice(1, 3) + const [startLine, numLines] = toRange.slice(1).split(',').map(Number) + const range = [startLine, startLine + numLines] + return range +} + +async function parseDiff(diffPath) { + const diff = await fs.readFile(diffPath, 'utf8') + const lines = diff.split('\n') + let currentFile = null + let currentFileChanges = [] + const fileChanges = new Map() + + for (const line of lines) { + if (line.startsWith('diff')) { + if (currentFile) { + fileChanges.set(currentFile, currentFileChanges) + } + currentFile = parseDiffFilePath(line) + currentFileChanges = [] + } + if (line.startsWith('@@')) { + currentFileChanges.push(parseDiffRange(line)) + } + } + + fileChanges.set(currentFile, currentFileChanges) + + return fileChanges +} + +function doesOverlap(range1, range2) { + const [start1, end1] = range1 + const [start2, end2] = range2 + return ( + (start1 >= start2 && start1 <= end2) || (end1 >= start2 && end1 <= end2) || (start2 >= start1 && end2 <= end1) + ) +} + +function isCloneInChanges(changes, cloneInstance) { + const fileName = cloneInstance.name + const cloneStart = cloneInstance.start + const cloneEnd = cloneInstance.end + const lineChangeRanges = changes.get(fileName) + + if (!lineChangeRanges) { + return false + } + + return lineChangeRanges.some((range) => doesOverlap([cloneStart, cloneEnd], range)) +} + +function isInChanges(changes, dupe) { + return isCloneInChanges(changes, dupe.firstFile) || isCloneInChanges(changes, dupe.secondFile) +} + +function filterDuplicates(report, changes) { + duplicates = [] + for (const dupe of report.duplicates) { + if (isInChanges(changes, dupe)) { + duplicates.push(dupe) + } + } + return duplicates +} + +function formatDuplicates(duplicates, commitHash, repoName) { + const baseUrl = `https://github.com/${repoName}` + return duplicates.map((dupe) => { + return { + first: formUrl(dupe.firstFile, commitHash), + second: formUrl(dupe.secondFile, commitHash), + numberOfLines: dupe.lines, + } + }) + function formUrl(file, commitHash) { + return `${baseUrl}/blob/${commitHash}/${file.name}#L${file.start}-L${file.end}` + } +} + +async function run() { + const rawDiffPath = process.argv[3] + const jscpdReportPath = process.argv[4] + const commitHash = process.argv[5] + const repoName = process.argv[6] + const changes = await parseDiff(rawDiffPath) + const jscpdReport = JSON.parse(await fs.readFile(jscpdReportPath, 'utf8')) + const filteredDuplicates = filterDuplicates(jscpdReport, changes) + + console.log('%s files changes', changes.size) + console.log('%s duplicates found', filteredDuplicates.length) + if (filteredDuplicates.length > 0) { + console.log(formatDuplicates(filteredDuplicates, commitHash, repoName)) + console.log( + '* Hint: if these duplicates appear unrelated to the changes, rebase onto or merge in the latest target branch.' + ) + process.exit(1) + } +} + +/** + * Mini-test Suite + */ +const testDiffFile = path.resolve(__dirname, 'test/test_diff.txt') +let testCounter = 0 +function assertEqual(actual, expected) { + if (actual !== expected) { + throw new Error(`Expected ${expected} but got ${actual}`) + } + testCounter += 1 +} + +async function test() { + test_parseDiffFilePath() + test_parseDiffRange() + test_doesOverlap() + await test_parseDiff() + await test_isCloneInChanges() + await test_isInChanges() + await test_filterDuplicates() + console.log('All tests passed (%s)', testCounter) +} + +function test_parseDiffFilePath() { + assertEqual( + parseDiffFilePath( + 'diff --git a/.github/workflows/copyPasteDetection.yml b/.github/workflows/copyPasteDetection.yml' + ), + '.github/workflows/copyPasteDetection.yml' + ) + assertEqual( + parseDiffFilePath('diff --git a/.github/workflows/filterDuplicates.js b/.github/workflows/filterDuplicates.js'), + '.github/workflows/filterDuplicates.js' + ) +} + +function test_parseDiffRange() { + assertEqual(parseDiffRange('@@ -1,4 +1,4 @@').join(','), '1,5') + assertEqual(parseDiffRange('@@ -10,4 +10,4 @@').join(','), '10,14') + assertEqual(parseDiffRange('@@ -10,4 +10,5 @@').join(','), '10,15') +} + +function test_doesOverlap() { + assertEqual(doesOverlap([1, 5], [2, 4]), true) + assertEqual(doesOverlap([2, 3], [2, 4]), true) + assertEqual(doesOverlap([2, 3], [1, 4]), true) + assertEqual(doesOverlap([1, 5], [5, 6]), true) + assertEqual(doesOverlap([1, 5], [6, 7]), false) + assertEqual(doesOverlap([6, 7], [1, 5]), false) + assertEqual(doesOverlap([2, 5], [4, 5]), true) +} + +async function test_parseDiff() { + const changes = await parseDiff(testDiffFile) + assertEqual(changes.size, 2) + assertEqual(changes.get('.github/workflows/copyPasteDetection.yml').length, 1) + assertEqual(changes.get('.github/workflows/filterDuplicates.js').length, 1) + assertEqual(changes.get('.github/workflows/filterDuplicates.js')[0].join(','), '1,86') + assertEqual(changes.get('.github/workflows/copyPasteDetection.yml')[0].join(','), '26,73') +} + +async function test_isCloneInChanges() { + const changes = await parseDiff(testDiffFile) + assertEqual( + isCloneInChanges(changes, { + name: '.github/workflows/filterDuplicates.js', + start: 1, + end: 86, + }), + true + ) + assertEqual( + isCloneInChanges(changes, { + name: '.github/workflows/filterDuplicates.js', + start: 80, + end: 95, + }), + true + ) + assertEqual( + isCloneInChanges(changes, { + name: '.github/workflows/filterDuplicates.js', + start: 87, + end: 95, + }), + false + ) + assertEqual( + isCloneInChanges(changes, { + name: 'some-fake-file', + start: 1, + end: 100, + }), + false + ) +} + +async function test_isInChanges() { + const changes = await parseDiff(testDiffFile) + const dupe = { + firstFile: { + name: '.github/workflows/filterDuplicates.js', + start: 1, + end: 86, + }, + secondFile: { + name: '.github/workflows/filterDuplicates.js', + start: 80, + end: 95, + }, + } + assertEqual(isInChanges(changes, dupe), true) + dupe.secondFile.start = 87 + assertEqual(isInChanges(changes, dupe), true) + dupe.firstFile.name = 'some-fake-file' + assertEqual(isInChanges(changes, dupe), false) +} + +async function test_filterDuplicates() { + assertEqual( + filterDuplicates( + { + duplicates: [ + { + firstFile: { + name: '.github/workflows/filterDuplicates.js', + start: 1, + end: 86, + }, + secondFile: { + name: '.github/workflows/filterDuplicates.js', + start: 80, + end: 95, + }, + }, + ], + }, + await parseDiff(testDiffFile) + ).length, + 1 + ) + assertEqual( + filterDuplicates( + { + duplicates: [ + { + firstFile: { + name: 'some-other-file', + start: 1, + end: 86, + }, + secondFile: { + name: '.github/workflows/filterDuplicates.js', + start: 90, + end: 95, + }, + }, + ], + }, + await parseDiff(testDiffFile) + ).length, + 0 + ) +} + +async function main() { + const mode = process.argv[2] + if (mode === 'run') { + await run() + } else if (mode === 'test') { + await test() + } else { + throw new Error('Invalid mode') + } +} + +void main() diff --git a/.github/workflows/jscpd.json b/.github/workflows/jscpd.json new file mode 100644 index 00000000000..dd5faa12b30 --- /dev/null +++ b/.github/workflows/jscpd.json @@ -0,0 +1,9 @@ +{ + "pattern": "packages/**/*.ts", + "ignore": ["**node_modules**", "**dist**", "**/scripts/**"], + "gitignore": true, + "threshold": 3.0, + "minLines": 10, + "output": "./", + "reporters": ["json"] +} diff --git a/.github/workflows/lintbranch.js b/.github/workflows/lintbranch.js new file mode 100644 index 00000000000..05fc677dac5 --- /dev/null +++ b/.github/workflows/lintbranch.js @@ -0,0 +1,67 @@ +// Check that branch name conforms to GitHub naming convention: +// https://docs.github.com/en/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names#naming-branches-and-tags + +// To run self-tests, +// node lintbranch.js test +// TODO: deduplicate code from lintbranch.js and lintcommit.js. + +function isValid(branchName) { + const branchNameRegex = /^[a-zA-Z][a-zA-Z0-9._/-]*$/ + + return branchNameRegex.test(branchName) +} + +function run(branchName) { + if (isValid(branchName)) { + console.log(`Branch name "${branchName}" is valid.`) + process.exit(0) + } else { + const helpUrl = + 'https://docs.github.com/en/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names#naming-branches-and-tags' + console.log(`Branch name "${branchName}" is invalid see ${helpUrl} for more information.`) + process.exit(1) + } +} + +function _test() { + const tests = { + 'feature/branch-name': true, + feature_123: true, + 'my-branch': true, + '123invalid-start': false, + '!invalid@start': false, + '': false, + 'another/valid-name134': true, + 'feature/123";id;{echo,Y2F0IC9ldGMvcGFzc3dk}|{base64,-d}|{bash,-i};#': false, + } + + let passed = 0 + let failed = 0 + + for (const [branchName, expected] of Object.entries(tests)) { + const result = isValid(branchName) + if (result === expected) { + console.log(`✅ Test passed for "${branchName}"`) + passed++ + } else { + console.log(`❌ Test failed for "${branchName}" (expected "${expected}", got "${result}")`) + failed++ + } + } + + console.log(`\n${passed} tests passed, ${failed} tests failed`) +} + +function main() { + const mode = process.argv[2] + + if (mode === 'test') { + _test() + } else if (mode === 'run') { + run(process.argv[3]) + } else { + throw new Error(`Unknown mode: ${mode}`) + } +} + +main() diff --git a/.github/workflows/lintcommit.js b/.github/workflows/lintcommit.js new file mode 100644 index 00000000000..47e194653a3 --- /dev/null +++ b/.github/workflows/lintcommit.js @@ -0,0 +1,211 @@ +// Checks that a PR title conforms to our custom flavor of "conventional commits" +// (https://www.conventionalcommits.org/). +// +// To run self-tests, simply run this script: +// +// node lintcommit.js test +// +// TODO: "PR must describe Problem in a concise way, and Solution". +// TODO: this script intentionally avoids github APIs so that it is locally-debuggable, but if those +// are needed, use actions/github-script as described in: https://github.com/actions/github-script?tab=readme-ov-file#run-a-separate-file +// + +const fs = require('fs') +// This script intentionally avoids github APIs so that: +// 1. it is locally-debuggable +// 2. the CI job is fast ("npm install" is slow) +// But if we still want to use github API, we can keep it fast by using `actions/github-script` as +// described in: https://github.com/actions/github-script?tab=readme-ov-file#run-a-separate-file +// +// const core = require('@actions/core') +// const github = require('@actions/github') + +const types = new Set([ + 'build', + // Don't allow "chore" because it's over-used. + // Instead, add a new type if absolutely needed (if the existing ones can't possibly apply). + // 'chore', + 'ci', + 'config', + 'deps', + 'docs', + 'feat', + 'fix', + 'perf', + 'refactor', + 'revert', + 'style', + 'telemetry', + 'test', + 'types', +]) + +// TODO: Validate against this once we are satisfied with this list. +const scopes = new Set([ + 'amazonq', + 'core', + 'explorer', + 'lambda', + 'logs', + 'redshift', + 'q-chat', + 'q-featuredev', + 'q-inlinechat', + 'q-transform', + 'sam', + 's3', + 'telemetry', + 'toolkit', + 'ui', + 'sagemakerunifiedstudio', +]) +void scopes + +/** + * Checks that a pull request title, or commit message subject, follows the expected format: + * + * type(scope): message + * + * Returns undefined if `title` is valid, else an error message. + */ +function validateTitle(title) { + const parts = title.split(':') + const subject = parts.slice(1).join(':').trim() + + if (title.startsWith('Merge')) { + return undefined + } + + if (parts.length < 2) { + return 'missing colon (:) char' + } + + const typeScope = parts[0] + + const [type, scope] = typeScope.split(/\(([^)]+)\)$/) + + if (/\s+/.test(type)) { + return `type contains whitespace: "${type}"` + } else if (type === 'chore') { + return 'Do not use "chore" as a type. If the existing valid types are insufficent, add a new type to the `lintcommit.js` script.' + } else if (!types.has(type)) { + return `invalid type "${type}"` + } else if (!scope && typeScope.includes('(')) { + return `must be formatted like type(scope):` + } else if (!scope && ['feat', 'fix'].includes(type)) { + return `"${type}" type must include a scope (example: "${type}(amazonq)")` + } else if (scope && scope.length > 30) { + return 'invalid scope (must be <=30 chars)' + } else if (scope && /[^- a-z0-9]+/.test(scope)) { + return `invalid scope (must be lowercase, ascii only): "${scope}"` + } else if (subject.length === 0) { + return 'empty subject' + } else if (subject.length > 100) { + return 'invalid subject (must be <=100 chars)' + } + + return undefined +} + +function run() { + const eventData = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8')) + const pullRequest = eventData.pull_request + + // console.log(eventData) + + if (!pullRequest) { + console.info('No pull request found in the context') + return + } + + const title = pullRequest.title + + const failReason = validateTitle(title) + const msg = failReason + ? ` +Invalid pull request title: \`${title}\` + +* Problem: ${failReason} +* Expected format: \`type(scope): subject...\` + * type: one of (${Array.from(types).join(', ')}) + * scope: lowercase, <30 chars + * subject: must be <100 chars + * documentation: https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#pull-request-title +* Hint: *close and re-open the PR* to re-trigger CI (after fixing the PR title). +` + : `Pull request title matches the [expected format](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#pull-request-title).` + + if (process.env.GITHUB_STEP_SUMMARY) { + fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, msg) + } + + if (failReason) { + console.error(msg) + process.exit(1) + } else { + console.info(msg) + } +} + +function _test() { + const tests = { + ' foo(scope): bar': 'type contains whitespace: " foo"', + 'build: update build process': undefined, + 'chore: update dependencies': + 'Do not use "chore" as a type. If the existing valid types are insufficent, add a new type to the `lintcommit.js` script.', + 'ci: configure CI/CD': undefined, + 'config: update configuration files': undefined, + 'deps: bump the aws-sdk group across 1 directory with 5 updates': undefined, + 'docs: update documentation': undefined, + 'feat(foo): add new feature': undefined, + 'feat(foo):': 'empty subject', + 'feat foo):': 'type contains whitespace: "feat foo)"', + 'feat(foo)): sujet': 'invalid type "feat(foo))"', + 'feat(foo: sujet': 'invalid type "feat(foo"', + 'feat(Q Foo Bar): bar': 'invalid scope (must be lowercase, ascii only): "Q Foo Bar"', + 'feat(scope):': 'empty subject', + 'feat(q foo bar): bar': undefined, + 'feat(foo): x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ': + 'invalid subject (must be <=100 chars)', + 'feat: foo': '"feat" type must include a scope (example: "feat(amazonq)")', + 'fix: foo': '"fix" type must include a scope (example: "fix(amazonq)")', + 'fix(a-b-c): resolve issue': undefined, + 'foo (scope): bar': 'type contains whitespace: "foo "', + 'invalid title': 'missing colon (:) char', + 'perf: optimize performance': undefined, + 'refactor: improve code structure': undefined, + 'revert: feat: add new feature': undefined, + 'style: format code': undefined, + 'test: add new tests': undefined, + 'types: add type definitions': undefined, + 'Merge staging into feature/lambda-get-started': undefined, + } + + let passed = 0 + let failed = 0 + + for (const [title, expected] of Object.entries(tests)) { + const result = validateTitle(title) + if (result === expected) { + console.log(`✅ Test passed for "${title}"`) + passed++ + } else { + console.log(`❌ Test failed for "${title}" (expected "${expected}", got "${result}")`) + failed++ + } + } + + console.log(`\n${passed} tests passed, ${failed} tests failed`) +} + +function main() { + const mode = process.argv[2] + + if (mode === 'test') { + _test() + } else { + run() + } +} + +main() diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 48f7fae3ea0..da8d0c6ea54 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -1,75 +1,216 @@ -# github actions: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions +# github actions: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-nodejs # setup-node: https://github.com/actions/setup-node name: CI on: push: - branches: [master] + branches: [master, staging] pull_request: - branches: [master, feature/*] + # By default, CI will trigger on opened/synchronize/reopened event types. + # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request + # Note: To re-run `lint-commits` after fixing the PR title, close-and-reopen the PR. + branches: [master, feature/*, staging] + +# Cancel old jobs when a pull request is updated. +concurrency: + group: ${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: + lint-commits: + # Note: To re-run `lint-commits` after fixing the PR title, close-and-reopen the PR. + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 20 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Validate Branch name + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref != ''}} + env: + BRANCH_NAME: ${{ github.event.pull_request.head.ref }} + run: | + node "$GITHUB_WORKSPACE/.github/workflows/lintbranch.js" run "$BRANCH_NAME" + - name: Check PR title + run: | + node "$GITHUB_WORKSPACE/.github/workflows/lintcommit.js" + + lint: + needs: lint-commits + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + vscode-version: [stable] + env: + NODE_OPTIONS: '--max-old-space-size=8192' + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - run: npm run testCompile + - run: npm run lint + + lint-duplicate-code: + needs: lint-commits + if: ${{ github.event_name == 'pull_request'}} + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + env: + NODE_OPTIONS: '--max-old-space-size=8192' + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Fetch fork upstream + env: + REPO_NAME: ${{ github.event.pull_request.head.repo.full_name }} + run: | + git remote add forkUpstream https://github.com/$REPO_NAME # URL of the fork + git fetch forkUpstream # Fetch fork + + - name: Merge in target branch to avoid false negatives. + env: + TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} + # Note: "git merge" should always succeed here, because GHA won't + # start the job if there are merge conflicts. https://github.com/orgs/community/discussions/11265 + # Also, because `git merge` makes a commit, we need to establish an identity to avoid 'Committer identity unknown' error + run: | + git config --global user.name "aws-toolkit-automation" + git config --global user.email "<>" + git merge origin/$TARGET_BRANCH + + - name: Compute git diff + env: + CURRENT_BRANCH: ${{ github.head_ref }} + TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} + run: git diff origin/$TARGET_BRANCH forkUpstream/$CURRENT_BRANCH > diff_output.txt + + - run: npm install -g jscpd + + - run: jscpd --config "$GITHUB_WORKSPACE/.github/workflows/jscpd.json" + + - if: always() + uses: actions/upload-artifact@v4 + with: + name: unfiltered-jscpd-report + path: ./jscpd-report.json + + - name: Check for Duplicates + env: + COMMIT_HASH: ${{ github.sha}} + REPO_NAME: ${{ github.repository }} + run: node "$GITHUB_WORKSPACE/.github/workflows/filterDuplicates.js" run diff_output.txt jscpd-report.json $COMMIT_HASH $REPO_NAME + macos: - name: macOS nodejs + needs: lint-commits + name: test macOS runs-on: macos-latest strategy: + fail-fast: false matrix: - node-version: [16.x] + node-version: [18.x] vscode-version: [minimum, stable, insiders] + package: [amazonq, toolkit] env: VSCODE_TEST_VERSION: ${{ matrix.vscode-version }} NODE_OPTIONS: '--max-old-space-size=8192' + AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/' + AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm ci - - run: npm run vscode:prepublish - name: Tests - uses: GabrielBB/xvfb-action@v1 + uses: coactions/setup-xvfb@v1 with: - run: npm test - - name: Code coverage + run: npm run test -w packages/${{ matrix.package }} + - name: Code coverage for ${{ matrix.package }} env: # Unset NODE_OPTIONS because of https://github.com/codecov/uploader/issues/475 NODE_OPTIONS: '' - if: ${{ github.repository == 'aws/aws-toolkit-vscode' && ( github.ref == 'master' || github.event_name == 'pull_request' ) }} - uses: codecov/codecov-action@v2 + if: ${{ github.repository == 'aws/aws-toolkit-vscode' && github.event_name == 'pull_request' && github.base_ref == 'master' }} + uses: codecov/codecov-action@v5 with: + flags: macos-${{ matrix.package }}-unittests verbose: true - file: ./coverage/coverage-final.json - flags: macos-unittests + file: ./coverage/${{ matrix.package }}/lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + + web: + needs: lint-commits + name: test Web + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + node-version: [18.x] + vscode-version: [stable, insiders] + env: + VSCODE_TEST_VERSION: ${{ matrix.vscode-version }} + NODE_OPTIONS: '--max-old-space-size=8192' + AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/' + AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/' + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - name: Tests + uses: coactions/setup-xvfb@v1 + with: + run: npm run testWeb windows: - name: Windows nodejs - runs-on: windows-2019 + needs: lint-commits + name: test Windows + runs-on: windows-latest strategy: + fail-fast: false matrix: - node-version: [16.x] + node-version: [18.x] vscode-version: [stable, insiders] + package: [amazonq, toolkit] env: VSCODE_TEST_VERSION: ${{ matrix.vscode-version }} NODE_OPTIONS: '--max-old-space-size=8192' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm ci - - run: npm run vscode:prepublish - name: Tests - run: npm test - - name: Code coverage + run: npm run test -w packages/${{ matrix.package }} + - name: Code coverage for ${{ matrix.package }} env: # Unset NODE_OPTIONS because of https://github.com/codecov/uploader/issues/475 NODE_OPTIONS: '' - if: ${{ github.repository == 'aws/aws-toolkit-vscode' && ( github.ref == 'master' || github.event_name == 'pull_request' ) }} - uses: codecov/codecov-action@v2 + if: ${{ github.repository == 'aws/aws-toolkit-vscode' && github.event_name == 'pull_request' && github.base_ref == 'master' }} + uses: codecov/codecov-action@v5 with: + flags: windows-${{ matrix.package }}-unittests verbose: true - file: ./coverage/coverage-final.json - flags: windows-unittests + file: ./coverage/${{ matrix.package }}/lcov.info + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/notification.yml b/.github/workflows/notification.yml new file mode 100644 index 00000000000..17f0327e5c1 --- /dev/null +++ b/.github/workflows/notification.yml @@ -0,0 +1,41 @@ +# github actions: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-nodejs +# setup-node: https://github.com/actions/setup-node + +name: Notifications + +on: + # `pull_request_target` (as opposed to `pull_request`) gives permissions to comment on PRs. + pull_request_target: + # By default, CI will trigger on opened/synchronize/reopened event types. + # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request + # Note: To re-run `lint-commits` after fixing the PR title, close-and-reopen the PR. + branches: [master, feature/*, staging] + +# Cancel old jobs when a pull request is updated. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + notify: + if: github.repository == 'aws/aws-toolkit-vscode' + runs-on: ubuntu-latest + permissions: + pull-requests: write + issues: read + steps: + - uses: actions/checkout@v4 + if: github.event_name == 'pull_request_target' + with: + fetch-depth: 20 + - uses: actions/setup-node@v4 + if: github.event_name == 'pull_request_target' + with: + node-version: '20' + - name: Comment about contribution guidelines + uses: actions/github-script@v7 + if: github.event_name == 'pull_request_target' + with: + script: | + const notify = require('.github/workflows/notify.js') + await notify({github, context}) diff --git a/.github/workflows/notify.js b/.github/workflows/notify.js new file mode 100644 index 00000000000..835acddf706 --- /dev/null +++ b/.github/workflows/notify.js @@ -0,0 +1,96 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +const { hasPath, dedupComment } = require('./utils') + +const testFilesMessage = + '- This pull request modifies code in `src/*` but no tests were added/updated.\n - Confirm whether tests should be added or ensure the PR description explains why tests are not required.\n' + +const changelogMessage = + '- This pull request implements a `feat` or `fix`, so it must include a changelog entry (unless the fix is for an *unreleased* feature). Review the [changelog guidelines](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#changelog).\n - Note: beta or "experiment" features that have active users *should* announce fixes in the changelog.\n - If this is not a feature or fix, use an appropriate type from the [title guidelines](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#pull-request-title). For example, telemetry-only changes should use the `telemetry` type.\n' + +/** + * Remind partner teams that tests are required. We don't need to remind them if: + * 1. They did not change anything in a src directory + * 2. They already have test files in the PR + * 3. We've already told them in a previous PR comment + */ +module.exports = async ({ github, context }) => { + const owner = context.repo.owner + const repo = context.repo.repo + const author = context.payload.pull_request.head.repo.owner.login + const pullRequestId = context.payload.pull_request.number + + const response = await github.rest.repos.compareCommitsWithBasehead({ + owner, + repo, + basehead: `${owner}:${context.payload.pull_request.base.ref}...${author}:${context.payload.pull_request.head.ref}`, + }) + + const filenames = response.data.files.map((file) => file.filename) + + const shouldAddTestFileMessage = requiresTestFilesMessage(filenames) + const shouldAddChangelogMessage = requiresChangelogMessage(filenames, context.payload.pull_request.title) + + if (!shouldAddTestFileMessage && !shouldAddChangelogMessage) { + return + } + + // Check for prior comments on the PR + const comments = await github.rest.issues.listComments({ + owner, + repo, + issue_number: pullRequestId, + }) + + let msg = '' + if (shouldAddTestFileMessage) { + msg += testFilesMessage + } + if (shouldAddChangelogMessage) { + msg += changelogMessage + } + + if (shouldAddTestFileMessage || shouldAddChangelogMessage) { + await dedupComment({ github, comments, owner, repo, pullRequestId, message: msg }) + } +} + +/** + * Require the changelog message if the scope is fix/feat AND there is no changelog item + */ +function requiresChangelogMessage(filenames, title) { + try { + return !hasPath(filenames, '.changes') && (title.startsWith('fix') || title.startsWith('feat')) + } catch (e) { + console.log(e) + return undefined + } +} + +/** + * Require the test files message if there are changes to source files but aren't any + * changes to the test files + */ +function requiresTestFilesMessage(filenames, title) { + if (/^\s*[mM]erge/.test(title)) { + console.log('"Merge" pull request') + return + } + + // Check if src directory changed + if (!hasPath(filenames, 'src/')) { + console.log('Did not find src files in the code changes') + return + } + + // Check if test files were added or modified + if (hasPath(filenames, '.test.ts')) { + console.log('Found test files in the code changes') + return + } + + return true +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000000..60863f3b5a4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,126 @@ +# This job performs the following: +# - Publish prerelease (not release) artifacts for feature/x branches and "nightly" mainline. + +name: Prerelease +on: + # schedule: + # - cron: '5 5 * * *' + workflow_dispatch: + inputs: + tag_name: + description: 'Tag name for release' + required: false + default: prerelease + push: + branches: [master, feature/*, release/*] + # tags: + # - v[0-9]+.[0-9]+.[0-9]+ + +jobs: + package: + runs-on: ubuntu-latest + env: + NODE_OPTIONS: '--max-old-space-size=8192' + outputs: + feature: ${{ steps.build.outputs.feature }} + tagname: ${{ steps.build.outputs.tagname }} + toolkit_version: ${{ steps.build.outputs.toolkit_version }} + amazonq_version: ${{ steps.build.outputs.amazonq_version }} + toolkit_changes: ${{ steps.build.outputs.toolkit_changes }} + amazonq_changes: ${{ steps.build.outputs.amazonq_changes }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + # - if: github.event_name == 'schedule' + # run: echo 'TAG_NAME=prerelease' >> $GITHUB_ENV + - if: github.event_name == 'workflow_dispatch' + run: echo "TAG_NAME=${{ github.event.inputs.tag_name }}" >> $GITHUB_ENV + - if: startsWith(github.ref_name, 'feature/') + run: | + FEAT_NAME=$(echo ${{ github.ref_name }} | sed 's/feature\///') + echo "FEAT_NAME=$FEAT_NAME" >> $GITHUB_ENV + echo "TAG_NAME=pre-$FEAT_NAME" >> $GITHUB_ENV + - if: startsWith(github.ref_name, 'release/') + run: | + RC_NAME=$(echo ${{ github.ref_name }} | sed 's/release\///') + echo "FEAT_NAME=" >> $GITHUB_ENV + echo "TAG_NAME=$RC_NAME" >> $GITHUB_ENV + - if: github.ref_name == 'master' + run: | + echo "FEAT_NAME=" >> $GITHUB_ENV + echo "TAG_NAME=prerelease" >> $GITHUB_ENV + - run: npm ci + - name: vsix + run: | + npm run createRelease -w packages/toolkit -w packages/amazonq # Generate CHANGELOG.md + npm run -w packages/toolkit package -- --feature "$FEAT_NAME" + npm run -w packages/amazonq package -- --feature "$FEAT_NAME" + - uses: actions/upload-artifact@v4 + with: + name: artifacts + path: '*.vsix' + retention-days: 10 + - name: Export outputs + id: build + run: | + write_package_info() { + PKG_NAME=$1 + PKG_DISPLAY_NAME=$(grep -m 1 displayName packages/${PKG_NAME}/package.json | grep -o '[a-zA-z][^\"]\+' | tail -n1) + echo "${PKG_NAME}_version=$(grep -m 1 version packages/${PKG_NAME}/package.json | grep -o '[0-9][^\"]\+' | sed 's/-SNAPSHOT//')" >> $GITHUB_OUTPUT + echo "${PKG_NAME}_changes<> $GITHUB_OUTPUT + # Add extension display name to the topmost changelog section. + cat packages/${PKG_NAME}/CHANGELOG.md | perl -ne 'BEGIN{$/="\n\n"} print; exit if $. == 2' | sed -e "1 s/## /## ${PKG_DISPLAY_NAME} - /" >> $GITHUB_OUTPUT + echo 'EOF' >> $GITHUB_OUTPUT + } + echo "feature=$FEAT_NAME" >> $GITHUB_OUTPUT + echo "tagname=$TAG_NAME" >> $GITHUB_OUTPUT + write_package_info toolkit + write_package_info amazonq + + publish: + needs: [package] + runs-on: ubuntu-latest + env: + # + # For `gh`. + # + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FEAT_NAME: ${{ needs.package.outputs.feature }} + TAG_NAME: ${{ needs.package.outputs.tagname }} + AWS_TOOLKIT_VERSION: ${{ needs.package.outputs.toolkit_version }} + AMAZON_Q_VERSION: ${{ needs.package.outputs.amazonq_version }} + # + # Used in release_notes.md + # + BRANCH: ${{ github.ref_name }} + AWS_TOOLKIT_CHANGES: ${{ needs.package.outputs.toolkit_changes }} + AMAZON_Q_CHANGES: ${{ needs.package.outputs.amazonq_changes }} + permissions: + contents: write + steps: + # Must perform checkout first, it deletes the target directory + # before running, thus would delete the downloaded artifacts. + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + - name: Delete existing prerelease + # "prerelease" (main branch), "pre-", or "rc-" + if: env.TAG_NAME == 'prerelease' || startsWith(env.TAG_NAME, 'pre-') || startsWith(env.TAG_NAME, 'rc-') + run: | + if [[ "$TAG_NAME" == rc-* ]]; then + echo "SUBJECT=AWS IDE Extensions Release Candidate: ${TAG_NAME#rc-}" >> $GITHUB_ENV + else + echo "SUBJECT=AWS IDE Extensions: ${FEAT_NAME:-${TAG_NAME}}" >> $GITHUB_ENV + fi + gh release delete "$TAG_NAME" --cleanup-tag --yes || true + # git push origin :"$TAG_NAME" || true + - name: Publish Prerelease + run: | + # AWS_TOOLKIT_CHANGES="$(head -14 CHANGELOG.md)" + envsubst < "$GITHUB_WORKSPACE/.github/workflows/release_notes.md" > "$RUNNER_TEMP/release_notes.md" + gh release create $TAG_NAME --prerelease --notes-file "$RUNNER_TEMP/release_notes.md" --title "$SUBJECT" --target $GITHUB_SHA artifacts/* diff --git a/.github/workflows/release_notes.md b/.github/workflows/release_notes.md new file mode 100644 index 00000000000..8a044acd831 --- /dev/null +++ b/.github/workflows/release_notes.md @@ -0,0 +1,19 @@ +This is an **unsupported preview build** of the `${BRANCH}` branch of AWS IDE Extensions for VSCode. + +# Install + +1. Download the vsix file(s) from "Assets" below. + - Amazon Q $AMAZON_Q_VERSION is provided by `amazon-q-vscode….vsix` + - AWS Toolkit $AWS_TOOLKIT_VERSION is provided by `aws-toolkit-vscode….vsix` +2. Run `Extensions: Install from VSIX...` from the VSCode [command palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) and choose the vsix file(s). + +# Changes + +${AWS_TOOLKIT_CHANGES} + +${AMAZON_Q_CHANGES} + +## Previous changes + +- [AWS Toolkit changelog](/packages/toolkit/CHANGELOG.md) +- [Amazon Q changelog](/packages/amazonq/CHANGELOG.md) diff --git a/.github/workflows/setup-release-candidate.yml b/.github/workflows/setup-release-candidate.yml new file mode 100644 index 00000000000..390669d22af --- /dev/null +++ b/.github/workflows/setup-release-candidate.yml @@ -0,0 +1,56 @@ +name: Setup Release Candidate + +on: + workflow_dispatch: + inputs: + commitId: + description: 'Commit ID to create RC from' + required: true + type: string + +jobs: + setup-rc: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.commitId }} + token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: true + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + + - name: Generate Branch Name + id: branch-name + run: | + echo "BRANCH_NAME=release/rc-$(date +%Y%m%d)" >> $GITHUB_OUTPUT + + - name: Install dependencies + run: npm ci + + - name: Generate license attribution + run: npm run scan-licenses + + - name: Create RC Branch + env: + BRANCH_NAME: ${{ steps.branch-name.outputs.BRANCH_NAME }} + run: | + git config user.name "aws-toolkit-automation" + git config user.email "<>" + + # Create RC branch from specified commit + git checkout -b $BRANCH_NAME + + # Add generated license files + git add LICENSE-THIRD-PARTY + # If there are no changes, then we don't need a new attribution commit + git commit -m "Update third-party license attribution for $BRANCH_NAME" || true + + # Push RC branch + git push origin $BRANCH_NAME diff --git a/.github/workflows/test/test_diff.txt b/.github/workflows/test/test_diff.txt new file mode 100644 index 00000000000..9614e902a5e --- /dev/null +++ b/.github/workflows/test/test_diff.txt @@ -0,0 +1,185 @@ +diff --git a/.github/workflows/copyPasteDetection.yml b/.github/workflows/copyPasteDetection.yml +index 793337de5..746b3cecd 100644 +--- a/.github/workflows/copyPasteDetection.yml ++++ b/.github/workflows/copyPasteDetection.yml +@@ -26,61 +26,47 @@ jobs: + with: + node-version: ${{ matrix.node-version }} + ++ - name: Determine if local ++ run: echo "IS_LOCAL=false" >> $GITHUB_ENV ++ + - name: Fetch fork upstream ++ if: ${{ env.IS_LOCAL == 'false' }} + run: | + git remote add forkUpstream https://github.com/${{ github.event.pull_request.head.repo.full_name }} # URL of the fork + git fetch forkUpstream # Fetch fork + + - name: Determine base and target branches for comparison. + run: | +- echo "CURRENT_BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV +- echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV +- - run: git diff --name-only origin/$TARGET_BRANCH forkUpstream/$CURRENT_BRANCH > diff_output.txt +- - run: | +- npm install -g jscpd ++ if [[ $IS_LOCAL == 'false' ]]; then ++ echo "CURRENT_BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV ++ echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV ++ else ++ echo "CURRENT_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV ++ echo "TARGET_BRANCH=master" >> $GITHUB_ENV ++ fi ++ ++ - name: Print base and target branches for comparison. ++ run: | ++ echo "CURRENT_BRANCH=$CURRENT_BRANCH" ++ echo "TARGET_BRANCH=$TARGET_BRANCH" ++ ++ - name: Compare target and current branches. ++ run: | ++ if [[ $IS_LOCAL == 'false' ]]; then ++ git diff origin/$TARGET_BRANCH forkUpstream/$CURRENT_BRANCH > diff_output.txt ++ else ++ git diff origin/$TARGET_BRANCH $CURRENT_BRANCH > diff_output.txt ++ fi ++ ++ - run: npm install -g jscpd + + - run: jscpd --config "$GITHUB_WORKSPACE/.github/workflows/jscpd.json" + +- - if: always() ++ - if: ${{ env.IS_LOCAL == 'false' }} + uses: actions/upload-artifact@v4 + with: + name: unfiltered-jscpd-report + path: ./jscpd-report.json + +- - name: Filter jscpd report for changed files +- run: | +- if [ ! -f ./jscpd-report.json ]; then +- echo "jscpd-report.json not found" +- exit 1 +- fi +- echo "Filtering jscpd report for changed files..." +- CHANGED_FILES=$(jq -R -s -c 'split("\n")[:-1]' diff_output.txt) +- echo "Changed files: $CHANGED_FILES" +- jq --argjson changed_files "$CHANGED_FILES" ' +- .duplicates | map(select( +- (.firstFile?.name as $fname | $changed_files | any(. == $fname)) or +- (.secondFile?.name as $sname | $changed_files | any(. == $sname)) +- )) +- ' ./jscpd-report.json > filtered-jscpd-report.json +- cat filtered-jscpd-report.json +- + - name: Check for duplicates +- run: | +- if [ $(wc -l < ./filtered-jscpd-report.json) -gt 1 ]; then +- echo "filtered_report_exists=true" >> $GITHUB_ENV +- else +- echo "filtered_report_exists=false" >> $GITHUB_ENV +- fi +- - name: upload filtered report (if applicable) +- if: env.filtered_report_exists == 'true' +- uses: actions/upload-artifact@v4 +- with: +- name: filtered-jscpd-report +- path: ./filtered-jscpd-report.json +- +- - name: Fail and log found duplicates. +- if: env.filtered_report_exists == 'true' +- run: | +- cat ./filtered-jscpd-report.json +- echo "Duplications found, failing the check." +- exit 1 ++ run: node "$GITHUB_WORKSPACE/.github/workflows/filterDuplicates.js" diff_output.txt jscpd-report.json +diff --git a/.github/workflows/filterDuplicates.js b/.github/workflows/filterDuplicates.js +new file mode 100644 +index 000000000..b2f1e913e +--- /dev/null ++++ b/.github/workflows/filterDuplicates.js +@@ -0,0 +1,85 @@ ++const fs = require('fs/promises') ++ ++function parseDiffFilePath(filePathLine) { ++ return filePathLine.split(' ')[2].split('/').slice(1).join('/') ++} ++ ++function parseDiffRange(rangeLine) { ++ const [_fromRange, toRange] = rangeLine.split(' ').slice(1, 3) ++ const [startLine, numLines] = toRange.slice(1).split(',').map(Number) ++ const range = [startLine, startLine + numLines] ++ return range ++} ++ ++async function parseDiff(diffPath) { ++ const diff = await fs.readFile(diffPath, 'utf8') ++ const lines = diff.split('\n') ++ let currentFile = null ++ let currentFileChanges = [] ++ const fileChanges = new Map() ++ ++ for (const line of lines) { ++ if (line.startsWith('diff')) { ++ if (currentFile) { ++ fileChanges.set(currentFile, currentFileChanges) ++ } ++ currentFile = parseDiffFilePath(line) ++ currentFileChanges = [] ++ } ++ if (line.startsWith('@@')) { ++ currentFileChanges.push(parseDiffRange(line)) ++ } ++ } ++ ++ return fileChanges ++} ++ ++function doesOverlap(range1, range2) { ++ const [start1, end1] = range1 ++ const [start2, end2] = range2 ++ return (start1 >= start2 && start1 <= end2) || (end1 >= start2 && end1 <= end2) ++} ++ ++function isCloneInChanges(changes, cloneInstance) { ++ const fileName = cloneInstance.name ++ const cloneStart = cloneInstance.start ++ const cloneEnd = cloneInstance.end ++ const lineChangeRanges = changes.get(fileName) ++ ++ if (!lineChangeRanges) { ++ return false ++ } ++ ++ return lineChangeRanges.some((range) => doesOverlap([cloneStart, cloneEnd], range)) ++} ++ ++function isInChanges(changes, dupe) { ++ return isCloneInChanges(changes, dupe.firstFile) || isCloneInChanges(changes, dupe.secondFile) ++} ++ ++function filterDuplicates(report, changes) { ++ duplicates = [] ++ for (const dupe of report.duplicates) { ++ if (isInChanges(changes, dupe)) { ++ duplicates.push(dupe) ++ } ++ } ++ return duplicates ++} ++ ++async function main() { ++ const rawDiffPath = process.argv[2] ++ const jscpdReportPath = process.argv[3] ++ const changes = await parseDiff(rawDiffPath) ++ const jscpdReport = JSON.parse(await fs.readFile(jscpdReportPath, 'utf8')) ++ const filteredDuplicates = filterDuplicates(jscpdReport, changes) ++ ++ console.log(filteredDuplicates) ++ console.log('%s files changes', changes.size) ++ console.log('%s duplicates found', filteredDuplicates.length) ++ if (filteredDuplicates.length > 0) { ++ process.exit(1) ++ } ++} ++ ++void main() diff --git a/.github/workflows/utils.js b/.github/workflows/utils.js new file mode 100644 index 00000000000..1a0e91ed210 --- /dev/null +++ b/.github/workflows/utils.js @@ -0,0 +1,32 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Create a comment on a PR if one does not already exist + */ +async function dedupComment({ github, pullRequestId, owner, repo, comments, message }) { + if (comments.data.some((comment) => comment.body.includes(message))) { + return + } + + await github.rest.issues.createComment({ + issue_number: pullRequestId, + owner, + repo, + body: message, + }) +} + +/* + * Check if path is included in at least one of the filename paths + */ +function hasPath(filenames, path) { + return filenames.some((file) => file.includes(path)) +} + +module.exports = { + dedupComment, + hasPath, +} diff --git a/.gitignore b/.gitignore index f22af4996bd..fb06d810f42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,38 +1,62 @@ +# Note: also used for `eslint --ignore-path`. + out dist node_modules -.vscode-test/ -.nyc_output/ -coverage/ +.vscode-test +coverage *.vsix +*.zip *.bk **/.DS_Store .idea telemetryCache quickStart*.html -README.quickstart.cloud9.md -README.quickstart.vscode.md .gitcommit -resources/debugger/__pycache__ +__pycache__ + +# Generated if running the `depcruise` command from the documentation example +/dependency-graph.svg # Auto generated definitions -src/**/*.gen.ts -# Telemetry definition for testing adding telemetry -src/shared/telemetry/vscodeTelemetry.json +packages/*/src/**/*.gen.ts +!packages/core/src/**/awsSamDebugConfiguration.gen.ts +!packages/core/src/shared/settings-*.gen.ts +src.gen/* # Test reports -.test-reports/ +.test-reports # Auto generated type definitions -src/shared/telemetry/clienttelemetry.d.ts -src/codewhisperer/client/codewhispererclient.d.ts -src/codewhisperer/client/codewhispereruserclient.d.ts +**/src/shared/telemetry/clienttelemetry.d.ts +**/src/codewhisperer/client/codewhispererclient.d.ts +**/src/codewhisperer/client/codewhispereruserclient.d.ts +**/src/auth/sso/oidcclientpkce.d.ts +**/src/sagemakerunifiedstudio/shared/client/gluecatalogapi.d.ts +**/src/sagemakerunifiedstudio/shared/client/sqlworkbench.d.ts # Generated by tests -src/testFixtures/**/bin -src/testFixtures/**/obj +**/src/testFixtures/**/bin +**/src/testFixtures/**/obj + +# Generated by copyFiles.ts +packages/*/LICENSE +packages/*/NOTICE +packages/toolkit/package.nls.json +packages/toolkit/resources +packages/amazonq/package.nls.json +packages/amazonq/resources # Icons -resources/icons/cloud9/generated/** -resources/fonts/aws-toolkit-icons.woff -resources/css/icons.css +packages/*/resources/fonts/aws-toolkit-icons.woff +packages/*/resources/css/icons.css + +# local configuration +.local.env +*.config.local.json + +# Created by `npm run webRun` when testing extension in web mode +.vscode-test-web + +# License scanning output +licenses-full.json diff --git a/.husky/pre-commit b/.husky/pre-commit index e12da904cc5..465dc0a8ba8 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,3 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - git secrets --register-aws || (echo 'Please install git-secrets https://github.com/awslabs/git-secrets to check for accidentally commited secrets!' && exit 1) git secrets --pre_commit_hook -- "" node_modules/.bin/pretty-quick --staged diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000000..190ede5ed35 --- /dev/null +++ b/.npmrc @@ -0,0 +1,5 @@ +# ensure that the project uses the public npm registry see: https://docs.npmjs.com/cli/v8/configuring-npm/npmrc +registry = "https://registry.npmjs.org/" +# `engine-strict=true` prevents dependabot from working, because _transitive_ deps can set "engines" +# more restrictive than our own package.json. https://github.com/dependabot/dependabot-core/issues/4072 +engine-strict=false diff --git a/.nycrc.json b/.nycrc.json deleted file mode 100644 index a0ea888c8bc..00000000000 --- a/.nycrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "all": true, - "include": ["src/**", "dist/src/**"], - "exclude": ["test/**", "integrationTest/**", "testFixtures/**"] -} diff --git a/.prettierignore b/.prettierignore index c97e66064a4..f3696615413 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,16 @@ out -resources/endpoints.json CHANGELOG.md -src/shared/telemetry/service-2.json +.github/PULL_REQUEST_TEMPLATE.md .changes +*.d.ts +*.gen.ts +dist +src.gen/** + +# Duplicate entries because prettier can be ran from root or within the workspace/subpackage. +# TODO: Avoid this. +src/shared/telemetry/service-2.json src/testFixtures/** + +packages/core/src/shared/telemetry/service-2.json +packages/core/src/testFixtures/** diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4135216c6d0..c5faaa009d6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,3 @@ { - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": ["dbaeumer.vscode-eslint", "eg2.vscode-npm-script"] + "recommendations": ["amodio.tsl-problem-matcher", "dbaeumer.vscode-eslint"] } diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index fa24e09328d..00000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,130 +0,0 @@ -// A launch configuration that compiles the extension and then opens it inside a new window -// Use IntelliSense to learn about possible attributes. -// Hover to view descriptions of existing attributes. -// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Extension", - "type": "extensionHost", - "request": "launch", - "debugWebviews": true, - "rendererDebugOptions": { - "urlFilter": "*amazonwebservices.aws-toolkit-vscode*", - "webRoot": "${workspaceFolder}" - }, - "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceFolder}"], - "env": { - "SSMDOCUMENT_LANGUAGESERVER_PORT": "6010", - "WEBPACK_DEVELOPER_SERVER": "http://localhost:8080" - }, - "outFiles": ["${workspaceFolder}/dist/**/*.js"], - "preLaunchTask": "watch" - }, - { - "name": "Attach to ASL Server", - "type": "node", - "request": "attach", - "port": 6009, - "restart": true, - "outFiles": ["${workspaceRoot}/dist/src/stepFunctions/asl/**.js"] - }, - { - "name": "Attach to SSM Document Language Server", - "type": "node", - "request": "attach", - "port": 6010, - "restart": true, - "outFiles": ["${workspaceRoot}/dist/src/ssmDocument/ssm/ssmServer.js"] - }, - { - "name": "Extension (webpack)", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceFolder}"], - "outFiles": ["${workspaceFolder}/dist/**/*.js"], - "preLaunchTask": "npm: compile" - }, - { - "name": "Extension Tests", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": [ - "--disable-extensions", - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/dist/src/test/index", - "${workspaceRoot}/dist/src/testFixtures/workspaceFolder" - ], - "env": { - "DEVELOPMENT_PATH": "${workspaceFolder}", - "AWS_TOOLKIT_AUTOMATION": "local" - }, - "outFiles": ["${workspaceFolder}/dist/**/*.js"], - "preLaunchTask": "${defaultBuildTask}" - }, - { - "name": "Extension Tests (current file)", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": [ - "--disable-extensions", - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/dist/src/test/index.js", - "${workspaceRoot}/dist/src/testFixtures/workspaceFolder" - ], - "env": { - "TEST_FILE": "${relativeFile}", - "DEVELOPMENT_PATH": "${workspaceFolder}", - "AWS_TOOLKIT_AUTOMATION": "local" - }, - "outFiles": ["${workspaceFolder}/dist/**/*.js"], - "preLaunchTask": "${defaultBuildTask}" - }, - { - "name": "Integration Tests", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": [ - "${workspaceFolder}/dist/src/testFixtures/workspaceFolder", - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/dist/src/integrationTest/index.js" - ], - "env": { - "DEVELOPMENT_PATH": "${workspaceFolder}", - "AWS_TOOLKIT_AUTOMATION": "local" - }, - "outFiles": ["${workspaceFolder}/dist/**/*.js"], - "preLaunchTask": "${defaultBuildTask}" - }, - { - "name": "Integration Tests (current file)", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": [ - "${workspaceFolder}/dist/src/testFixtures/workspaceFolder", - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/dist/src/integrationTest/index.js" - ], - "env": { - "TEST_FILE": "${relativeFile}", - "DEVELOPMENT_PATH": "${workspaceFolder}", - "AWS_TOOLKIT_AUTOMATION": "local" - }, - "outFiles": ["${workspaceFolder}/dist/**/*.js"], - "preLaunchTask": "${defaultBuildTask}" - } - ], - "compounds": [ - { - "name": "Extension + Attach to SSM Document Language Server", - "configurations": ["Extension", "Attach to SSM Document Language Server"] - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json index d5240393191..f954c48cf36 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,6 @@ }, "typescript.preferences.importModuleSpecifier": "relative", "javascript.preferences.importModuleSpecifier": "relative", - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "npm.packageManager": "npm" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 1ffa27b1082..00000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,76 +0,0 @@ -// See https://go.microsoft.com/fwlink/?LinkId=733558 -// for the documentation about the tasks.json format -{ - "version": "2.0.0", - "tasks": [ - { - "label": "watch", - "type": "npm", - "script": "watch", - "problemMatcher": "$tsc-watch", - "isBackground": true, - "group": { - "kind": "build", - "isDefault": true - }, - "dependsOn": ["serve"] - }, - { - "label": "serve", - "type": "npm", - "script": "serve", - "group": "build", - "isBackground": true, - "problemMatcher": { - "owner": "custom", - "pattern": { - "regexp": ".", - "file": 1, - "location": 2, - "message": 3 - }, - "background": { - "activeOnStart": true, - "beginsPattern": "Project is running at", - "endsPattern": "compiled successfully" - } - } - }, - { - "type": "npm", - "script": "lint", - "problemMatcher": "$eslint-stylish" - }, - { - "type": "npm", - "script": "lintfix", - "problemMatcher": "$eslint-stylish" - }, - { - "type": "npm", - "script": "compile", - "dependsOn": ["Kill Tasks"], - "problemMatcher": "$tsc" - }, - { - "type": "npm", - "script": "clean", - "dependsOn": ["Kill Tasks"], - "problemMatcher": [] - }, - { - "label": "Kill Tasks", - "type": "process", - "command": "${input:killTasks}", - "problemMatcher": [] - } - ], - "inputs": [ - { - "id": "killTasks", - "type": "command", - "command": "workbench.action.tasks.terminate", - "args": "terminateAll" - } - ] -} diff --git a/.vscodeignore b/.vscodeignore deleted file mode 100644 index 86b0cff0376..00000000000 --- a/.vscodeignore +++ /dev/null @@ -1,20 +0,0 @@ -# Ignore everything by default. #1899 -* -*/** - -# Allowlist -!dist/* -!dist/libs -!dist/*/!(testFixtures|test)/**/!(*.*.map) -!dist/*/!(*.*.map) -!resources/** -!syntaxes/** -!templates/** -!types/** -!LICENSE -!NOTICE -!package.json -!package.nls.json -!quickStart** -!README.** -!CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index eb12dc02d35..00000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,534 +0,0 @@ -## 1.62.0 2023-01-31 - -- **Bug Fix** SAM template.yaml syntax support for PayloadFormatVersion, HttpApi event source, HttpApiFunctionAuth #2867 -- **Bug Fix** iam: ignore permission check when denial comes from organization csp policy -- **Bug Fix** Improve extension start-up performance, especially on devices with slower file systems -- **Bug Fix** Duplicate resource type definitions break the "Resources" node (#3132) -- **Bug Fix** SAM template.yaml syntax support for FunctionResponseTypes #2924 -- **Feature** SAM: run/debug now works for a larger variety of TypeScript projects -- **Feature** Upload Lambda wizard will skip prompts and auto select parent directory when invoked from a template file. - -## 1.61.0 2023-01-12 - -- **Bug Fix** AWS regions are not dynamically fetched by the Toolkit -- **Bug Fix** S3: saving files from the editor overwrites existing content types -- **Bug Fix** S3: file editor fails on binary files with no file extension -- **Feature** CodeWhisperer: more responsive Auto-Suggestions -- **Feature** Copy Lambda Function URL in AWS Explorer - -## 1.60.0 2022-12-16 - -- **Bug Fix** CodeWhisperer: fix potential undefined reference - -## 1.59.0 2022-12-15 - -- **Bug Fix** Help button on the "save connection" prompt does nothing -- **Bug Fix** CodeWhisperer may alter editor.hover.enabled configuration -- **Feature** CodeWhisperer: Add 'Do not show again' button to authentication migration prompt - -## 1.58.0 2022-12-06 - -- **Feature** S3: "Download As" action defaults to the last-used download path. -- **Feature** Amazon CodeCatalyst: faster listing of repositories and Dev Environments -- **Feature** Cloud9: browse and generate code for EventBridge Schemas from AWS Explorer -- **Feature** Amazon CodeCatalyst: Connecting to AWS Builder ID when coming from the browser has been made easier - -## 1.57.0 2022-12-01 - -- **Feature** Amazon CodeCatalyst: Connect VS Code to your remote Dev Environments. -- **Feature** Amazon CodeCatalyst: Clone your repositories to your local workspace. -- **Feature** Amazon CodeCatalyst: Connect using your AWS Builder ID. - -## 1.56.0 2022-11-28 - -- **Feature** Amazon CodeWhisperer now adds new access methods with AWS Builder ID and AWS IAM Identity Center to enable and get started. -- **Feature** Amazon CodeWhisperer recommendations are more context aware. We are removing the overlaps from CodeWhisperer suggestions specifically when the cursor is inside a code block. -- **Feature** Amazon CodeWhisperer now supports TypeScript and C# programming languages. -- **Feature** Amazon CodeWhisperer is now available as a supported feature and no longer an experimental feature. -- **Feature** Amazon CodeWhisperer now supports JavaScript for Security Scan to catch security vulnerabilities. - -## 1.55.0 2022-11-23 - -- **Bug Fix** logging: `aws.viewLogsAtMessage` no longer fails when the log message cannot be found -- **Feature** SAM: the `Sync SAM Application` command remembers your most recent selections per-region. -- **Feature** SAM: deployment of CloudFormation templates now uses `sam sync` by default, reducing the amount of time it takes to deploy to AWS. The `aws.samcli.legacyDeploy` setting can be used to revert to the old experience. - -## 1.54.0 2022-11-19 - -- **Bug Fix** JSON-schemas download logic is brittle #2957 -- **Bug Fix** "Connect" and "Choose Profile" open create credentials wizard/profile selector when credentials aren't present in aws configs -- **Feature** SAM: create, run and debug nodejs18.x Lambdas -- **Feature** New credentials experience. Includes the ability to set up SSO ("IAM Identity Center") from AWS Toolkit (`AWS: Connect to AWS` > `Add New Connection`) - -## 1.53.0 2022-10-24 - -- **Bug Fix** Execessive CodeWhisperer latency caused by unnecessary refreshing of credentials -- **Bug Fix** CodeWhisperer auto trigger not working correctly in certain circumstances - -## 1.52.0 2022-10-20 - -- **Bug Fix** CodeWhisperer left/right arrow keybinding conflict with other plugins -- **Feature** Enable syntax features for untitled (unsaved) cloudformation/sam yaml files - -## 1.51.0 2022-10-01 - -- **Bug Fix** styling issue in CodeWhisperer reference log -- **Bug Fix** CodeWhisperer duplicate closing bracket when using recommendation inside brackets -- **Bug Fix** CodeWhisperer triggered by React plugin generated code snippets -- **Bug Fix** cached ECS/EC2 credentials not refreshed when expired -- **Bug Fix** malformed ECS icons on recent versions of VS Code -- **Bug Fix** CodeWhisperer sometimes edits user's code -- **Feature** running commands on ECS tasks can now be cancelled -- **Feature** "AWS (Developer)" menu now includes a "Show Global State" item -- **Feature** Resources (in AWS Explorer) can list more resource types for EC2, IoT, RDS, Redshift, NetworkManager, and other services -- **Feature** Prompt users to install YAML plugin when AWSTemplateFormatVersion becomes available in their YAML document -- **Feature** ECS tasks can be opened up in the terminal -- **Feature** Improved CodeWhisperer inline suggestion experience - -## 1.50.0 2022-09-13 - -- **Bug Fix** Debugging Python image-based lambdas fails with `Init failed error=exec: "-m": executable file not found` -- **Feature** if "Create Credentials Profile" fails, error message now shows "Edit Credentials" button -- **Feature** Decrease the amount of space SAM/CFN schemas take on the status bar -- **Feature** CodeWhisperer now supports .jsx files - -## 1.49.0 2022-08-29 - -- **Bug Fix** CodeWhisperer adds redundant closing brackets/parens/braces -- **Feature** Don't open webviews in a split window (except SAM debug configuration editor). -- **Removal** Python 3.6 is no longer supported - -## 1.48.0 2022-08-15 - -- **Bug Fix** "API proposal" error on vscode 1.63 or older - -## 1.47.0 2022-08-06 - -- **Bug Fix** Delve debugger installation fails for Go Lambdas on MacOS -- **Bug Fix** restored `Connect to AWS` command (alias to the `Choose AWS Profile` command) -- **Bug Fix** CodeWhisperer: when pressing up or down does not move cursor when suggestion is active -- **Bug Fix** CodeWhisperer access token validation may sometimes fail -- **Bug Fix** "Connect to AWS" does not handle missing credentials file -- **Feature** basic syntax highlighting for ~/.aws/credentials and ~/.aws/config files -- **Feature** `Choose AWS Profile` quickpick now includes an `Edit Credentials` action -- **Feature** Add `Edit Credentials` command which opens all known credential files -- **Feature** CodeWhisperer: References include a link to the source repository if possible. -- **Feature** Auto-connect tries a maximum of one non-default profile instead of three - -## 1.46.0 2022-07-25 - -- **Bug Fix** "Read-only file system" error when deploying or debugging a SAM project #2395 - -## 1.45.0 2022-07-07 - -- **Bug Fix** `Add SAM Debug Configuration` codelenses in source files are now disabled by default. (To enable the codelenses, use the `AWS: Toggle SAM hints in source files` command or the `Enable SAM hints` setting.) -- **Bug Fix** CodeWhisperer features were not fully disabled if the experiment was disabled -- **Bug Fix** 'security token expired' errors when using CodeWhisperer in Cloud9 with managed credentials - -## 1.44.0 2022-06-30 - -- **Bug Fix** Fixed a bug where CodeWhisperer incorrectly changes the `snippetSuggestions` setting in VSCode - -## 1.42.0 2022-06-23 - -- **Feature** [CodeWhisperer](https://aws.amazon.com/codewhisperer) uses machine learning to generate code suggestions from the existing code and comments in your IDE. Supported languages include: Java, Python, and JavaScript. - -## 1.41.0 2022-06-22 - -- **Bug Fix** Credentials are now automatically refreshed on certain AWS API errors - -## 1.40.0 2022-06-17 - -- **Bug Fix** The `Upload Lambda` command now automatically causes the Toolkit to login -- **Bug Fix** S3 "Presigned URL" feature may silently fail #2687 -- **Bug Fix** Fix issue where some http errors were logging undefined on error -- **Feature** UI: buttons for Lambda invoke, API invoke, and other forms are now always visible at the top of the form - -## 1.39.0 2022-06-06 - -- **Bug Fix** StepFunctions: allow state machines with non-object values for Parameters property -- **Bug Fix** Fix incorrect use of Markdown in new SAM application READMEs -- **Bug Fix** Fix: syntax support (JSON schemas) for CloudFormation/SAM yaml sometimes doesn't work -- **Bug Fix** Credential profiles that do not require user-input now correctly refresh when expired -- **Feature** "Upload Lambda" from any folder in VS Code File Explorer -- **Feature** "Send Feedback" form always enables Send button and doesn't require text input -- **Feature** SAM: create, run and debug nodejs16.x Lambdas -- **Feature** CDK features are now found in the _Developer Tools_ view. This view will contain more ways to work with local project resources in future releases. - -## 1.38.0 2022-05-12 - -- **Breaking Change** Removed the `aws.onDefaultRegionMissing` setting -- **Bug Fix** SAM template.yaml syntax support for condition, requestmodel, requestparameters, custom resources, SSMParameterReadPolicy and AWSSecretsManagerGetSecretValuePolicy -- **Bug Fix** Fixed issue with API Gateway 'Invoke on AWS' resource names always showing 'resource.path' -- **Bug Fix** S3 File Viewer: file paths containing reserved URI characters are now handled correctly -- **Feature** `Edit SAM Debug Configuration` and `View Toolkit Logs` commands are now shown in the AWS Explorer main menu -- **Feature** Lambda: Use Upload Lambda from a folder, template file, or the command palette. -- **Feature** renamed `SAM Debug Configuration Editor` command to `Edit SAM Debug Configuration` -- **Feature** SAM run/debug now uses the current active credentials (if the `aws.credentials` launch-config field is not set) -- **Feature** Show "Add Debug Config" codelens in JS/TS files even if package.json is missing #2579 -- **Feature** "Show or Hide Regions" is now one command that supports multiple selections -- **Feature** added `Show AWS Commands...` command to AWS Explorer menu -- **Feature** Open schemas directly when clicking on schema item nodes - -## 1.37.0 2022-03-25 - -- **Bug Fix** SAM run/debug: slow debugger attachment for API configurations now shows a cancellable notification instead of automatically failing -- **Feature** "Adding Go (Golang) as a supported language for code binding generation through the EventBridge Schemas service" - -## 1.36.0 2022-03-03 - -- **Bug Fix** Step Functions: fixed issue with opening a graph render from the editor -- **Bug Fix** ECS: avoid null reference error when loading nodes #2441 -- **Feature** SAM/Lambda: add support for .NET 6 (dotnet6) -- **Feature** ECS: Container node displays when there is no running containers -- **Feature** Cloud9: user can choose which Resources to show -- **Feature** ECS: Toolkit checks if the current credentials have permissions for "Run Command in Container" - -## 1.35.0 2022-01-13 - -- **Bug Fix** Schemas: handle errors for invalid settings configurations on activation -- **Bug Fix** Logging: Toolkit logs are now cleaned up automatically by the extension to prevent excessive storage consumption -- **Bug Fix** fix newline in 'About Toolkit' command -- **Bug Fix** S3: fix 'Upload to Parent...' command -- **Bug Fix** Credentials: profile field names no longer need to be all lowercase -- **Feature** ECS Exec: only show tasks that have the ECS Exec agent. #2416 -- **Feature** IoT: add attached policies in Things subtree - -## 1.34.0 2021-11-22 - -- **Bug Fix** Improve auto-connect reliability -- **Bug Fix** Toolkit appeared stuck at "Connecting..." -- **Bug Fix** CDK: fixed performance issue with project detection. Detection is no longer limited to a depth of two directories. -- **Feature** S3: Upload now supports multiple files -- **Feature** Add AWS IoT Explorer -- **Feature** AWS Explorer shows ECS resources and supports "Run command in container" - -## 1.33.0 2021-11-04 - -- **Breaking Change** Bumping VS Code minimum version: 1.42.0 => 1.44.2 -- **Bug Fix** Credentials: Handle case when running in an ECS container but no credentials are available. -- **Bug Fix** Resources: Avoid redundant close of active editor when resource documents are explicitly closed - -## 1.32.0 2021-10-21 - -- **Breaking Change** DEPRECATION: SAM actions using `dotnetcore2.1` runtime (Phase 2 Lambda deprecation) -- **Bug Fix** Resources: Better handling of unsupported resource actions -- **Bug Fix** Resources: Filter S3 bucket resources by region in list view -- **Bug Fix** Resources: Exclude resource types that do not support LIST in Cloud9 -- **Bug Fix** Resources: Handle undefined type schema properties - -## 1.31.0 2021-10-14 - -- **Bug Fix** StepFunctions: "Publish" command now asks for target region -- **Feature** SAM "Edit Debug Configuration" UI is no longer "Beta" -- **Feature** Surface read-only support for hundreds of resources under the Resources node in the AWS Explorer -- **Feature** Create new Step Functions state machine template from AWS Explorer - -## 1.30.0 2021-10-04 - -- **Breaking Change** DEPRECATION: SAM actions using `python2.7` and `nodejs10.x` runtimes (Phase 2 Lambda deprecation) -- **Breaking Change** SAM run/debug: The launch configuration options 'timeoutSec' and 'memoryMb' no longer apply to template or API targets -- **Bug Fix** SAM/nodejs: do not check for "tsc" (typescript compiler) for plain javascript projects -- **Bug Fix** SAM Debug Configuration Editor: All fields will persist after Save or Invoke action. -- **Bug Fix** SAM: refresh timeout timer during build step -- **Bug Fix** SAM/typescript: fix run/debug for target=code if source dir name has special chars -- **Bug Fix** App Runner: fix issue with create wizard failing at the end after backing out of the select file dialog -- **Feature** SAM: `arm64` support (through SAM CLI's Architectures property) on select runtimes -- **Feature** Step Functions: Adds ability to render state machine graph from AWS Explorer. -- **Feature** Credentials: SSO profiles will reconnect on startup if the token is still valid -- **Feature** SAM run/debug: You can now change where SAM CLI builds your lambda by adding a 'buildDir' field to your launch configuration's "sam" section -- **Feature** UI: Toolkit statusbar now shows on the left and takes less space -- **Feature** Step Functions: enable visualizing ASL files in YAML (not asl-yaml) mode. -- **Feature** SAM/CFN schema support: avoid changes to user settings, improve reliability. -- **Feature** Adds Command Palette and CDK Explorer tree visualization capability for Step Functions state machines defined using the AWS CDK -- **Feature** SAM/typescript: search node_modules in the workspace for "tsc" typescript compiler -- **Feature** SAM Debug Configuration Editor: The UI has been updated to more closely match VS Code's style - -## 1.29.0 2021-08-19 - -- **Bug Fix** SAM: the Toolkit now correctly skips sending '--env-vars' when no environment variables are present in the launch config -- **Bug Fix** Credentials: correctly handle different `source_profile` combinations -- **Bug Fix** SAM/CFN: fix JSON schema path causing a symlink on some operating systems -- **Bug Fix** SAM/CFN JSON schema: schemas now load correctly on first-load -- **Bug Fix** SAM/CFN JSON schema: write yaml.customTags to user-global settings (instead of workspace-local) -- **Feature** SAM Application support for the python3.9 runtime - -## 1.28.0 2021-08-09 - -- **Bug Fix** SAM Invoke Webview: fix 'Show All Fields' causing a blank page -- **Bug Fix** CloudWatch Logs: show a placeholder message instead of an error when no logs exist -- **Feature** Credentials: support for ECS container provided credentials -- **Feature** Credentials: Improved guidance during new credential profile creation -- **Feature** Added Getting Started walkthroughs to aid in configuring the Toolkit -- **Feature** S3: new command/action: 'Generate Presigned URL' -- **Feature** App Runner: You can now create, pause, resume, deploy, and delete App Runner services within the AWS explorer. -- **Feature** New SAM applications come with toolkit-specific instructions -- **Feature** Apply JSON schemas to Cloudformation and SAM templates (must be named `template.yaml`) - -## 1.27.0 2021-07-01 - -- **Bug Fix** SAM: fixed issue with downloading deployed lambdas -- **Bug Fix** Credentials: Validate attached IAM role when deciding if EC2 instance credentials are available - -## 1.26.0 2021-06-30 - -- **Bug Fix** S3: improved performance in private VPC (via getBucketLocation) -- **Bug Fix** Add new setting `aws.samcli.lambda.timeout` and remove `aws.samcli.debug.attach.timeout.millis` setting. The new setting sets the maximum time to wait for a local Lambda to start. -- **Bug Fix** CloudWatch Logs: timestamps were incorrectly shown in 12 hour notation instead of 24 hour notation -- **Bug Fix** Settings: write 'recently used buckets' setting as JSON object -- **Feature** Renamed "Import Lambda" -> "Download Lambda" for clarity -- **Feature** New command: `AWS: Upload current file to S3` -- **Feature** File Explorer: "Deploy SAM Application" is available from the context-menu for template.yaml files #263 -- **Feature** SAM run/debug: support TypeScript SAM Lambda projects #1845 -- **Feature** credentials: support for credentials provided by EC2 instance metadata and environment variables - -## 1.25.0 2021-05-10 - -- **Bug Fix** Credentials: cannot access 'canAutoConnect' of undefined -- **Feature** UX: Add progress notification when connecting to AWS -- **Feature** SAM run/debug: fail early so that build/invoke errors are more obvious #1689 -- **Feature** CDK: search for CDK projects up to 2 levels deep (previously 1) -- **Feature** CDK: menu includes standard items if AWS view is hidden -- **Feature** Skip auto-connect until AWS Explorer is shown #1433 -- **Feature** Toggle CodeLenses via "AWS: Toggle SAM hints in source files" command -- **Feature** SAM run/debug: Add support for Go 1.x -- **Feature** UX: write logs to extension's 'globalStoragePath' for all operating systems #1692 -- **Feature** Create aws-sam debug configurations via Command Palette using the `AWS: Add SAM Debug Configuration` command -- **Removal** Settings: remove "Enable CDK Explorer" option (VSCode has built-in support for showing/hiding panels already) - -## 1.24.0 2021-04-22 - -- **Bug Fix** SAM Python debugging: restore retry to ensure successful attach #1666 -- **Feature** SAM run/debug: Add support for Java 8, Java 8.al2, and Java 11 runtimes using Maven and Gradle -- **Feature** CDK panel now appears below the AWS Explorer instead of the VSCode File Explorer -- **Feature** UI: Refresh AWS Explorer after performing "Deploy SAM application" - -## 1.23.0 2021-04-16 - -- **Feature** Beta UI for editing and directly invoking AWS SAM debug configurations -- **Feature** AWS Explorer: clicking "Failed to load" node navigates to failure details #1569 -- **Feature** SAM Deploy wizard: show recently-used S3 buckets; ability to input S3 bucket name manually #1527 -- **Feature** SAM run/debug: Add support for Java 8, Java 8.al2, and Java 11 runtimes using Maven and Gradle -- **Feature** Step Functions: adds ability to publish/update state machine from ASL YAML files. -- **Feature** SAM run/debug: improve display of partial lines #1581 -- **Feature** "Create Lambda SAM Application": navigate to README.md instead of template.yaml #1574 - -## 1.22.0 2021-03-19 - -- **Bug Fix** fix unwanted "invalid SAM CLI" error on startup -- **Feature** StepFunctions: show "View Logs" button on failure - -## 1.21.0 2021-03-17 - -- **Breaking Change** SAM debug: remove nodejs8.10 support -- **Bug Fix** Toolkit correctly handles failures when importing Lambdas for supported language families that have not been added explicitly as importable -- **Bug Fix** Launch configurations created by the Toolkit use correct relative paths -- **Feature** Support ${workspaceFolder} in aws-sam debug configs -- **Feature** Renaming "Create new SAM Application" to "Create Lambda SAM Application" to make it clear that this is an entrypoint for creating a Lambda function -- **Feature** SAM deploy wizard: optionally create a new S3 bucket - -## 1.20.0 2021-02-04 - -- **Feature** SAM templates handle Global values correctly when Resource-level fields are missing. -- **Feature** Support for SAM CLI 1.17: SAM create/run nodejs14.x - -## 1.19.0 2021-02-01 - -- **Bug Fix** Schemas: download failure would not trigger code generation under certain conditions -- **Feature** "Create new SAM Application" command suggests a more-intuitive name -- **Feature** Support for SAM CLI 1.16: SAM create/run dotnet5.0 -- **Feature** List API Gateway names with their ID (so Toolkit can list APIs with identical names) -- **Feature** Improved validation when searching for SAM CLI #1465 - -## 1.18.0 2021-01-07 - -- **Bug Fix** WatchedFiles improvements (Windows) #1416 -- **Bug Fix** SAM debug: Fix deployment for image based lambdas in sam cli 1.14+ #1448 -- **Bug Fix** SAM debug: fix payload JSON validation #1440 -- **Feature** Adds ASL YAML linting and visualization support. -- **Feature** SAM debug: use debugpy instead of ptvsd #1365 -- **Feature** SAM debug: Ignore build failures and attempt to continue invoke/deploy -- **Feature** SAM debug: detect & surface "low disk space" -- **Feature** Adds Amazon States Language (YAML) format to the ASL Language Server. Adds option to choose YAML format when creating new Step Functions state machine from a template. - -## 1.17.0 2020-12-11 - -- **Bug Fix** Automatically add runtime to the autogenerated launch configuration for Image-based Lambdas -- **Feature** API Gateway support: debug local SAM resources, list and run remote resources - -## 1.16.0 2020-12-01 - -- **Bug Fix** Fix showing templates from .aws-sam in Sam Deploy (#1380) -- **Bug Fix** Fix creating S3 buckets in us-east-1 -- **Bug Fix** Retain view state for Step Functions and Lambda when changing tabs -- **Feature** Container Image Support in Lambda -- **Feature** Add an explorer node for managing ECR repositories -- **Feature** NodeJS and Python Lambda functions can be imported from an AWS account into a local workspace -- **Feature** Lambda functions can be updated with code from ZIP files and directories containing built or unbuilt code -- **Feature** Codelenses in source files can create launch configurations that reference template.yaml resources. -- **Feature** "Create new SAM Application" action is available from the context menu of Lambda nodes in the AWS Explorer -- **Feature** The amount of CloudWatch Logs entries retrieved per request is now configurable. -- **Feature** "Deploy SAM Application" action is available from the context menu of Lambda, CloudFormation, and Region nodes in the AWS Explorer - -## 1.15.0 2020-10-06 - -- **Bug Fix** Fix issues which prevented SAM debugging in WSL #1300 -- **Feature** Add support for debugging dotnet 3.1 local lambdas (requires minimum SAM CLI version of 1.4.0) -- **Feature** Add Arn and Region to Lambda invoke view - -## 1.14.0 2020-09-30 - -- **Bug Fix** Fix ASL validation bug marking states as unreachable when defined before a Choice state -- **Feature** Add AWS Systems Manager integration to allow users to view, create and publish Automation documents. Support for code completion and validation with templates and code snippets to help users author their Automation documents. -- **Feature** When deploying a SAM application, the S3 bucket is now chosen from a list. Previously, the bucket name had to be typed in. - -## 1.13.0 2020-08-24 - -- **Feature** Toolkit automatically adds a launch configuration to the workspace when creating SAM applications -- **Feature** CloudWatch Logs functionality -- **Feature** Amazon States Language Server: Add validation for new ASL specification released on August 11. - -## 1.12.0 2020-07-30 - -- **Feature** A new experience for locally Running/Debugging Lambdas with SAM that uses VS Code launch configurations (PR #1215) -- **Feature** SAM Apps that are in SAM Templates are now run/debugged through the Run panel via `aws-sam` Launch Configurations. -- **Feature** Add S3 integration to allow users to create buckets, list buckets, list files and folders, upload files, download files, delete files, delete buckets, and more! - -## 1.11.0 2020-07-18 - -- **Breaking Change** Bumped minimum (inclusive) supported SAM CLI version from 0.38.0 to 0.47.0. -- **Bug Fix** Amazon States Language Server: Replaces "True" strings of End with boolean in snippets. -- **Bug Fix** Makes the ItemsPath property of Map state optional in ASL linter. -- **Bug Fix** Amazon States Language Server: Adds validation of next property for Catch of Map state. -- **Bug Fix** Amazon States Language Server: Adds missing "Comment" property for ChoiceRules, Catcher and Retrier. -- **Feature** Amazon States Language Server: Adds validation of JSON Paths within Parameters. -- **Feature** Added `dotnetcore3.1` app creation and local run support. Local debug is not currently supported. -- **Feature** support SAM CLI version 1.x - -## 1.10.0 2020-05-27 - -- **Feature** Add basic visualisation capability for step function state machines defined in YAML. -- **Feature** Step Functions Linter: Resource property of Task state will accept any string instead of just arn. Additional disallowed properties will be marked as invalid. -- **Feature** If a file conflict is detected when downloading event schemas code bindings, a confirmation prompt is shown - -## 1.9.0 2020-04-29 - -- **Breaking Change** Bumping VS Code minimum version: 1.31.1 => 1.42.0 -- **Bug Fix** Bug fixes for step functions language server.: One is related to the error when there is "Default" property missing on "Choice" state. Second, “Unreachable state” error when the default state is declared before being referenced by “Choice” state. -- **Bug Fix** Fixed a validation issue with VS Code's `settings.json` and `launch.json` files (#1027) -- **Feature** Add context menu command to copy ARNs from the AWS Explorer -- **Feature** Bumped maximum (exclusive) supported SAM CLI version from 0.50.0 to 0.60.0. -- **Feature** Users are shown a notification reflecting changes to how usage data is gathered. Usage data can still be configured through the editor's settings. -- **Feature** Visualising of step functions step machines will be allowed when ARN strings within ASL definition are invalid. - -## 1.8.0 2020-03-31 - -- **Bug Fix** SAM applications deployed through the toolkit now support IAM resources with custom names -- **Bug Fix** Fix issue where CodeLenses appeared on wrong lines in .js files when adding or removing lines -- **Feature** Toolkit dynamically chooses an available port when debugging SAM applications, starting at port 5858 and counting upwards until one is found -- **Feature** Rebranding the toolkit as the "AWS Toolkit" -- **Feature** New Step Function capabilities: Step Function state machine resources are now shown in the AWS Explorer. Language support (auto-completion, validation) for authoring state machine files. State machines can be created from starting templates. State machines can be downloaded from, published to, and executed within an account. - -## 1.7.0 2020-02-18 - -- **Feature** The Toolkit now supports China and GovCloud regions. If you have a shared credentials profile based in one of these regions, you can add a "region" property to that profile, and the Toolkit will know to use a different region set. -- **Feature** Added the 'About AWS Toolkit' command and menu option to show AWS Toolkit versioning details that are useful to include with bug reports. - -## 1.6.1 2020-02-10 - -- **Bug Fix** Fixed an issue related to toolkit metrics - -## 1.6.0 2020-02-06 - -- **Breaking Change** Minimum version of SAM CLI has been adjusted from 0.32.0 to 0.38.0 to accommodate new SAM application support for EventBridge Schemas -- **Bug Fix** AWS Explorer no longer shows service nodes under regions where the service is not available (#850) -- **Bug Fix** Fixed an issue where invalid credentials were reused until VS Code was closed and re-opened, even if the credentials source was updated. It is no longer necessary to restart VS Code. (#705) -- **Feature** The MFA prompt now shows which MFA Device a code is being asked for. -- **Feature** When credentials are invalid a notification is shown. To help diagnose these situations, a button was added to the notification that can open the logs. -- **Feature** Removed the ability to create node.js 8.10 SAM Applications. This runtime has been deprecated. See https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html for more information. -- **Feature** When changes are made to Shared Credentials files, they will be picked up by the Toolkit the next time credentials are selected during the 'Connect to AWS' command. -- **Feature** Added support to locally run SAM applications in containers. -- **Feature** AWS Explorer now sorts region nodes by the region name -- **Feature** Credentials were previously shown by their Shared Credentials profile names. They are now displayed in a "type:name" format, to better indicate the type of Credentials being used, and to support additional Credentials types in the future. Shared Credentials are shown with the type "profile". -- **Feature** Added the ability to create new Serverless Applications with EventBridge Schemas support. - -## 1.5.0 2020-01-06 - -- **Breaking Change** Minimum version of SAM CLI has been adjusted from 0.16.0 to 0.32.0 to accommodate new runtime support -- **Feature** Bumped maximum (exclusive) supported SAM CLI version from 0.40.0 to 0.50.0. -- **Feature** SAM Application support for the python3.8 runtime -- **Feature** Reduced plugin size and startup time significantly -- **Feature** SAM Application support for the nodejs12.x runtime -- **Feature** The StatusBar item displaying the current credentials used by the toolkit now shows when no credentials are being used. It can also be clicked to change the Toolkit's active credentials. -- **Feature** The Toolkit now applies configuration changes to the log level when it changes instead of the next time the toolkit is started (#860) -- **Feature** The folder depth within a workspace that SAM Template files are searched for is now configurable. Previously, this was fixed at 2. - -## 1.4.0 2019-12-02 - -- **Feature** Added support for Amazon EventBridge schema registry, making it easy to discover and write code for events in EventBridge - -## 1.3.0 2019-11-22 - -- **Bug Fix** AWS Explorer now shows a node indicating when CloudFormation Stacks cannot be found in a region -- **Bug Fix** AWS Explorer now sorts the resources that belong to each CloudFormation Stack -- **Bug Fix** AWS Explorer now shows a node indicating when Lambda Functions cannot be found in a region -- **Feature** CDK projects can now be visualized with the CDK Explorer - -## 1.2.0 2019-10-17 - -- **Bug Fix** Add '--no-interactive' flag to 'sam init' calls when SAM CLI version is greater than or equal to 0.30.0 -- **Feature** Added docker network option support for invoking sam applications -- **Feature** Ansi codes are removed from text shown in the Output tab when Locally Invoking Lambda handlers -- **Feature** Adding support for SAM CLI 0.30.0 features in `sam init`: --app-template and --dependency-manager -- **Feature** Bumped maximum (exclusive) supported SAM CLI version from 0.30.0 to 0.40.0. - -## 1.1.0 2019-09-20 - -- **Bug Fix** Creating SAM Applications into a different folder than the current VS Code workspaces will now open an application file after app creation (#678) -- **Feature** Support credential_process (#317) -- **Feature** Improved the description of the selection item when picking a location for a new SAM Application (#673, #675) -- **Feature** Added JSON validation for ECS task definition intellisense -- **Feature** Bumped maximum (exclusive) supported SAM CLI version from 0.23.0 to 0.30.0. - -## 1.0.0 - -* A toast greets the user upon launching a new version of the toolkit for the first time which provides a link to a quick start page. This quick start page can be re-accessed through the explorer's context menu. (#610-612) -* Local Run/Debug now honors MemorySize values from SAM Template file (#509) -* Local Run/Debug now honors Timeout values from SAM Template file (#510) -* Local Run/Debug now honors the Globals section from SAM Template file -* Fixed issue preventing users from connecting with assumed roles (#620) -* Added ability to report an issue from the AWS Explorer menu (#613) -* Added SAM Application-related commands to the AWS Explorer menu -* Removed support for nodejs6.10 SAM Applications -* Regions that are not in the standard AWS partition have been removed from the UI until proper partition support can be added - -## 0.2.1 (Developer Preview) - -* Fixed issue preventing users from connecting with assumed roles (#620) - -## 0.2.0 (Developer Preview) - -* Local Run/Debug is now available for .NET Core 2.1 functions within SAM Applications -* Local Run/Debug is now available for Python 2.7, 3.6, and 3.7 functions within SAM Applications -* Local Run/Debug is now available for NodeJS 10.x functions within SAM Applications -* Local Run/Debug of SAM Lambda Functions now outputs to the Output and Debug Console, and reduces timing issues for attaching the debugger -* Removed Lambda view that showed the Lambda Policy -* Removed Lambda view that showed the Lambda Configuration -* Removed unsupported Lambda runtimes from the 'Create New SAM Application' wizard. -* The AWS Explorer menu items no longer appear on other VS Code panel menus -* When creating a new SAM Application, the toolkit now checks for a valid SAM CLI version before prompting the user for inputs -* When deploying a SAM Application, the toolkit now checks for a valid SAM CLI version before prompting the user for inputs -* Telemetry now sends AWS account data -* Minimum SAM CLI version has been bumped to 0.16.0 - -## 0.1.2 (Developer Preview) - -* Bumped maximum (exclusive) supported SAM CLI version from 0.16.0 to 0.23.0. - -## 0.1.1 (Developer Preview) - -* Updated Marketplace page to display information on how to use the Toolkit once installed - -## 0.1.0 (Developer Preview) - -* Initial release diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac024ddc915..9992cd16dcf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,11 @@ codebase and sending pull requests. ## Getting Started +This project is set up as a typescript monorepo. The documentation throughout this project +is referring to the subprojects [`packages/toolkit/`](./packages/toolkit/) and [`packages/core/`](./packages/core/). +See [arch_develop.md](./docs/arch_develop.md#monorepo-structure) to understand the +structure of this package before contributing. + ### Find things to do If you're looking for ideas about where to contribute, consider @@ -24,6 +29,8 @@ To develop this project, install these dependencies: - [Git](https://git-scm.com/downloads) - (optional) Set `git blame` to ignore noise-commits: `git config blame.ignoreRevsFile .git-blame-ignore-revs` - [AWS `git secrets`](https://github.com/awslabs/git-secrets) +- [TypeScript + Webpack Problem Matcher](https://marketplace.visualstudio.com/items?itemName=amodio.tsl-problem-matcher) + - Not installing will result in the following error during building: `Error: Invalid problemMatcher reference: $ts-webpack-watch` - (optional) [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) - (optional) [Docker](https://docs.docker.com/get-docker/) @@ -35,15 +42,27 @@ Then clone the repository and install NPM packages: ### Run -You can run the extension from Visual Studio Code: +Due to the monorepo structure of the project, you must open the project using the +`aws-toolkit-vscode.code-workspace` project file. + +1. Run the `File: Open Workspace from File...` command in vscode. +2. Select the `aws-toolkit-vscode.code-workspace` project file. + +To run the extension from VSCode as a Node.js app: 1. Select the Run panel from the sidebar. 2. From the dropdown at the top of the Run pane, choose `Extension`. -3. Press `F5` to launch a new instance of Visual Studio Code with the extension installed and the debugger attached. +3. Press `F5` to launch a new instance of VSCode with the extension installed and the debugger attached. + +To run the extension from VSCode in "web mode" (a browser app, or "PWA"): + +1. Select the Run panel from the sidebar. +2. From the dropdown at the top of the Run pane, choose `Extension (web)`. +3. Press `F5` to launch a new instance of VSCode (web mode) with the extension installed and the debugger attached. ### Build -When you launch the extension or run tests from Visual Studio Code, it will automatically build the extension and watch for changes. +When you launch the extension or run tests from VSCode, it will automatically build the extension and watch for changes. You can also use these NPM tasks (see `npm run` for the full list): @@ -73,11 +92,31 @@ You can also use these NPM tasks (see `npm run` for the full list): ### Guidelines +- Architecture: [arch_overview.md](./docs/arch_overview.md) - Project patterns and practices: [CODE_GUIDELINES.md](./docs/CODE_GUIDELINES.md) - [VS Code Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines) + - [Webview guidance](https://code.visualstudio.com/api/ux-guidelines/webviews) - [VS Code API Documentation](https://code.visualstudio.com/api/references/vscode-api) - [VS Code Extension Capabilities](https://code.visualstudio.com/api/extension-capabilities/common-capabilities) +### Prerelease artifacts + +- CI automatically publishes GitHub [prereleases](https://github.com/aws/aws-toolkit-vscode/releases) + for `master` and `feature/x` branches, including `.vsix` artifacts which can + be used to test the latest build for that branch. Each prerelease and its + artifact are continually updated from the HEAD of its branch. +- PR artifacts: each pull request is processed by an AWS CodeBuild job which + runs all tests and provides the build result via the _Details_ link as shown + below. + - CI artifact + +### Debug failing integration tests + +- Check for recent changes in each of these projects: + - https://github.com/microsoft/vscode-python (releases) + - https://github.com/aws/aws-sam-cli/releases + - https://github.com/aws/aws-sam-cli-app-templates/ (`master` branch, not releases!) + ### Technical notes - VSCode extensions have a [100MB](https://github.com/Microsoft/vscode-vsce/issues/179) file size limit. @@ -96,8 +135,8 @@ You can also use these NPM tasks (see `npm run` for the full list): 1. Declare a global unhandledRejection handler. ```ts - process.on('unhandledRejection', e => { - getLogger('channel').error( + process.on('unhandledRejection', (e) => { + getLogger().error( localize( 'AWS.channel.aws.toolkit.activation.error', 'Error Activating {0} Toolkit: {1}', @@ -113,12 +152,22 @@ You can also use these NPM tasks (see `npm run` for the full list): 2. Put a breakpoint on it. 3. Run all tests. +--- + +### Web Mode + +The AWS Toolkit VSCode extension has a support (with limited functionality) for running in a web browser, eg [vscode.dev](https://vscode.dev). + +See [web.md](./docs/web.md) for working with the web mode implementation of the extension. + +--- + ### Test See [TESTPLAN.md](./docs/TESTPLAN.md) to understand the project's test structure, mechanics and philosophy. -You can run tests directly from Visual Studio Code: +You can run tests directly from VSCode. Due to the monorepo structure of the project, you must [open the project via the `aws-toolkit-vscode.code-workspace` project file](#run). 1. Select `View > Debug`, or select the Debug pane from the sidebar. 2. From the dropdown at the top of the Debug pane, select the `Extension Tests` configuration. @@ -127,41 +176,61 @@ You can run tests directly from Visual Studio Code: You can also run tests from the command line: npm run test - npm run integrationTest + npm run testInteg Tests will write logs to `./.test-reports/testLog.log`. #### Run a specific test -To run a single test in VSCode, do any one of: +To run a single test in VSCode, do any _one_ of the following: - Run the _Extension Tests (current file)_ launch-config. -- Use Mocha's [it.only()](https://mochajs.org/#exclusive-tests) or `describe.only()`. -- Run in your terminal: - + - Note: if you don't see this in the vscode debug menu, confirm that you opened the project + [via the `aws-toolkit-vscode.code-workspace` project file](#run). +- or... Use Mocha's [it.only()](https://mochajs.org/#exclusive-tests) or `describe.only()`. +- or... Run in your terminal: - Unix/macOS/POSIX shell: ``` - TEST_FILE=src/test/foo.test npm run test + TEST_FILE=../core/src/test/foo.test.ts npm run test ``` - Powershell: ``` - $Env:TEST_FILE = "src/test/foo.test"; npm run test + $Env:TEST_FILE = "../core/src/test/foo.test.ts"; npm run test ``` - -- To run all tests in a particular subdirectory, you can edit +- or... To run all tests in a particular subdirectory, you can edit `src/test/index.ts:rootTestsPath` to point to a subdirectory: ``` rootTestsPath: __dirname + '/shared/sam/debugger/' ``` -### Coverage report +#### Run all tests in a specific folder + +To run tests against a specific folder in VSCode, do any one of: + +- Add the TEST_DIR environment variable to one of the testing launch configs and run it +- Run in your terminal + - Unix/macOS/POSIX shell: + ``` + TEST_DIR=../core/src/test/foo npm run test + ``` + - Powershell: + ``` + $Env:TEST_DIR = "../core/src/test/foo"; npm run test + ``` + +#### Run jscpd ("Copy-Paste Detection") + +If the "Copy-Paste Detection" CI job fails, you will find it useful to check things locally. To +check a specific file: -You can find the coverage report at `./coverage/index.html` after running the tests. Tests ran from the workspace launch config won't generate a coverage report automatically because it can break file watching. A few manual steps are needed instead: + npx jscpd --config .github/workflows/jscpd.json --pattern packages/…/src/foo.ts -- Run the command `Tasks: Run Build Task` if not already active -- Instrument built code with `npm run instrument` -- Exercise the code (`Extension Tests`, `Integration Tests`, etc.) -- Generate a report with `npm run report` +See the [jscpd cli documentation](https://github.com/kucherenko/jscpd/tree/master/apps/jscpd) for +more options. + +### Coverage report + +You can find the coverage report at `./coverage/amazonq/lcov-report/index.html` and `./coverage/toolkit/lcov-report/index.html` after running the tests. Tests ran from the workspace launch config won't generate a coverage report automatically because it can break file watching. ### CodeCatalyst Blueprints @@ -171,9 +240,12 @@ You can find documentation to create VSCode IDE settings for CodeCatalyst bluepr Before sending a pull request: +1. Treat all work as PUBLIC. Private `feature/x` branches will _not_ be squash-merged at release time. This has several benefits: + - Avoids mistakes (accidental exposure to public)! + - Avoids needing to erase (squash-merge) history. 1. Check that you are working against the latest source on the `master` branch. -2. Check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. -3. Open an issue to discuss any significant work. +1. Check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. +1. Open an issue to discuss any significant work. To send a pull request: @@ -181,47 +253,91 @@ To send a pull request: 2. Modify the source; focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. - Read the [project guidelines](#guidelines), this is very important for non-trivial changes. 3. Commit to your fork [using clear commit messages](#commit-messages). -4. Update the changelog by running `npm run newChange`. - - Note: the main purpose of the `newChange` task is to avoid merge conflicts. +4. Update the [changelog](#changelog). 5. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/). 6. Pay attention to any CI failures reported in the pull request. -### Commit messages +### Changelog -Generally your PR description should be a copy-paste of your commit message(s). -If your PR description provides insight and context, that also should exist in -the commit message. Source control (Git) is our source-of-truth, not GitHub. +Pull requests that change **customer-impacting behavior** must include a changelog item(s). Run one +or both of the following commands: -Follow these [commit message guidelines](https://cbea.ms/git-commit/): - -- Subject: single line up to 50-72 characters - - Imperative voice ("Fix bug", not "Fixed"/"Fixes"/"Fixing"). -- Body: for non-trivial or uncommon changes, explain your motivation for the - change and contrast your implementation with previous behavior. +- For changes relevant to Amazon Q: + ``` + npm run newChange -w packages/amazonq + ``` +- For changes relevant to AWS Toolkit: + ``` + npm run newChange -w packages/toolkit + ``` - - Often you can save a _lot_ of words by using this simple template: - ``` - Problem: … - Solution: … - ``` +The audience for the changelog is _the user_. The changelog is presented to users by VSCode and the +marketplace. It is a "micro-blog" for advertising improvements to users. It is the _primary_ way of +communicating changes to customers. Please consider this when writing changelog entries. + +Mentioning low-level details like "function x now takes argument y", will not be useful, because it +doesn't say what that means in terms of the user experience. Instead, describe the effect from the +user's point of view. + +> [!TIP] +> +> - Describe the change in a way that is _meaningful to the customer_. If you can't describe the _customer impact_ then it probably shouldn't be in the changelog. +> - ✅ `Connection wizard sometimes shows the old (stale) connection` +> - ✅ `Faster startup after VSCode restarts` +> - ❌ `Remove the cache when the connection wizard is re-launched` (code internals are not relevant to customers) +> - ❌ `Update telemetry definitions` (not customer-impacting) +> - "Bug Fix" changes should describe the _problem being fixed_. Don't say "Fixed" in the +> description, it's redundant. Example: +> - ❌ `Fixed S3 bug which caused filenames to be uppercase` +> - ✅ `S3 filenames are always uppercase` +> - To update an _existing_ changelog item, just edit its `.changes/next-release/….json` file, you don't need to re-run `npm run newChange`. +> - If there are multiple unrelated changes, run `npm run newChange` for each change. +> - Include the feature that the change affects, Q, CodeWhisperer, etc. + +### Pull request title + +The title of your pull request must follow this format (checked by [lintcommit.js](.github/workflows/lintcommit.js)): + +- format: `type(scope): subject...` +- type: must be a valid type (`build`, `ci`, `config`, `deps`, `docs`, `feat`, `fix`, `perf`, `refactor`, `style`, `telemetry`, `test`, `types`) + - see [lintcommit.js](.github/workflows/lintcommit.js)) + - "chore" is intentionally rejected because it tends to be over-used. + - user-facing changes should always choose "feat" or "fix", and include a [changelog](#changelog) item. +- scope: lowercase, <30 chars +- subject: must be <100 chars + +### Pull request description + +Your PR description should provide a brief "Problem" and "Solution" pair. This +structure often gives much more clarity, more concisely, than a typical +paragraph of explanation. + + Problem: + Foo does nothing when user clicks it. + + Solution: + - Listen to the click event. + - Emit telemetry on success/failure. -A [good commit message](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project) -has a short subject line and unlimited detail in the body. [Good explanations](https://nav.al/explanations) are acts of creativity. The "tiny subject line" constraint reminds you to clarify the essence of the commit, and makes the log easy for humans to scan. The commit log is an artifact that will outlive most code. -Prefix the subject with `type(topic):` ([conventional -commits](https://www.conventionalcommits.org/) format): this again helps humans -(and scripts) scan and omit ranges of the history at a glance. +### Commit messages -### CI artifact +Source control (Git) is our source-of-truth, not GitHub. However since most PRs +are squash-merged, it's most important that your [pull request description](#pull-request-description) +is well-formed so that the merged commit has the relevant info. -Each commit and pull request is processed by an automated system which runs -all tests and provides the build result via the _Details_ link as shown below. +If you expect your commits to be preserved ("regular merge"), then follow [these +guidelines](https://cbea.ms/git-commit/): -CI artifact +- Subject: single line up to 50-72 characters + - Imperative voice ("Fix bug", not "Fixed"/"Fixes"/"Fixing"). + - [Formatted as `type(scope): subject...`](#pull-request-title). + - Helps humans _and_ scripts scan and omit ranges of the history at a glance. +- Body: describe the change as a [Problem/Solution pair](#pull-request-description). ## Tooling @@ -231,10 +347,54 @@ generating SDKs, etc. ### Toolkit developer settings (`aws.dev.*`) -The [DevSettngs](https://github.com/aws/aws-toolkit-vscode/blob/479b9d45b5f5ad30fc10567e649b59801053aeba/src/shared/settings.ts#L553) class defines various developer-only settings that change the behavior of the +The [DevSettings](https://github.com/aws/aws-toolkit-vscode/blob/479b9d45b5f5ad30fc10567e649b59801053aeba/src/shared/settings.ts#L553) class defines various developer-only settings that change the behavior of the Toolkit for testing and development purposes. To use a setting just add it to your `settings.json`. At runtime, if the Toolkit reads any of these settings, -the "AWS" statusbar item will [change its color](https://github.com/aws/aws-toolkit-vscode/blob/479b9d45b5f5ad30fc10567e649b59801053aeba/src/credentials/awsCredentialsStatusBarItem.ts#L45). Use the setting `aws.dev.forceDevMode` to trigger this effect on start-up. +the "AWS" statusbar item will [change its color](https://github.com/aws/aws-toolkit-vscode/blob/479b9d45b5f5ad30fc10567e649b59801053aeba/src/credentials/awsCredentialsStatusBarItem.ts#L45). + +The `aws.dev.forceDevMode` setting enables or disables Toolkit "dev mode". Without this setting, the presence of any other `aws.dev.*` setting defined in `DevSettings` implicitly enables "dev mode". + +### Logging + +- Use `getLogger()` to log debugging messages, warnings, etc. + - Example: `getLogger().error('topic: widget failed: %O', { foo: 'bar', baz: 42 })` +- Log messages are written to the extension Output channel, which you can view in vscode by visiting the "Output" panel and selecting `AWS Toolkit Logs` or `Amazon Q Logs`. +- Use the `aws.dev.logfile` setting to set the logfile path to a fixed location, so you can follow + and filter logs using shell tools like `tail` and `grep`. + - Note: this always logs at **debug log-level** (though you can temporarily override that from the `AWS Toolkit Logs` UI). + - Example `settings.json`: + ``` + "aws.dev.logfile": "~/awstoolkit.log", + ``` + then you can tail the logfile in your terminal: + ``` + tail -F ~/awstoolkit.log + ``` +- Use the Output panel to watch and filter Toolkit logs (including telemetry) in VSCode. + - Enter text in the Output panel filter box to show only log messages with that text. + +#### Enabling Debug Logs + +How to enable more detailed debug logs in the extensions. +If you need to report an issue attach these to give the most detailed information. + +1. Open the Command Palette (`cmd/ctrl` + `shift` + `p`), then search for "View Logs". Choose either `AWS: View Logs` or `Amazon Q: View Logs`. + - ![](./docs/images/logsView.png) +2. Click the gear icon on the bottom right and select `Debug` + - ![](./docs/images/logsSetDebug.png) +3. Click the gear icon again and select `Set As Default`. This will ensure we stay in `Debug` until explicitly changed. + - ![](./docs/images/logsSetDefault.png) +4. Open the Command Palette again and select `Reload Window`. +5. Now you should see additional `[debug]` prefixed logs in the output. + - ![](./docs/images/logsDebugLog.png) +6. To export logs, click the kebab (`...`), select `Export Logs`, and then select the appropriate channel (`Amazon Q Logs` for Amazon Q) + - ![](./docs/images/openExportLogs.png) + - ![](./docs/images/exportAmazonQLogs.png) + +### Telemetry + +- See [docs/telemetry.md](./docs/telemetry.md) for guidelines on developing telemetry in this project. +- To watch Toolkit telemetry events, use the `Amazon Q: View Logs` command (see [Logging](#logging) above) and enter "telemetry" in the filter box. ### Service Endpoints @@ -248,12 +408,136 @@ Example: } ``` -### Telemetry and Automation +Overrides specifically for CodeCatalyst can be set using the `aws.dev.codecatalystService` setting. This is a JSON object consisting of keys/values required to override API calls to CodeCatalyst: `region`, `endpoint`, `hostname`, and `gitHostname`. If this setting is present, then all keys need to be explicitly provided. -Metrics are only emitted if the extension is assumed to be ran from an actual user rather than automation scripts. -This condition is checked through an environment variable `AWS_TOOLKIT_AUTOMATION` which is set by test entry points. -If any truthy value is present, telemetry will be dropped if the current build is not a release version. Utility functions, -such as `assertTelemetry`, can be used to test specific telemetry emits even in automation. +Example: + +```json +"aws.dev.codecatalystService": { + "region": "us-west-2", + "endpoint": "https://codecatalyst-gamma.example.com", + "hostname": "integ.stage.example.com", + "gitHostname": "git.gamma.source.example.com", +} +``` + +Overrides specifically for CodeWhisperer/Amazon Q can be set using the `aws.dev.codewhispererService` setting. This is a JSON object consisting of keys/values required to override API calls to CodeWhisperer/Amazon Q: `region` and `endpoint`. If this setting is present, then all keys need to be explicitly provided. + +Example: + +```json +"aws.dev.codewhispererService": { + "region": "us-west-2", + "endpoint": "https://codewhisperer-gamma.example.com" +} +``` + +Overrides specifically for the Amazon Q language server can be set using the `aws.dev.amazonqLsp` setting. This is a JSON object consisting of keys/values required to override language server: `manifestUrl`, `supportedVersions`, `id`, and `path`. + +Example: + +```json +"aws.dev.amazonqLsp": { + "manifestUrl": "https://custom.url/manifest.json", + "supportedVersions": "4.0.0", + "id": "AmazonQ", + "path": "/custom/path/to/local/lsp/folder", + "ui": "/custom/path/to/chat-client/ui" +} +``` + +Overrides specifically for the Amazon Q Workspace Context language server can be set using the `aws.dev.amazonqWorkspaceLsp` setting. This is a JSON object consisting of keys/values required to override language server: `manifestUrl`, `supportedVersions`, `id`, and `path`. + +Example: + +```json +"aws.dev.amazonqWorkspaceLsp": { + "manifestUrl": "https://custom.url/manifest.json", + "supportedVersions": "4.0.0", + "id": "AmazonQ", + "path": "/custom/path/to/local/lsp/folder", +} +``` + +### Environment variables + +Environment variables can be used to modify the behaviour of VSCode. The following are environment variables that can be used to configure the extension: + +#### General AWS + +- `AWS_ACCESS_KEY_ID`: The AWS access key associated with an IAM account. If defined, this environment variable overrides the value for the profile setting aws_access_key_id. For more information see [environment variables to configure the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) +- `AWS_SECRET_ACCESS_KEY`: The secret key associated with the access key. This is essentially the "password" for the access key. If defined, this environment variable overrides the value for the profile setting aws_secret_access_key. For more information see [environment variables to configure the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) +- `AWS_REGION`: The AWS Region to send the request to. If defined, this environment variable overrides the values in the environment variable AWS_DEFAULT_REGION and the profile setting region. For more information see [environment variables to configure the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) +- `AWS_SDK_LOAD_CONFIG`: Controls how the AWS SDK for javascript loads it's configuration when initialized. If the AWS_SDK_LOAD_CONFIG environment variable has been set to a truthy value, the SDK for JavaScript automatically searches for a config file when it loads. For more information see [the shared config file documentation](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-region.html#setting-region-config-file) +- `AWS_SHARED_CREDENTIALS_FILE`: The location of the file that the AWS CLI uses to store access keys. The default path is `~/.aws/credentials` +- `AWS_CONFIG_FILE`: The location of the file that the AWS CLI uses to store configuration profiles. The default path is `~/.aws/config` + +#### General OS + +- `HOME`: The home directory location for the current user in Linux and other Unix-like operating systems. +- `SSH_AUTH_SOCK`: The location of a UNIX domain socket used by ssh-agent and SSH clients for agent-based authentication +- `USERPROFILE`: The absolute path to the profile folder for the current user in Windows +- `HOMEPATH`: The path to the home directory for the current user in Windows, without including the drive letter +- `PROGRAMFILES/PROGRAMFILES(X86)`: The default installation directory for Windows +- `WINDIR`: The location of the Windows installation directory +- `PATH`: The set of directories where executable programs live + +#### Codecatalyst + +- `__DEV_ENVIRONMENT_ID`: The ID of the running development environment. Automatically set when running the toolkit in Codecatalyst +- `__DEV_ENVIRONMENT_PROJECT_NAME`: The project name associated with the running development environment. Automatically set when running the toolkit in Codecatalyst +- `__DEV_ENVIRONMENT_SPACE_NAME`: The space name associated with the running development environment. Automatically set when running the toolkit in Codecatalyst +- `__DEV_ENVIRONMENT_ORGANIZATION_NAME`: The organization name associated with the running development environment. Automatically set when running the toolkit in Codecatalyst + +The following are environment variable versions of the user `settings.json` overrides mentioned [here](#codecatalyst-settings). These will always override the toolkit defaults and those defined in `settings.json`. +Unlike the user setting overrides, not all of these environment variables have to be set to make use of them. + +- `__CODECATALYST_REGION`: for aws.dev.codecatalystService.region +- `__CODECATALYST_ENDPOINT`: for aws.dev.codecatalystService.endpoint +- `__CODECATALYST_HOSTNAME`: for aws.dev.codecatalystService.hostname +- `__CODECATALYST_GIT_HOSTNAME`: for aws.dev.codecatalystService.gitHostname + +#### Codewhisperer/Amazon Q + +The following are environment variable versions of the user `settings.json` overrides mentioned [here](#codewhisperer-settings). These will always override the toolkit defaults and those defined in `settings.json`. +Unlike the user setting overrides, not all of these environment variables have to be set to make use of them. + +- `__CODEWHISPERER_REGION`: for aws.dev.codewhispererService.region +- `__CODEWHISPERER_ENDPOINT`: for aws.dev.codewhispererService.endpoint +- `__AMAZONQLSP_MANIFEST_URL`: for aws.dev.amazonqLsp.manifestUrl +- `__AMAZONQLSP_SUPPORTED_VERSIONS`: for aws.dev.amazonqLsp.supportedVersions +- `__AMAZONQLSP_ID`: for aws.dev.amazonqLsp.id +- `__AMAZONQLSP_PATH`: for aws.dev.amazonqLsp.path +- `__AMAZONQLSP_UI`: for aws.dev.amazonqLsp.ui +- `__AMAZONQWORKSPACELSP_MANIFEST_URL`: for aws.dev.amazonqWorkspaceLsp.manifestUrl +- `__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS`: for aws.dev.amazonqWorkspaceLsp.supportedVersions +- `__AMAZONQWORKSPACELSP_ID`: for aws.dev.amazonqWorkspaceLsp.id +- `__AMAZONQWORKSPACELSP_PATH`: for aws.dev.amazonqWorkspaceLsp.path + +#### Lambda + +- `AUTH_UTIL_LAMBDA_ARN`: The Auth Util Lambda is used to log into using Builder ID/IdC automatically when running e2e tests. This is the arn that points to the auth util lambda. + +#### ECS + +- `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI`: The relative HTTP URL endpoint for the SDK to use when making a request for credentials. The value is appended to the default Amazon ECS hostname of 169.254.170.2. For more information see [container credential provider](https://docs.aws.amazon.com/sdkref/latest/guide/feature-container-credentials.html) +- `AWS_CONTAINER_CREDENTIALS_FULL_URI`: The full HTTP URL endpoint for the SDK to use when making a request for credentials. This includes both the scheme and the host. For more information see [container credential provider](https://docs.aws.amazon.com/sdkref/latest/guide/feature-container-credentials.html) + +#### Step functions + +- `SSMDOCUMENT_LANGUAGESERVER_PORT`: The port the ssm document language server should start debugging on + +#### CI/Testing + +- `GITHUB_ACTION`: The name of the current GitHub Action workflow step that is running +- `CODEBUILD_BUILD_ID`: The unique ID of the current CodeBuild build that is executing +- `AWS_TOOLKIT_AUTOMATION`: If tests are currently being ran +- `TEST_SSO_STARTURL`: The start url you want to use on E2E tests +- `TEST_SSO_REGION`: The region for the start url you want to use on E2E tests +- `AWS_TOOLKIT_TEST_NO_COLOR`: If the tests should include colour in their output +- `DEVELOPMENT_PATH`: The path to the aws toolkit vscode project +- `TEST_DIR` - The directory where the test runner should find the tests +- `AMAZONQ_FEATUREDEV_ITERATION_TEST` - Controls whether to enable multiple iteration testing for Amazon Q feature development ### SAM/CFN ("goformation") JSON schema @@ -261,6 +545,17 @@ See [docs/cfn-schema-support.md](./docs/cfn-schema-support.md) for how to fix and improve the JSON schema that provides auto-completion and syntax checking of SAM and CloudFormation `template.yaml` files. +### Custom Lint Rules + +The package.json 'devDependencies' includes `eslint-plugin-aws-toolkits`. This is a local eslint plugin where we define custom lint rules. Additional lint rules and tests for lint rules can be added to this plugin: + +1. Define a new rule in `plugins/eslint-plugin-aws-toolkits/lib/rules`. +2. Create a test for your rule in `plugins/eslint-plugin-aws-toolkits/test/rules` and run with `npm run test` in the root directory of `eslint-plugin-aws-toolkits`. +3. Register your rule in `plugins/eslint-plugin-aws-toolkits/index.ts`. +4. Enable your rule in `.eslintrc`. + +Writing lint rules can be tricky if you are unfamiliar with the process. Use an AST viewer such as https://astexplorer.net/ + ### AWS SDK generator When the AWS SDK does not (yet) support a service but you have an API @@ -321,29 +616,44 @@ requests just from the model/types. ### Webview dev-server -Webviews can be hot-reloaded (updated without restarting the extension) by running a developer server provided by webpack. This server is started automatically when running the `Extension` launch configuration. You can also start it by running `npm serve`. Note that only frontend components will be updated; if you change backend code you will still need to restart the development extension. +Webviews can be refreshed to show changes to `.vue` code when running in Debug mode. You do not have to +reload the Debug VS Code window. + +- Use `Command Palette` -> `Reload Webviews` +- Only the frontend `.vue` changes will be reflected. If changing any backend code you must restart Debug mode. + +This works by continuously building the final Vue webview files (`webpack watch`) and then serving them through a local server (`webpack serve`). Whenever a webview is loaded it will grab the latest build from the server. ### Font generation -For extensions to contribute their own codicons, VS Code requires a font file as well as how that font maps to codicon IDs. The mapping is found in `package.json` under the `icons` contribution point. Icons located in `resources/icons` are stitched together at build-time into a single font, automatically adding mappings to `package.json`. More information can be found [here](docs/icons.md). +For extensions to contribute their own codicons, VSCode requires a font file as well as how that font maps to codicon IDs. The mapping is found in `package.json` under the `icons` contribution point. Icons located in `resources/icons` are stitched together at build-time into a single font, automatically adding mappings to `package.json`. More information can be found [here](docs/icons.md). As a simple example, let's say I wanted to add a new icon for CloudWatch log streams. I would do the following: 1. Place the icon in `resources/icons/aws/cloudwatch`. I'l name the icon `log-stream.svg`. -1. Use `npm run generatePackage` to update `package.json`. Commit this change with the new icon. +1. Use `npm run generateIcons` to update `package.json`. Commit this change with the new icon. 1. You can now use the icon in the Toolkit: ```ts getIcon('aws-cloudwatch-log-stream') ``` -### Beta artifacts +### VSCode Marketplace + +The [marketplace page](https://marketplace.visualstudio.com/itemdetails?itemName=AmazonWebServices.aws-toolkit-vscode) +is defined in `packages/toolkit/README.md`. The `vsce` package tool always [replaces relative image paths](https://github.com/microsoft/vscode-vsce/blob/9478dbd11ea2e7adb23ec72923e889c7bb215263/src/package.ts#L885) +with URLs pointing to `HEAD` on GitHub (`https://github.com/aws/aws-toolkit-vscode/raw/HEAD/…/foo.gif`). + +Note therefore: -The Toolkit codebase contains logic in `src/dev/beta.ts` to support development during private betas. Creating a beta artifact requires a _stable_ URL to source Toolkit builds from. This URL should be added to `src/dev/config.ts`. Subsequent Toolkit artifacts will have their version set to `1.999.0` with a commit hash. Builds will automatically query the URL to check for a new build once a day and on every reload. +1. Don't delete images from `docs/marketplace/` unless the _current published_ + AWS Toolkit release doesn't depend on them. +2. `HEAD` implies that the URL depends on the current _default branch_ (i.e. + `master`). Changes to other branches won't affect the marketplace page. -## Importing icons from other open source repos +### Importing icons from other projects -If you are contribuing visual assets from other open source repos, the source repo must have a compatible license (such as MIT), and we need to document the source of the images. Follow these steps: +If you are contribuing visual assets from other open source repos, the source repo must have a compatible license (such as MIT), and we need to document the source of the images. Follow these steps ([example: #227](https://github.com/aws/aws-toolkit-vscode/pull/227)): 1. Use a separate location in this repo for every repo/organization where images are sourced from. See `resources/icons/vscode` as an example. 1. Copy the source repo's licence into this destination location's LICENSE.txt file @@ -359,7 +669,41 @@ If you are contribuing visual assets from other open source repos, the source re 1. Add an entry [here](docs/icons.md#third-party) summarizing the new destination location, where the assets were sourced from, and a brief rationale. -[PR #227](https://github.com/aws/aws-toolkit-vscode/pull/227) shows an example. +## Using new vscode APIs + +The minimum required vscode version specified in [package.json](https://github.com/aws/aws-toolkit-vscode/blob/07119655109bb06105a3f53bbcd86b812b32cdbe/package.json#L16) +is decided by the version of vscode running in other supported vscode-compatible targets (e.g. web). + +But you can still use the latest vscode APIs, by checking the current running vscode version. For example, to use a vscode 1.64 API: + +1. Check the vscode version: `semver.gte(vscode.version, '1.64.0')` +2. Disable the feature if is too old. That could mean just skipping the code entirely, or showing a different UI. + +Full example: https://github.com/aws/aws-toolkit-vscode/blob/7cb97a2ef0a765862d21842693967070b0dcdd49/src/shared/credentials/defaultCredentialSelectionDataProvider.ts#L54-L76 + +## Preview Releases and Experiments + +There are several ways to make pre-production changes available on a "preview" or "experimental" basis: + +- **Experimental features:** settings defined in [aws.experiments](https://github.com/aws/aws-toolkit-vscode/blob/4dcee33931693380739eaa5d44e92fa4545a9666/package.json#L228-L241) + are available in the vscode settings UI so that customers **can discover and enable them.** + This mechanism is intended for non-production features which are ready for + early access / preview feedback from interested customers. +- **Developer-only features:** the `aws.dev.forceDevMode` setting can be used as + a condition to enable features only for users who have + `"aws.dev.forceDevMode": true` in their settings. These features are intended + to be part of the mainline branch, but are _not_ presented to customers in the + VSCode settings UI. Example: [EC2 commands were gated on `aws.isDevMode`](https://github.com/aws/aws-toolkit-vscode/blob/4dcee33931693380739eaa5d44e92fa4545a9666/package.json#L1115-L1126) + so the functionality could be merged to mainline while it was under development. +- **Beta artifacts:** For a "private beta" launch, `src/dev/beta.ts` contains + logic to check a hardcoded, stable URL serving the latest `.vsix` build for + the private beta. The hardcoded URL defined in [`dev/config.ts:betaUrl`](https://github.com/aws/aws-toolkit-vscode/blob/d9c27234c0732b021d07e184a865213d6efde8ec/src/dev/config.ts#L9) + also forces the Toolkit to declare version `99.0` (since "private beta" has no semver and to + avoid unwanted auto-updating from VSCode marketplace). Beta builds of the Toolkit automatically + query the URL once per session per day. + - Beta users may want to run the `Extensions: Disable Auto Update for All Extensions` command + [disable VSCode's auto-update feature](https://code.visualstudio.com/docs/editor/extension-marketplace#_extension-autoupdate), + to avoid ovewriting the beta Toolkit with the marketplace release. ## Code of Conduct diff --git a/LICENSE-THIRD-PARTY b/LICENSE-THIRD-PARTY new file mode 100644 index 00000000000..efdb4af3017 --- /dev/null +++ b/LICENSE-THIRD-PARTY @@ -0,0 +1,10428 @@ +@aws/language-server-runtimes +0.2.128 + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + +****************************** + +@aws/language-server-runtimes-types +0.1.56 + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + +****************************** + +@opentelemetry/api +1.9.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/api-logs +0.200.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/core +2.0.1 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/exporter-logs-otlp-http +0.200.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/exporter-metrics-otlp-http +0.200.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/otlp-exporter-base +0.200.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/otlp-transformer +0.200.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/resources +2.0.1 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/sdk-logs +0.200.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/sdk-metrics +2.0.1 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/sdk-trace-base +2.0.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@opentelemetry/semantic-conventions +1.33.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@protobufjs/aspromise +1.1.2 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@protobufjs/base64 +1.1.2 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@protobufjs/codegen +2.0.4 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@protobufjs/eventemitter +1.1.0 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@protobufjs/fetch +1.1.0 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@protobufjs/float +1.0.2 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@protobufjs/inquire +1.1.0 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@protobufjs/path +1.1.2 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@protobufjs/pool +1.1.0 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@protobufjs/utf8 +1.1.0 +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@smithy/abort-controller +4.0.2 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +****************************** + +@smithy/node-http-handler +4.0.4 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +****************************** + +@smithy/protocol-http +5.1.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@smithy/querystring-builder +4.0.2 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@smithy/types +4.2.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +@smithy/util-uri-escape +4.0.0 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +****************************** + +@types/node +22.8.4 + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE + + +****************************** + +ajv +8.17.1 +The MIT License (MIT) + +Copyright (c) 2015-2021 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +ansi-colors +4.1.1 +The MIT License (MIT) + +Copyright (c) 2015-present, Brian Woodward. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +ansi-gray +0.1.1 +The MIT License (MIT) + +Copyright (c) <%= year() %>, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +ansi-regex +5.0.1 +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +ansi-styles +4.3.0 +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +ansi-wrap +0.1.0 +The MIT License (MIT) + +Copyright (c) 2015, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +aproba +1.2.0 +Copyright (c) 2015, Rebecca Turner + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + +****************************** + +are-we-there-yet +1.1.7 +Copyright (c) 2015, Rebecca Turner + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +available-typed-arrays +1.0.5 +MIT License + +Copyright (c) 2020 Inspect JS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +aws-sdk +2.1692.0 + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2012-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +balanced-match +1.0.2 +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +base64-js +1.5.1 +The MIT License (MIT) + +Copyright (c) 2014 Jameson Little + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +bl +4.1.0 +The MIT License (MIT) +===================== + +Copyright (c) 2013-2019 bl contributors +---------------------------------- + +*bl contributors listed at * + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +brace-expansion +1.1.11 +MIT License + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +buffer +5.7.1 +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh, and other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +call-bind +1.0.7 +MIT License + +Copyright (c) 2020 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +chownr +1.1.4 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +cliui +8.0.1 +Copyright (c) 2015, Contributors + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +clone +2.1.2 +Copyright © 2011-2015 Paul Vorbach + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +clone-buffer +1.0.0 +The MIT License (MIT) + +Copyright (c) 2016 Blaine Bublitz , Eric Schoffstall and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +clone-stats +1.0.0 +## The MIT License (MIT) ## + +Copyright (c) 2014 Hugh Kennedy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +cloneable-readable +1.1.3 +The MIT License (MIT) + +Copyright (c) 2016 Matteo Collina + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +code-point-at +1.1.0 +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +color-convert +2.0.1 +Copyright (c) 2011-2016 Heather Arthur + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +****************************** + +color-name +1.1.4 +The MIT License (MIT) +Copyright (c) 2015 Dmitry Ivanov + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +****************************** + +color-support +1.1.3 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +concat-map +0.0.1 +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +console-control-strings +1.1.0 +Copyright (c) 2014, Rebecca Turner + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +core-util-is +1.0.3 +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + + +****************************** + +decompress-response +4.2.1 +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +deep-extend +0.6.0 +The MIT License (MIT) + +Copyright (c) 2013-2018, Viacheslav Lotsmanov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +define-data-property +1.1.4 +MIT License + +Copyright (c) 2023 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +define-properties +1.1.4 +The MIT License (MIT) + +Copyright (C) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +****************************** + +delegates +1.0.0 +Copyright (c) 2015 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +detect-libc +1.0.3 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +duplexer +0.1.2 +license: MIT +authors: Raynos + +****************************** + +emoji-regex +8.0.0 +license: MIT +authors: Mathias Bynens + +****************************** + +end-of-stream +1.4.4 +The MIT License (MIT) + +Copyright (c) 2014 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +****************************** + +es-abstract +1.20.2 +The MIT License (MIT) + +Copyright (C) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +es-define-property +1.0.0 +MIT License + +Copyright (c) 2024 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +es-errors +1.3.0 +MIT License + +Copyright (c) 2024 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +es-to-primitive +1.2.1 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +escalade +3.1.2 +MIT License + +Copyright (c) Luke Edwards (lukeed.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +event-stream +3.3.5 +license: MIT +authors: Dominic Tarr (http://bit.ly/dominictarr) + +****************************** + +events +1.1.1 +MIT + +Copyright Joyent, Inc. and other Node contributors. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +expand-template +2.0.3 +The MIT License (MIT) + +Copyright (c) 2018 Lars-Magnus Skog + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +****************************** + +fancy-log +1.3.3 +The MIT License (MIT) + +Copyright (c) 2014, 2015, 2018 Blaine Bublitz and Eric Schoffstall + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +fast-deep-equal +3.1.3 +MIT License + +Copyright (c) 2017 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +fast-uri +3.0.6 +Copyright (c) 2021 The Fastify Team +Copyright (c) 2011-2021, Gary Court until https://github.com/garycourt/uri-js/commit/a1acf730b4bba3f1097c9f52e7d9d3aba8cdcaae +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of any contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * * * + +The complete list of contributors can be found at: +- https://github.com/garycourt/uri-js/graphs/contributors + +****************************** + +for-each +0.3.3 +The MIT License (MIT) + +Copyright (c) 2012 Raynos. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +from +0.1.7 +Apache License, Version 2.0 + +Copyright (c) 2011 Dominic Tarr + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +****************************** + +fs-constants +1.0.0 +The MIT License (MIT) + +Copyright (c) 2018 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +fs.realpath +1.0.0 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---- + +This library bundles a version of the `fs.realpath` and `fs.realpathSync` +methods from Node.js v0.10 under the terms of the Node.js MIT license. + +Node's license follows, also included at the header of `old.js` which contains +the licensed code: + + Copyright Joyent, Inc. and other Node contributors. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + +****************************** + +function-bind +1.1.2 +Copyright (c) 2013 Raynos. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +****************************** + +function.prototype.name +1.1.5 +The MIT License (MIT) + +Copyright (c) 2016 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +functions-have-names +1.2.3 +MIT License + +Copyright (c) 2019 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +gauge +2.7.4 +Copyright (c) 2014, Rebecca Turner + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +get-caller-file +2.0.5 +ISC License (ISC) +Copyright 2018 Stefan Penner + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +get-intrinsic +1.2.4 +MIT License + +Copyright (c) 2020 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +get-symbol-description +1.0.0 +MIT License + +Copyright (c) 2021 Inspect JS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +github-from-package +0.0.0 +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +glob +7.2.3 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +## Glob Logo + +Glob's logo created by Tanya Brassie , licensed +under a Creative Commons Attribution-ShareAlike 4.0 International License +https://creativecommons.org/licenses/by-sa/4.0/ + + +****************************** + +gopd +1.0.1 +MIT License + +Copyright (c) 2022 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +has +1.0.3 +license: MIT +authors: Thiago de Arruda + +****************************** + +has-bigints +1.0.2 +MIT License + +Copyright (c) 2019 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +has-property-descriptors +1.0.2 +MIT License + +Copyright (c) 2022 Inspect JS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +has-proto +1.0.3 +MIT License + +Copyright (c) 2022 Inspect JS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +has-symbols +1.0.3 +MIT License + +Copyright (c) 2016 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +has-tostringtag +1.0.0 +MIT License + +Copyright (c) 2021 Inspect JS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +has-unicode +2.0.1 +Copyright (c) 2014, Rebecca Turner + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + +****************************** + +hasown +2.0.2 +MIT License + +Copyright (c) Jordan Harband and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +hpagent +1.2.0 +MIT License + +Copyright (c) 2020 Tomas Della Vedova + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +iconv-lite +0.6.3 +Copyright (c) 2011 Alexander Shtuchkin + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +****************************** + +ieee754 +1.1.13 +Copyright 2008 Fair Oaks Labs, Inc. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +inflight +1.0.6 +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +inherits +2.0.4 +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + + +****************************** + +ini +1.3.8 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +internal-slot +1.0.3 +MIT License + +Copyright (c) 2019 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +is +3.3.0 +(The MIT License) + +Copyright (c) 2013 Enrico Marino +Copyright (c) 2014 Enrico Marino and Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +is-arguments +1.1.1 +The MIT License (MIT) + +Copyright (c) 2014 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +is-bigint +1.0.4 +MIT License + +Copyright (c) 2018 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +is-boolean-object +1.1.2 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +is-callable +1.2.4 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +is-date-object +1.0.5 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +is-electron +2.2.2 +The MIT License (MIT) + +Copyright (c) 2016-2018 Cheton Wu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +is-fullwidth-code-point +3.0.0 +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +is-generator-function +1.0.10 +The MIT License (MIT) + +Copyright (c) 2014 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +is-negative-zero +2.0.2 +The MIT License (MIT) + +Copyright (c) 2014 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +is-number-object +1.0.7 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +is-regex +1.1.4 +The MIT License (MIT) + +Copyright (c) 2014 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +is-shared-array-buffer +1.0.2 +MIT License + +Copyright (c) 2021 Inspect JS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +is-string +1.0.7 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +is-symbol +1.0.4 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +is-typed-array +1.1.9 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +is-weakref +1.0.2 +MIT License + +Copyright (c) 2020 Inspect JS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +isarray +1.0.0 +license: MIT +authors: Julian Gruber + +****************************** + +jaro-winkler +0.2.8 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Thomas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +jmespath +0.16.0 +Copyright 2014 James Saryerwinnie + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +****************************** + +jose +5.10.0 +The MIT License (MIT) + +Copyright (c) 2018 Filip Skokan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +json-schema-traverse +1.0.0 +MIT License + +Copyright (c) 2017 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +long +5.3.1 + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +****************************** + +mac-ca +3.1.1 +BSD 3-Clause License + +Copyright (c) 2018, José F. Romaniello +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +****************************** + +make-dir +1.3.0 +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +map-stream +0.0.7 +license: MIT +authors: Dominic Tarr (http://dominictarr.com) + +****************************** + +mimic-response +2.1.0 +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +minimatch +3.1.2 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +minimist +1.2.8 +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +mkdirp-classic +0.5.3 +The MIT License (MIT) + +Copyright (c) 2020 James Halliday (mail@substack.net) and Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +napi-build-utils +1.0.2 +MIT License + +Copyright (c) 2018 inspiredware + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +node-abi +2.30.1 +MIT License + +Copyright (c) 2016 Lukas Geiger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +node-addon-api +3.2.1 +The MIT License (MIT) +===================== + +Copyright (c) 2017 Node.js API collaborators +----------------------------------- + +*Node.js API collaborators listed at * + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +****************************** + +node-forge +1.3.1 +You may use the Forge project under the terms of either the BSD License or the +GNU General Public License (GPL) Version 2. + +The BSD License is recommended for most projects. It is simple and easy to +understand and it places almost no restrictions on what you can do with the +Forge project. + +If the GPL suits your project better you are also free to use Forge under +that license. + +You don't have to do anything special to choose one license or the other and +you don't have to notify anyone which license you are using. You are free to +use this project in commercial projects as long as the copyright header is +left intact. + +If you are a commercial entity and use this set of libraries in your +commercial software then reasonable payment to Digital Bazaar, if you can +afford it, is not required but is expected and would be appreciated. If this +library saves you time, then it's saving you money. The cost of developing +the Forge software was on the order of several hundred hours and tens of +thousands of dollars. We are attempting to strike a balance between helping +the development community while not being taken advantage of by lucrative +commercial entities for our efforts. + +------------------------------------------------------------------------------- +New BSD License (3-clause) +Copyright (c) 2010, Digital Bazaar, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Digital Bazaar, Inc. nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DIGITAL BAZAAR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + + +****************************** + +noop-logger +0.1.1 +license: MIT +authors: undefined + +****************************** + +npmlog +4.1.2 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +number-is-nan +1.0.1 +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +object-assign +4.1.1 +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +object-inspect +1.13.2 +MIT License + +Copyright (c) 2013 James Halliday + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +object-keys +1.1.1 +The MIT License (MIT) + +Copyright (C) 2013 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +****************************** + +object.assign +4.1.4 +The MIT License (MIT) + +Copyright (c) 2014 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +****************************** + +once +1.4.0 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +parse-node-version +1.0.1 +The MIT License (MIT) + +Copyright (c) 2018 Blaine Bublitz and Eric Schoffstall + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +path-is-absolute +1.0.1 +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +pause-stream +0.0.11 +Dual Licensed MIT and Apache 2 + +The MIT License + +Copyright (c) 2013 Dominic Tarr + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + ----------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2013 Dominic Tarr + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +****************************** + +pify +3.0.0 +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +prebuild-install +5.3.6 +The MIT License (MIT) + +Copyright (c) 2015 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +process-nextick-args +2.0.1 +# Copyright (c) 2015 Calvin Metcalf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.** + + +****************************** + +protobufjs +7.4.0 +This license applies to all parts of protobuf.js except those files +either explicitly including or referencing a different license or +located in a directory containing a different LICENSE file. + +--- + +Copyright (c) 2016, Daniel Wirtz All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of its author, nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- + +Code generated by the command line utilities is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. + + +****************************** + +pump +3.0.0 +The MIT License (MIT) + +Copyright (c) 2014 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +****************************** + +punycode +1.3.2 +license: MIT +authors: Mathias Bynens + +****************************** + +querystring +0.2.0 + +Copyright 2012 Irakli Gozalishvili. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + + +****************************** + +rc +1.2.8 +Apache License, Version 2.0 + +Copyright (c) 2011 Dominic Tarr + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +****************************** + +readable-stream +3.6.2 +Node.js is licensed for use as follows: + +""" +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +This license applies to parts of Node.js originating from the +https://github.com/joyent/node repository: + +""" +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + + +****************************** + +regexp.prototype.flags +1.4.3 +The MIT License (MIT) + +Copyright (C) 2014 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +****************************** + +registry-js +1.16.1 +MIT License + +Copyright (c) 2017 GitHub Desktop + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +remove-trailing-separator +1.1.0 +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +****************************** + +replace-ext +1.0.1 +The MIT License (MIT) + +Copyright (c) 2014 Blaine Bublitz , Eric Schoffstall and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +require-directory +2.1.1 +The MIT License (MIT) + +Copyright (c) 2011 Troy Goode + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +require-from-string +2.0.2 +The MIT License (MIT) + +Copyright (c) Vsevolod Strukchinsky (github.com/floatdrop) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +rxjs +7.8.2 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +****************************** + +safe-buffer +5.2.1 +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +safer-buffer +2.1.2 +MIT License + +Copyright (c) 2018 Nikita Skovoroda + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +sax +1.2.1 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +==== + +`String.fromCodePoint` by Mathias Bynens used according to terms of MIT +License, as follows: + + Copyright Mathias Bynens + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +semver +5.7.2 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +set-blocking +2.0.0 +Copyright (c) 2016, Contributors + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +set-function-length +1.2.2 +MIT License + +Copyright (c) Jordan Harband and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +side-channel +1.0.6 +MIT License + +Copyright (c) 2019 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +signal-exit +3.0.7 +The ISC License + +Copyright (c) 2015, Contributors + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +simple-concat +1.0.1 +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +simple-get +3.1.1 +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +source-map +0.6.1 + +Copyright (c) 2009-2011, Mozilla Foundation and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the names of the Mozilla Foundation nor the names of project + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +split +1.0.1 +license: MIT +authors: Dominic Tarr (http://bit.ly/dominictarr) + +****************************** + +stream-combiner +0.2.2 +Copyright (c) 2012 'Dominic Tarr' + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +string-width +4.2.3 +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +string.prototype.trimend +1.0.5 +MIT License + +Copyright (c) 2017 Khaled Al-Ansari + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +string.prototype.trimstart +1.0.5 +MIT License + +Copyright (c) 2017 Khaled Al-Ansari + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +string_decoder +1.3.0 +Node.js is licensed for use as follows: + +""" +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +This license applies to parts of Node.js originating from the +https://github.com/joyent/node repository: + +""" +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + + + +****************************** + +strip-ansi +6.0.1 +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +strip-json-comments +2.0.1 +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +tar-fs +2.1.1 +The MIT License (MIT) + +Copyright (c) 2014 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +****************************** + +tar-stream +2.2.0 +The MIT License (MIT) + +Copyright (c) 2014 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +****************************** + +through +2.3.8 +Apache License, Version 2.0 + +Copyright (c) 2011 Dominic Tarr + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +****************************** + +time-stamp +1.1.0 +The MIT License (MIT) + +Copyright (c) 2015-2017, Jon Schlinkert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +tslib +2.8.1 +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +****************************** + +tunnel-agent +0.6.0 +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +****************************** + +typescript +4.9.5 +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + + +****************************** + +unbox-primitive +1.0.2 +MIT License + +Copyright (c) 2019 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +undici +6.21.2 +MIT License + +Copyright (c) Matteo Collina and Undici contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +undici-types +6.19.8 +MIT License + +Copyright (c) Matteo Collina and Undici contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +url +0.10.3 +The MIT License (MIT) + +Copyright Joyent, Inc. and other Node contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +util +0.12.5 +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + + +****************************** + +util-deprecate +1.0.2 +(The MIT License) + +Copyright (c) 2014 Nathan Rajlich + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +uuid +8.0.0 +The MIT License (MIT) + +Copyright (c) 2010-2020 Robert Kieffer and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +vinyl +2.2.1 +The MIT License (MIT) + +Copyright (c) 2013 Blaine Bublitz , Eric Schoffstall and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +vscode-jsonrpc +8.2.0 +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +vscode-languageserver +9.0.1 +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +vscode-languageserver-protocol +3.17.5 +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +vscode-languageserver-textdocument +1.0.12 +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +vscode-languageserver-types +3.17.5 +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +vscode-nls +5.2.0 +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +vscode-nls-dev +4.0.4 +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +vscode-uri +3.1.0 +The MIT License (MIT) + +Copyright (c) Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +****************************** + +which-boxed-primitive +1.0.2 +MIT License + +Copyright (c) 2019 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +which-pm-runs +1.1.0 +The MIT License (MIT) + +Copyright (c) 2017-2022 Zoltan Kochan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +which-typed-array +1.1.8 +The MIT License (MIT) + +Copyright (c) 2015 Jordan Harband + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +wide-align +1.1.5 +Copyright (c) 2015, Rebecca Turner + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + +****************************** + +win-ca +3.5.1 +MIT License + +Copyright (c) 2020 Stas Ukolov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +wrap-ansi +7.0.0 +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +wrappy +1.0.2 +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +****************************** + +xml2js +0.6.2 +Copyright 2010, 2011, 2012, 2013. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + + +****************************** + +xmlbuilder +11.0.1 +The MIT License (MIT) + +Copyright (c) 2013 Ozgur Ozcitak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +y18n +5.0.8 +Copyright (c) 2015, Contributors + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + + +****************************** + +yargs +17.7.2 +MIT License + +Copyright 2010 James Halliday (mail@substack.net); Modified work Copyright 2014 Contributors (ben@npmjs.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +yargs-parser +21.1.1 +Copyright (c) 2016, Contributors + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md index aa208edd9a2..b841f69ec0c 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,50 @@ -# AWS Toolkit for Visual Studio Code +# AWS Extensions for Visual Studio Code -| System | Status | -| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Build ([main branch](https://github.com/aws/aws-toolkit-vscode/commits/master)) | [![GitHub CI status](https://github.com/aws/aws-toolkit-vscode/workflows/CI/badge.svg?branch=master)](https://github.com/aws/aws-toolkit-vscode/actions?query=branch%3Amaster) ![CodeBuild Build Status - main branch](https://codebuild.us-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiMlluaDRTMnZLdmMvcFREQVQ4RjFoK0FUSTZPdlRVcWJlQ2gwRElLT2gxZDhMeno5MThZZnlXdURDVFFjOWdqSEQ5QjVBYm0xSURoU3E1RTVHejltcnZrPSIsIml2UGFyYW1ldGVyU3BlYyI6IkY3SE9CaG1oMHhJUmsyakkiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master) [![Coverage](https://img.shields.io/codecov/c/github/aws/aws-toolkit-vscode/master.svg)](https://codecov.io/gh/aws/aws-toolkit-vscode/branch/master) [![LGTM Grade](https://img.shields.io/lgtm/grade/javascript/github/aws/aws-toolkit-vscode)](https://lgtm.com/projects/g/aws/aws-toolkit-vscode/) | -| [Marketplace](https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.aws-toolkit-vscode) | [![Marketplace Version](https://img.shields.io/vscode-marketplace/v/AmazonWebServices.aws-toolkit-vscode.svg) ![Marketplace Downloads](https://img.shields.io/vscode-marketplace/d/AmazonWebServices.aws-toolkit-vscode.svg)](https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.aws-toolkit-vscode) | +[![Coverage](https://img.shields.io/codecov/c/github/aws/aws-toolkit-vscode/master.svg)](https://codecov.io/gh/aws/aws-toolkit-vscode/branch/master) -The AWS Toolkit for Visual Studio Code is an extension for working with AWS services such as AWS Lambda. +This project is open source. We encourage issues, feature requests, code reviews, pull requests or +any positive contribution. See [CONTRIBUTING.md](CONTRIBUTING.md) to get started. -The Toolkit is available from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/itemdetails?itemName=AmazonWebServices.aws-toolkit-vscode). +### Amazon Q -This is an open source project because we want you to be involved. We love issues, feature requests, code reviews, pull -requests or any positive contribution. See [CONTRIBUTING.md](CONTRIBUTING.md). +[![Marketplace Version](https://img.shields.io/vscode-marketplace/v/AmazonWebServices.amazon-q-vscode.svg) ![Marketplace Downloads](https://img.shields.io/vscode-marketplace/d/AmazonWebServices.amazon-q-vscode.svg)](https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.amazon-q-vscode) + +Amazon Q for VS Code is a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.amazon-q-vscode) for connecting your IDE to [Amazon Q](https://aws.amazon.com/q/developer/) and leveraging generative AI to accelerate your software development. + +- Code faster with inline code suggestions as you type +- Chat with [Amazon Q](https://aws.amazon.com/q/developer/) to generate code, explain code, and get answers to questions about software development +- Analyze and fix security vulnerabilities in your project +- Upgrade your Java applications + +[Project Directory](https://github.com/aws/aws-toolkit-vscode/tree/master/packages/amazonq) + +### AWS Toolkit + +[![Marketplace Version](https://img.shields.io/vscode-marketplace/v/AmazonWebServices.aws-toolkit-vscode.svg) ![Marketplace Downloads](https://img.shields.io/vscode-marketplace/d/AmazonWebServices.aws-toolkit-vscode.svg)](https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.aws-toolkit-vscode) + +AWS Toolkit is a [VS Code extension](https://marketplace.visualstudio.com/itemdetails?itemName=AmazonWebServices.aws-toolkit-vscode) for connecting your IDE to your AWS resources: + +- Connect with [IAM credentials](https://docs.aws.amazon.com/sdkref/latest/guide/access-users.html), + [IAM Identity Center (SSO)](https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html), + or [AWS Builder ID](https://docs.aws.amazon.com/signin/latest/userguide/differences-aws_builder_id.html) +- Connect VSCode to your EC2 instances +- Connect to your [CodeCatalyst](https://codecatalyst.aws/) Dev Environments +- Debug your Lambda functions using [SAM CLI](https://github.com/aws/aws-sam-cli) +- Check and autocomplete code in SAM/CFN (CloudFormation) `template.yaml` files +- `Open Terminal` on your EC2 instances or ECS tasks +- `Search Log Group` on your CloudWatch logs +- Browse your AWS resources + +[Project Directory](https://github.com/aws/aws-toolkit-vscode/tree/master/packages/toolkit) ## Documentation -- The [Quick Start Guide](README.quickstart.vscode.md) provides an overview - of common Toolkit tasks. -- The [User Guide](https://docs.aws.amazon.com/console/toolkit-for-vscode/welcome) - contains detailed instructions for getting up and running with the Toolkit. +- Quick Start Guides for... + - [Amazon Q](https://marketplace.visualstudio.com/itemdetails?itemName=AmazonWebServices.amazon-q-vscode) + - [AWS Toolkit](https://marketplace.visualstudio.com/itemdetails?itemName=AmazonWebServices.aws-toolkit-vscode) +- [FAQ / Troubleshooting](./docs/faq-credentials.md) +- [User Guide](https://docs.aws.amazon.com/console/toolkit-for-vscode/welcome) +- General info about [AWS SDKs and Tools](https://docs.aws.amazon.com/sdkref/latest/guide/overview.html) ## Feedback @@ -27,7 +54,24 @@ We want your feedback! - [Ask a question](https://github.com/aws/aws-toolkit-vscode/issues/new?labels=guidance&template=guidance_request.md) - [Request a new feature](https://github.com/aws/aws-toolkit-vscode/issues/new?labels=feature-request&template=feature_request.md) - [File an issue](https://github.com/aws/aws-toolkit-vscode/issues/new?labels=bug&template=bug_report.md) +- Or [send a pull request](CONTRIBUTING.md)! + +## License Scanning + +To generate license reports and attribution documents for third-party dependencies: + +```bash +npm run scan-licenses + +# Or run directly +./scripts/scan-licenses.sh +``` + +This generates: + +- `LICENSE-THIRD-PARTY` - Attribution document for distribution +- `licenses-full.json` - Complete license data ## License -The **AWS Toolkit for Visual Studio Code** is distributed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0). +This project and the subprojects within **(AWS Toolkit for Visual Studio Code, Amazon Q for Visual Studio Code)** is distributed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0). diff --git a/README.quickstart.cloud9.md b/README.quickstart.cloud9.md deleted file mode 100644 index e17daefce07..00000000000 --- a/README.quickstart.cloud9.md +++ /dev/null @@ -1,146 +0,0 @@ -# AWS Toolkit - -The AWS Toolkit extension for AWS Cloud9 that enables you to interact with [Amazon Web Services (AWS)](https://aws.amazon.com/what-is-aws/). -See the [user guide](https://docs.aws.amazon.com/cloud9/latest/user-guide/toolkit-welcome.html) for complete documentation. - -Try the [AWS Code Sample Catalog](https://docs.aws.amazon.com/code-samples/latest/catalog/welcome.html) to start coding with the AWS SDK. - -# Features - -- [AWS Explorer](#ui-components-aws-expl) - - API Gateway - - App Runner - - CloudFormation stacks - - [CloudWatch Logs](https://docs.aws.amazon.com/cloud9/latest/user-guide/cloudwatch-logs-toolkit.html) - - ECR - - [ECS](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/ecs-exec.html) - - IoT explorer - - Lambda functions - - S3 explorer -- [Developer Tools](#ui-components-dev-tools) - - [CDK Explorer](#ui-components-cdk-expl) - - [CodeWhisperer](#codewhisperer) -- [AWS Serverless Applications (SAM)](#sam-and-lambda) -- [`AWS:` Commands](#aws-commands) - ---- - -## AWS Explorer - -The **AWS Explorer** provides access to the AWS services that you can work with when using the Toolkit. To see the **AWS Explorer**, choose the **AWS** icon in the **Activity bar**. - -## ![Overview, AWS Explorer](./resources/marketplace/cloud9/overview-aws-explorer-en.png) - -## Developer Tools - -The **Developer Tools** panel is a section for developer-focused tooling curated for working in an IDE. The **Developer Tools** panel can be found underneath the **AWS Explorer** when the **AWS icon** is selected in the **Activity bar**. - -## { [Return to Top](#top) } - -## CDK Explorer - -The **AWS CDK Explorer** enables you to work with [AWS Cloud Development Kit (CDK)](https://aws.amazon.com/cdk/) applications. It shows a top-level view of your CDK applications that have been sythesized in your workspace. - -With the CDK explorer, you can navigate the CDK application's infrastructure stacks, resources, and policies. - -For full details see the [AWS CDK Explorer](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/cdk-explorer.html) in the user guide. - -{ [Return to Top](#top) } - -## Amazon CodeWhisperer - -**Amazon CodeWhisperer** provides inline code suggestions using machine learning and natural language processing on the contents of your current file. Supported languages include: Java, Python and Javascript. - -Once enabled, CodeWhisperer will provide code suggestions automatically and can also be requested manually using option+c (mac) / alt+c (PC). To accept a suggestion and add it to your file, press Tab, Enter or click on it. To dismiss a suggestion, press escape or keep typing. - -For more information, see [Amazon CodeWhisperer](https://aws.amazon.com/codewhisperer) in our user guide. - -## { [Return to Top](#top) } - -## AWS Serverless Applications - -The AWS Toolkit enables you to develop [AWS serverless applications](https://aws.amazon.com/serverless/) locally. It also provides _Inline Actions_ in Cloud9 to do the following: - -- Use SAM (serverless application model) templates to build and debug your locally developed AWS serverless applications. -- Run selected [AWS Lambda](https://aws.amazon.com/lambda/) functions. - -To start debugging with a SAM template, click the `Add Debug Configuration` _Inline Action_ in the template file. - -![Add Debug Configuration Template](./resources/marketplace/cloud9/Codelens-YAML-template.png) - -###### The _Inline Action_ indicator in the SAM template allows you to add a debug configuration for the serverless application. - -Alternatively, you can run and debug just the AWS Lambda function and exclude other resources defined by the SAM template. Again, use an _Inline Action_ indicator for an AWS Lambda-function handler. (A _handler_ is a function that Lambda calls to start execution of a Lambda function.) - -![Add Debug Configuration Direct](./resources/marketplace/cloud9/Codelens-direct-function.png) - -###### The _Inline Action_ indicator in the application file lets you add a debug configuration for a selected AWS Lambda function. - -When you run a debug session, the status and results are shown in the **AWS Toolkit** output channel. If the toolkit does not have an open **AWS Toolkit** output channel, one can be created with the New Tab button. - -![Configure and Run](./resources/marketplace/cloud9/sam-configure-and-run-still-en.png) - -###### After a local run is complete, the output appears in the **OUTPUT** tab. - -When you're satisfied with performance, you can [deploy your serverless application](https://docs.aws.amazon.com/cloud9/latest/user-guide/deploy-serverless-app.html). The SAM template is converted to a CloudFormation template, which is then used to deploy all the application's assets to the AWS Cloud. - -### Supported runtimes - -The Toolkit _local SAM debugging_ feature supports these runtimes: - -- JavaScript (Node.js 12.x, 14.x) -- Python (3.7, 3.8, 3.9) - -For more information see [Working with AWS Serverless Applications](https://docs.aws.amazon.com/cloud9/latest/user-guide/serverless-apps-toolkit.html) in the user guide. - -{ [Return to Top](#top) } - ---- - -## `AWS:` Commands - -The Toolkit provides commands (prefixed with `AWS:`) to the AWS Cloud9 _Go to Anything panel_, available by clicking the search bar and typing "." or via hotkey. - -| OS | Hotkey | -| :------ | :------- | -| Windows | `CTRL-.` | -| macOS | `CMD-.` | - -![Go to Anything panel](./resources/marketplace/cloud9/open-commands-en.png) - -| AWS Command | Description | -| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `AWS: About Toolkit` | Displays information about the AWS Toolkit. | -| `AWS: Add SAM Debug Configuration` | Creates an `aws-sam` Debug Configuration from a function in the given source file | -| `AWS: Connect to AWS` | Select or create a connection to AWS. | -| `AWS: Create a new Issue on Github` | Opens the AWS Toolkit's [New Issue page on Github](https://github.com/aws/aws-toolkit-vscode/issues/new/choose). | -| `AWS: Create Credentials Profile` | Creates an AWS credentials profile. | -| `AWS: Create Lambda SAM Application` | Generates code files for a new AWS serverless Lambda application. For more information, see [Creating a Serverless Application](https://docs.aws.amazon.com/cloud9/latest/user-guide/latest/user-guide/create-sam.html) in the user guide. | -| `AWS: Create new CloudFormation Template` | Creates a new starter Cloudformation Template | -| `AWS: Create new SAM Template` | Creates a new starter SAM Template | -| `AWS: Deploy SAM Application` | Deploys a local serverless application to an AWS account. For more information, see [Deploying a Serverless Application](https://docs.aws.amazon.com/cloud9/latest/user-guide/deploy-serverless-app.html) in the user guide. | -| `AWS: Edit Credentials` | Opens the `~/.aws/credentials` or `~/.aws/config` file for editing. | -| `AWS: Edit SAM Debug Configuration` | Shows a tool that helps you create, edit, run, and debug a SAM _launch config_ (`type:aws-sam`). | -| `AWS: Detect SAM CLI` | Checks whether the Toolkit can communicate correctly with the AWS SAM CLI that is installed. | -| `AWS: Show or Hide Regions` | Adds or removes AWS Regions in the **AWS Explorer**. | -| `AWS: Sign out` | Disconnect the Toolkit from the current AWS connection. | -| `AWS: Submit Quick Feedback...` | Submit a private, one-way message and sentiment to the AWS Toolkit dev team. For larger issues that warrant conversations or bugfixes, please submit an issue in Github with the **AWS: Create a New Issue on Github** command. | -| `AWS: Toggle SAM hints in source files` | Toggles AWS SAM-related Inline Actions in source files | -| `AWS: View Toolkit Logs` | Displays log files that contain general Toolkit diagnostic information. | -| `AWS: View Quick Start` | Open this quick-start guide. | -| `AWS: View Toolkit Documentation` | Opens the [user guide](https://docs.aws.amazon.com/cloud9/latest/user-guide/toolkit-welcome.html) for the Toolkit. | -| `AWS: View Source on GitHub` | Opens the [GitHub repository](https://github.com/aws/aws-toolkit-vscode) for the Toolkit. | - -{ [Return to Top](#top) } - ---- - -# Get help - -For additional details on how to use the AWS Toolkit, see the [user guide](https://docs.aws.amazon.com/cloud9/latest/user-guide/toolkit-welcome.html). - -To report issues with the Toolkit or to propose Toolkit code changes, see the [aws/aws-toolkit-vscode](https://github.com/aws/aws-toolkit-vscode) repository on GitHub. - -You can also [contact AWS](https://aws.amazon.com/contact-us/) directly. - -{ [Return to Top](#top) } diff --git a/README.quickstart.vscode.md b/README.quickstart.vscode.md deleted file mode 100644 index e82b091080f..00000000000 --- a/README.quickstart.vscode.md +++ /dev/null @@ -1,313 +0,0 @@ -# AWS Toolkit - -The AWS Toolkit extension for Visual Studio Code enables you to interact with [Amazon Web Services (AWS)](https://aws.amazon.com/what-is-aws/). -See the [user guide](https://docs.aws.amazon.com/console/toolkit-for-vscode/welcome) for complete documentation. - -Try the [AWS Code Sample Catalog](https://docs.aws.amazon.com/code-samples/latest/catalog/welcome.html) to start coding with the AWS SDK. - -See [Setup](#additional-setup-steps) for prerequisites. If you run into a problem, try [support](#get-help). - -# Features - -- [AWS Explorer](#ui-components-aws-expl) - - API Gateway - - [App Runner](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/using-apprunner.html) - - CloudFormation stacks - - CloudWatch Logs - - ECR - - [ECS](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/ecs-exec.html) - - EventBridge schemas - - [IoT explorer](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/iot-start.html) - - Lambda functions - - S3 explorer - - Step Functions -- [Developer Tools](#ui-components-dev-tools) - - [CDK Explorer](#ui-components-cdk-expl) - - [CodeWhisperer](#codewhisperer) -- [AWS Serverless Applications (SAM)](#sam-and-lambda) -- [Amazon CloudWatch Logs](#cloudwatchlogs) -- [Amazon EventBridge Schemas](#eventbridge) -- [Amazon ECS task definition files](#ecs-files) -- [AWS Step Functions](#sfn-files) -- [AWS Systems Manager](#ssm-files) -- [`AWS:` Commands](#aws-commands) -- [Experimental Features](#experimental-features) - ---- - -## AWS Explorer - -The **AWS Explorer** provides access to the AWS services that you can work with when using the Toolkit. To see the **AWS Explorer**, choose the **AWS** icon in the **Activity bar**. - -![Overview, AWS Explorer](./resources/marketplace/vscode/overview-aws-explorer.png) - -{ [Return to Top](#top) } - -## Developer Tools - -The **Developer Tools** panel is a section for developer-focused tooling curated for working in an IDE. The **Developer Tools** panel can be found underneath the **AWS Explorer** when the **AWS icon** is selected in the **Activity bar**. - -![Overview, Developer Tools](./resources/marketplace/vscode/overview-codewhisperer.png) - -## { [Return to Top](#top) } - -## CDK Explorer - -The **AWS CDK Explorer** enables you to work with [AWS Cloud Development Kit (CDK)](https://aws.amazon.com/cdk/) applications. It shows a top-level view of your CDK applications that have been sythesized in your workspace. - -![Overview, AWS CDK Explorer](./resources/marketplace/vscode/overview-cdk.png) - -With the CDK explorer, you can navigate the CDK application's infrastructure stacks, resources, and policies. - -![AWS CDK Tree View](./resources/marketplace/vscode/cdk-tree-view.png) - -For full details see the [AWS CDK Explorer](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/cdk-explorer.html) in the user guide. - -{ [Return to Top](#top) } - -## Amazon CodeWhisperer - -**Amazon CodeWhisperer** provides inline code suggestions using machine learning and natural language processing on the contents of your current file. Supported languages include: Java, Python and Javascript. - -Once enabled, CodeWhisperer will provide code suggestions automatically and can also be requested manually using option+c (mac) / alt+c (PC). CodeWhisperer may provide multiple suggestions; use the left- and right-arrow to navigate between suggestions. To accept a suggestion and add it to your file, press Tab. To dismiss a suggestion, press escape or keep typing. - -In addition to providing code suggestions within your current file, CodeWhisperer can scan your entire package to identify security issues. To initiate a security scan, go to the CodeWhisperer section in the **Developer Tools** panel and select “Run security scan”. Results will be listed in the Problems panel. - -![CodeWhisperer](./resources/marketplace/vscode/overview-codewhisperer.png) - -For more information, see [Amazon CodeWhisperer](https://aws.amazon.com/codewhisperer) in our user guide. - -{ [Return to Top](#top) } - -## AWS Serverless Applications - -The AWS Toolkit enables you to develop [AWS serverless applications](https://aws.amazon.com/serverless/) locally. It also provides _CodeLenses_ in VS Code to do the following: - -- Use SAM (serverless application model) templates to build and debug your locally developed AWS serverless applications. -- Run selected [AWS Lambda](https://aws.amazon.com/lambda/) functions. - -To start debugging with a SAM template, click the `Add Debug Configuration` _CodeLens_ in the template file. - -![Add Debug Configuration Template](./resources/marketplace/vscode/Codelens-YAML-template.png) - -###### The _CodeLens_ indicator in the SAM template allows you to add a debug configuration for the serverless application. - -Alternatively, you can run and debug just the AWS Lambda function and exclude other resources defined by the SAM template. Again, use a _CodeLens_ indicator for an AWS Lambda-function handler. (A _handler_ is a function that Lambda calls to start execution of a Lambda function.) - -![Add Debug Configuration Direct](./resources/marketplace/vscode/Codelens-direct-function.png) - -###### The _CodeLens_ indicator in the application file lets you add a debug configuration for a selected AWS Lambda function. - -When you run a debug session, the status and results are shown in the **OUTPUT** panel when the **AWS Toolkit** output channel is selected. - -![Configure and Run](./resources/marketplace/vscode/sam-configure-and-run-still.png) - -###### After a local run is complete, the output appears in the **OUTPUT** tab. - -When you're satisfied with performance, you can [deploy your serverless application](https://docs.aws.amazon.com/console/toolkit-for-vscode/deploy-serverless-app). The SAM template is converted to a CloudFormation template, which is then used to deploy all the application's assets to the AWS Cloud. - -### Launch config auto-completion ("IntelliSense") - -The `Add Debug Configuration` _CodeLens_ creates launch configs of type -`aws-sam` in the VS Code `launch.json` file. You can also create these entries -by hand. - -When editing `launch.json` configs, AWS Toolkit provides auto-completion and -contextual documentation, as shown below. - -Debug Configuration inline documentation - -### Supported runtimes - -The Toolkit _local SAM debugging_ feature supports these Lambda runtimes: - -- C# (.NET Core 2.1, 3.1; .NET 5.0, .NET 6.0) -- Go (1.x) -- Java (8, 8.al2, 11) -- JavaScript/TypeScript (Node.js 12.x, 14.x) -- Python (3.6, 3.7, 3.8, 3.9) - -For more information see [Working with AWS Serverless Applications](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/serverless-apps.html) in the user guide. To get setup for local debugging, see [Configuring your toolchain](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/setup-toolchain.html). - -{ [Return to Top](#top) } - ---- - -## Amazon CloudWatch Logs - -The AWS Toolkit provides support for [Amazon CloudWatch Logs](https://aws.amazon.com/cloudwatch/). Using the [AWS Explorer](#ui-components-aws-expl) of the Toolkit, you can perform the following operations on Log Groups: - -- List CloudWatch Log Groups -- View Log Streams for a Log Group - -Viewing a Log Stream will immediately load the most recent 10,000 lines or 1 MB of data (whichever is smaller), and their timestamps. From this view, users can access the following actions: - -- Load older or newer log events -- Save currently-loaded Log Stream to a log file -- Copy Log Stream name - -Example: - -![View Log Groups](./resources/marketplace/vscode/cloudwatch-logs.png) - -For full details, see [Working with CloudWatch Logs](https://docs.aws.amazon.com/console/toolkit-for-vscode/latest/userguide/cloudwatchlogs.html) in the Toolkit's user guide. - -{ [Return to Top](#top) } - ---- - -## Amazon EventBridge Schemas - -The AWS Toolkit provides support for [Amazon EventBridge](https://aws.amazon.com/eventbridge) schemas. Using the [AWS Explorer](#ui-components-aws-expl) of the Toolkit, you can perform the following operations on these schemas: - -- View an available schema -- Search for an available schema -- Generate code for an available schema - -Example: - -![View Amazon EventBridge Schemas](./resources/marketplace/vscode/eventbridge-search.png) - -For full details, see [Working with Amazon EventBridge Schemas](https://docs.aws.amazon.com/console/toolkit-for-vscode/eventbridge-schemas) in the Toolkit's user guide. - -{ [Return to Top](#top) } - ---- - -## Amazon ECS task definition files - -The AWS Toolkit provides support for [Amazon Elastic Container Service (Amazon ECS)](https://aws.amazon.com/ecs). With the Toolkit installed in VS Code, IntelliSense functionality is provided for Amazon ECS task-definition files that you are updating in the editor. - -Example: - -![Amazon ECS IntelliSense for Task Definition Files](./resources/marketplace/vscode/ecs-task-def-intellisense.png) - -For full details, see [Working with Amazon Elastic Container Service](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/ecs.html) in the Toolkit's user guide. - -{ [Return to Top](#top) } - ---- - -## AWS Systems Manager Documents - -The AWS Toolkit provides support for [AWS Systems Manager Documents](https://docs.aws.amazon.com/systems-manager/latest/userguide/automation-documents.html). With the Toolkit installed in VS Code, you have access to the following features when working on Automation Documents: - -- Download, edit, and publish your Automation documents -- Code completion and validation for both formats of documents: YAML and JSON -- Templates and code snippets to help kickstart your Automation document - -Example: - -![AWS Systems Manager](./resources/marketplace/vscode/ssm-snippets.png) - -For full details, see [Working with AWS Systems Manager Documents](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/ssmDocuments.html) in the Toolkit's user guide. - -{ [Return to Top](#top) } - ---- - -## AWS Step Functions - -The AWS Toolkit provides support for [AWS Step Functions](https://docs.aws.amazon.com/step-functions). With the Toolkit installed in VS Code, working with state machines is a more streamlined process. - -- Create, update, execute, and download state machines. -- See live graph visualizations of your state machine. -- Take advantage of features such as code completion and validation, and code snippets. - -Example: - -![AWS Step Functions](./resources/marketplace/vscode/sfn-state-machine.png) - -For full details, see [Working with AWS Step Functions](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/stepfunctions.html) in the Toolkit's user guide. - -{ [Return to Top](#top) } - ---- - -## `AWS:` Commands - -The Toolkit provides commands (prefixed with `AWS:`) to the VS Code _command -palette_, available by selecting _View > Command Palette_ or by typing -`CTRL-SHIFT-p` (macOS: `CMD-SHIFT-p`). - -![Command Palette](./resources/marketplace/vscode/open-command-palette.gif) - -| AWS Command | Description | -| :--------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `AWS: About Toolkit` | Displays information about the AWS Toolkit. | -| `AWS: Add SAM Debug Configuration` | Creates an `aws-sam` Debug Configuration from a function in the current source file | -| `AWS: Connect to AWS` | Select or create a connection to AWS. For more information, see [Connecting to AWS](https://docs.aws.amazon.com/console/toolkit-for-vscode/connect). | -| `AWS: Copy Log Stream Name` | Copies the name of the active CloudWatch Log Stream | -| `AWS: Create a new Step Functions state machine` | Generates a new Amazon States Language definition to use as the definition for a new Step Functions state machine. For more information, see [State Machine Templates](https://docs.aws.amazon.com//toolkit-for-vscode/latest/userguide/bulding-stepfunctions.html#templates-stepfunctions) in the user guide. | -| `AWS: Create a new Issue on Github` | Opens the AWS Toolkit's [New Issue page on Github](https://github.com/aws/aws-toolkit-vscode/issues/new/choose). | -| `AWS: Create a new Systems Manager Document locally` | Creates a new Systems Manager Document from a template in either YAML or JSON | -| `AWS: Create Credentials Profile` | Creates an AWS credentials profile. For more information, see [Setting Up Your AWS Credentials](https://docs.aws.amazon.com/console/toolkit-for-vscode/setup-credentials) in the user guide. | -| `AWS: Create Lambda SAM Application` | Generates code files for a new AWS serverless Lambda application. For more information, see [Creating a Serverless Application](https://docs.aws.amazon.com/console/toolkit-for-vscode/create-sam) in the user guide. | -| `AWS: Create new CloudFormation Template` | Creates a new starter Cloudformation Template | -| `AWS: Create new SAM Template` | Creates a new starter SAM Template | -| `AWS: Deploy SAM Application` | Deploys a local serverless application to an AWS account. For more information, see [Deploying a Serverless Application](https://docs.aws.amazon.com/console/toolkit-for-vscode/deploy-serverless-app) in the user guide. | -| `AWS: Detect SAM CLI` | Checks whether the Toolkit can communicate correctly with the AWS SAM CLI that is installed. | -| `AWS: Edit Credentials` | Opens the `~/.aws/credentials` or `~/.aws/config` file for editing. | -| `AWS: Edit SAM Debug Configuration` | Shows a tool that helps you create, edit, run, and debug a SAM _launch config_ (`type:aws-sam`). | -| `AWS: Focus on Explorer View` | Opens the **AWS** panel and focuses the [_Explorer_](#ui-components-aws-expl). | -| `AWS: Publish state machine to Step Functions` | Creates or updates a remote state machine using the local Amazon States Language definition file. For more information, see [Work With State Machines in VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/bulding-stepfunctions.html#starting-stepfunctions) in the user guide. | -| `AWS: Publish a Systems Manager Document` | Creates a new document or updates an existing document in your account using the currently open document. | -| `AWS: Render state machine graph` | Renders the state machine definition into a graph visualization. For more information, see [State Machine Graph Visualization](https://docs.aws.amazon.com//toolkit-for-vscode/latest/userguide/bulding-stepfunctions.html#bulding-stepfunctions-visualizations) in the user guide. | -| `AWS: Report an Issue` | In the [GitHub repository](https://github.com/aws/aws-toolkit-vscode) for the Toolkit, opens the page to [create a new issue](https://github.com/aws/aws-toolkit-vscode/issues/new/choose). | -| `AWS: Save Current Log Content To File` | Saves currently-loaded CloudWatch Log Stream data to a local `.log` file. | -| `AWS: Show or Hide Regions` | Adds or removes AWS Regions in the **AWS Explorer**. | -| `AWS: Sign out` | Disconnect the Toolkit from the current AWS connection. | -| `AWS: Submit Quick Feedback...` | Submit a private, one-way message and sentiment to the AWS Toolkit dev team. For larger issues that warrant conversations or bugfixes, please submit an issue in Github with the **AWS: Create a New Issue on Github** command. | -| `AWS: Toggle SAM hints in source files` | Toggles AWS SAM-related Codelenses in source files | -| `AWS: View Toolkit Logs` | Displays log files that contain general Toolkit diagnostic information. | -| `AWS: View Quick Start` | Open this quick-start guide. | -| `AWS: View CDK Documentation` | Opens the [user guide](https://docs.aws.amazon.com/console/toolkit-for-vscode/aws-cdk-apps) for the CDK portion of the Toolkit. | -| `AWS: View Toolkit Documentation` | Opens the [user guide](https://docs.aws.amazon.com/console/toolkit-for-vscode/welcome) for the Toolkit. | -| `AWS: View Source on GitHub` | Opens the [GitHub repository](https://github.com/aws/aws-toolkit-vscode) for the Toolkit. | -| `AWS: Focus on CDK (Preview) View` | Opens the **AWS** panel and focuses the **CDK** view. | - -{ [Return to Top](#top) } - ---- - -# Experimental Features - -Sometimes we'll introduce experimental features that we're trying out. These may have bugs, usability problems or may not be fully functional, and because these -aren't ready for prime-time we'll hide them behind an experimental feature flag. - -Experimental features can be enabled in the extension preferences -(`Preferences -> Settings -> Extensions -> AWS Configuration -> Experiments`). - -Please note that experimental features may be disabled or removed at any time. - -{ [Return to Top](#top) } - ---- - -# Setup - -To access most features of the AWS Toolkit, complete the [Setting Up](https://docs.aws.amazon.com/console/toolkit-for-vscode/getting-started) steps from the user guide. - -1. [Create an AWS account](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/) (see also [Prerequisites](https://docs.aws.amazon.com/console/toolkit-for-vscode/setup-toolkit#setup-prereq)). -1. [Create and configure](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/establish-credentials.html) your AWS credentials. -1. [Connect the Toolkit](https://docs.aws.amazon.com/console/toolkit-for-vscode/connect) to AWS with those credentials. - -To develop [serverless applications](https://aws.amazon.com/serverless/) with the Toolkit, you must [set up your toolchain](https://docs.aws.amazon.com/console/toolkit-for-vscode/setup-toolchain) and do the following on the local machine where the Toolkit is installed: - -1. Install the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html) (Command Line Interface). -1. Install and start [Docker](https://docs.docker.com/install/). -1. Install the AWS [SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). - -{ [Return to Top](#top) } - ---- - -# Get help - -For additional details on how to use the AWS Toolkit, see the [user guide](https://docs.aws.amazon.com/console/toolkit-for-vscode/welcome). - -To report issues with the Toolkit or to propose Toolkit code changes, see the [aws/aws-toolkit-vscode](https://github.com/aws/aws-toolkit-vscode) repository on GitHub. - -You can also [contact AWS](https://aws.amazon.com/contact-us/) directly. - -{ [Return to Top](#top) } diff --git a/aws-toolkit-vscode.code-workspace b/aws-toolkit-vscode.code-workspace new file mode 100644 index 00000000000..f03aafae2fe --- /dev/null +++ b/aws-toolkit-vscode.code-workspace @@ -0,0 +1,19 @@ +{ + "folders": [ + { + "path": ".", + }, + { + "path": "packages/toolkit", + }, + { + "path": "packages/core", + }, + { + "path": "packages/amazonq", + }, + ], + "settings": { + "typescript.tsdk": "node_modules/typescript/lib", + }, +} diff --git a/buildspec/lint.yml b/buildspec/lint.yml new file mode 100644 index 00000000000..d44799d1186 --- /dev/null +++ b/buildspec/lint.yml @@ -0,0 +1,40 @@ +version: 0.2 + +# Run unprivileged for most phases (except those marked "run-as: root"). +run-as: codebuild-user + +env: + variables: + # Implicitly passed by the AWS automation pipeline: + # VSCODE_TEST_VERSION + # GITHUB_READONLY_TOKEN + AWS_TOOLKIT_TEST_NO_COLOR: '1' + +phases: + install: + run-as: root + runtime-versions: + nodejs: 16 + commands: + - bash buildspec/shared/linux-install.sh + + pre_build: + commands: + - export HOME=/home/codebuild-user + - bash buildspec/shared/linux-pre_build.sh + + build: + commands: + - export HOME=/home/codebuild-user + - npm run compile -w packages/core + - npm run testCompile -w packages/ --if-present + - npm run lint + - VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}" + - CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g') # Encode `#` in the URL because otherwise the url is clipped in the Codecov.io site + - CI_BUILD_ID="${CODEBUILD_BUILD_ID}" + +reports: + unit-test: + files: + - '*' + base-directory: '.test-reports' diff --git a/buildspec/linuxE2ETests.yml b/buildspec/linuxE2ETests.yml new file mode 100644 index 00000000000..4cb2f08f03d --- /dev/null +++ b/buildspec/linuxE2ETests.yml @@ -0,0 +1,53 @@ +version: 0.2 + +# Run unprivileged for most phases (except those marked "run-as: root"). +run-as: codebuild-user + +env: + variables: + AWS_TOOLKIT_TEST_NO_COLOR: '1' + # Suppress noisy apt-get/dpkg warnings like "debconf: unable to initialize frontend: Dialog"). + DEBIAN_FRONTEND: 'noninteractive' + # Required dir overrides, otherwise the test will likely fail due to too long path names. + # E.g. WARNING: IPC handle is longer than 107 chars, try a shorter --user-data-dir + # followed by Error: Could not delete obsolete instance handle Error: ENOENT: no such file or directory, unlink + AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/' + AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/' + +phases: + install: + run-as: root + runtime-versions: + nodejs: 16 + commands: + # - '>/dev/null add-apt-repository universe' + # - '>/dev/null apt-get -qq install -y apt-transport-https' + # - '>/dev/null apt-get -qq update' + # - '>/dev/null apt-get -qq install -y ca-certificates' + # - 'apt-get install --reinstall ca-certificates' + - bash buildspec/shared/linux-install.sh + # increase file watcher count (ENOSPC error) + - sysctl fs.inotify.max_user_watches=524288 + + pre_build: + commands: + - export HOME=/home/codebuild-user + - bash buildspec/shared/setup-github-token.sh + - bash buildspec/shared/linux-pre_build.sh + + build: + commands: + - export HOME=/home/codebuild-user + # Ignore failure until throttling issues are fixed. + - xvfb-run npm run testE2E; npm run mergeReports -- "$?" + - VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}" + - CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g') + - CI_BUILD_ID="${CODEBUILD_BUILD_ID}" + - test -n "${CODECOV_TOKEN}" && [ "$TARGET_BRANCH" = "master" ] && ./codecov --token=${CODECOV_TOKEN} --branch=${CODEBUILD_RESOLVED_SOURCE_VERSION} --repository=${CODEBUILD_SOURCE_REPO_URL} --file=./coverage/amazonq/lcov.info --file=./coverage/toolkit/lcov.info + finally: + - rm -rf ~/.aws/sso/cache || true +reports: + e2e-test: + files: + - '*' + base-directory: '.test-reports' diff --git a/buildspec/linuxIntegrationTests.yml b/buildspec/linuxIntegrationTests.yml index c7652f841f8..dacab125b89 100644 --- a/buildspec/linuxIntegrationTests.yml +++ b/buildspec/linuxIntegrationTests.yml @@ -1,74 +1,106 @@ version: 0.2 +# Run unprivileged for most phases (except those marked "run-as: root"). +run-as: codebuild-user + env: variables: - AWS_TOOLKIT_TEST_USER_DIR: '/tmp/' + # Implicitly passed by the AWS automation pipeline: + # VSCODE_TEST_VERSION + # GITHUB_READONLY_TOKEN AWS_TOOLKIT_TEST_NO_COLOR: '1' - NO_COVERAGE: 'true' # Suppress noisy apt-get/dpkg warnings like "debconf: unable to initialize frontend: Dialog"). DEBIAN_FRONTEND: 'noninteractive' + # Required dir overrides, otherwise the test will likely fail due to too long path names. + # E.g. WARNING: IPC handle is longer than 107 chars, try a shorter --user-data-dir + # followed by Error: Could not delete obsolete instance handle Error: ENOENT: no such file or directory, unlink + AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/' + AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/' phases: install: + run-as: root runtime-versions: - nodejs: 16 + nodejs: 18 dotnet: 6.0 java: latest commands: + - bash buildspec/shared/linux-install.sh - '>/dev/null add-apt-repository universe' - '>/dev/null apt-get -qq install -y apt-transport-https' - '>/dev/null apt-get -qq update' - '>/dev/null apt-get -qq install -y ca-certificates' - 'apt-get install --reinstall ca-certificates' - - 'add-apt-repository -y ppa:deadsnakes/ppa' # Other dependencies. - - 'apt-get -qq install -y jq python3.7 python3.8 python3-pip' - # Fail early if any of these not found. - - 'python3.7 --version' - - 'python3.8 --version' - # Dependencies for running vscode. - - '>/dev/null apt-get -yqq install libatk1.0-0 libgtk-3-dev libxss1 xvfb libnss3-dev libasound2 libasound2-plugins libsecret-1-0' - - '>/dev/null pip3 install --upgrade aws-sam-cli' - # Print info about sam (version, location, …). - - 'pip3 show aws-sam-cli' - - '>/dev/null pip3 install --upgrade awscli' - - '>/dev/null pip3 install pylint' - # Install latest version of Go (known to 'goenv') - - '>/dev/null VERSION=$(goenv install --list | tail -n 1) && 2>/dev/null goenv install $VERSION' - - '>/dev/null goenv global $VERSION && go env -w GOPROXY=direct' - - go version + - 'apt-get -qq install -y jq' + - | + curl --silent -LO https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip + unzip -q aws-sam-cli-linux-x86_64.zip -d samcli + sudo ./samcli/install + rm -rf samcli # login to DockerHub so we don't get throttled - - docker login --username $(echo $DOCKER_HUB_TOKEN | jq -r '.username') --password $(echo $DOCKER_HUB_TOKEN | jq -r '.password') || true + # - docker login --username $(echo $DOCKER_HUB_TOKEN | jq -r '.username') --password $(echo $DOCKER_HUB_TOKEN | jq -r '.password') || true # increase file watcher count so CodeLens tests do not fail unexpectedly (ENOSPC error) - sysctl fs.inotify.max_user_watches=524288 + # start Docker + # - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay& + - timeout 15 sh -c "until docker info; do echo .; sleep 1; done" + # Add user to "docker" group. + # - usermod -aG docker codebuild-user + # Ensure that "docker" group has permissions to the socket. + # - chown codebuild-user /var/run/docker.sock + - chmod 666 /var/run/docker.sock + # Pull Docker Images for SAM tests + + # Nodejs + - | + docker pull public.ecr.aws/sam/build-nodejs18.x:latest + docker pull public.ecr.aws/sam/build-nodejs20.x:latest + docker pull public.ecr.aws/sam/build-nodejs22.x:latest + # Java + - | + docker pull public.ecr.aws/sam/build-java8.al2:latest + docker pull public.ecr.aws/sam/build-java11:latest + docker pull public.ecr.aws/sam/build-java17:latest + # Python + - | + docker pull public.ecr.aws/sam/build-python3.10:latest + docker pull public.ecr.aws/sam/build-python3.11:latest + docker pull public.ecr.aws/sam/build-python3.12:latest + docker pull public.ecr.aws/sam/build-python3.13:latest + # Dotnet + - | + docker pull public.ecr.aws/sam/build-dotnet6:latest pre_build: commands: - # If present, log into CodeArtifact. Provides a nice safety net in case NPM is down. - # Should only affect tests run through IDEs team-hosted CodeBuild. + - export HOME=/home/codebuild-user + - bash buildspec/shared/setup-github-token.sh + - bash buildspec/shared/linux-pre_build.sh + # Print info (version, location, …) or fail early. - | - if [ "$TOOLKITS_CODEARTIFACT_DOMAIN" ] && [ "$TOOLKITS_CODEARTIFACT_REPO" ] && [ "TOOLKITS_$ACCOUNT_ID" ]; then - if aws codeartifact login --tool npm --domain "$TOOLKITS_CODEARTIFACT_DOMAIN" --domain-owner "$TOOLKITS_ACCOUNT_ID" --repository "$TOOLKITS_CODEARTIFACT_REPO" > /dev/null 2>&1; then - echo "Connected to CodeArtifact" - else - echo "CodeArtifact connection failed. Falling back to npm" - fi - fi - # make sure that SAM is in the path, is not automatically done on CodeBuild - - USER_BASE_PATH=$(python -m site --user-base) && export PATH=$PATH:$USER_BASE_PATH/bin - # start Docker - - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay& - - timeout 15 sh -c "until docker info; do echo .; sleep 1; done" + python3 --version + sam --version + # Install latest version of Go (known to 'goenv') + # - eval "$(goenv init -)" + # - 'export PATH="$GOROOT/bin:$PATH:$GOPATH/bin"' + # - '>/dev/null VERSION=$(goenv install --list | tail -n 1) && 2>/dev/null goenv install $VERSION' + # - '>/dev/null goenv global $VERSION && go env -w GOPROXY=direct' + # - go version build: commands: - - npm ci --unsafe-perm - - xvfb-run npm run integrationTest + - export HOME=/home/codebuild-user + - xvfb-run npm run testInteg; npm run mergeReports -- "$?" - VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}" - CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g') - CI_BUILD_ID="${CODEBUILD_BUILD_ID}" - + - test -n "${CODECOV_TOKEN}" && [ "$TARGET_BRANCH" = "master" ] && ./codecov --token=${CODECOV_TOKEN} --branch=${CODEBUILD_RESOLVED_SOURCE_VERSION} --repository=${CODEBUILD_SOURCE_REPO_URL} --file=./coverage/amazonq/lcov.info --file=./coverage/toolkit/lcov.info + post_build: + commands: + # Destroy .netrc to avoid leaking $GITHUB_READONLY_TOKEN. + - rm "$HOME/.netrc" reports: integ-test: files: diff --git a/buildspec/linuxTests.yml b/buildspec/linuxTests.yml index e3b444c129d..241b5bb193a 100644 --- a/buildspec/linuxTests.yml +++ b/buildspec/linuxTests.yml @@ -1,43 +1,54 @@ version: 0.2 +# Run unprivileged for most phases (except those marked "run-as: root"). +run-as: codebuild-user + env: + # For "pipefail". + shell: bash variables: - AWS_TOOLKIT_TEST_USER_DIR: '/tmp/' AWS_TOOLKIT_TEST_NO_COLOR: '1' + # Required dir overrides, otherwise the test will likely fail due to too long path names. + # E.g. WARNING: IPC handle is longer than 107 chars, try a shorter --user-data-dir + # followed by Error: Could not delete obsolete instance handle Error: ENOENT: no such file or directory, unlink + AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/' + AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/' phases: install: + run-as: root runtime-versions: - nodejs: 16 - + nodejs: 18 commands: - # Without this, "Unable to locate package libatk1.0-0". - - '>/dev/null apt-get -yqq update' - # Dependencies for running vscode. - - '>/dev/null apt-get -yqq install libatk1.0-0 libgtk-3-dev libxss1 xvfb libnss3-dev libasound2 libasound2-plugins libsecret-1-0' + - bash buildspec/shared/linux-install.sh pre_build: commands: - # If present, log into CodeArtifact. Provides a nice safety net in case NPM is down. - # Should only affect tests run through IDEs team-hosted CodeBuild. - - | - if [ "$TOOLKITS_CODEARTIFACT_DOMAIN" ] && [ "$TOOLKITS_CODEARTIFACT_REPO" ] && [ "$TOOLKITS_ACCOUNT_ID" ]; then - if aws codeartifact login --tool npm --domain "$TOOLKITS_CODEARTIFACT_DOMAIN" --domain-owner "$TOOLKITS_ACCOUNT_ID" --repository "$TOOLKITS_CODEARTIFACT_REPO" > /dev/null 2>&1; then - echo "Connected to CodeArtifact" - else - echo "CodeArtifact connection failed. Falling back to npm" - fi - fi - - npm ci --unsafe-perm + - export HOME=/home/codebuild-user + - bash buildspec/shared/linux-pre_build.sh build: commands: - - npm run testCompile - - npm run lint - - xvfb-run npm test --silent + - export HOME=/home/codebuild-user + # TODO: Enable if we run into issues with GHA linting or remove. + # - | + # if [ "$VSCODE_TEST_VERSION" = 'insiders' ] ; then + # npm run testCompile + # npm run lint + # fi + - | + { + # Ensure that "foo | run_and_report" fails correctly. + set -o pipefail + . buildspec/shared/common.sh + { 2>&1 xvfb-run npm test --silent; npm run mergeReports -- "$?"; } | run_and_report 2 \ + 'rejected promise not handled' \ + 'This typically indicates a bug. Read https://developer.mozilla.org/docs/Web/JavaScript/Guide/Using_promises#error_handling' + } - VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}" - CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g') # Encode `#` in the URL because otherwise the url is clipped in the Codecov.io site - CI_BUILD_ID="${CODEBUILD_BUILD_ID}" + - test -n "${CODECOV_TOKEN}" && [ "$TARGET_BRANCH" = "master" ] && ./codecov --token=${CODECOV_TOKEN} --branch=${CODEBUILD_RESOLVED_SOURCE_VERSION} --repository=${CODEBUILD_SOURCE_REPO_URL} --file=./coverage/amazonq/lcov.info --file=./coverage/toolkit/lcov.info || true reports: unit-test: diff --git a/buildspec/packageTestVsix.yml b/buildspec/packageTestVsix.yml index a70f2493b3c..73f15c0ba10 100644 --- a/buildspec/packageTestVsix.yml +++ b/buildspec/packageTestVsix.yml @@ -1,5 +1,8 @@ version: 0.2 +# Run unprivileged for most phases (except those marked "run-as: root"). +run-as: codebuild-user + env: variables: AWS_TOOLKIT_TEST_USER_DIR: '/tmp/' @@ -8,33 +11,24 @@ env: phases: install: + run-as: root runtime-versions: - nodejs: 16 + nodejs: 18 + commands: + - bash buildspec/shared/linux-install.sh pre_build: commands: - # If present, log into CodeArtifact. Provides a nice safety net in case NPM is down. - # Should only affect tests run through IDEs team-hosted CodeBuild. - - | - if [ "$TOOLKITS_CODEARTIFACT_DOMAIN" ] && [ "$TOOLKITS_CODEARTIFACT_REPO" ] && [ "$TOOLKITS_ACCOUNT_ID" ]; then - if aws codeartifact login --tool npm --domain "$TOOLKITS_CODEARTIFACT_DOMAIN" --domain-owner "$TOOLKITS_ACCOUNT_ID" --repository "$TOOLKITS_CODEARTIFACT_REPO" > /dev/null 2>&1; then - echo "Connected to CodeArtifact" - else - echo "CodeArtifact connection failed. Falling back to npm" - fi - fi - # --unsafe-perm is needed because CodeBuild/CodePipeline runs as root - - npm ci --unsafe-perm + - export HOME=/home/codebuild-user + - bash buildspec/shared/linux-pre_build.sh build: commands: + - export HOME=/home/codebuild-user # Generate CHANGELOG.md - - npm run createRelease - - npm run generateNonCodeFiles - - cp ./README.quickstart.vscode.md ./README.md - - npm run package + - npm run createRelease -w packages/toolkit -w packages/amazonq + - npm run package -w packages/toolkit -w packages/amazonq artifacts: files: - - aws-toolkit-vscode* - discard-paths: true + - '*.vsix' diff --git a/buildspec/shared/common.sh b/buildspec/shared/common.sh new file mode 100644 index 00000000000..a0dbc2e5837 --- /dev/null +++ b/buildspec/shared/common.sh @@ -0,0 +1,49 @@ +#!/bin/env bash + +# Common functions used by other CI scripts. +# "Include" this file by sourcing (not executing) it: +# . buildspec/shared/common.sh + +# Ignore these patterns when deciding if the build should fail. +# - "waiting for browser": from `ssoAccessTokenProvider.test.ts`, unclear how to fix it. +# - "HTTPError: Response code …": caused by github rate-limiting. +# - "npm WARN deprecated querystring": transitive dep of aws sdk v2 (check `npm ls querystring`), so that's blocked until we migrate to v3. +_ignore_pat='HTTPError: Response code 403\|HTTPError: Response code 404\|npm WARN deprecated querystring\|npm WARN deprecated' + +# Do not print (noisy) lines matching these patterns. +# - "ERROR:bus… Failed to connect to the bus": noise related to "xvfb". https://github.com/cypress-io/cypress/issues/19299 +_discard_pat='ERROR:bus.cc\|ERROR:viz_main_impl.cc\|ERROR:command_buffer_proxy_impl.cc' + +# Expects stdin + two args: +# 1: error code to return on failure +# 2: grep pattern +# 3: message shown at end of report, if pattern was found in stdin. +# Usage: +# echo foo | run_and_report 1 '.*' 'You must fix this. See https://example.com' +run_and_report() { + set -o pipefail + local errcode="${1}" + local pat="${2}" + local msg="${3}" + local r=0 + mkfifo testout + (grep -v "$_discard_pat" testout &) + # Capture messages that we may want to fail (or report) later. + tee testout \ + | { grep > testout-err --line-buffered -E "$pat" || true; } + + echo '' + + if grep -v "${_ignore_pat}" testout-err | grep "$pat" | sort; then + printf '\nERROR: Test output matched this pattern %s times:\n "%s"\n%s\n\n' \ + "$(grep -c "${pat}" testout-err)" \ + "$pat" \ + " ${msg}" + r=${errcode} + else + printf '\nOK: Not found in test output: "%s"\n' "$pat" + fi + + rm -f testout testout-err + return "$r" +} diff --git a/buildspec/shared/linux-install.sh b/buildspec/shared/linux-install.sh new file mode 100644 index 00000000000..e50c363bf84 --- /dev/null +++ b/buildspec/shared/linux-install.sh @@ -0,0 +1,29 @@ +#!/bin/env bash + +# Common code for "install" phase of linux codebuild CI job. + +set -e + +set +x +test -n "$VSCODE_TEST_VERSION" || { + echo 'missing $VSCODE_TEST_VERSION' + exit 1 +} +set -x + +# Without this, "Unable to locate package libatk1.0-0". +apt-get > /dev/null -yqq update +# Dependencies for running vscode. +apt-get > /dev/null -yqq install libatk1.0-0 libgtk-3-dev libxss1 xvfb libasound2 libasound2-plugins + +# +# Prepare env for unprivileged user. We cannot run vscode as root. +# +# "codebuild-user": https://github.com/aws/aws-codebuild-docker-images/blob/2f796bb9c81fcfbc8585832b99a5f780ae2b2f52/ubuntu/standard/6.0/Dockerfile#L56 +mkdir -p ~codebuild-user +chown -R codebuild-user:codebuild-user /tmp ~codebuild-user . +chmod +x ~codebuild-user +ls -ld ~codebuild-user + +curl -Os https://uploader.codecov.io/latest/linux/codecov +chmod +x codecov diff --git a/buildspec/shared/linux-pre_build.sh b/buildspec/shared/linux-pre_build.sh new file mode 100644 index 00000000000..102103ff30c --- /dev/null +++ b/buildspec/shared/linux-pre_build.sh @@ -0,0 +1,26 @@ +#!/bin/env bash + +# Common code for "pre_build" phase of linux codebuild CI job. + +set -e +# Ensure that "foo | run_and_report" fails correctly. +set -o pipefail + +_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Include common functions. +. "${_SCRIPT_DIR}/common.sh" + +# If present, log into CodeArtifact. Provides a fallback in case NPM is down. +# Should only affect tests run through Toolkits-hosted CodeBuild. +if [ "$TOOLKITS_CODEARTIFACT_DOMAIN" ] && [ "$TOOLKITS_CODEARTIFACT_REPO" ] && [ "$TOOLKITS_ACCOUNT_ID" ]; then + if aws codeartifact login --tool npm --domain "$TOOLKITS_CODEARTIFACT_DOMAIN" --domain-owner "$TOOLKITS_ACCOUNT_ID" --repository "$TOOLKITS_CODEARTIFACT_REPO" > /dev/null 2>&1; then + echo "Connected to CodeArtifact" + else + echo "CodeArtifact connection failed. Falling back to npm" + fi +fi + +# TODO: move this to the "install" phase? +export NODE_OPTIONS='--max-old-space-size=8192' +npm 2>&1 ci | run_and_report 2 'npm WARN deprecated' 'Deprecated dependencies must be updated.' diff --git a/buildspec/shared/setup-github-token.sh b/buildspec/shared/setup-github-token.sh new file mode 100755 index 00000000000..7279a757d15 --- /dev/null +++ b/buildspec/shared/setup-github-token.sh @@ -0,0 +1,21 @@ +#!/bin/env bash + +set -e + +test -n "$GITHUB_READONLY_TOKEN" || { + echo 'missing $GITHUB_READONLY_TOKEN' + exit 1 +} + +# Authenticate all "git" (and "curl --netrc") github calls, to avoid rate-limiting. +# NOTE: the "login" value is arbitrary. +printf "machine github.com login oauth password ${GITHUB_READONLY_TOKEN:-unknown}\n" >> "$HOME/.netrc" +printf "machine api.github.com login oauth password ${GITHUB_READONLY_TOKEN:-unknown}\n" >> "$HOME/.netrc" +printf "machine uploads.github.com login oauth password ${GITHUB_READONLY_TOKEN:-unknown}\n" >> "$HOME/.netrc" +# Print ratelimit info. +curl 2> /dev/null --netrc -L -I https://api.github.com/ | grep x-ratelimit | sed 's/\(.*\)/ \1/' +# Validate ratelimit. +curl 2> /dev/null --netrc -L -I https://api.github.com/ | grep > /dev/null 'x-ratelimit-limit: *[0-9][0-9][0-9][0-9]\+' || { + echo 'invalid github token, or rate limit too low (expected 5000+)' + exit 1 +} diff --git a/buildspec/windowsTests.yml b/buildspec/windowsTests.yml index ef81fb7a837..4b19dec5eb2 100644 --- a/buildspec/windowsTests.yml +++ b/buildspec/windowsTests.yml @@ -5,10 +5,10 @@ env: phases: install: runtime-versions: - nodejs: 16 + nodejs: 18 commands: - | - if(-Not($Env:CODE_COV_TOKEN -eq $null)) { + if(-Not($Env:CODECOV_TOKEN -eq $null)) { choco install -y --no-progress codecov } @@ -30,15 +30,16 @@ phases: build: commands: - - npm run testCompile + - npm run compile -w packages/core + - npm run testCompile -w packages/ --if-present - npm run lint - $env:TEST_REPORT_DIR="$env:CODEBUILD_SRC_DIR/.test_reports"; npm run test - | - if(-Not($Env:CODEBUILD_BUILD_SUCCEEDING -eq "0" -Or $Env:CODE_COV_TOKEN -eq $null)) { + if(-Not($Env:CODEBUILD_BUILD_SUCCEEDING -eq "0" -Or $Env:CODECOV_TOKEN -eq $null)) { $env:VCS_COMMIT_ID=$Env:CODEBUILD_RESOLVED_SOURCE_VERSION; $env:CI_BUILD_URL=[uri]::EscapeUriString($Env:CODEBUILD_BUILD_URL); $env:CI_BUILD_ID=$Env:CODEBUILD_BUILD_ID; - codecov -t $Env:CODE_COV_TOKEN ` + codecov -t $Env:CODECOV_TOKEN ` --flag unittest ` -f "build/reports/jacoco/coverageReport/coverageReport.xml" ` -c $Env:CODEBUILD_RESOLVED_SOURCE_VERSION diff --git a/cloud9-toolkit-install.sh b/cloud9-toolkit-install.sh deleted file mode 100644 index 8b85a56a63a..00000000000 --- a/cloud9-toolkit-install.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/env bash - -# By default, this script gets the latest VSIX from: -# https://github.com/aws/aws-toolkit-vscode/releases/ -# else the first argument must be a URL or file pointing to a toolkit VSIX or -# ZIP (containing a VSIX). -# -# USAGE: -# cloud9-toolkit-install.sh [URL|FILE] -# curl -LO https://raw.githubusercontent.com/aws/aws-toolkit-vscode/master/cloud9-toolkit-install.sh && bash cloud9-toolkit-install.sh -# EXAMPLES: -# cloud9-toolkit-install.sh https://github.com/aws/aws-toolkit-vscode/releases/download/v1.24.0/aws-toolkit-vscode-1.24.0.vsix -# cloud9-toolkit-install.sh toolkit.zip - -set -eu - -# Script currently depends on $HOME so running as root is not supported. -if [ "$(whoami)" = root ]; then - echo "cannot run as root" - exit 1 -fi - -_log() { - echo >&2 "$@" -} - -# Runs whatever is passed. -# -# On failure: -# - prints the command output -# - exits the script -_run() { - local out - if ! out="$("$@" 2>&1)"; then - _log "Command failed (output below): '${*}'" - echo "$out" | sed 's/^/ /' - _log "Command failed (output above): '${*}'" - exit 1 - fi -} - -# Gets the first existing directory, or exits if none are found. -_any_dir() { - for d in "$@"; do - if test -d "$d"; then - echo "$d" - return - fi - done - - _log "error: None of the expected dirs exist:" - for d in "$@"; do - _log " $d" - done - exit 1 -} - -_setglobals() { - # Example: - # https://github.com/aws/aws-toolkit-vscode/releases/tag/v1.24.0 - TOOLKIT_LATEST_RELEASE_URL="$(curl -Ls -o /dev/null -w '%{url_effective}' 'https://github.com/aws/aws-toolkit-vscode/releases/latest')" - # Example: - # 1.24.0 - TOOLKIT_LATEST_VERSION="$(echo "$TOOLKIT_LATEST_RELEASE_URL" | grep -oh '[0-9]\+\.[0-9]\+\.[0-9]\+$')" - # Example: - # https://github.com/aws/aws-toolkit-vscode/releases/download/v1.24.0/aws-toolkit-vscode-1.24.0.vsix - TOOLKIT_LATEST_ARTIFACT_URL="https://github.com/aws/aws-toolkit-vscode/releases/download/v${TOOLKIT_LATEST_VERSION}/aws-toolkit-vscode-${TOOLKIT_LATEST_VERSION}.vsix" - # URL or local filepath pointing to toolkit VSIX or ZIP (containing a VSIX). - TOOLKIT_FILE=${1:-} - TOOLKIT_INSTALL_PARENT="$(_any_dir "/projects/.c9/aws-toolkit-vscode" "$HOME/.c9/dependencies/aws-toolkit-vscode" "$HOME/environment/.c9/extensions" "$HOME/.c9/aws-toolkit-vscode")" - # Hash name is 128 chars long. - TOOLKIT_INSTALL_DIR="$(_any_dir "$(realpath ${TOOLKIT_INSTALL_PARENT}/????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????)" "$(realpath ${TOOLKIT_INSTALL_PARENT}/extension)")" - SCRIPT_WORKDIR="$HOME/toolkit" -} - -_main() { - ( - if test -f "$TOOLKIT_FILE"; then - # Ensure full path (before `cd` below). - TOOLKIT_FILE="$(readlink -f "$TOOLKIT_FILE")" - fi - - echo "Script will DELETE these directories:" - echo " ${TOOLKIT_INSTALL_DIR}" - echo " ${SCRIPT_WORKDIR}" - read -n1 -r -p "ENTER to continue, CTRL-c to cancel" - rm -rf "${TOOLKIT_INSTALL_DIR}.old" - mv "$TOOLKIT_INSTALL_DIR" "${TOOLKIT_INSTALL_DIR}.old" - rm -rf "$SCRIPT_WORKDIR" - - cd "$HOME/" - mkdir -p "$SCRIPT_WORKDIR" - mkdir -p "$TOOLKIT_INSTALL_PARENT" - cd "${SCRIPT_WORKDIR}" - - # Set default URL if no argument was provided. - if test -z "$TOOLKIT_FILE"; then - _log "Latest release:" - _log " URL : $TOOLKIT_LATEST_RELEASE_URL" - _log " version: $TOOLKIT_LATEST_VERSION" - _log " VSIX : $TOOLKIT_LATEST_ARTIFACT_URL" - TOOLKIT_FILE="$TOOLKIT_LATEST_ARTIFACT_URL" - fi - - TOOLKIT_FILE_EXTENSION="${TOOLKIT_FILE: -4}" - if test -f "$TOOLKIT_FILE"; then - _log "Local file (not URL): ${TOOLKIT_FILE}" - if [ "$TOOLKIT_FILE_EXTENSION" = ".zip" ] || [ "$TOOLKIT_FILE_EXTENSION" = ".ZIP" ]; then - _log 'File is a .zip file' - _run unzip -- "$TOOLKIT_FILE" - _run unzip -- *.vsix - else - _log 'File is not .zip file, assuming .vsix' - _run unzip -- "$TOOLKIT_FILE" - fi - else - _log "URL: ${TOOLKIT_FILE}" - _log 'Deleting toolkit.zip' - rm -rf toolkit.zip - _log 'Downloading...' - curl -o toolkit.zip -L "$TOOLKIT_FILE" - if [ "$TOOLKIT_FILE_EXTENSION" = ".zip" ] || [ "$TOOLKIT_FILE_EXTENSION" = ".ZIP" ]; then - _log 'File is a .zip file' - _run unzip -- toolkit.zip - _run unzip -- *.vsix - else - _log 'File is not .zip file, assuming .vsix' - _run unzip -- toolkit.zip - fi - fi - - mv extension "$TOOLKIT_INSTALL_DIR" - _log "Toolkit installed to: $TOOLKIT_INSTALL_DIR" - _log "Refresh Cloud9 to load it" - ) -} - -_setglobals "$@" -_main diff --git a/codecov.yml b/codecov.yml index 266a3af1d18..1e348859f11 100644 --- a/codecov.yml +++ b/codecov.yml @@ -5,6 +5,12 @@ codecov: notify: require_ci_to_pass: no +ignore: + - 'node_modules/' + - 'plugins/' + - 'scripts/' + - 'src.gen/' + coverage: precision: 2 round: down @@ -12,13 +18,93 @@ coverage: status: project: + # Note: `default` measures the entire project. + # It does NOT define defaults "inherited" by other `project` items. + # https://docs.codecov.com/docs/commit-status + default: + informational: true # Always pass. Ignore `target`, `threshold`, etc. + target: 80 + threshold: 5 + only_pulls: true + codewhisperer: + informational: true # Always pass. Ignore `target`, `threshold`, etc. + paths: + - 'packages/core/src/codewhisperer/' + - 'packages/core/src/amazonq*/*' + - 'packages/amazonq/src/' + flags: + - 'codewhisperer' + target: 80 + threshold: 5 + only_pulls: true + amazonqFeatureDev: + informational: true # Always pass. Ignore `target`, `threshold`, etc. + paths: + - 'packages/core/src/amazonqFeatureDev/*' + flags: + - 'amazonqFeatureDev' + target: 80 + threshold: 5 + only_pulls: true + amazonqGumby: + informational: true # Always pass. Ignore `target`, `threshold`, etc. + paths: + - 'packages/core/src/amazonqGumby/*' + target: 80 + threshold: 5 + only_pulls: true + codewhispererChat: + informational: true # Always pass. Ignore `target`, `threshold`, etc. + paths: + - 'packages/core/src/codewhispererChat/*' + target: 80 + threshold: 5 + only_pulls: true + applicationcomposer: + informational: true # Always pass. Ignore `target`, `threshold`, etc. + paths: + - 'packages/core/src/applicationcomposer/*' + target: 80 + threshold: 5 + only_pulls: true + stepFunctions: + informational: true # Always pass. Ignore `target`, `threshold`, etc. + paths: + - 'packages/core/src/stepFunctions/*' + target: 50 + threshold: 10 + only_pulls: true + threatComposer: + informational: true # Always pass. Ignore `target`, `threshold`, etc. + paths: + - 'packages/core/src/threatComposer/*' + target: 80 + threshold: 5 + only_pulls: true + patch: default: - threshold: 1 - informational: true - patch: no - changes: no + # Note: `default` measures the entire project. + # It does NOT define defaults "inherited" by other `project` items. + # https://docs.codecov.com/docs/commit-status + target: 90 + threshold: 5 + only_pulls: true + informational: false # Fail if PR changes are not covered. + # branches: + # - master + changes: false -comment: off +comment: false github_checks: annotations: false + +flags: + codewhisperer: + paths: + - 'packages/core/src/codewhisperer/' + - 'packages/core/src/amazonq*/*' + - 'packages/amazonq/src/' + amazonqFeatureDev: + paths: + - 'packages/core/src/amazonqFeatureDev/' diff --git a/designs/credentials/credentials-management.md b/designs/credentials/credentials-management.md index 3c9daf2e4a3..a0d53b92318 100644 --- a/designs/credentials/credentials-management.md +++ b/designs/credentials/credentials-management.md @@ -31,11 +31,11 @@ A formatted version of the Credentials Provider Id may be surfaced to users, how When the user connects to AWS in the Toolkit, a Credentials Provider is requested, which is then used to obtain credentials. The toolkit requests a Credentials Provider by checking which credentials provider factories support the provider's credentials type. The factories of interest are queried to see which (if any) have the requested Credentials Provider. -At the time this document was written, Shared Credentials are the only supported Credentials. Additional credentials providers will reside at [/src/credentials/providers](/src/credentials/providers) as they are implemented. +At the time this document was written, Shared Credentials are the only supported Credentials. Additional credentials providers will reside at [/src/credentials/providers](/packages/core/src/credentials/providers) as they are implemented. ### Shared Credentials Profiles -Profiles are retrieved from the user's shared credentials files. The profile is handled and validated differently based on which fields are present. Handling and validation logic can be found in [sharedCredentialsProvider.ts](/src/credentials/providers/sharedCredentialsProvider.ts). +Profiles are retrieved from the user's shared credentials files. The profile is handled and validated differently based on which fields are present. Handling and validation logic can be found in [sharedCredentialsProvider.ts](/packages/core/src/credentials/providers/sharedCredentialsProvider.ts). Only profiles that are considered valid are provided to the toolkit. When validation issues are detected, they are written to the logs to help users understand why the toolkit is having difficulties with a profile. Users running the 'Connect to AWS' command will not see invalid profiles in the list of Credentials. diff --git a/designs/modify-resources-attached-to-code-pipeline.md b/designs/modify-resources-attached-to-code-pipeline.md index 8b8960ac471..312377449e9 100644 --- a/designs/modify-resources-attached-to-code-pipeline.md +++ b/designs/modify-resources-attached-to-code-pipeline.md @@ -150,7 +150,7 @@ The confirmation prompt can be implemented using one of two UI facilities: ### API Client -The `ResourceGroupsTaggingAPI` service client will be used. The client will be set up in a manner consistent with the Lambda and CloudFormation clients, allowing the clients to be stubbed out in unit tests. See the Toolkit's Lambda Client [Interface](/src/shared/clients/lambdaClient.ts) and [Implementation](/src/shared/clients/defaultLambdaClient.ts) as an example. A new client factory will be added to [ToolkitClientBuilder](/src/shared/clients/toolkitClientBuilder.ts) and [DefaultToolkitClientBuilder](/src/shared/clients/defaultToolkitClientBuilder.ts). +The `ResourceGroupsTaggingAPI` service client will be used. The client will be set up in a manner consistent with the Lambda and CloudFormation clients, allowing the clients to be stubbed out in unit tests. See the Toolkit's Lambda Client [Interface](/packages/core/src/shared/clients/lambdaClient.ts) and [Implementation](/packages/core/src/shared/clients/defaultLambdaClient.ts) as an example. A new client factory will be added to [ToolkitClientBuilder](/packages/core/src/shared/clients/toolkitClientBuilder.ts) and [DefaultToolkitClientBuilder](/packages/core/src/shared/clients/defaultToolkitClientBuilder.ts). ### Fallback Handling diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 47b44de3660..179ad9d4833 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,355 +1 @@ -# Architecture - -An overview of the architecture for various components within the Toolkit. - -## Commands - -Many parts of the VS Code API relies on the use of 'commands' which are simply functions bound to a global ID. For small projects, this simplicity is great. But the base API doesn't offer a lot of common functionality that a large project might want: logging, error handling, etc. - -For the Toolkit, common command functionality is implemented in [Commands](../src/shared/vscode/commands2.ts). The goal with this abstraction is to increase consistency across the Toolkit for anything related to commands. - -### Examples - -- Registering and execution: - - ```ts - const command = Commands.register('helloWorld', () => console.log('Hello, World!')) - command.execute() - ``` - -- Using parameters: - - ```ts - const showMessage = async (message: string) => vscode.window.showInformationMessage(message) - const command = Commands.register('aws.showMessage', showMessage) - command.execute('Hello, World!') - ``` - -- Creating a CodeLens: - - ```ts - // The built CodeLens should be returned through a `vscode.CodeLensProvider` implementation - const range = new vscode.Range(0, 0, 0, 0) - const lens = command.build('Hello, World!').asCodeLens(range, { title: 'Click me!' }) - ``` - -- Creating a tree node: - - ```ts - // `node` will execute the command when clicked in the explorer tree - // This object should be returned as a child of some other tree node - const node = command.build('Hello, World!').asTreeNode({ label: 'Click me!' }) - ``` - -### Advanced Uses - -Complex programs often require more than just simple functions to act as entry-points. Large amounts of state may be involved, which can be very difficult to test and reason about without proper management. One way to make it easier to isolate state from a given command is by 'declaring' it. This associates a command with all the required dependencies while still making it look like any other command. - -- Command declaration and registration: - - ```ts - const command = Commands.declare('aws.showMessage2', (state: Record) => (key: string) => { - return showMessage(state[key] ?? 'Message not found') - }) - - command.register({ hello: 'Hello, World!', goodbye: 'Goodbye, World!' }) - command.execute('goodbye') - ``` - -- Prototype binding: - - ```ts - // This class won't show duplicate messages that use the same key unlike the previous example - class Messages { - private readonly shown = new Map>() - public constructor(private readonly state: Record) {} - - public showMessage(key: string) { - const promise = this.shown.get(key) ?? showMessage(this.state[key] ?? 'Message not found') - this.shown.set( - key, - promise.finally(() => this.shown.delete(key)) - ) - - return promise - } - } - - const command = Commands.from(Messages).declareShowMessage('aws.showMessage3') - const messages = new Messages({ hello: 'Hello, World!', goodbye: 'Goodbye, World!' }) - - command.register(messages) - command.execute('goodbye') - command.execute('hello') - command.execute('goodbye') - ``` - -## Exceptions - -Large applications often have a correspondingly large number of failure points. For feature-level logic, these failures are often non-recoverable. The best we can do is show the user that something went wrong and maybe offer guidance on how to fix it. - -Because this is such a common pattern, shared error handling logic is defined by `ToolkitError` found [here](../src/shared/errors.ts). This class provides the basic structure for errors throughout the Toolkit. - -### Chaining - -Exceptions that occur deep in a call stack often do not contain enough context to correctly diagnose the problem. A stack trace is helpful, but only if the reader has access to both the source code and source map. - -By adding additional information as the exception bubbles up, we can create a better view of the program state when the problem occured. This is done via `ToolkitError.chain`. The `chain` function serves as a standard way to establish a clear cause-and-effect relationship for errors. - -### Handlers - -Any code paths exercised via `Commands` will have errors handled by `handleError` in [extensions.ts](../src/extension.ts). A better API for error handling across more than just commands will be added in a future PR. - -### Best Practices - -Implementations still need to adhere to some basic principles for this to work nicely: - -- Do not catch errors unless something meaningful can be added. -- Do not swallow errors unless the functionality is non-critical to the feature. -- Do not directly show the user an error message for failed actions. Use `ToolkitError` instead. -- Use `chain` when re-throwing errors with additional information. -- Use `CancellationError` for explicit workflow cancellations. -- Define meaninful error codes when creating new errors. - -## Webviews (Vue framework) - -The current implementation uses Vue 3 with Single File Components (SFCs) for modularity. Each webview -is bundled into a single file and packaged into the toolkit at release time. Vue applications may be composed -of individual components in a parent/child heiracrchy. Each component is able to act independently within an -application, however, they must respect the following principles: - -1. State can only be stored in a child component if it is not being used for two-way communication (via events) -2. If there is two-way communication, store state in the parent -3. Data should flow down, actions should flow up - -Be very mindful about state managment; violating these principles will lead to buggy and hard-to-debug software. - -### Bundling - -Each webview is bundled into a single file to speed up load times as well as isolate the 'web' modules from the 'node' modules. Webview bundles are automatically generated on compilation by targeting `index.ts` files when located under a `vue` directory. All bundles are placed under `dist` in the same relative location. - -Generated bundle names map based off their path relative to `src`: `src/foo/vue/bar/index.ts` -> `dist/src/foo/vue/bar/index.js` - -Running the extension in development mode (e.g. via the `Extension` launch task) starts a local server to automatically rebuild and serve webviews in real-time via hot-module reloading. It's assumed that the server runs on port `8080`, so make sure that nothing is already using that port. Otherwise webviews will not be displayed during development. - -### Client/Server - -The VS Code API restricts our Webviews to a single `postMessage` function. To simplify developing Webviews, we use a client/server architecture to handle message passing between the view and the extension. This does not mean that clients are restricted to 1 message = 1 response, rather, the frontend ("client") needs to send the first message. - -Webview (frontend) clients can be created via `WebviewClientFactory`. This generates a simple Proxy to send messages to the extension, mapping the function name to the command name. Unique IDs are also generated to stop requests from receiving extraneous responses. It is **highly** recommended to use the [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) extension for syntax highlighting and type-checking when working with SFCs. Keep in mind that this is purely a development tool: users of the toolkits do not require Volar to view webviews. - -Commands and events are defined on the backend via sub-classes of `VueWebview`. Classes define all the functions and events that will be available to the frontend. These sub-classes can be turned into a fully-resolved class by using either `VueWeview.compilePanel` or `VueWebview.compileView`. The resulting classes can finally be used to instantiate webview panels or view. Panels are shown by calling `show`, while views must be registered before they can be seen. - -### Examples - -- Creating and executing a webview: - - ```ts - // Export the class so the frontend code can use it for types - export class MyVueWebview extends VueWebview { - public readonly id = 'my.view' - public readonly source = 'myView.js' - public readonly onBar = new vscode.EventEmitter() - - public constructor(private readonly myData: string) {} - - public init() { - return this.myData - } - - public foo() { - return 'foo' - } - - public bar() { - this.onBar.fire(0) - } - } - - // Create panel bindings using our class - const Panel = VueWebview.compilePanel(MyVueWebview) - - // `context` is `ExtContext` provided on activation - const view = new Panel(context, 'hello') - view.show({ title: 'My title' }) - view.server.onFoo.fire(1) - ``` - -- Creating the client on the frontend: - - ```ts - import { MyView } from './backend.ts' - const client = WebviewClientFactory.create() - ``` - -- A basic request/response with error handling: - - ```ts - client - .foo() - .then(response => console.log(response)) - .catch(err => console.log(err)) - ``` - - The backend protocol is allowed to throw errors. These result in rejected Promises on the frontend. - -- Registering for events: - - ```ts - client.onBar(num => console.log(num)) - ``` - -- Methods called `init` will only return data on the initial webview load: - - ```ts - client.init(data => (this.data = data ?? this.data)) - ``` - -### Testing - -Currently only manual testing is done. Future work will include setting up some basic unit testing capacity via `JSDOM` and `Vue Testing Library`. Strict type-checking may also be enforced on SFCs; currently the type-checking only exists locally due to gaps in the type definitions for the DOM provided by Vue/TypeScript. - -## Prompters - -A 'prompter' can be thought of as any UI element that displays ('prompts') the user to make some choice or selection, returning their response. This interface is captured with the abstract base class `Prompter` which also contains some extra logic for convenience. Instances of the class can be used alone by calling the async method `prompt`, or by feeding them into a `Wizard`. - -```ts -const prompter = createInputBox() -const response = await prompter.prompt() - -// Verify that the user did not cancel the prompt -if (isValidResponse(response)) { - // `response` is now typed as `string` -} -``` - -### Quick Picks - -Pickers can be constructed by using the `createQuickPick` factory function. This currently takes two parameters: a collection of 'items' (required), and an object defining additional options. The items can be an array, a Promise for an array, or an `AsyncIterable`. All collections should resolve to the `DataQuickPickItem` interface. Extra configuration options are derived from valid properties on VS Code's `QuickPick` interface, e.g. `title` sets the title of the resulting picker. Some extra options are also present that change or enhance the default behavior of the picker. For example, using `filterBoxInputSettings` causes the picker to create a new quick pick item based off the user's input. - -#### Items - -A picker item is simply an extension of VS Code's `QuickPickItem` interface, encapsulating the data it represents in the aptly named `data` field: - -```ts -// This can be typed as `DataQuickPickItem` -const item = { - label: 'An item' - data: 'some data' -} -``` - -If the user selects this item, then 'some data' should be returned. Note that the type of data (and therefore type of `Prompter`) can largely be inferred; explicit typing, if done at all, should be limited to item definitions: - -```ts -// Results in `QuickPickPrompter` -const prompter = createQuickPick([item]) - -// Results in `QuickPickPrompter` -const prompter = createQuickPick([{ label: 'Another item', data: 0 }]) -``` - -Often we deal with items derived asychronously (usually by API calls). `createQuickPick` can handle this scenario, showing a loading bar while items load in. For example, consider a scenario where we want to show the user a list of CloudWatch log groups to select. In this case the API is _paginated_, so we should use the `pageableToCollection` utility method to make it easier to map: - -```ts -interface LogGroup extends CloudWatchLogs.LogGroup { - logGroupName: string - storedBytes: number -} -function isValidLogGroup(obj?: CloudWatchLogs.LogGroup): obj is LogGroup { - return !!obj && typeof obj.logGroupName === 'string' && typeof obj.storedBytes === 'number' -} - -const requester = (request: CloudWatchLogs.DescribeLogGroupsRequest) => - client.invokeDescribeLogGroups(request, sdkClient) -const collection = pageableToCollection(requester, request, 'nextToken', 'logGroups') - -const groupToItem = (group: LogGroup) => ({ label: group.logGroupName, data: group }) -const items = collection.flatten().filter(isValidLogGroup).map(groupToItem) -const prompter = createQuickPick(items) // Results in `QuickPickPrompter` -``` - -If we did not care about pagination, we could call the `promise` method on `collection`, causing all items to load in at once: - -```ts -const items = collection.flatten().filter(isValidLogGroup).map(groupToItem).promise() -const prompter = createQuickPick(items) // Results in `QuickPickPrompter` -``` - -### Input Box - -A new input box prompter can be created using the `createInputBox` factory function. Like `createQuickPick`, the input is derived from the properties of VS Code's `InputBox` interface. - -### Testing - -Quick pick prompters can be tested using `createQuickPickTester`, returning an interface that executes actions on the picker. This currently acts on the real VS Code API, meaning the actions happen asynchronously. Very basic example: - -```ts -const items = [ - { label: '1', data: 1 }, - { label: '2', data: 2 }, -] -const tester = createQuickPickTester(createQuickPick(items)) - -tester.assertItems(['1', '2']) // Assert that the prompt displays exactly two items with labels '1' and '2'. -tester.acceptItem('1') // Accept an item with label '1'. This will fail if no item is found. -await tester.result(items[0].data) // Execute the actions, asserting the final result is equivalent to the first item's data -``` - -## Wizards - -Abstractly, a 'wizard' is a collection of discrete, linear steps (subroutines), where each step can potentially be dependent on prior steps, that results in some final state. Wizards are extremely common in top-level flows such as creating a new resource, deployments, or confirmation messages. For these kinds of flows, we have a shared `Wizard` class that handles the bulk of control flow and state management logic for us. - -### Creating a Wizard (Quick Picks) - -A new wizard can be created by extending off the base `Wizard` class, using the template type to specify the shape of the wizard state. All wizards have an internal 'form' property that is used to assign steps. We can assign UI elements (namely, quick picks) using the `bindPrompter` method on form elements. This method accepts a callback that should return a `Prompter` given the current state. For this example, we will use `createQuickPick` and `createInputBox` for our prompters: - -```ts -interface ExampleState { - foo: string - bar?: number -} - -class ExampleWizard extends Wizard { - public constructor() { - super() - - // Note that steps should only be assigned in the constructor by convention - // This first step will always be shown as we did not specify any dependencies - this.form.foo.bindPrompter(() => createInputBox({ title: 'Enter a string' })) - - // Our second step is only shown if the length of `foo` is greater than 5 - // Because of this, we typed `bar` as potentially being `undefined` in `ExampleState` - const items = [ - { label: '1', data: 1 }, - { label: '2', data: 2 }, - ] - this.form.bar.bindPrompter(state => createQuickPick(items, { title: `Select a number (${state.foo})` }), { - showWhen: state => state.foo?.length > 5, - }) - } -} -``` - -### Executing - -Wizards can be ran by calling the async `run` method: - -```ts -const wizard = new ExampleWizard() -const result = await wizard.run() -``` - -Note that all wizards can potentially return `undefined` if the workflow was cancelled. - -### Testing - -Use `createWizardTester` on an instance of a wizard. Tests can then be constructed by asserting both the user-defined and internal state. Using the above `ExampleWizard`: - -```ts -const tester = createWizardTester(new ExampleWizard()) -tester.foo.assertShowFirst() // Fails if `foo` is not shown (or not shown first) -tester.bar.assertDoesNotShow() // True since `foo` is not assigned an explicit value -tester.foo.applyInput('Hello, world!') // Manipulate 'user' state -tester.bar.assertShow() // True since 'foo' has a defined value -``` +This document was renamed to [arch_overview.md](./arch_overview.md) diff --git a/docs/CODE_GUIDELINES.md b/docs/CODE_GUIDELINES.md index fb9c6c8ca53..90153905b12 100644 --- a/docs/CODE_GUIDELINES.md +++ b/docs/CODE_GUIDELINES.md @@ -11,6 +11,27 @@ that cannot be enforced by a `lint` build-task. instead of inventing new conventions. - Convention: provide global editor commands as an alternative to browsing items in the Explorer. - Instead of needing to visit service _Foo_ in the Explorer to _View_ its items, consider also providing a `AWS: Foo: View Item` command. +- [Webview guidance](https://code.visualstudio.com/api/ux-guidelines/webviews) +- Webview costs: + - Webviews very easily lead to the [inner-platform effect](https://en.wikipedia.org/wiki/Inner-platform_effect). + Because they are fully isolated from vscode and its extensions, they must include any code + and frameworks. + - Leads to extra dependencies. + - Webviews inherit none of vscode's standard features. This means features like keyboard + shortcuts, syntax highlighting, and editor navigation, are not available to users. Instead + users must learn the custom _web application_ embedded in the webview. + +## Dependencies + +Dependencies can be very high-leverage if they solve a difficult problem. +Dependencies are also a [maintenance burden](https://github.com/aws/aws-toolkit-vscode/pulls?q=is%3Apr+author%3Aapp%2Fdependabot+is%3Aclosed) and security risk +Copy-pasting or "inlining" a dependency doesn't solve that, of course--it only hides the problem (another cost). +So before taking on a new dependency, ask: + +- is this solving a problem that is worth the cost? +- could the problem be solved in some other way that involves a smaller cost? For example, using + an isolated function with good test coverage, or a native vscode feature such as a TreeView or + quickpick menu. ## Naming @@ -60,13 +81,10 @@ that is a net cost. - Telemetry: "active" vs "passive" - Active (`passive:false`) metrics are those intended to appear in DAU count. - Icons: - - Where possible, use IDE-specific standard icons (e.g. standard VSCode - standard icons in VSCode, and Cloud9 standard icons in Cloud9). The typical - (maintainable) way to do this is to use _named_ icons (what VSCode calls + - Where possible, use standard VSCode icons. The typical (maintainable) + way to do this is to use _named_ icons (what VSCode calls [codicons](https://microsoft.github.io/vscode-codicons/)) as opposed to icons shipped with the Toolkit build and referenced by _path_. - - For cases where icons must be statically defined (package.json), if Cloud9 - does not support the VSCode standard icon, use the Cloud9 icon. - Changelog guidelines - Prefer active voice: "You can do X" instead of "X can be done" - Avoid unnecessary use of `lodash` (which we may remove in the future). @@ -91,6 +109,13 @@ that is a net cost. - Localize UI messages. Do _not_ localize log and exception messages. - Use `extensionUtilities.getIdeProperties()` to automatically match IDE terminology (e.g. VS Code : CodeLens :: AWS Cloud9 : Inline Action) +- Refactoring tools allow us to avoid "premature abstraction". Avoid wrappers + or other abstractions until the need is clear and obvious. + +### Exceptions + +_See also [arch_develop.md](./arch_develop.md#exceptions)._ + - Bubble-up error conditions, do not sink them to random hidden places (such as logs only), expecting callers to figure out the failure mode. If a caller spawns a process that fails, the caller should get an exception, callback, or @@ -101,10 +126,22 @@ that is a net cost. attaching metadata to the error, or retrying the failed action. Do not just log and rethrow the error without additional context as to where the error occured. - Do not log a stack trace unless it's truly a fatal exception. Stack traces are - noisy and mostly useless in production because the extension is 'bundled', removing - anything useful from the trace. -- Refactoring tools allow us to avoid "premature abstraction". Avoid wrappers - until the need is clear and obvious. + noisy and mostly useless in production because the extension is bundled + (webpacked), removing anything useful from the trace. + - When _debugging_ the extension, you can tell the debugger to break on + exceptions, so logging the stacktrace is unnecessary there. +- Do not use multiple logger calls to log what is semantically a single + message. Use a string template or printf-style syntax (`%s` ) to format the + message: + - GOOD: + ``` + getLogger().error(`Failed to create %s: %s`, foo, (err as Error).message) + ``` + - BAD: + ``` + getLogger().error(`Failed to create %s`, foo) + getLogger().error(err) + ``` ## Test guidelines @@ -143,7 +180,7 @@ that is a net cost. - [Testing Refresh Button](https://github.com/aws/aws-toolkit-vscode/blob/b34c8f7650c862c388992781844695b014b5d974/src/test/shared/ui/prompters/rolePrompter.test.ts#L58-L65) -- Use [`testCommand`](../src/test/shared/vscode/testUtils.ts) for testing commands created by `Commands.declare` +- Use [`testCommand`](../packages/core/src/test/shared/vscode/testUtils.ts) for testing commands created by `Commands.declare` - Prefer executing the real command directly when possible ## Code guidelines @@ -200,11 +237,11 @@ that is a net cost. - PREFER: ```ts - things.filter(o => o.isFoo) + things.filter((o) => o.isFoo) ``` - INSTEAD OF: ```ts - things.filter(thing => thing.isFoo) + things.filter((thing) => thing.isFoo) ``` ## User settings diff --git a/docs/TESTPLAN.md b/docs/TESTPLAN.md index 15cc6fce378..56a299c9a5b 100644 --- a/docs/TESTPLAN.md +++ b/docs/TESTPLAN.md @@ -15,31 +15,48 @@ Ratio of unit to integ tests: 90% unit tests, 10% system/acceptance tests. ## Test categories -The test suite has two categories of tests: +The test suite has the following categories of tests: - Unit Tests: **fast** tests - Live in `src/test/` - The `vscode` API is available. + - Use `getTestWindow()` to inspect or manipulate `vscode.window` - The Toolkit code is invoked as a library, not as an extension activated in VSCode's typical lifecycle. - Call functions and create objects directly. - May mock state where needed, though this is discouraged in favor of "fake" data/objects/files. - May use the filesystem. - Main property is that [the test is fast](https://pycon-2012-notes.readthedocs.io/en/latest/fast_tests_slow_tests.html). - Global state is shared across tests, thus there is a risk that later tests are polluted by earlier tests. +- Lint Tests: + - Live in `src/testLint` + - Can run from CLI with `npm run lint` + - Any type of test related to the format/quality/content of the code + - Does not have context of the `vscode` api - Integration Tests: **slow** tests - - Live in `src/integrationTest/` + - Live in `src/testInteg/` - Use a full instance of VSCode with an activated instance of the extension. - Global state is shared across tests, thus there is a risk that later tests are polluted by earlier tests. - Trigger VSCode commands and UI elements to test codepaths as from an actual user session, instead of invoking functions directly. - Do not use mocks. +- E2E Tests: **slow** tests + - Live in `src/testE2E` + - These tests are heavier than Integration tests. + - Some E2E tests have a more complicated architecture, described in [TEST_E2E](./TEST_E2E.md) +- Performance Tests: **slow** tests + - Live in `src/testInteg/perf`. + - A subset of integration tests focused on catching performance regressions. + - Use a combination of operation counting and system usage statistics such as cpu usage, memory usage, and duration. + - Each test is often repeated 10 or more times for less variant system usage statistics, then median of runs is used. ## Test files +Currently, most testing code lives in the subproject `packages/core/` due to the move to monorepo. See [arch_develop.md](./arch_develop.md#monorepo-structure). + - `src/test/` : unit tests - `src/test/globalSetup.test.ts` : - defines global setup functions run before and after each test - defines global utility functions such as `getTestLogger()` -- `src/integrationTest/` : integration tests +- `src/testInteg/` : integration tests - `src/test/testRunner.ts` : used by _both_ the unit tests and integration tests to discover tests, setup the test framework, and run tests. - `src/testFixtures/` : test data (sample projects, SAM templates, ...) @@ -47,6 +64,8 @@ The test suite has two categories of tests: - `.vscode/launch.json` : defines VSCode launch configs useful for Toolkit developers, e.g. the `Extension Tests` config runs all tests in `src/test/`. +Many tests required running in an activated extension environment, but the core-lib is just a library and does not have any extension to activate itself. Core-lib tests are run from `packages/toolkit`, and web tests are run from `packages/amazonq`. + ## How we test VSCode documentation describes an [extension testing](https://code.visualstudio.com/api/working-with-extensions/testing-extension) @@ -65,10 +84,52 @@ modifications/workarounds in `src/test/testRunner.ts`. - Many failure modes (as opposed to the "happy path") are not tested. - No performance/benchmark regression tests. - No UI tests (to exercise webviews). - - https://github.com/redhat-developer/vscode-extension-tester + - https://github.com/redhat-developer/vscode-extension-tester - Missing acceptance tests: - Connect to AWS - Fixed credentials and fixed credentials with assume roles - Testing AWS SDK client functionality is cumbersome, verbose, and low-yield. - Test code uses multiple “mocking” frameworks, which is confusing, error-prone, hard to onboard, and hard to use. - Coverage not counted for integ tests (because of unresolved tooling issue). +- [Backlog](https://github.com/aws/aws-toolkit-vscode/blob/0f3685ab0dc8af3a214136ebfa233829d5d72b2c/src/shared/telemetry/exemptMetrics.ts) of metrics that do not pass validation but are temporarily exempted to not fail CI. + +## Window + +Certain VS Code API calls are not easily controlled programtically. `vscode.window` is a major source of these functions as it is closely related to the UI. To facilitate some semblance of UI testing, all unit tests have access to a proxied `vscode.window` object via `getTestWindow()`. + +### Inspecting State + +The test window will capture relevant UI state that can be inspected at test time. For example, you can check to see if any messages were shown by looking at `getTestWindow().shownMessages` which is an array of message objects. + +Some VS Code API operations do not expose "native" UI elements that can be inspected. In these cases, in-memory test versions have been created. In every other case the native VS Code API is used directly and extended to make them more testable. + +### Event-Driven Interactions + +Checking the state works well if user interactions are not required by the code being tested. But many times the code will be waiting for the user's response. + +To handle this, test code can register event handler that listen for when a certain type of UI element is shown. For example, if we wanted to always accept the first item of a quick pick we can do this: + +```ts +getTestWindow().onDidShowQuickPick(async (picker) => { + // Some pickers load items asychronously + // Wait until the picker is not busy before accepting an item + await picker.untilReady() + picker.acceptItem(picker.items[0]) +}) +``` + +Utility functions related to events can be used to iterate over captured UI elements: + +```ts +const pickers = captureEvent(getTestWindow().onDidShowQuickPick) +const firstPicker = await pickers.next() +const secondPicker = await pickers.next() +``` + +Exceptions thrown within one of these handlers will cause the current test to fail. This allows you to make assertions within the callback without worrying about causing the test to hang. + +## Common issues + +### Stubbing VSCode outside of core + +- Stubbing VSCode imports (like executeCommand) does not work outside of core. For now you will need to put any tests that require spying/stubbing VSCode imports in core until we move more source files into the amazon q package diff --git a/docs/TEST_E2E.md b/docs/TEST_E2E.md new file mode 100644 index 00000000000..10927c098e3 --- /dev/null +++ b/docs/TEST_E2E.md @@ -0,0 +1,43 @@ +### Test E2E + +This document describes the testing architecture specifically for more complicated E2E tests running the toolkits. + +#### Amazon Q E2E Tests + +Since VSCode does not have native support for writing E2E/UI tests for components occuring inside of a webview, we have taken a slightly different approach to write E2E tests for Amazon Q. + +The E2E tests for Amazon Q follow the following flow: ![Amazon Q E2E flow](./images/amazon_q_e2e.png) + +This means instead of running the E2E tests inside of the webview, we hoist the Mynah UI package into the test structure using JSDOM. This allows us to directly interface with Mynah UI allowing us to not rely on any DOM elements, making tests a lot easier to create and simple to maintain. + +The downsides are that we technically aren't testing any of the actual contents of the webview to make sure things are behaving correctly. Instead, we are testing that when we programatically send a message to the UI that the Mynah UI <-> Amazon Q VSCode code interacts successfully. + +With this approach, the follow things can be tested: + +- Whether or not certain features show/not show depending on the status of the users auth +- Run requests directly against the backend and see if we get results back +- Clicking any follow up buttons (including examples) + +## Flare Chat E2E Test flow (Not implemented yet) + +This is the new flow that should be introduced when we moved to Flare chat. + +```mermaid +sequenceDiagram + participant test as Test + participant framework as Test Framework + participant ui as Virtual DOM + participant lsp as Language Server + participant mynah as Mynah UI + + test->>test: starts + test->>framework: creates test framework + framework->>ui: adds mynah ui to virtual dom + test->>lsp: waits for language server activation + test->>mynah: triggers action on mynah ui + mynah->>framework: sends message + framework->>lsp: sends message + lsp->>framework: gets response + framework->>ui: displays response + test->>ui: assert test expectations +``` diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 00000000000..ffb08bf88c5 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,35 @@ +# AWS Toolkit API + +Details about any publicly accessible functionalities exposed through [extension commands](https://code.visualstudio.com/api/references/vscode-api#commands) or [exported APIs](https://code.visualstudio.com/api/references/vscode-api#extensions). + +## Pseudo (Internal-only) API + +### Commands + +#### `aws.codeWhisperer.connect` + +**Signature**: _async (source: string, startUrl?: string, region?: string, customizationArn?: string, customizationNamePrefix?: string) => Promise_ + +Shortcut command to directly connect to Identity Center or prompt start URL entry, as well as set a customization for CodeWhisperer requests. + +This command supports the following arguments: + +- source: An identifier of the caller of this command. This can be used for something like telemetry. +- startUrl and region. If both arguments are provided they will be used, otherwise the command prompts for them interactively. +- customizationArn: select customization by ARN. If provided, `customizationNamePrefix` is ignored. +- customizationNamePrefix: select customization by prefix, if `customizationArn` is `undefined`. + +### Extension API + +#### `listConnections` + +**Signature**: _async () => Promise_ + +This is an API that exposes the metadata of SSO connections of AWS Toolkit. It returns a list of `AwsConnection` which contains below fields: + +- id: string that presents the id of the connection +- label: label of the connection +- type: type of the connection, currently only 'sso' is returned +- ssoRegion: region of the connection, e.g: us-west-2 +- startUrl: start url of the connection +- scopes?: list of the scopes of the connection diff --git a/docs/arch_develop.md b/docs/arch_develop.md new file mode 100644 index 00000000000..a3ad998d3e1 --- /dev/null +++ b/docs/arch_develop.md @@ -0,0 +1,621 @@ +# Architecture: structure + +> Describes the codebase structure and shared modules, and how to work with it. +> Corresponds to the "Development view" of the [4+1 architectural views](https://en.wikipedia.org/wiki/4%2B1_architectural_view_model). + +## Monorepo Structure + +**The easiest way to open the project for running/debugging:** File > Open Workspace from File > choose `aws-toolkit-vscode/aws-toolkit-vscode.code-workspace` + +This project is currently set up as a typescript monorepo with the following subprojects: + +- [`packages/core/`](./packages/core/) + - Currently, this package contains almost all of the functionality required for the extension. + It was created by moving all of the code from `packages/tookit/` to here. + We are currently working on separating non-shareable code out of the core library into the relevant + subproject. Running tests for most of the toolkit extension logic occurs in this subproject. +- [`packages/toolkit/`](./packages/toolkit/) + - Currently, this package is a barebones wrapper that calls activation functions from the core library. + As we discover what code cannot be shared across other projects, it will be moved to this package. + Running and packaging the extension occurs from this subproject. + +If you are considering contributing, please consider whether your implementation should live in the core +library or in `packages/toolkit`. If your work could be re-used by other packages (e.g. auth mechanisms, +utilities), then it may belong in the core library. If instead you are adding something toolkit specific +(eg. an integration to a new AWS service in the Explorer Tree), consider putting it in `packages/toolkit`. +To import from the core library, please export your desired code using `index.ts` files and add an appropriate `exports` statement +in `packages/core/package.json`. + +Unless otherwise stated, the documentation throughout this project is referring to the code and +functionality in `packages/core/` and `packages/toolkit`. + +Current quirks of the current monorepo status that should be resolved/evaluated in later versions (TODO): + +- [**Running the test suites in VSCode has changed**](../CONTRIBUTING.md#test) +- The [root package.json](../package.json) contains common dependencies for subprojects, and workspace + entries for each of the subprojects. + - This package contains shortcuts to some of the `npm` scripts found in the subproject(s). + - `createRelease` and `newChange` run at the subproject level only, e.g. from root level, try npm run createRelease -w packages/toolkit + - To run a script not present in the root `package.json`, use `npm run -w packages/toolkit + + +` +``` + +rather than: + +```ts +// foo.js +import 'resources/mycss.css' + +export function foo() { + // some javascript actions +} + +// webview.ts +webviewView.webview.html = ` + + + + + +` +``` + +### Testing + +Currently only manual testing is done. Future work will include setting up some basic unit testing capacity via `JSDOM` and `Vue Testing Library`. Strict type-checking may also be enforced on SFCs; currently the type-checking only exists locally due to gaps in the type definitions for the DOM provided by Vue/TypeScript. + +## Prompters + +A 'prompter' can be thought of as any UI element that displays ('prompts') the user to make some choice or selection, returning their response. This interface is captured with the abstract base class `Prompter` which also contains some extra logic for convenience. Instances of the class can be used alone by calling the async method `prompt`, or by feeding them into a `Wizard`. + +```ts +const prompter = createInputBox() +const response = await prompter.prompt() + +// Verify that the user did not cancel the prompt +if (isValidResponse(response)) { + // `response` is now typed as `string` +} +``` + +### Quick Picks + +Pickers can be constructed by using the `createQuickPick` factory function. This currently takes two parameters: a collection of 'items' (required), and an object defining additional options. The items can be an array, a Promise for an array, or an `AsyncIterable`. All collections should resolve to the `DataQuickPickItem` interface. Extra configuration options are derived from valid properties on VS Code's `QuickPick` interface, e.g. `title` sets the title of the resulting picker. Some extra options are also present that change or enhance the default behavior of the picker. For example, using `filterBoxInputSettings` causes the picker to create a new quick pick item based off the user's input. + +#### Items + +A picker item is simply an extension of VS Code's `QuickPickItem` interface, encapsulating the data it represents in the aptly named `data` field: + +```ts +// This can be typed as `DataQuickPickItem` +const item = { + label: 'An item' + data: 'some data' +} +``` + +If the user selects this item, then 'some data' should be returned. Note that the type of data (and therefore type of `Prompter`) can largely be inferred; explicit typing, if done at all, should be limited to item definitions: + +```ts +// Results in `QuickPickPrompter` +const prompter = createQuickPick([item]) + +// Results in `QuickPickPrompter` +const prompter = createQuickPick([{ label: 'Another item', data: 0 }]) +``` + +Often we deal with items derived asychronously (usually by API calls). `createQuickPick` can handle this scenario, showing a loading bar while items load in. For example, consider a scenario where we want to show the user a list of CloudWatch log groups to select. In this case the API is _paginated_, so we should use the `pageableToCollection` utility method to make it easier to map: + +```ts +interface LogGroup extends CloudWatchLogs.LogGroup { + logGroupName: string + storedBytes: number +} +function isValidLogGroup(obj?: CloudWatchLogs.LogGroup): obj is LogGroup { + return !!obj && typeof obj.logGroupName === 'string' && typeof obj.storedBytes === 'number' +} + +const requester = (request: CloudWatchLogs.DescribeLogGroupsRequest) => + client.invokeDescribeLogGroups(request, sdkClient) +const collection = pageableToCollection(requester, request, 'nextToken', 'logGroups') + +const groupToItem = (group: LogGroup) => ({ label: group.logGroupName, data: group }) +const items = collection.flatten().filter(isValidLogGroup).map(groupToItem) +const prompter = createQuickPick(items) // Results in `QuickPickPrompter` +``` + +If we did not care about pagination, we could call the `promise` method on `collection`, causing all items to load in at once: + +```ts +const items = collection.flatten().filter(isValidLogGroup).map(groupToItem).promise() +const prompter = createQuickPick(items) // Results in `QuickPickPrompter` +``` + +### Input Box + +A new input box prompter can be created using the `createInputBox` factory function. Like `createQuickPick`, the input is derived from the properties of VS Code's `InputBox` interface. + +### Testing + +Quick pick prompters can be tested using `createQuickPickTester`, returning an interface that executes actions on the picker. This currently acts on the real VS Code API, meaning the actions happen asynchronously. Very basic example: + +```ts +const items = [ + { label: '1', data: 1 }, + { label: '2', data: 2 }, +] +const tester = createQuickPickTester(createQuickPick(items)) + +tester.assertItems(['1', '2']) // Assert that the prompt displays exactly two items with labels '1' and '2'. +tester.acceptItem('1') // Accept an item with label '1'. This will fail if no item is found. +await tester.result(items[0].data) // Execute the actions, asserting the final result is equivalent to the first item's data +``` + +## Wizards + +Abstractly, a 'wizard' is a collection of discrete, linear steps (subroutines), where each step can potentially be dependent on prior steps, that results in some final state. Wizards are extremely common in top-level flows such as creating a new resource, deployments, or confirmation messages. For these kinds of flows, we have a shared `Wizard` class that handles the bulk of control flow and state management logic for us. + +### 1. `Wizard` Class + +Create a new wizard by extending the base `Wizard` class, using the template type to specify the +shape of the wizard state. All wizards have an internal `form` property that is used to assign +steps. You can assign UI elements (namely, quickpicks) using the `bindPrompter` method on form +elements. This method accepts a callback that should return a `Prompter` given the current state. +For this example, we will use `createQuickPick` and `createInputBox` for our prompters: + +If you need to call async functions to construct your `Wizard` subclass, define your init logic in +the `init()` method instead of the constructor. + +```ts +interface ExampleState { + foo: string + bar?: number +} + +class ExampleWizard extends Wizard { + public constructor() { + super() + + // Note that steps should only be assigned in the constructor by convention + // This first step will always be shown as we did not specify any dependencies + this.form.foo.bindPrompter(() => createInputBox({ title: 'Enter a string' })) + + // Our second step is only shown if the length of `foo` is greater than 5 + // Because of this, we typed `bar` as potentially being `undefined` in `ExampleState` + const items = [ + { label: '1', data: 1 }, + { label: '2', data: 2 }, + ] + this.form.bar.bindPrompter((state) => createQuickPick(items, { title: `Select a number (${state.foo})` }), { + showWhen: (state) => state.foo?.length > 5, + }) + } +} +``` + +### 2. `CompositeWizard` Class + +`CompositeWizard` extends `Wizard` to create and manage a collection of nested/child wizards. + +Extend this class to create a wizard that contains other wizards as part of a prompter flow. +Use `this.createWizardPrompter()` to use a wizard as a prompter in the `CompositeWizard`. + +Example: + +```ts + +// Child wizard +class ChildWizard extends Wizard {...} + + +// Composite wizard +interface SingleNestedWizardForm { + ... + singleNestedWizardNestedProp: string + ... +} + +class SingleNestedWizard extends CompositeWizard { + constructor() { + super() + ... + this.form.singleNestedWizardNestedProp.bindPrompter(() => + this.createWizardPrompter(ChildWizard) + ) + ... + } +} + +``` + +### Executing + +Wizards can be ran by calling the async `run` method: + +```ts +const wizard = new ExampleWizard() +const result = await wizard.run() +``` + +Note that all wizards can potentially return `undefined` if the workflow was cancelled. + +### Testing + +#### Using `WizardTester` + +Use `createWizardTester` on an instance of a wizard. Tests can then be constructed by asserting both the user-defined and internal state. Using the above `ExampleWizard`: + +```ts +const tester = await createWizardTester(new ExampleWizard()) +tester.foo.assertShowFirst() // Fails if `foo` is not shown (or not shown first) +tester.bar.assertDoesNotShow() // True since `foo` is not assigned an explicit value +tester.foo.applyInput('Hello, world!') // Manipulate 'user' state +tester.bar.assertShow() // True since 'foo' has a defined value +``` + +#### Using `PrompterTester` + +Use `PrompterTester` to simulate user behavior (click, input and selection) on prompters to test end-to-end flow of a wizard. + +Example: + +```ts +// 1. Register PrompterTester handlers +const prompterTester = PrompterTester.init() + .handleInputBox('Input Prompter title 1', (inputBox) => { + // Register Input Prompter handler + inputBox.acceptValue('my-source-bucket-name') + }) + .handleQuickPick('Quick Pick Prompter title 2', (quickPick) => { + // Register Quick Pick Prompter handler + + // Optional assertion can be added as part of the handler function + assert.strictEqual(quickPick.items.length, 2) + assert.strictEqual(quickPick.items[0].label, 'Specify required parameters and save as defaults') + assert.strictEqual(quickPick.items[1].label, 'Specify required parameters') + // Choose item + quickPick.acceptItem(quickPick.items[0]) + }) + .handleQuickPick( + 'Quick Pick Prompter with various handler behavior title 3', + (() => { + // Register handler with dynamic behavior + const generator = (function* () { + // First call, choose '**' + yield async (picker: TestQuickPick) => { + await picker.untilReady() + assert.strictEqual(picker.items[1].label, '**') + picker.acceptItem(picker.items[1]) + } + // Second call, choose BACK button + yield async (picker: TestQuickPick) => { + await picker.untilReady() + picker.pressButton(vscode.QuickInputButtons.Back) + } + // Third and subsequent call + while (true) { + yield async (picker: TestQuickPick) => { + await picker.untilReady() + picker.acceptItem(picker.items[1]) + } + } + })() + + return (picker: TestQuickPick) => { + const next = generator.next().value + return next(picker) + } + })() + ) + .build() + +// 2. Run your wizard class +const result = await wizard.run() + +// 3. Assert your tests +prompterTester.assertCallAll() +prompterTester.assertCallOrder('Input Prompter title 1', 1) +``` + +## Module path debugging + +Node has an environment variable `NODE_DEBUG=module` that helps to debug module imports. This can be helpful on windows, which can load node modules into uppercase or lower case drive letters, depending on the drive letter of the parent module. + +You can enable this by adding `"NODE_DEBUG": "module"` into the env of your launch config that you are using. + +When enabled you can see the file that the import is looking for, the module load request, and the relative file requested. + +``` +MODULE 88184: looking for ["/aws-toolkit-vscode/packages/core/dist/src"] +MODULE 88184: Module._load REQUEST ./codewhisperer/commands/basicCommands parent: /aws-toolkit-vscode/packages/core/dist/src/extension.js +MODULE 88184: RELATIVE: requested: ./codewhisperer/commands/basicCommands from parent.id /aws-toolkit-vscode/packages/core/dist/src/extension.js +``` diff --git a/docs/arch_features.md b/docs/arch_features.md new file mode 100644 index 00000000000..7a9c239d584 --- /dev/null +++ b/docs/arch_features.md @@ -0,0 +1,59 @@ +# Architecture: design and implementation of product features + +> How the main end-user features are designed and where (in code) they are implemented. +> Corresponds to the "Logical view" of the [4+1 architectural views](https://en.wikipedia.org/wiki/4%2B1_architectural_view_model). + +## Explorer + +TODO + +## Local debugging of SAM Lambdas + +TODO + +## Remote connect + +Toolkit provides "remote connect" for CodeCatalyst, EC2, and ECS (terminal only). This means +customers can connect (1) a new VSCode instance and (2) a VSCode Terminal to remote machines in AWS +and CodeCatalyst. + +### Design of remote connect + +For connecting a new VSCode instance, remote connect works like this: + +1. User chooses the machine they want to connect to (CodeCatalyst dev env, or EC2 machine) +1. Toolkit ensures that the [vscode remote-ssh extension](https://code.visualstudio.com/docs/remote/ssh) is installed. +1. Toolkit automatically downloads a private copy of `session-manager-plugin`, or uses its previous copy. +1. Toolkit ensures that the user's `~/.ssh/config` file contains a special host-name pattern. + - The SSH config item defines a `ProxyCommand` that invokes a Toolkit-provided shell script [codecatalyst_connect](/packages/core/resources/codecatalyst_connect) or [ec2_connect](/packages/core/resources/ec2_connect). +1. Toolkit starts a SSM session using the service API. +1. Toolkit starts a new instance of VSCode with environment variables containing values needed to connect (SSM session id, etc). +1. VSCode invokes `ssh` which invokes the Toolkit-defined `ProxyCommand` mentioned above, which uses the environment variables to invoke `session-manager-plugin` to create an SSH connection. +1. VSCode's remote-ssh feature uses the SSH connection to provide remote VSCode session on the remote machine. + +For connecting a new VSCode _terminal_, remote connect works like this: + +1. User chooses the machine they want to connect to (CodeCatalyst dev env, EC2, or ECS machine) +1. Toolkit automatically downloads a private copy of `session-manager-plugin`, or uses its previous copy. +1. Toolkit starts a SSM session using the service API. +1. Toolkit [builds a session-manager-plugin command](https://github.com/aws/aws-toolkit-vscode/blob/c77fc076fd0ed837d077bc0318716b711a2854c8/packages/core/src/ecs/util.ts#L92-L104) and [passes it to a new VSCode Terminal](https://github.com/aws/aws-toolkit-vscode/blob/c77fc076fd0ed837d077bc0318716b711a2854c8/packages/core/src/ecs/commands.ts#L141-L147). +1. VSCode displays the terminal, so the user can enter shell commands on the remote machine. + +For EC2 specifically, there are a few additional steps: + +1. Remote window connections are only supported for EC2 instances running a linux based OS such as Amazon Linux or Ubuntu. However, the terminal option is supported by all OS, and will open a Powershell-based terminal for Windows instances. +1. If connecting to EC2 instance via remote window, the toolkit generates temporary SSH keys (30 second lifetime), with the public key sent to the remote instance. + - Key type is ed25519 if supported, or RSA otherwise. + - Lines in `.ssh/authorized_keys` marked with the comment `#AWSToolkitForVSCode` will be removed by AWS Toolkit. + - Assumes `.sss/authorized_keys` can be found under `/home/ec2-user/` on Amazon Linux and `/home/ubuntu/` on Ubuntu. +1. If insufficient permissions are detected on the attached IAM role, toolkit will prompt to add an inline policy with the necessary actions. +1. If SSM sessions remain open after closing the window/terminal, the toolkit will terminate them on-shutdown, or when starting another session to the same instance. + +### Implementation of remote connect + +These modules show how to use and extend the "remote connect" functionality: + +- [shared/remoteSession.ts](/packages/core/src/shared/remoteSession.ts) +- CodeCatalyst: [openDevEnv()](https://github.com/aws/aws-toolkit-vscode/blob/c77fc076fd0ed837d077bc0318716b711a2854c8/packages/core/src/codecatalyst/model.ts#L252) +- EC2: [openSessionInTerminal()](https://github.com/aws/aws-toolkit-vscode/blob/c77fc076fd0ed837d077bc0318716b711a2854c8/packages/core/src/ec2/model.ts#L147) +- ECS: [openTaskInTerminal()](https://github.com/aws/aws-toolkit-vscode/blob/c77fc076fd0ed837d077bc0318716b711a2854c8/packages/core/src/ecs/commands.ts#L133) diff --git a/docs/arch_overview.md b/docs/arch_overview.md new file mode 100644 index 00000000000..76e9ce2e3f1 --- /dev/null +++ b/docs/arch_overview.md @@ -0,0 +1,13 @@ +# Architecture: overview + +> Learn about the architectural goals and constraints of this codebase (AWS +> Toolkit and Amazon Q extensions, and the "core" package shared by both). + +The architecture is described from these [views](https://en.wikipedia.org/wiki/4%2B1_architectural_view_model): + +- [Guidelines](./CODE_GUIDELINES.md) +- [Structure ("Development" view)](./arch_develop.md) +- [Features ("Logical" view)](./arch_features.md) +- [Runtime behavior ("Process view")](./arch_runtime.md) +- [Target ("Physical" view)](./arch_target.md) +- [User stories ("Scenarios" view)](./arch_user_stories.md) diff --git a/docs/arch_runtime.md b/docs/arch_runtime.md new file mode 100644 index 00000000000..e1cbfd01661 --- /dev/null +++ b/docs/arch_runtime.md @@ -0,0 +1,54 @@ +# Architecture: runtime behavior and communication + +> Describes the _runtime_ behavior and design of AWS Toolkit. +> Corresponds to the "Process view" of the [4+1 architectural views](https://en.wikipedia.org/wiki/4%2B1_architectural_view_model). + +## Environment variables + +TODO: move from CONTRIBUTING.md + +## VSCode context keys + +VScode extensions can use vscode 'setContext' command to set special context keys which are +available in `package.json`. Use this only if there is no other alternative (it's shared, global, +mutable state). + +### Defining a new setContext key + +If you must define a new key (is it _really_ necessary?), follow these guidelines: + +- Choose a prefix as follows (as [recommended](https://code.visualstudio.com/api/extension-guides/command#using-a-custom-when-clause-context)): + - `packages/core/` should use `aws.` prefix + - `packages/toolkit/` should use `aws.toolkit.` prefix + - `packages/amazonq/` should use `amazonq.` prefix +- Use brevity. Less is more. +- Document it in the list below. + +### setContext keys + +#### setContext keys owned by packages/core/ + +These keys are currently set by the core/ package, but many of them may eventually be migrated to +toolkit/ or amazonq/ if appropriate. + +- `aws.codecatalyst.connected`: CodeCatalyst connection is active. +- `aws.codewhisperer.connected`: CodeWhisperer connection is active. +- `aws.codewhisperer.connectionExpired`: CodeWhisperer connection is active, but the connection is expired. +- `aws.isDevMode`: AWS Toolkit is running in "developer mode". +- `aws.isWebExtHost`: true when the _extension host_ is running in a web browser, as opposed to + nodejs (i.e. the environment has no "compute"). + - Compare to `isWeb`, which vscode defines when the _UI_ is web, but says nothing about the + _extension host_. +- `aws.isSageMaker`: AWS Toolkit is running in the SageMaker Code Editor. + +#### setContext keys owned by packages/toolkit/ + +- TODO + +#### setContext keys owned by packages/amazonq/ + +- TODO + +## How our components communicate + +TODO: vscode events; the "globals" module; activate(); EventEmitters; ...? diff --git a/docs/arch_target.md b/docs/arch_target.md new file mode 100644 index 00000000000..e8c38dae348 --- /dev/null +++ b/docs/arch_target.md @@ -0,0 +1,33 @@ +# Architecture: target requirements + +> Describes the expected environment when the product is running on a user's machine. +> Corresponds to the "Physical view" of the [4+1 architectural views](https://en.wikipedia.org/wiki/4%2B1_architectural_view_model). + +## Requirements / supported platforms + +TODO + +Software dependencies: Any external software components, libraries, or frameworks that the desktop +application depends on, such as runtime environments (e.g., .NET Framework, Java Runtime +Environment), database management systems, or third-party libraries. + +Installation and deployment: The process of installing and deploying the desktop application on the +target hardware platforms. This may include details about installation packages (e.g., MSI, EXE, +DMG), installation scripts, deployment tools, and any specific configuration steps required. + +System files and directories: The physical layout and structure of the application files on the +target system, including the location of executable files, libraries, configuration files, data +files, and log files. + +Network configuration (if applicable): If the desktop application requires network connectivity for +certain features or services, the Physical View may describe the network protocols, ports, and any +specific network configurations required. + +Hardware peripherals and devices: Any specific hardware peripherals or devices that the desktop +application interacts with, such as printers, scanners, cameras, or external storage devices. + +Security considerations: Any physical security measures or considerations related to the deployment +environment, such as data encryption, secure storage, or access control mechanisms. + +Monitoring and logging: The mechanisms and tools used for monitoring the application's performance, +logging application events, and collecting diagnostic information on the target hardware platforms. diff --git a/docs/arch_user_stories.md b/docs/arch_user_stories.md new file mode 100644 index 00000000000..5e61e4965fe --- /dev/null +++ b/docs/arch_user_stories.md @@ -0,0 +1,6 @@ +# User stories (scenarios) + +> Describes the main "user stories" of AWS Toolkit. +> Corresponds to the "Scenarios view" of the [4+1 architectural views](https://en.wikipedia.org/wiki/4%2B1_architectural_view_model). + +TODO diff --git a/docs/build.md b/docs/build.md new file mode 100644 index 00000000000..ffa9e1a8a81 --- /dev/null +++ b/docs/build.md @@ -0,0 +1,27 @@ +# Build + +The AmazonQ features rely on the `codewhisperer-streaming` service, to support both Sigv4 and Bearer token modes of this service, +two clients are generated from the service's smithy models and placed in +`src.gen/@amzn/amazon-q-developer-streaming-client` and `src.gen/@amzn/codewhisperer-streaming` respectively (For more +information about these clients and how they are generated, please see this +[quip document](https://quip-amazon.com/2dAWAvTIYXXr/Build-instructions-for-AWS-CodeWhisperer-Streaming-Typescript-client)). + +## @amzn/amazon-q-developer-streaming client + +This client is a standalone npm project in typescript, and it is added to +the project as a workspace in the project's root `package.json` with the line `"workspaces": [ ..., "src.gen/@amzn/amazon-q-developer-streaming" ]`. +The client may be manually built using `npm run build -w @amzn/amazon-q-developer-streaming"`. +The `generateClients` run script ensures that this dependency is +built before the toolkit project itself. Workspaces are automatically ready to +be imported in the root toolkit project by their declared package.json name, +(`@amzn/amazon-q-developer-streaming` in this case). + +## @amzn/codewhisperer-streaming client + +This client is a standalone npm project in typescript, and it is added to +the project as a workspace in the project's root `package.json` with the line `"workspaces": [ ..., "src.gen/@amzn/codewhisperer-streaming" ]`. +The client may be manually built using `npm run build -w @amzn/codewhisperer-streaming"`. +The `generateClients` run script ensures that this dependency is +built before the toolkit project itself. Workspaces are automatically ready to +be imported in the root toolkit project by their declared package.json name, +(`@amzn/codewhisperer-streaming` in this case). diff --git a/docs/faq-credentials.md b/docs/faq-credentials.md new file mode 100644 index 00000000000..d38b09b66ae --- /dev/null +++ b/docs/faq-credentials.md @@ -0,0 +1,19 @@ +# FAQ / Troubleshooting - Auth/SSO/Credentials + +### AWS Builder ID "Invalid client provided" + +During AWS Builder ID sign in, some users ran in to this error in the browser. +This is due to a stale state in `~/.aws/sso`. + +Issue [aws-toolkit-vscode#3667](https://github.com/aws/aws-toolkit-vscode/issues/3667) + +#### Solution + +1. Rename the current folder: `mv ~/.aws/sso ~/.aws/sso-OLD` +2. Attempt to sign in again with AWS Builder ID +3. If sign is is successful you can remove the old folder: `rm -rf ~/.aws/sso-OLD` + 1. Or revert the change: `mv ~/.aws/sso-OLD ~/.aws/sso` + +### AWS Shared Credentials File + +When authenticating with IAM credentials, the profile name, access key, and secret key will be stored on disk at a default location of `~/.aws/credentials` on Linux and MacOS, and `%USERPROFILE%\.aws\credentials` on Windows machines. The toolkit also supports editting this file manually, with the format specified [here](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html#file-format-creds). The credentials files also supports [role assumption](https://docs.aws.amazon.com/sdkref/latest/guide/access-assume-role.html) and [MFA](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa.html). Note that this credentials file is shared between all local AWS development tools. For more information, see the full documentation [here](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html). diff --git a/docs/faq-debug.md b/docs/faq-debug.md new file mode 100644 index 00000000000..df35fb7d800 --- /dev/null +++ b/docs/faq-debug.md @@ -0,0 +1,5 @@ +# FAQ / Troubleshooting - Debugging/Development + +### Enabling Debug Logs + +[See steps here to enable more detailed debug logs for the extension.](../CONTRIBUTING.md#enabling-debug-logs) diff --git a/docs/icons.md b/docs/icons.md index 56636aa32a2..46e252d5dac 100644 --- a/docs/icons.md +++ b/docs/icons.md @@ -1,15 +1,16 @@ # Icons -All icons that are used in the Toolkit can be found in `resources/icons`. +All icons that are used in the extensions can be found in `resources/icons`. -A [build script](../../scripts/build/generateIcons.ts) generates Toolkit artifacts: +A [build script](../scripts/generateIcons.ts) generates extension artifacts in [core/](../packages/core/): -- `resources/icons/cloud9/generated` - `resources/fonts/aws-toolkit-icons.woff` - `resources/css/icons.css` -- `contributes.icons` in [package.json](../../package.json) +- `contributes.icons` in [amazonq package.json](../packages/amazonq/package.json) and [toolkit package.json](../packages/toolkit/package.json) -This script should be ran using `npm run generatePackage` after making updates. Any changes made to `package.json` should be committed with the relevant icons. +This script should be ran using `npm run generateIcons` after making updates. Any changes made to `package.json` should be committed with the relevant icons. Type checking in `core/` relies on the entries in `core/package.json`. However, the individual extensions require entries in their `package.json`s as well. Currently, resources (including icons) are shared between `core/` and the individual extensions. If `contributes.icons` in each of the extensions does not match the entry in `core/`, then CI will fail. + +To sync the icons to the individual extensions, run `npm run copyFiles && npm run generateIcons` for each extension. ## Fonts @@ -29,7 +30,7 @@ If your desired icon does not work well as a font, see [Theme Overrides](#theme- ## Identifiers -Icons (except those in `cloud9`) can be referenced within the Toolkit by concatenating the icon path with hyphens, omitting the 'theme' if applicable. +Icons can be referenced within the Toolkit by concatenating the icon path with hyphens, omitting the 'theme' if applicable. Examples: @@ -48,11 +49,6 @@ For example, if I wanted to use a special App Runner service icon, then I need t - `resources/icons/aws/dark/apprunner-service.svg` - `resources/icons/aws/light/apprunner-service.svg` -A similar format is used for overriding icons only on Cloud9: - -- `resources/icons/cloud9/dark/aws-apprunner-service.svg` -- `resources/icons/cloud9/light/aws-apprunner-service.svg` - These icons will **not** be usuable as Codicons or as font icons. ## Third Party diff --git a/docs/images/amazon_q_e2e.png b/docs/images/amazon_q_e2e.png new file mode 100644 index 00000000000..9e5ad7f5729 Binary files /dev/null and b/docs/images/amazon_q_e2e.png differ diff --git a/docs/images/dependency-graph-small.svg b/docs/images/dependency-graph-small.svg new file mode 100644 index 00000000000..30c43a4bb80 --- /dev/null +++ b/docs/images/dependency-graph-small.svg @@ -0,0 +1,90 @@ + + + + + + +dependency-cruiser output + + +cluster_node_modules + +node_modules + + +cluster_src + +src + + +cluster_src/shared + +shared + + +cluster_src/srcShared + +srcShared + + + +node_modules/fs-extra + + + + + +fs-extra + + + + + +src/shared/extensionUtilities.ts + + +extensionUtilities.ts + + + + + +src/shared/filesystemUtilities.ts + + +filesystemUtilities.ts + + + + + +src/shared/extensionUtilities.ts->src/shared/filesystemUtilities.ts + + + + + +src/shared/filesystemUtilities.ts->node_modules/fs-extra + + + + + +src/srcShared/fs.ts + + +fs.ts + + + + + +src/srcShared/fs.ts->src/shared/extensionUtilities.ts + + + + + diff --git a/docs/images/exportAmazonQLogs.png b/docs/images/exportAmazonQLogs.png new file mode 100644 index 00000000000..ae74b3c7df1 Binary files /dev/null and b/docs/images/exportAmazonQLogs.png differ diff --git a/docs/images/logsDebugLog.png b/docs/images/logsDebugLog.png new file mode 100644 index 00000000000..625353dab55 Binary files /dev/null and b/docs/images/logsDebugLog.png differ diff --git a/docs/images/logsSetDebug.png b/docs/images/logsSetDebug.png new file mode 100644 index 00000000000..2bf736279ee Binary files /dev/null and b/docs/images/logsSetDebug.png differ diff --git a/docs/images/logsSetDefault.png b/docs/images/logsSetDefault.png new file mode 100644 index 00000000000..1b7de39b9ce Binary files /dev/null and b/docs/images/logsSetDefault.png differ diff --git a/docs/images/logsView.png b/docs/images/logsView.png new file mode 100644 index 00000000000..1222169a5c5 Binary files /dev/null and b/docs/images/logsView.png differ diff --git a/docs/images/openExportLogs.png b/docs/images/openExportLogs.png new file mode 100644 index 00000000000..4a9902f8c28 Binary files /dev/null and b/docs/images/openExportLogs.png differ diff --git a/docs/lsp.md b/docs/lsp.md new file mode 100644 index 00000000000..49a6ad00b87 --- /dev/null +++ b/docs/lsp.md @@ -0,0 +1,137 @@ +# Flare Language Server + +## Chat Activation flow + +```mermaid +sequenceDiagram + participant user as User + participant ext as Extension + participant webview as Chat Webview + participant flare as Amazon Q LSP + participant backend as Amazon Q Backend + + user->>ext: opens IDE + ext->>ext: activates + ext->>webview: loads UI + ext->>flare: initialize process + flare->>flare: starts and waits + user->>webview: interacts + webview->>ext: sends message + ext->>flare: sends message + flare->>backend: call api + backend->>flare: returns + flare->>ext: display + ext->>webview: display +``` + +## Language Server Debugging + +If you want to connect a local version of language-servers to aws-toolkit-vscode, follow these steps: + +1. Clone https://github.com/aws/language-servers.git and set it up in the same workspace as this project by cmd+shift+p and "add folder to workspace" and selecting the language-servers folder that you just cloned. Your VS code folder structure should look like below. + + ``` + /aws-toolkit-vscode + /toolkit + /core + /amazonq + /language-servers + ``` + +2. Inside of the language-servers project run: + ``` + npm install + npm run compile + npm run package + ``` + to get the project setup +3. You need to open VScode user settings (Cmd+Shift+P and Search "Open User Settings (JSON)") and add the lines below at the bottom of the settings to enable the lsp experiment: + ``` + "aws.experiments": { + "amazonqLSP": true, + "amazonqLSPInline": true, // optional: enables inline completion from flare + "amazonqLSPChat": true // optional: enables chat from flare + } + ``` +4. Uncomment the `__AMAZONQLSP_PATH` and `__AMAZONQLSP_UI` variables in the `amazonq/.vscode/launch.json` extension configuration +5. Use the `Launch LSP with Debugging` configuration and set breakpoints in VSCode or the language server, Once you run "Launch LSP with Debugging" a new window should start, wait for the plugin to show up there. Then go to the run menu again and run "Attach to Language Server (amazonq)" after this you should be able to add breakpoints in the LSP code. +6. (Optional): Enable `"amazonq.trace.server": "on"` or `"amazonq.trace.server": "verbose"` in your VSCode settings to view detailed log messages sent to/from the language server. These log messages will show up in the "Amazon Q Language Server" output channel + +### Breakpoints Work-Around + +If the breakpoints in your language-servers project remain greyed out and do not trigger when you run `Launch LSP with Debugging`, your debugger may be attaching to the language server before it has launched. You can follow the work-around below to avoid this problem. If anyone fixes this issue, please remove this section. + +1. Set your breakpoints and click `Launch LSP with Debugging` +2. Once the debugging session has started, click `Launch LSP with Debugging` again, then `Cancel` on any pop-ups that appear +3. On the debug panel, click `Attach to Language Server (amazonq)` next to the red stop button +4. Click `Launch LSP with Debugging` again, then `Cancel` on any pop-ups that appear + +## Language Server Runtimes Debugging + +If you want to connect a local version of language-server-runtimes to aws-toolkit-vscode, follow these steps: + +1. Clone https://github.com/aws/language-server-runtimes.git and set it up in the same workspace as this project by cmd+shift+p and "add folder to workspace" and selecting the language-server-runtimes folder that you just cloned. Your VS code folder structure should look like below. + + ``` + /aws-toolkit-vscode + /toolkit + /core + /amazonq + /language-servers + /language-server-runtimes + ``` + +2. Inside of the language-server-runtimes project run: + + ``` + npm install + npm run compile + cd runtimes + npm run prepub + cd out + ``` + + If you get an error running `npm run prepub`, you can instead run `npm run prepub:copyFiles` to skip cleaning and testing. + +3. Choose one of the following approaches: + +### Option A: Using npm pack (Recommended) + +3a. Create a package file: + + npm pack + +You will see a file created like this: `aws-language-server-runtimes-0.*.*.tgz` + +4a. Inside of language-servers, find the package where you need the change. + +For example, if you would like the change in `language-servers/app/aws-lsp-codewhisperer-runtimes`, you would run: + + cd language-servers/app/aws-lsp-codewhisperer-runtimes + + npm install ../../../language-server-runtimes/runtimes/out/aws-language-server-runtimes-0.*.*.tgz + + npm run compile + +5a. If you need the change in aws-toolkit-vscode run: + + cd aws-toolkit-vscode + + npm install ../language-server-runtimes/runtimes/out/aws-language-server-runtimes-0.*.*.tgz + +### Option B: Using npm link (Alternative) + +3b. Create npm links: + + npm link + cd ../../types + npm link + +4b. Inside of aws-toolkit-vscode run: + + npm install + npm link @aws/language-server-runtimes @aws/language-server-runtimes-types + +## Amazon Q Inline Activation + +- In order to get inline completion working you must open a supported file type defined in CodewhispererInlineCompletionLanguages in `packages/amazonq/src/app/inline/completion.ts` diff --git a/docs/marketplace/vscode/CC_dev_env.gif b/docs/marketplace/vscode/CC_dev_env.gif new file mode 100644 index 00000000000..eccaff663b8 Binary files /dev/null and b/docs/marketplace/vscode/CC_dev_env.gif differ diff --git a/docs/marketplace/vscode/Lambda_step_through_debugging.gif b/docs/marketplace/vscode/Lambda_step_through_debugging.gif new file mode 100644 index 00000000000..a6f8a6f62d0 Binary files /dev/null and b/docs/marketplace/vscode/Lambda_step_through_debugging.gif differ diff --git a/docs/marketplace/vscode/S3.gif b/docs/marketplace/vscode/S3.gif new file mode 100644 index 00000000000..7d67c78f3c6 Binary files /dev/null and b/docs/marketplace/vscode/S3.gif differ diff --git a/docs/marketplace/vscode/amazonq/auth-Q.gif b/docs/marketplace/vscode/amazonq/auth-Q.gif new file mode 100644 index 00000000000..e4713fcd352 Binary files /dev/null and b/docs/marketplace/vscode/amazonq/auth-Q.gif differ diff --git a/docs/marketplace/vscode/amazonq/chat.gif b/docs/marketplace/vscode/amazonq/chat.gif new file mode 100644 index 00000000000..61201a04b3c Binary files /dev/null and b/docs/marketplace/vscode/amazonq/chat.gif differ diff --git a/docs/marketplace/vscode/amazonq/dev.gif b/docs/marketplace/vscode/amazonq/dev.gif new file mode 100644 index 00000000000..f4e5c0e5488 Binary files /dev/null and b/docs/marketplace/vscode/amazonq/dev.gif differ diff --git a/docs/marketplace/vscode/amazonq/inline.gif b/docs/marketplace/vscode/amazonq/inline.gif new file mode 100644 index 00000000000..fe43870a7d3 Binary files /dev/null and b/docs/marketplace/vscode/amazonq/inline.gif differ diff --git a/docs/marketplace/vscode/amazonq/security-scan.gif b/docs/marketplace/vscode/amazonq/security-scan.gif new file mode 100644 index 00000000000..af3f8cc4b8c Binary files /dev/null and b/docs/marketplace/vscode/amazonq/security-scan.gif differ diff --git a/docs/marketplace/vscode/amazonq/transform.png b/docs/marketplace/vscode/amazonq/transform.png new file mode 100644 index 00000000000..915cc801283 Binary files /dev/null and b/docs/marketplace/vscode/amazonq/transform.png differ diff --git a/docs/marketplace/vscode/appComposer.gif b/docs/marketplace/vscode/appComposer.gif new file mode 100644 index 00000000000..1647611c3c1 Binary files /dev/null and b/docs/marketplace/vscode/appComposer.gif differ diff --git a/docs/marketplace/vscode/auth-Toolkit.gif b/docs/marketplace/vscode/auth-Toolkit.gif new file mode 100644 index 00000000000..e08d4f06959 Binary files /dev/null and b/docs/marketplace/vscode/auth-Toolkit.gif differ diff --git a/docs/marketplace/vscode/awsExplorer.gif b/docs/marketplace/vscode/awsExplorer.gif new file mode 100644 index 00000000000..c489ceae7f5 Binary files /dev/null and b/docs/marketplace/vscode/awsExplorer.gif differ diff --git a/docs/marketplace/vscode/cfn_autocompletion.gif b/docs/marketplace/vscode/cfn_autocompletion.gif new file mode 100644 index 00000000000..efa5f8144d2 Binary files /dev/null and b/docs/marketplace/vscode/cfn_autocompletion.gif differ diff --git a/docs/marketplace/vscode/codewhisperer.gif b/docs/marketplace/vscode/codewhisperer.gif new file mode 100644 index 00000000000..d6cca9f983a Binary files /dev/null and b/docs/marketplace/vscode/codewhisperer.gif differ diff --git a/docs/marketplace/vscode/codewhispererChat.gif b/docs/marketplace/vscode/codewhispererChat.gif new file mode 100644 index 00000000000..4d82f29eb7f Binary files /dev/null and b/docs/marketplace/vscode/codewhispererChat.gif differ diff --git a/docs/marketplace/vscode/creds.gif b/docs/marketplace/vscode/creds.gif new file mode 100644 index 00000000000..40d1fb29364 Binary files /dev/null and b/docs/marketplace/vscode/creds.gif differ diff --git a/docs/marketplace/vscode/cw_logs.gif b/docs/marketplace/vscode/cw_logs.gif new file mode 100644 index 00000000000..74f6aa6e239 Binary files /dev/null and b/docs/marketplace/vscode/cw_logs.gif differ diff --git a/docs/marketplace/vscode/download-Lambda.gif b/docs/marketplace/vscode/download-Lambda.gif new file mode 100644 index 00000000000..7a049822013 Binary files /dev/null and b/docs/marketplace/vscode/download-Lambda.gif differ diff --git a/docs/marketplace/vscode/ecs-terminal.gif b/docs/marketplace/vscode/ecs-terminal.gif new file mode 100644 index 00000000000..e3ca93da20d Binary files /dev/null and b/docs/marketplace/vscode/ecs-terminal.gif differ diff --git a/docs/marketplace/vscode/getting-started.gif b/docs/marketplace/vscode/getting-started.gif new file mode 100644 index 00000000000..2b6bc4a98d3 Binary files /dev/null and b/docs/marketplace/vscode/getting-started.gif differ diff --git a/docs/marketplace/vscode/infraComposer.webp b/docs/marketplace/vscode/infraComposer.webp new file mode 100644 index 00000000000..ff417f35537 Binary files /dev/null and b/docs/marketplace/vscode/infraComposer.webp differ diff --git a/docs/marketplace/vscode/stepFunctions.gif b/docs/marketplace/vscode/stepFunctions.gif new file mode 100644 index 00000000000..069ced6411d Binary files /dev/null and b/docs/marketplace/vscode/stepFunctions.gif differ diff --git a/docs/marketplace/vscode/threatComposer.gif b/docs/marketplace/vscode/threatComposer.gif new file mode 100644 index 00000000000..11b8b96a56d Binary files /dev/null and b/docs/marketplace/vscode/threatComposer.gif differ diff --git a/docs/telemetry-perf.md b/docs/telemetry-perf.md new file mode 100644 index 00000000000..892ba8f6a57 --- /dev/null +++ b/docs/telemetry-perf.md @@ -0,0 +1,613 @@ +# Telemetry Performance Metrics + +Visual representations of performance telemetry metrics + +## Amazon Q Inline + +### codewhispererFirstCompletionLatency + +How long it took to receive the first suggestion after we started calling the getRecommendations API + +```mermaid + sequenceDiagram + participant User + participant invoke as Inline invoked + participant rService as Recommendation Service + participant rHandler as Recommendation Handler + participant backend as CWSPR backend + participant sdk as Create CWSPR SDK + participant token as Toolkit auth + + + User->>invoke: Finished typing + invoke->>rService: calls + rService->>rHandler: calls + rHandler->>sdk: calls + + sdk->>token: Start getting bearer token + token->>sdk: Finished getting bearer token + + sdk->>rHandler: Return client + note over rHandler, backend: codewhispererFirstCompletionLatency + rect rgb(230, 230, 230, 0.5) + loop Get paginated recommendations + rHandler->>backend: calls + end + backend->>rHandler: first response received + end + rHandler->>User: show results + backend->>rHandler: all the other responses + rHandler->>User: add to already shown results +``` + +### codewhispererEndToEndLatency + +How long it took from when we started calling the getRecommendations API to when the first suggestion was shown + +```mermaid + sequenceDiagram + participant User + participant invoke as Inline invoked + participant rService as Recommendation Service + participant rHandler as Recommendation Handler + participant backend as CWSPR backend + participant sdk as Create CWSPR SDK + participant token as Toolkit auth + + User->>invoke: Finished typing + invoke->>rService: calls + rService->>rHandler: calls + rHandler->>sdk: calls + + sdk->>token: Start getting bearer token + token->>sdk: Finished getting bearer token + + sdk->>rHandler: Return client + note over User, backend: codewhispererEndToEndLatency + rect rgb(230, 230, 230, 0.5) + loop Get paginated recommendations + rHandler->>backend: calls + end + backend->>rHandler: first response received + rHandler->>User: show results + end + + backend->>rHandler: all the other responses + rHandler->>User: add to already shown results +``` + +### codewhispererAllCompletionsLatency + +How long it took to complete all paginated calls + +```mermaid + sequenceDiagram + participant User + participant invoke as Inline invoked + participant rService as Recommendation Service + participant rHandler as Recommendation Handler + participant backend as CWSPR backend + participant sdk as Create CWSPR SDK + participant token as Toolkit auth + + + User->>invoke: Finished typing + invoke->>rService: calls + rService->>rHandler: calls + rHandler->>sdk: calls + + sdk->>token: Start getting bearer token + token->>sdk: Finished getting bearer token + + sdk->>rHandler: Return client + note over User, backend: codewhispererAllCompletionsLatency + rect rgb(230, 230, 230, 0.5) + loop Get paginated recommendations + rHandler->>backend: calls + end + backend->>rHandler: first response received + rHandler->>User: show results + backend->>rHandler: all the other responses + end + + + rHandler->>User: add to already shown results +``` + +### codewhispererPostprocessingLatency + +How long it took to display the first suggestion after it received the first response from the API + +```mermaid + sequenceDiagram + participant User + participant invoke as Inline invoked + participant rService as Recommendation Service + participant rHandler as Recommendation Handler + participant backend as CWSPR backend + participant sdk as Create CWSPR SDK + participant token as Toolkit auth + + + User->>invoke: Finished typing + invoke->>rService: calls + rService->>rHandler: calls + rHandler->>sdk: calls + + sdk->>token: Start getting bearer token + token->>sdk: Finished getting bearer token + + sdk->>rHandler: Return client + loop Get paginated recommendations + rHandler->>backend: calls + end + note over User, backend: codewhispererPostprocessingLatency + rect rgb(230, 230, 230, 0.5) + backend->>rHandler: first response received + rHandler->>User: show results + end + + backend->>rHandler: all the other responses + rHandler->>User: add to already shown results +``` + +### codewhispererCredentialFetchingLatency + +How long it took to get the bearer token + +```mermaid + sequenceDiagram + participant User + participant invoke as Inline invoked + participant rService as Recommendation Service + participant rHandler as Recommendation Handler + participant backend as CWSPR backend + participant sdk as Create CWSPR SDK + participant token as Toolkit auth + + User->>invoke: Finished typing + invoke->>rService: calls + rService->>rHandler: calls + rHandler->>sdk: calls + + note over sdk, token: codewhispererCredentialFetchingLatency + rect rgb(230, 230, 230, 0.5) + sdk->>token: Start getting bearer token + token->>sdk: Finished getting bearer token + end + sdk->>rHandler: Return client + loop Get paginated recommendations + rHandler->>backend: calls + end + + backend->>rHandler: first response received + rHandler->>User: show results + + backend->>rHandler: all the other responses + rHandler->>User: add to already shown results +``` + +### codewhispererPreprocessingLatency + +How long it took to create the client and get ready to start sending getRecommendation API calls + +```mermaid + sequenceDiagram + participant User + participant invoke as Inline invoked + participant rService as Recommendation Service + participant rHandler as Recommendation Handler + participant backend as CWSPR backend + participant sdk as Create CWSPR SDK + participant token as Toolkit auth + + User->>invoke: Finished typing + invoke->>rService: calls + rService->>rHandler: calls + rHandler->>sdk: calls + + note over rHandler, token: codewhispererPreprocessingLatency + rect rgb(230, 230, 230, 0.5) + sdk->>token: Start getting bearer token + token->>sdk: Finished getting bearer token + sdk->>rHandler: Return client + end + loop Get paginated recommendations + rHandler->>backend: calls + end + + backend->>rHandler: first response received + rHandler->>User: show results + + backend->>rHandler: all the other responses + rHandler->>User: add to already shown results +``` + +### codewhisperer_perceivedLatency duration + +How long it took from when the user stopped pressing a key to when they were shown a response + +```mermaid + sequenceDiagram + participant User + participant invoke as Inline invoked + participant rService as Recommendation Service + participant rHandler as Recommendation Handler + participant backend as CWSPR backend + participant sdk as Create CWSPR SDK + participant token as Toolkit auth + + User->>invoke: Finished typing + note over User, token: codewhisperer_perceivedLatency duration + rect rgb(230, 230, 230, 0.5) + invoke->>rService: calls + rService->>rHandler: calls + rHandler->>sdk: calls + sdk->>token: Start getting bearer token + token->>sdk: Finished getting bearer token + sdk->>rHandler: Return client + + loop Get paginated recommendations + rHandler->>backend: calls + end + + backend->>rHandler: first response received + rHandler->>User: show results + + backend->>rHandler: all the other responses + rHandler->>User: add to already shown results + end +``` + +## Amazon Q Chat + +### amazonq_chatRoundTrip + +Measures sequential response times in Q chat, from user input to message display. Tracks time intervals between key events: editor receiving the message, feature processing, and final message rendering + +```mermaid + sequenceDiagram + participant User + participant chat as Chat UI + participant vscode as VSCode + participant event as Event Recorder + participant partner as Partner team code + participant telemetry + + User->>chat: Write chat message and press enter + chat->>vscode: send message with timestamp + vscode->>event: record chatMessageSent/editorReceivedMessage timestamps + vscode->>partner: forward chat message + partner->>event: record featureReceivedMessage timestamp + partner->>partner: call backend/get response + partner->>vscode: forward response contents + vscode->>chat: display message + chat->>vscode: send stop-chat-message-telemetry event + vscode->>event: record messageDisplayed timestamp + event->>vscode: get the telemetry timestamps + vscode->>telemetry: emit amazonq_chatRoundTrip with telemetry timestamps +``` + +### cwsprChatTimeToFirstChunk + +The time between when the conversation stream is created and when we got back the first usable result + +```mermaid + sequenceDiagram + participant user as User + participant chat as Chat UI + participant vscode as VSCode extension host + participant generateResponse as Generate response + participant backend as Q service backend + + user->>chat: Presses enter with message + chat->>vscode: Tell VSCode to generate a response + vscode->>generateResponse: start generating + generateResponse->>backend: start stream + backend->>backend: create conversation id + backend->>generateResponse: get conversation id + note over backend, generateResponse: cwsprChatTimeToFirstChunk + rect rgb(230, 230, 230, 0.5) + backend->>backend: generate first chunk + backend->>generateResponse: chunk received + end + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + loop for each subsequent chunk + backend->>backend: generate next chunk + backend->>generateResponse: chunk received + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + end +``` + +### cwsprChatTimeBetweenChunks + +An array of time when successive pieces of data are received from the server + +```mermaid + sequenceDiagram + participant user as User + participant chat as Chat UI + participant vscode as VSCode extension host + participant generateResponse as Generate response + participant backend as Q service backend + + user->>chat: Presses enter with message + chat->>vscode: Tell VSCode to generate a response + vscode->>generateResponse: start generating + generateResponse->>backend: start stream + backend->>backend: create conversation id + backend->>generateResponse: get conversation id + + loop for each subsequent chunk + note over backend, generateResponse: cwsprChatTimeBetweenChunks + rect rgb(230, 230, 230, 0.5) + backend->>backend: generate next chunk + backend->>generateResponse: chunk received + generateResponse->>generateResponse: record timestamp + end + + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + end +``` + +### cwsprChatFullResponseLatency + +The time between when the conversation id was created and the final response from the server was received + +```mermaid + sequenceDiagram + participant user as User + participant chat as Chat UI + participant vscode as VSCode extension host + participant generateResponse as Generate response + participant backend as Q service backend + + user->>chat: Presses enter with message + chat->>vscode: Tell VSCode to generate a response + vscode->>generateResponse: start generating + generateResponse->>backend: start stream + backend->>backend: create conversation id + backend->>generateResponse: get conversation id + + note over backend, chat: cwsprChatFullResponseLatency + rect rgb(230, 230, 230, 0.5) + loop for each subsequent chunk + backend->>backend: generate next chunk + backend->>generateResponse: chunk received + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + end + backend->>generateResponse: final chunk received + end + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk +``` + +### cwsprChatTimeToFirstUsableChunk + +The time between the initial server request, including creating the conversation stream, and the first usable result + +```mermaid + sequenceDiagram + participant user as User + participant chat as Chat UI + participant vscode as VSCode extension host + participant generateResponse as Generate response + participant backend as Q service backend + + user->>chat: Presses enter with message + chat->>vscode: Tell VSCode to generate a response + vscode->>generateResponse: start generating + note over backend, generateResponse: cwsprChatTimeToFirstUsableChunk + rect rgb(230, 230, 230, 0.5) + generateResponse->>backend: start stream + backend->>backend: create conversation id + backend->>generateResponse: get conversation id + backend->>backend: generate first chunk + backend->>generateResponse: chunk received + end + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + loop for each subsequent chunk + backend->>backend: generate next chunk + backend->>generateResponse: chunk received + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + end +``` + +### cwsprChatFullServerResponseLatency + +The time between the initial server request, including creating the conversation stream, and the final response from the server + +```mermaid + sequenceDiagram + participant user as User + participant chat as Chat UI + participant vscode as VSCode extension host + participant generateResponse as Generate response + participant backend as Q service backend + + user->>chat: Presses enter with message + chat->>vscode: Tell VSCode to generate a response + vscode->>generateResponse: start generating + note over backend, chat: cwsprChatFullServerResponseLatency + rect rgb(230, 230, 230, 0.5) + generateResponse->>backend: start stream + backend->>backend: create conversation id + backend->>generateResponse: get conversation id + loop for each subsequent chunk + backend->>backend: generate next chunk + backend->>generateResponse: chunk received + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + end + backend->>generateResponse: final chunk received + end + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk +``` + +### cwsprChatTimeToFirstDisplay + +The time between the user pressing enter and when the first piece of data is displayed to the user + +```mermaid + sequenceDiagram + participant user as User + participant chat as Chat UI + participant vscode as VSCode extension host + participant generateResponse as Generate response + participant backend as Q service backend + note over backend, user: cwsprChatTimeToFirstDisplay + rect rgb(230, 230, 230, 0.5) + user->>chat: Presses enter with message + chat->>vscode: Tell VSCode to generate a response + vscode->>generateResponse: start generating + generateResponse->>backend: start stream + backend->>backend: create conversation id + backend->>generateResponse: get conversation id + backend->>backend: generate first chunk + backend->>generateResponse: chunk received + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + end + loop for each subsequent chunk + backend->>backend: generate next chunk + backend->>generateResponse: chunk received + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + end +``` + +### cwsprChatTimeBetweenDisplays + +An array of time when successive pieces of server responses are displayed to the user + +```mermaid + sequenceDiagram + participant user as User + participant chat as Chat UI + participant vscode as VSCode extension host + participant generateResponse as Generate response + participant backend as Q service backend + + user->>chat: Presses enter with message + chat->>vscode: Tell VSCode to generate a response + vscode->>generateResponse: start generating + generateResponse->>backend: start stream + backend->>backend: create conversation id + backend->>generateResponse: get conversation id + + note over backend, chat: cwsprChatTimeBetweenDisplays + rect rgb(230, 230, 230, 0.5) + loop for each subsequent chunk + backend->>backend: generate next chunk + backend->>generateResponse: chunk received + generateResponse->>vscode: send chunk to display + vscode->>chat: display chunk + chat->>vscode: record display timestamp + end + end +``` + +### cwsprChatFullDisplayLatency + +The time between the user pressing enter and the entire response being rendered + +```mermaid + sequenceDiagram + participant user as User + participant chat as Chat UI + participant vscode as VSCode extension host + participant generateResponse as Generate response + participant backend as Q service backend + + note over backend, user: cwsprChatFullDisplayLatency + rect rgb(230, 230, 230, 0.5) + user->>chat: Presses enter with message + chat->>vscode: Tell VSCode to generate a response + vscode->>generateResponse: start generating + generateResponse->>backend: start stream + backend->>backend: create conversation id + backend->>generateResponse: get conversation id + generateResponse->>backend: start stream + backend->>backend: create conversation id + loop for each subsequent chunk + backend->>backend: generate next chunk + backend->>vscode: send chunk to display + vscode->>chat: display chunk + end + end + +``` + +## Crash Monitoring + +We make an attempt to gather information regarding when the IDE crashes, then report it to telemetry. This is the diagram of the steps that take place. + +### Sequence Diagram + +> Keep in mind that the entire sequence below is duplicated for each instance of our extension. +> They all work together to "crash check" on behalf of the other crashed extension instance. + +`Crash Service`: The high level "service" that starts the heartbeats and crash checks + +`Heartbeat`: Sends heartbeats which signal that the extension is still running and has not crashed + +`Crash Checker`: Observes the heartbeats, reporting a telemetry event if a crash is detected + +`File System State`: The user's file system where we store the heartbeat files from each extension instance + +```mermaid +%%{init: {'theme':'default'}}%% +sequenceDiagram + autonumber + + participant VSC as VS Code + participant Service as Crash Service + participant Checker as Crash Checker + participant Heartbeat as Heartbeat + participant State as File System State + participant Telemetry as Telemetry + + rect rgb(121, 210, 121) + alt Extension Startup + VSC ->> Service: activate() - Start Service + + Service ->> Heartbeat: Start Heartbeats + Heartbeat ->> State: Send Initial Heartbeat
(in a folder add a unique file w/ timestamp) + rect rgb(64, 191, 64) + par every N minutes + Heartbeat ->> State: Send Heartbeat
(overwrite the unique file w/ new timestamp) + end + end + + Service ->> Checker: Start Crash Checking + rect rgb(64, 191, 64) + par every N*2 minutes + Checker ->> Checker: If computer went to sleep, skip this iteration (gives time for a heartbeat) + Checker ->> State: Request all heartbeat timestamps (readdir all heartbeat files) + State ->> Checker: Receive all heartbeat timestamps + loop for each crashed extension (it's timestamp >= N*2 minutes) + Checker ->> State: Delete heartbeat file + Checker ->> Telemetry: Send metric representing a crash: session_end + end + end + end + end + end + + rect rgb(255, 128, 128) + alt Graceful Shutdown + VSC ->> Service: deactivate() - Stop Service + Service ->> Checker: Stop + Service ->> Heartbeat: Stop + Heartbeat ->> State: Delete timestamp file
(This is missed when a crash happens) + end + end + +``` diff --git a/docs/telemetry.md b/docs/telemetry.md new file mode 100644 index 00000000000..7750750c967 --- /dev/null +++ b/docs/telemetry.md @@ -0,0 +1,344 @@ +# Telemetry + +## Development + +See [aws-toolkit-common/telemetry](https://github.com/aws/aws-toolkit-common/tree/main/telemetry#telemetry) for full details about defining telemetry metrics. + +- You can define new metrics during development by adding items to + [telemetry/vscodeTelemetry.json](https://github.com/aws/aws-toolkit-vscode/blob/21ca0fca26d677f105caef81de2638b2e4796804/src/shared/telemetry/vscodeTelemetry.json). + - The `generateClients` build task generates new symbols in `shared/telemetry/telemetry`, which you can import via: + ``` + import { telemetry } from '/shared/telemetry/telemetry' + ``` + - When your feature is released, the "development" metrics you defined in `vscodeTelemetry.json` should be upstreamed to [aws-toolkit-common](https://github.com/aws/aws-toolkit-common/blob/main/telemetry/definitions/commonDefinitions.json). +- Metrics are dropped (not posted to the service) if the extension is running in [CI or other + automation tasks](https://github.com/aws/aws-toolkit-vscode/blob/21ca0fca26d677f105caef81de2638b2e4796804/src/shared/vscode/env.ts#L71-L73). + - You can always _test_ telemetry via [assertTelemetry()](https://github.com/aws/aws-toolkit-vscode/blob/21ca0fca26d677f105caef81de2638b2e4796804/src/test/testUtil.ts#L164), regardless of the current environment. + +## Guidelines + +- Use `run()` where possible. It automatically sets the `result` and `reason` fields. See below for details. + - `run()` gets the `reason` value from the `Error.code` property of any exception that is thrown. + - Your code can throw `ToolkitError` with a `code` field to communicate errors, validation issues, or [cancellation](https://github.com/aws/aws-toolkit-vscode/blob/06661f84530c6b5331579871d685515700b7767e/src/auth/sso/model.ts#L138). See below. +- The `reason` and `result` fields are standard metric fields shared by all Toolkits (VSCode, JetBrains, VisualStudio). + They should be used instead of special-purpose metrics or fields. +- `result` allows the Toolkits team to monitor all features for potential regressions. +- `reason` gives insight into the cause of a `result=Failed` metric. +- `telemetry.record()` called during a `telemetry.foo.run(…)` context will automatically annotate the current `foo` metric. + - For example, the cloudwatch logs feature adds `hasTimeFilter` info its metrics by [calling telemetry.record()](https://github.com/aws/aws-toolkit-vscode/blob/06661f84530c6b5331579871d685515700b7767e/src/cloudWatchLogs/cloudWatchLogsUtils.ts#L21-L24). + +## Incrementally Building a Metric + +User actions or other features may have multiple stages/steps, called a "workflow" or just "flow". A telemetry ["trace"](https://opentelemetry.io/docs/concepts/signals/traces/) captures a flow as tree of ["spans"](https://opentelemetry.io/docs/concepts/signals/traces/#spans). + +For example, `setupThing()` has multiple steps until it is completed, ending with `lastSetupStep()`. + +```typescript +function setupThing() { + setupStep1() + setupStep2() + ... + lastSetupStep() +} +``` + +
+ +If we want to send a metric event, lets call it `metric_setupThing`, then the code could look something like this: + +```typescript +function setupThing() { + try { + ... + lastSetupStep() + telemetry.metric_setupThing.emit({result: 'Succeeded', ...}) + } + catch (e) { + telemetry.metric_setupThing.emit({result: 'Failed', reason: 'Not Really Sure Why' ...}) + } +} +``` + +Here we emitted a final metric based on the failure or success of the entire execution. Each metric is discrete and immediately gets sent to the telemetry service. + +
+ +But usually code is not flat and there are many nested calls. If something goes wrong during the execution it would be useful to have more specific information at the area of failure. For that we can use `run()` along with `telemetry.record()`. + +`run()` accepts a callback, and when the callback is executed, any uses of `telemetry.record()` at _any nesting level_ during execution of that callback, will update the +attributes of the ["current metric"](https://github.com/aws/aws-toolkit-vscode/blob/13cb98d5315092ddc9eb5ba898e5f26810dada25/src/shared/telemetry/spans.ts#L233). +And at the end (that is, when `run()` returns) we will emit a single metric with the last updated attributes. +[Example](https://github.com/aws/aws-toolkit-vscode/blob/06661f84530c6b5331579871d685515700b7767e/src/cloudWatchLogs/cloudWatchLogsUtils.ts#L21-L24) + +When an exception is thrown from a `run()` context, `run()` will [automatically set](https://github.com/aws/aws-toolkit-vscode/blob/a583825bec6cb68c4942fa60d185644833528532/src/shared/errors.ts#L273-L289) +the `reason` field based on the Error `code` field. You can explicitly set `code` when throwing +a `ToolkitError`, for [example](https://github.com/aws/aws-toolkit-vscode/blob/d08e59952a6c75a5c6c00fdc464e26750c0e85f5/src/auth/auth.ts#L530): + + throw new ToolkitError('No sso-session name found in ~/.aws/config', { code: 'NoSsoSessionName' }) + +Note: prefer reason codes with a format similar to existing codes (not sentences). You can find existing codes by searching the codebase: + + git grep 'code: ' + +### Example + +```typescript +setupThing() + +function setupThing() { + // Start the run() for metric_setupThing + telemetry.metric_setupThing.run(span => { + // Update the metric with initial attributes + span.record({sessionId: '123456'}) // now no matter where the control flow exits after this line in this method, this attribute will always be set + ... + setupStep2() + ... + + if (userInput.CancelSelected) { + // By setting the `cancelled` attribute to true, the `result` attribute will be set to Cancelled + throw new ToolkitError("Thing has been cancelled", { cancelled: true}) + } + }) + // At this point the final values from the `record()` calls are used to emit a the final metric. + // If no exceptions have been thrown, the `result` attribute is automatically set to Success. +} + +function setupStep2() { + try { + // Do work + } + catch (e) { + // Here we can update the metric with more specific information regarding the failure. + + // Also notice we are able to use `telemetry.metric_setupThing` versus `span`. + // This is due to `metric_setupThing` being added to the "context" from the above run() + // callback argument. So when we use record() below it will update the same + // thing that span.record() does. + + // Keep in mind record() must be run inside the callback argument of run() for + // the attributes of that specific metric to be updated. + telemetry.metric_setupThing.record({ + workDone: // ... + }) + // If this exception is allowed to propogate to the `run()`, then the `result` will be automatically set to Failed and the `reason` to the `code` set here + throw new ToolkitError(e as Error, { code: "SomethingWentWrongInStep2"}) + } +} +``` + +
+ +Finally, if `setupStep2()` was the thing that failed we would see a metric like: + +``` +{ + "metadata.metricName": "metric_setupThing", + "sessionId": "123456", + "result": "Failed", + "reason": "SomethingWentWrongInStep2", + ... +} +``` + +## Adding a "Stack Trace" to your metric + +When errors are thrown we do not attach the stack trace in telemetry. We only know about the error itself, but +not the path it took to get there. We sometimes need this stack trace to debug, and only have telemetry to get insight on what happened since we do not have access to logs. + +### Scenario + +Common example: _"I have a function, `thisFailsSometimes()` that is called in multiple places. The function sometimes fails, I know from telemetry, but I do not know if it is failing when it is a specific caller. If I knew the call stack/trace that it took to call my function that would help me debug."_ + +```typescript +function runsSuccessfully() { + thisFailsSometimes(1) // this succeeds +} + +function thisThrows() { + thisFailsSometimes(0) // this fails +} + +function failsDependingOnInput(num: number) { + return telemetry.my_Metric.run(() => { + if (number === 0) { + throw Error('Cannot be 0') + } + ... + }) +} +``` + +### Solution + +On class methods, use the `@withTelemetryContext()` decorator to add context to the execution. Depending on the args set, it provides features like emitting the result, or adding it's context to errors. + +> NOTE: Decorators are currently only supported for methods and not functions + +```typescript +class MyClass { + @withTelemetryContext({ name: 'runsSuccessfully', class: 'MyClass' }) + public runsSuccessfully() { + failsDependingOnInput(1) + } + + @withTelemetryContext({ name: 'thisThrows', class: 'MyClass', errorCtx: true }) + public thisThrows() { + failsDependingOnInput(0) + } + + @withTelemetryContext({ name: 'failsDependingOnInput' class: 'MyClass', emit: true, errorCtx: true}) + private failsDependingOnInput(num: number) { + if (number === 0) { + throw Error('Cannot be 0') + } + ... + } +} + +// Results in a metric: { source: 'MyClass#thisThrows,failsDependingOnInput', result: 'Failed' } +// Results in an error that has context about the methods that lead up to it. +new MyClass().thisThrows() +``` + +Separately if you must use a function, add a value to `function` in the options of a `run()`. This will result in a stack of functions identifiers that were previously called +before `failsDependingOnInput()` was run. You can then retrieve the stack in the `run()` of your final metric using `getFunctionStack()`. + +```typescript +function runsSuccessfully() { + telemetry.my_Metric.run(() => failsDependingOnInput(1), { functionId: { name: 'runsSuccessfully' }}) +} + +function thisThrows() { + telemetry.my_Metric.run(() => failsDependingOnInput(0), { functionId: { source: 'thisThrows' }}) +} + +function failsDependingOnInput(num: number) { + return telemetry.my_Metric.run(() => { + telemetry.record({ theCallStack: asStringifiedStack(telemetry.getFunctionStack())}) + if (number === 0) { + throw Error('Cannot be 0') + } + ... + }, { functionId: { name: 'failsDependingOnInput' }}) +} + +// Results in a metric: { theCallStack: 'thisThrows:failsDependingOnInput', result: 'Failed' } +// { theCallStack: 'thisThrows:failsDependingOnInput' } implies 'thisThrows' was run first, then 'failsDependingOnInput'. See docstrings for more info. +thisThrows() +``` + +### Important Notes + +- If a nested function does not use a `run()` then it will not be part of the call stack. + + ```typescript + function a() { + return telemetry.my_Metric.run(() => b(), { functionId: { name: 'a' } }) + } + + function b() { + return c() + } + + function c() { + return telemetry.my_Metric.run(() => asStringifiedStack(telemetry.getFunctionStack()), { + functionId: { name: 'c' }, + }) + } + + c() // result: 'a:c', note that 'b' is not included + ``` + +- If you do not want your `run()` to emit telemetry, set `emit: false` in the options + + ```typescript + function a() { + return telemetry.my_Metric.run(() => b(), { functionId: { name: 'a' }, emit: false }) + } + ``` + +- If you want to add to the function stack, but don't have a specific metric to use, + use the metric named `function_call`. Also look to set `emit: false` if you do not + want it to emit telemetry. + + ```typescript + function a() { + return telemetry.function_call.run(() => b(), { functionId: { name: 'a' }, emit: false }) + } + ``` + +- If your function name is generic, look to make it unique so there is no confusion. + + ```typescript + function a() { + return telemetry.my_Metric.run(() => b(), { functionId: { name: 'aButMoreUnique' } }) + } + ``` + +## Tracing Telemetry Events + +All telemetry events include a traceId in addition to other attributes. Traceids allow for improved tracking and correlation of related events across a single operation or user flow. + +### What is a traceId? + +A traceId is a unique identifier that is generated for the top-level telemetry event in a flow and then propagated to all subsequent related events. This allows us to group and analyze all events associated with a particular operation. + +### How it works + +1. When a top-level telemetry event is created (e.g., `vscode_executeCommand`), a new traceId is generated. +2. This traceId is then attached to all subsequent related telemetry events that occur as part of the same operation or flow. +3. The traceId remains consistent for all events within the same flow + +### Example + +Consider a flow where `vscode_executeCommand` triggers `amazonq_enterFocusChat` and `amazonq_openChat`. The resulting telemetry events would look like this: + +``` +vscode_executeCommand: +traceId: 'aaaaa-aaaaa-aaaaa-aaaaa-aaaaa' + +amazonq_enterFocusChat +traceId: 'aaaaa-aaaaa-aaaaa-aaaaa-aaaaa' + +amazonq_openChat +traceId: 'aaaaa-aaaaa-aaaaa-aaaaa-aaaaa' +``` + +allowing us to look up `traceId=aaaaa-aaaaa-aaaaa-aaaaa-aaaaa` in our telemetry instance and find all the related events. + +For more information visit the OpenTelemetry documentation on traces: https://opentelemetry.io/docs/concepts/signals/traces/ + +### Manual Trace ID Instrumentation + +In certain scenarios you may need to manually instrument disjoint flows to track how a `traceId` propagates through them. e.g. + +1. Measuring the time it takes for a message to travel from Amazon Q chat, through VS Code, and back to the customer. +2. Determining the duration for Amazon Q inline to display a message to the user. + +In these cases, where there isn't a direct hierarchy of function calls, manual instrumentation of the `traceId` is necessary. + +#### Implementation Options + +#### 1. When not currently running in a span + +If you're not within an active span and you know the `traceId` you want to use: + +```javascript +telemetry.withTraceId(() => { + // Code to be executed within this trace +}, 'myTraceId') +``` + +This method wraps the provided function with the specified traceId + +#### 2. When currently running in a span + +If you're already executing within a span (e.g., vscode_executeCommand) and you know the traceId you want to use: + +```javascript +telemetry.record({ + traceId: 'myTraceId', +}) +``` + +This approach records the traceId for the current span and all future spans within the same execution context. diff --git a/docs/vscode_behaviors.md b/docs/vscode_behaviors.md new file mode 100644 index 00000000000..6d2dca2546a --- /dev/null +++ b/docs/vscode_behaviors.md @@ -0,0 +1,31 @@ +# VS Code Behaviors + +Many VS Code behavoirs for certain APIs or user interactions with the IDE are not clearly documented, +or documented at all. Please add any findings to this document. + +## `deactivate()` - extension shutdown + +This method is defined as part of the VS Code extension API, and is run on a **graceful** shutdown +for each extension. + +- Our extension and its `deactivate()` function are in the Extension Host process [1] +- The Extension Host process has at most 5 seconds to shut down, after which it will exit. [1] +- The vscode API will be unreliable at deactivation time. So certain VSC APIs like the filesystem may not work. [1] + - The VSC Filesystem API has been confirmed to not work +- In `Run & Debug` mode, closing the Debug IDE instance behaves differently depending on how it is closed + - The regular close button in the Debug IDE instance results in a graceful shutdown + - The red square in the root IDE instance to stop the debugging session results on a non-graceful shutdown, meaning `deactivate()` is not run. +- `Reload Window` triggers `deactivate()` + +Sources: + +- [[1]](https://github.com/Microsoft/vscode/issues/47881#issuecomment-381910587) +- [[2]](https://github.com/microsoft/vscode/issues/122825#issuecomment-814218149) + +## State (`globalState`, `Memento`) + +TODO: + +- How it behaves between remote (ssh) and local +- How it is not completely reliable. Reads/writes have no guarantee to work (though we are fine in most cases) +- How it can break as observed with crash monitoring work. At a certain point writes were seemingly succeeding, but did not actually propagate to all IDE instances. diff --git a/docs/web.md b/docs/web.md new file mode 100644 index 00000000000..1540ddf8134 --- /dev/null +++ b/docs/web.md @@ -0,0 +1,174 @@ +# Web Mode + +This extension can run in a web browser (eg: [vscode.dev](https://vscode.dev)), but with limited functionality compared to +the desktop version. + +## Running the Web mode implementation + +You can run the Web mode implementation of the extension in the following ways. + +## Pre-requisites + +[See the CONTRIBUTING document.](../CONTRIBUTING.md#setup) + +### General Notes + +- To see logs, using the Command Palette search: `Toggle Developer Tools`. Then go to the `Console` tab. In web mode VS Code seems to duplicate log messages, idk how to fix this. +- The difference between web mode and Node.js/desktop is that in web mode everything runs in browser environment so certain things like Node.js modules will **not** be available. + +## Running in an actual Browser (Recommended) + +The following steps will result in a Chrome window running with VS Code +and the web version of the AWS Toolkit extension installed: + +1. (Recommended) To have the Chrome window save your VS Code state after closing it, you need to modify the node module which executes the playwright method that opens it. In `node_modules/@vscode/test-web/out/index.js#openBrowser()` make the following change: + + Before: + + ```typescript + const browser = await browserType.launch({ headless, args, devtools: options.devTools }) + const context = await browser.newContext({ viewport: null }) + ``` + + After: + + ```typescript + const tempContextDir = path.join(process.cwd(), '.vscode-test-web/aws-toolkit-user-dir') + const browser = await browserType.launchPersistentContext(tempContextDir, { + headless, + args, + devtools: options.devTools, + viewport: null, + }) + const context = browser + ``` + +2. In the `Run & Debug` menu select the `Extension Web` option + +> Note: To stop the debug session, you need to click the read `Disconnect` button multiple times + +> Note: Setting breakpoints in VS Code works + +#### (OPTIONAL) Enabling CORS + +By default, we disable CORS in web mode since certain endpoints +such as telemetry or auth do not support CORS (at the moment of writing) for the VSCode origin. + +In the case you want to enable CORS in the browser to test CORS compatibility +do the following: + +- In `package.json` find the `webRun` script +- Temporarily remove `--browserOption=--disable-web-security` + +Now when you run the extension in the browser it will do CORS checks. + +### Troubleshooting + +- `Extension Web` fails to launch + - Update `@vscode/test-web` by running `$ npm install @vscode/test-web@latest` + - Update/install playwright with `$ npx playwright install`. This will be mentioned in one of the build tasks outputs. + +## Running in [vscode.dev](https://vscode.dev) + +The following will explain how to get your latest local development changes running in the actual `vscode.dev`. Use this if you want to test on an actual VS Code Web instance. + +1. Build the extension. We need the Web mode entrypoint file to exist. +2. OPTIONAL: Start up your browser with security disabled. Certain functionalities do not support CORS and will fail otherwise. + - On MacOS from the CLI is similar to `/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --disable-web-security` +3. `cd` to the extension you want to test in `packages/`. Eg: `packages/amazonq/`. + - We need to do this since the following command hosts your build from the `cwd`. +4. Follow the [VS Code documentation](https://code.visualstudio.com/api/extension-guides/web-extensions#test-your-web-extension-in-vscode.dev) for setting up certs, serving your the latest changes, and installing the extension to `vscode.dev`. + +## Testing in VSCode Web Mode + +The following steps will result in a VSCode Extension window running +with the AWS Toolkit extension installed. While it looks the same as a typical +VS Code window, in the background it is running in a Browser context. + +- In the `Run & Debug` menu run: `Extension Tests (web)` + +> NOTE: Tests do not spin up an actual browser window, but if we find a good reason to switch it will require some additional work. The current way does not require dowloading a separate browser like Chromium. + +## Adding Web mode specific npm modules + +If you need to manage npm modules required for Web mode, such as a [browserfied module](https://www.npmjs.com/package/os-browserify), see [the documentation here](../packages/core/src/web/README.md#packagejson). + +## Finding incompatible transitive dependencies + +For example, if I have a Typescript module, `myFile.ts`, that imports a module which imports another module (transitive dependency) such as `fs-extra`, +when I execute `myFile.ts` in the browser it will break due to `fs-extra` not being browser compatible. + +> INFO: A common error is `Cannot read properties of undefined (reading 'native')` caused by `fs-extra` + +It may be difficult to determine which module imported `fs-extra` due to a nested chain of transitive dependencies. + +As a solution, we can use [`dependency-cruiser`](https://www.npmjs.com/package/dependency-cruiser) to generate a dependency diagram +to help us visualize the imports and determine which module is importing a certain module. + +### How to use + +1. Install the `graphviz` cli, this provides the `dot` cli command + - Mac: `brew install graphviz` + - Others: [See documentation](https://www.graphviz.org/download/) +2. Temporarily install `dependency-cruiser` + - IMPORTANT: You will want to revert this install when done + - `npm i dependency-cruiser` +3. Run `npx depcruise {RELATIVE_PATH_TO_FILE} --reaches "{YOUR_MODULE}" --output-type dot | dot -T svg > dependency-graph.svg` + - For example `npx depcruise src/shared/fs/fs.ts --reaches "fs-extra" --output-type dot | dot -T svg > dependency-graph.svg`, generates the following which shows that `fs-extra` is imported by `fileSystemUtilities.ts`: + ![Dependency Graph](./images/dependency-graph-small.svg) + +## Behavior of module exports in tests + +- **In Web mode**, state is not shared between the actual extension code and the unit test code. I.e you cannot modify an exported module variable in the extension code and see that change from the tests. +- However, state stored in `globalThis` is observable from the tests. + +When running web tests, the context between the extension code and test code is not shared. +Though it is shared in the Node version of the extension. + +- Does NOT work in Web mode tests: + - Module code: + ```typescript + export let myGlobal = 'A' + function activate() { + // Change the exported module variable. + myGlobal = 'B' + } + ``` + - Test code: + ```typescript + // Web unit test + import { myGlobal } from '../src/extension.ts' + describe('test', function () { + it('test', function () { + assert.strictEqual(myGlobal, 'B') // Fails in Web (but not Node.js). The value is 'A'. + }) + }) + ``` +- DOES work in Web mode tests: + - Module code: + ```typescript + ;(globalThis as any).myGlobal = 'A' + function activate() { + ;(globalThis as any).myGlobal = 'B' + } + ``` + - Test code: + ```typescript + // Web unit test + describe('test', function () { + it('test', function () { + assert.strictEqual((globalThis as any).myGlobal, 'B') // Passes in Web and Node.js. + }) + }) + ``` + +### Web Worker + +The assumption for the behavior is due to how Web Workers work. We (VS Code) use them to run our extension and test code in the browser. The scripts share module exports differently compared to a different environment such as Node.js. + +- [`WorkerGlobalScope`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope) + - The context of the executing code is contained within itself and is not accessible to other scripts (tests). + - VS Code uses Dedicated Workers since `globalThis` is indicated as a [`DedicatedWorkerGlobalScope`](https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope) when debugging + - `globalThis` is one object (that I could find so far) which **is shared** between our extension and test scripts. A guess to why is that the main script spawns another web worker (for unit tests) and passes on the `DedicatedWorkerGlobalScope`. See [`"Workers can also spawn other workers"`](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers). + - `globalThis` returns `global` in Node.js, or a `WorkerGlobalScope` in the browser + - NOTE: `globalThis` is shared across all of VS Code, including all extensions. diff --git a/package-lock.json b/package-lock.json index 553a7269356..ef7e8c05e28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1773 +1,2650 @@ { - "name": "aws-toolkit-vscode", - "version": "1.63.0-SNAPSHOT", - "lockfileVersion": 2, + "name": "root", + "version": "0.0.1", + "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "aws-toolkit-vscode", - "version": "1.63.0-SNAPSHOT", + "name": "root", + "version": "0.0.1", "hasInstallScript": true, "license": "Apache-2.0", + "workspaces": [ + "packages/core/src/web", + "packages/*", + "plugins/*" + ], "dependencies": { - "@aws-sdk/client-sso": "^3.181.0", - "@aws-sdk/client-sso-oidc": "^3.181.0", - "@aws-sdk/credential-provider-ini": "^3.46.0", - "@aws-sdk/credential-provider-process": "^3.15.0", - "@aws-sdk/credential-provider-sso": "^3.38.0", - "@aws-sdk/util-arn-parser": "^3.46.0", - "@iarna/toml": "^2.2.5", - "adm-zip": "^0.5.9", - "amazon-states-language-service": "^1.8.0", - "async-lock": "^1.3.0", - "aws-sdk": "^2.1267.0", - "aws-ssm-document-language-service": "^1.0.0", - "bytes": "^3.1.2", - "cross-spawn": "^7.0.3", - "fast-json-patch": "^3.1.1", - "fs-extra": "^10.0.1", - "got": "^11.8.5", - "immutable": "^4.0.0", - "js-yaml": "^4.1.0", - "jsonc-parser": "^3.0.0", - "lodash": "^4.17.21", - "mime-types": "^2.1.32", - "moment": "^2.29.4", - "portfinder": "^1.0.25", - "semver": "^7.3.5", - "strip-ansi": "^5.2.0", - "tcp-port-used": "^1.0.1", - "typescript": "^4.8.4", - "uuid": "^8.3.2", - "vscode-languageclient": "^6.1.4", - "vscode-languageserver": "^6.1.1", - "vscode-languageserver-textdocument": "^1.0.3", - "vscode-nls": "^5.0.0", - "vue": "^3.2.31", - "winston": "^3.7.1", - "winston-transport": "^4.5.0", - "xml2js": "^0.4.19", - "yaml": "^1.9.2", - "yaml-cfn": "^0.3.1" + "@aws/language-server-runtimes": "^0.2.128", + "@types/node": "^22.7.5", + "jaro-winkler": "^0.2.8", + "vscode-nls": "^5.2.0", + "vscode-nls-dev": "^4.0.4" }, "devDependencies": { - "@aws-toolkits/telemetry": "^1.0.87", - "@cspotcode/source-map-support": "^0.8.1", - "@sinonjs/fake-timers": "^8.1.0", - "@types/adm-zip": "^0.4.34", - "@types/async-lock": "^1.1.3", - "@types/bytes": "^3.1.0", - "@types/cross-spawn": "^6.0.0", - "@types/fs-extra": "^9.0.11", - "@types/glob": "^7.1.1", - "@types/js-yaml": "^4.0.5", - "@types/lodash": "^4.14.180", - "@types/marked": "^4.0.2", - "@types/mime-types": "^2.1.1", - "@types/mocha": "^10.0.0", - "@types/node": "^14.18.5", - "@types/readline-sync": "^1.4.3", - "@types/semver": "^7.3.6", - "@types/sinon": "^10.0.5", - "@types/sinonjs__fake-timers": "^8.1.1", - "@types/tcp-port-used": "^1.0.1", - "@types/uuid": "^8.3.3", - "@types/vscode": "1.50.0", - "@types/vscode-webview": "^1.57.0", - "@types/xml2js": "^0.4.8", - "@typescript-eslint/eslint-plugin": "^5.38.0", - "@typescript-eslint/parser": "^5.38.0", - "@vscode/codicons": "^0.0.32", - "@vscode/test-electron": "^2.2.3", - "@vue/compiler-sfc": "^3.2.40", - "circular-dependency-plugin": "^5.2.2", - "css-loader": "^6.5.1", - "esbuild-loader": "2.20.0", - "eslint": "^8.26.0", - "eslint-config-prettier": "8.3", + "@aws-toolkits/telemetry": "^1.0.329", + "@playwright/browser-chromium": "^1.43.1", + "@stylistic/eslint-plugin": "^2.11.0", + "@types/he": "^1.2.3", + "@types/jaro-winkler": "^0.2.4", + "@types/vscode": "^1.68.0", + "@types/vscode-webview": "^1.57.1", + "@types/webpack-env": "^1.18.5", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.14.1", + "@vscode/codicons": "^0.0.33", + "@vscode/test-electron": "^2.3.8", + "@vscode/test-web": "^0.0.65", + "@vscode/vsce": "^2.19.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-aws-toolkits": "file:plugins/eslint-plugin-aws-toolkits", "eslint-plugin-header": "^3.1.1", - "eslint-plugin-no-null": "^1.0.2", - "glob": "^7.1.7", - "husky": "^7.0.2", - "json-schema-to-typescript": "^11.0.2", - "marked": "^4.0.10", - "mocha": "^10.1.0", - "mocha-junit-reporter": "^2.0.0", - "mocha-multi-reporters": "^1.5.1", - "nyc": "^15.1.0", - "prettier": "^2.7.1", - "prettier-plugin-sh": "^0.8.1", - "pretty-quick": "^3.1.0", - "readline-sync": "^1.4.9", - "sinon": "^14.0.0", - "ts-mockito": "^2.5.0", - "ts-node": "^10.7.0", - "umd-compat-loader": "^2.1.2", - "vsce": "^2.6.3", - "vscode-nls-dev": "^3.3.1", - "vue-loader": "^16.8.1", - "vue-style-loader": "^4.1.3", - "webfont": "^11.2.26", - "webpack": "^5.65.0", - "webpack-cli": "^4.9.1", - "webpack-dev-server": "^4.9.2" - }, + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-security-node": "^1.1.4", + "eslint-plugin-unicorn": "^54.0.0", + "husky": "^9.0.7", + "prettier": "^3.3.3", + "prettier-plugin-sh": "^0.14.0", + "pretty-quick": "^4.0.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4", + "webpack": "^5.95.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.15.2", + "webpack-merge": "^5.10.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "dev": true, + "license": "MIT", "engines": { - "vscode": "^1.50.1" + "node": ">=0.10.0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" + "node_modules/@amzn/amazon-q-developer-streaming-client": { + "resolved": "src.gen/@amzn/amazon-q-developer-streaming-client", + "link": true + }, + "node_modules/@amzn/codewhisperer-streaming": { + "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": ">=6.0.0" - } - }, - "node_modules/@aws-crypto/ie11-detection": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", - "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", - "dependencies": { - "tslib": "^1.11.1" + "node": ">=14.0.0" } }, - "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/sha256-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", - "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", - "dependencies": { - "@aws-crypto/ie11-detection": "^2.0.0", - "@aws-crypto/sha256-js": "^2.0.0", - "@aws-crypto/supports-web-crypto": "^2.0.0", - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.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/@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/@aws-crypto/sha256-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", - "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", + "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": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^1.11.1" } }, - "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/@aws-crypto/supports-web-crypto": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", - "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", + "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/@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/@aws-crypto/util": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.1.tgz", - "integrity": "sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ==", + "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.1.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" } }, - "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/@aws-sdk/abort-controller": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.178.0.tgz", - "integrity": "sha512-ptDkCB06BJrYdhKzamM9yI15LxcGkPczY80hzKAY/aecm09alnW27uCt5HJJx2nCd18IUH28ZO1sc7DTLOWb3A==", + "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/types": "3.178.0", - "tslib": "^2.3.1" + "@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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/abort-controller/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.181.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.181.0.tgz", - "integrity": "sha512-p/HsYVAO7fgjFfn4LqlnlpSKPXGPegAwjPpY+SqyK3/Hj1OtED4whG8LTgxZSTtYwNIkHEjrHnXTiymyXh34Aw==", - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.178.0", - "@aws-sdk/fetch-http-handler": "3.178.0", - "@aws-sdk/hash-node": "3.178.0", - "@aws-sdk/invalid-dependency": "3.178.0", - "@aws-sdk/middleware-content-length": "3.178.0", - "@aws-sdk/middleware-host-header": "3.178.0", - "@aws-sdk/middleware-logger": "3.178.0", - "@aws-sdk/middleware-recursion-detection": "3.178.0", - "@aws-sdk/middleware-retry": "3.178.0", - "@aws-sdk/middleware-serde": "3.178.0", - "@aws-sdk/middleware-stack": "3.178.0", - "@aws-sdk/middleware-user-agent": "3.178.0", - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/node-http-handler": "3.178.0", - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/smithy-client": "3.180.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/url-parser": "3.178.0", - "@aws-sdk/util-base64-browser": "3.170.0", - "@aws-sdk/util-base64-node": "3.170.0", - "@aws-sdk/util-body-length-browser": "3.170.0", - "@aws-sdk/util-body-length-node": "3.170.0", - "@aws-sdk/util-defaults-mode-browser": "3.180.0", - "@aws-sdk/util-defaults-mode-node": "3.180.0", - "@aws-sdk/util-user-agent-browser": "3.178.0", - "@aws-sdk/util-user-agent-node": "3.178.0", - "@aws-sdk/util-utf8-browser": "3.170.0", - "@aws-sdk/util-utf8-node": "3.170.0", - "tslib": "^2.3.1" + "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": ">=12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.181.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.181.0.tgz", - "integrity": "sha512-cMrEVAgLDM4AlGuMS6uzWSCYohIth2FcqWq4102lWve2kPQK5E6bl3SdWFLOIZc75ladkbwUXivBYtjNtLeRQA==", - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.178.0", - "@aws-sdk/fetch-http-handler": "3.178.0", - "@aws-sdk/hash-node": "3.178.0", - "@aws-sdk/invalid-dependency": "3.178.0", - "@aws-sdk/middleware-content-length": "3.178.0", - "@aws-sdk/middleware-host-header": "3.178.0", - "@aws-sdk/middleware-logger": "3.178.0", - "@aws-sdk/middleware-recursion-detection": "3.178.0", - "@aws-sdk/middleware-retry": "3.178.0", - "@aws-sdk/middleware-serde": "3.178.0", - "@aws-sdk/middleware-stack": "3.178.0", - "@aws-sdk/middleware-user-agent": "3.178.0", - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/node-http-handler": "3.178.0", - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/smithy-client": "3.180.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/url-parser": "3.178.0", - "@aws-sdk/util-base64-browser": "3.170.0", - "@aws-sdk/util-base64-node": "3.170.0", - "@aws-sdk/util-body-length-browser": "3.170.0", - "@aws-sdk/util-body-length-node": "3.170.0", - "@aws-sdk/util-defaults-mode-browser": "3.180.0", - "@aws-sdk/util-defaults-mode-node": "3.180.0", - "@aws-sdk/util-user-agent-browser": "3.178.0", - "@aws-sdk/util-user-agent-node": "3.178.0", - "@aws-sdk/util-utf8-browser": "3.170.0", - "@aws-sdk/util-utf8-node": "3.170.0", - "tslib": "^2.3.1" + "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": ">=12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/config-resolver": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.178.0.tgz", - "integrity": "sha512-8xL98TGMaVULIN7HRWV2q1o0Y2p38QuweehzM8yXCZrrLOyHgWo3waP2RNVeddOB7MrSwwU/gw9rXSv7YHLZ6w==", + "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/signature-v4": "3.178.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-config-provider": "3.170.0", - "@aws-sdk/util-middleware": "3.178.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/config-resolver/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.46.0.tgz", - "integrity": "sha512-dBCyVBJ1nVi+lvM1jV6LFw8FpGjdeCglLMTmZUxJLBMh/Lp+GWtnGxd7u38WnH5gxKC4xLnYj9zP1t0ha1tGzQ==", + "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/property-provider": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==", + "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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-imds": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.46.0.tgz", - "integrity": "sha512-nQidNDq6mjas/wFOi9XvHkvNmzM/XdSB/eRh6CH+wQeb8RjAlGm2Ivg0mpz/iIxjPXDIduW8aI/gFU3+3um6CQ==", + "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/node-config-provider": "3.46.0", - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/url-parser": "3.46.0", - "tslib": "^2.3.0" - }, - "engines": { - "node": ">= 12.0.0" + "@aws-sdk/types": "3.357.0", + "@smithy/types": "^1.1.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" } }, - "node_modules/@aws-sdk/credential-provider-imds/node_modules/@aws-sdk/node-config-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.46.0.tgz", - "integrity": "sha512-Hzz860d1GNZSSX74TywfUu125l8BT6JkJuKG0QDhuC+9xklNfC1hgziihldHu6xL7DzY5UKgjyzdNBQfqCqLbw==", + "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/property-provider": "3.46.0", - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@aws-sdk/types": "3.357.0", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-sdk/credential-provider-imds/node_modules/@aws-sdk/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-imds/node_modules/@aws-sdk/querystring-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.46.0.tgz", - "integrity": "sha512-xxTnIXLbx4Jq16Utza7wh4HpPfVyCL0c+6NU2t+kXZ2sgOWhx2XAhShcZVbEkA/61UAMEIhyNBVE+z9OFz6X5g==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@smithy/types": "^1.2.0", + "@smithy/util-config-provider": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-imds/node_modules/@aws-sdk/shared-ini-file-loader": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.46.0.tgz", - "integrity": "sha512-Rp7Z1X23kvyRCziOxXu2PYCCPT5CQ5t8O4WoKrEkMT9Vqm2gluXOcCnL4iOpRkSRGEZT7lfe5OCM8ApNRTIHpQ==", + "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": { - "tslib": "^2.3.0" + "@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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-imds/node_modules/@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==", + "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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-imds/node_modules/@aws-sdk/url-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.46.0.tgz", - "integrity": "sha512-foMB0AC3QDy+KfvRxsMXvJQZXr9CMzdupcNIXwKRZog82tEEc09dVeUjuJrO4H+A2eK84SyawRfy+ow+LRqvqw==", + "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": { - "@aws-sdk/querystring-parser": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" } }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.46.0.tgz", - "integrity": "sha512-D+3YLWzCaFUUcbLHAsJIoaI8AhoSpIl3c0a3spVN64f+V1XtwunbJO6VXsIF78RLe0kTP7IA/Rey86ydQVeqcw==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.46.0", - "@aws-sdk/credential-provider-imds": "3.46.0", - "@aws-sdk/credential-provider-sso": "3.46.0", - "@aws-sdk/credential-provider-web-identity": "3.46.0", - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-credentials": "3.46.0", - "tslib": "^2.3.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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@smithy/protocol-http": "^1.2.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/shared-ini-file-loader": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.46.0.tgz", - "integrity": "sha512-Rp7Z1X23kvyRCziOxXu2PYCCPT5CQ5t8O4WoKrEkMT9Vqm2gluXOcCnL4iOpRkSRGEZT7lfe5OCM8ApNRTIHpQ==", + "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": { - "tslib": "^2.3.0" + "@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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==", + "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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-credentials": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-credentials/-/util-credentials-3.46.0.tgz", - "integrity": "sha512-d5bDyCDVYi6ThBY8AntAKooExayFuLUnCXsDkmmWpHlp26JZv9s1/DsXR219ELgu8jIAWiID54HjfEYf8qa6Vw==", + "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": { - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "tslib": "^2.3.0" + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.37.0.tgz", - "integrity": "sha512-VOfWtUBbICb7xEHRFN7+fRA+move/3HT4mZt7C5KBXIaILT3b8hrK1mT/fRQ3dx9dF56PEGj/WkACOBjMzIcdg==", + "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": { - "@aws-sdk/property-provider": "3.37.0", - "@aws-sdk/shared-ini-file-loader": "3.37.0", - "@aws-sdk/types": "3.37.0", - "@aws-sdk/util-credentials": "3.37.0", - "tslib": "^2.3.0" + "tslib": "^2.5.0" }, "engines": { - "node": ">= 10.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.46.0.tgz", - "integrity": "sha512-y3zv6FtaEu1cjtun6vQ1S/2aya70cPjCcoQhSrsH9TDYXp/ZRk4PN6xdVGGpkZX2kZhowGU5DvhOGK48IqrNZg==", + "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": { - "@aws-sdk/client-sso": "3.46.0", - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-credentials": "3.46.0", - "tslib": "^2.3.0" + "@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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/abort-controller": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.46.0.tgz", - "integrity": "sha512-XrCocRwajh3jkI/Y2PCZjYUZcJfmCa4DYM5nnW2+w4o7ez7vXEQ1j5FCI+/ogJIqfccnmEIlLZGlfzmc6vVbJw==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/client-sso": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.46.0.tgz", - "integrity": "sha512-n+dHT9azUW4pFA7a98gV/qFYdNwoMQ/4Y4tvPE28s9CKx8O0OIDlOwLPrhSBETCuRJNfnug1vNnFIzvOapfCkg==", - "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.46.0", - "@aws-sdk/fetch-http-handler": "3.46.0", - "@aws-sdk/hash-node": "3.46.0", - "@aws-sdk/invalid-dependency": "3.46.0", - "@aws-sdk/middleware-content-length": "3.46.0", - "@aws-sdk/middleware-host-header": "3.46.0", - "@aws-sdk/middleware-logger": "3.46.0", - "@aws-sdk/middleware-retry": "3.46.0", - "@aws-sdk/middleware-serde": "3.46.0", - "@aws-sdk/middleware-stack": "3.46.0", - "@aws-sdk/middleware-user-agent": "3.46.0", - "@aws-sdk/node-config-provider": "3.46.0", - "@aws-sdk/node-http-handler": "3.46.0", - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/smithy-client": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/url-parser": "3.46.0", - "@aws-sdk/util-base64-browser": "3.46.0", - "@aws-sdk/util-base64-node": "3.46.0", - "@aws-sdk/util-body-length-browser": "3.46.0", - "@aws-sdk/util-body-length-node": "3.46.0", - "@aws-sdk/util-user-agent-browser": "3.46.0", - "@aws-sdk/util-user-agent-node": "3.46.0", - "@aws-sdk/util-utf8-browser": "3.46.0", - "@aws-sdk/util-utf8-node": "3.46.0", - "tslib": "^2.3.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": ">=12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/config-resolver": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.46.0.tgz", - "integrity": "sha512-R7YGDhvVE1VIS7uyjG3rZE1nrRu/+YVBq/pPlq5f4Tis3EoUooPfr5yYOVAuZI1CGsgycbCi6jnaqLGIfxUFmQ==", + "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": { - "@aws-sdk/signature-v4": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-config-provider": "3.46.0", - "tslib": "^2.3.0" + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/fetch-http-handler": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.46.0.tgz", - "integrity": "sha512-uOfdwbUCG+2LQ4iMkxD/izlzjnIrB5P5HtH7L5w1EFIsdxDXeFnnql0FaEcOvaEEg2rs9z0t+oLwMJZnNNtqAg==", + "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": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/querystring-builder": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-base64-browser": "3.46.0", - "tslib": "^2.3.0" + "@smithy/types": "^1.2.0", + "@smithy/util-uri-escape": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/hash-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.46.0.tgz", - "integrity": "sha512-rABF9k5uSJdqmwBeYnu+2+iWEmPNVsoBy9bwLvEmGfh557wAwh3dL5IDf+NiIFrd8GTOF/2HV1477XXBl15C0g==", + "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": { - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-buffer-from": "3.46.0", - "tslib": "^2.3.0" + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/invalid-dependency": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.46.0.tgz", - "integrity": "sha512-KumWtDstAKpKRQbHA95AeHpBNtxCXHVbUk+nFAiXcBP281yEalUbyK0W5Q2bDl26L3z6zHodg3OJllHYavJKMg==", - "dependencies": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.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/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/is-array-buffer": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.46.0.tgz", - "integrity": "sha512-HcQtJgZDQgo7ivD79GF82pTf+zYGjsgzKG7lkUBEetSfkV0W8h6XfhN6DmuYQuCcu1Pt9IkN7haYNPiPdfDhvg==", + "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": { - "tslib": "^2.3.0" + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/middleware-content-length": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.46.0.tgz", - "integrity": "sha512-Vf17vKAZ9n2ZlkMoHmCXMHAJegw3djC8qxe2sGdHSGyozfJNpA77ec32ldLvBtQ82LPmSaqdhcbP0/oYCalnzA==", + "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": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@smithy/middleware-stack": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-stream": "^1.1.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.46.0.tgz", - "integrity": "sha512-I+WsUzpyzS9l5Dt64kyp7v+9KeYPOCviYmVw2kM1EZRdAeo+jiCRxU5LnDJ9ORxfRwGcEmQV7xb4UpqXcn2N6Q==", + "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": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/middleware-logger": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.46.0.tgz", - "integrity": "sha512-xkB98tfZc1pSeks0a7jagYIVHxJfoxHX7wcASzBa3IjyodZpSqDW392edF9c3kSCv6G6PGRbG+F1F6j7ZTVpRQ==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - }, - "engines": { - "node": ">= 12.0.0" + "@smithy/querystring-parser": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/middleware-retry": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.46.0.tgz", - "integrity": "sha512-YkNNs2dUcriLwy4pYG7nfa850tD8dtFUeE/IQ+YBMbWDedT31UFkCfHUdjBK1GFbIv+G1N+ZVGBCkWq1OuhKXw==", + "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": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/service-error-classification": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0", - "uuid": "^8.3.2" + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/middleware-serde": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.46.0.tgz", - "integrity": "sha512-5M56VUm/stsSabauHxFrv1BSoH0VPyMB1V4vewAD3cp5YGiUpChYxjhcBbzi0QvI65HLxa6nLedwrE+g1uVJ1Q==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/middleware-stack": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.46.0.tgz", - "integrity": "sha512-+3SmpYo12i9Gn7L/HEJqAv5+OieZL9zfXungFKr96rTpcvYDZWQblTP3tugBtvGV6V4tzvebMkUTWxBB6p+dhQ==", + "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": { - "tslib": "^2.3.0" + "@smithy/is-array-buffer": "^1.1.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.46.0.tgz", - "integrity": "sha512-n9VEWlIXxbJXCr2IpocNJQUW7dhCdAcPKmxV0T5LZ/AygKsLvbWy40u2Qm9/eB1MYpqiheeb5MsY3UXxHgnOlg==", + "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": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/node-config-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.46.0.tgz", - "integrity": "sha512-Hzz860d1GNZSSX74TywfUu125l8BT6JkJuKG0QDhuC+9xklNfC1hgziihldHu6xL7DzY5UKgjyzdNBQfqCqLbw==", + "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": { - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 10.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/node-http-handler": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.46.0.tgz", - "integrity": "sha512-SqeKskt47u/ZIeN5b6lmjOAx0yLiY/WDQ6N9Z6LRJCYiSZ7oHflA1jPWkX20qWOKioa2iHBVTNNX2lu8yFkWbg==", + "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": { - "@aws-sdk/abort-controller": "3.46.0", - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/querystring-builder": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@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": ">= 12.0.0" + "node": ">= 10.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/protocol-http": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.46.0.tgz", - "integrity": "sha512-hKqHYEp/JDfOl5kZKp0CRgvbsg+c52Ss4KwuRoU9vA56VZ5TpfgHznajdme97xedsE40hnZeitv2BKEMbkYCqg==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/querystring-builder": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.46.0.tgz", - "integrity": "sha512-YYGRK291ro+KR3TX0jjyGRdMGHn6D2CBD89oXj8tAV3djeMIpFSGDrEL+NKeJvp7aBNlEnQ9kSfzyQuzQVvJWA==", + "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": { - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-uri-escape": "3.46.0", - "tslib": "^2.3.0" + "@smithy/service-error-classification": "^1.1.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/querystring-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.46.0.tgz", - "integrity": "sha512-xxTnIXLbx4Jq16Utza7wh4HpPfVyCL0c+6NU2t+kXZ2sgOWhx2XAhShcZVbEkA/61UAMEIhyNBVE+z9OFz6X5g==", + "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": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/service-error-classification": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.46.0.tgz", - "integrity": "sha512-hFDh/qtvKX9xUPxjGiDvumKsoO/3+eL4hi6X3qWN8lHg49wixjwcwlCEPn9jhdFJ9TRXc20CgPxWv4+V96Yf/A==", + "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": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/shared-ini-file-loader": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.46.0.tgz", - "integrity": "sha512-Rp7Z1X23kvyRCziOxXu2PYCCPT5CQ5t8O4WoKrEkMT9Vqm2gluXOcCnL4iOpRkSRGEZT7lfe5OCM8ApNRTIHpQ==", + "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": { - "tslib": "^2.3.0" + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/signature-v4": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.46.0.tgz", - "integrity": "sha512-qtI1t0CrEhVCxaezmmBpMe1WmQxdxho8oPiMEKWIUkkXQFg78Eg3jnXlLhjL4+MGHMqBB3mV7nGO6k8qu8H9rA==", + "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": { - "@aws-sdk/is-array-buffer": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-hex-encoding": "3.46.0", - "@aws-sdk/util-uri-escape": "3.46.0", - "tslib": "^2.3.0" + "@smithy/abort-controller": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/smithy-client": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.46.0.tgz", - "integrity": "sha512-Dzx4CR+rOkr5hXbLhnOfnrPWmSs4O9BTjFWD+4oh+RTXq0It8g+fWZxPcdvRCDU4GjS9Gtbkw0f0pN3FMCEszQ==", + "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": { - "@aws-sdk/middleware-stack": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "strnum": "^1.0.5" }, - "engines": { - "node": ">= 12.0.0" + "bin": { + "fxparser": "src/cli/cli.js" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==", + "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": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/url-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.46.0.tgz", - "integrity": "sha512-foMB0AC3QDy+KfvRxsMXvJQZXr9CMzdupcNIXwKRZog82tEEc09dVeUjuJrO4H+A2eK84SyawRfy+ow+LRqvqw==", + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/querystring-parser": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-base64-browser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.46.0.tgz", - "integrity": "sha512-oDlExDHYVOXsHFwFCA+CxZlGiHWeO53l0xoohpTIwGV6u48jED/4GrNM6iWVT6Vwd4skqtRMM41IHXjtiCtp/g==", + "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": "^2.3.0" + "tslib": "^1.11.1" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-base64-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.46.0.tgz", - "integrity": "sha512-/ruNBm21Ptk+IGhwTphs8j5oDCjNIrUSipDoRtUuMGQR9TnNzup0e+sJDqP0BrKKM+tcvqEUhz+MScxbwJrwmg==", + "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-sdk/util-buffer-from": "3.46.0", - "tslib": "^2.3.0" - }, - "engines": { - "node": ">= 12.0.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-sdk/credential-provider-sso/node_modules/@aws-sdk/util-body-length-browser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.46.0.tgz", - "integrity": "sha512-OJgMlBv4gEdmHCdZO9htysz9GMw0mS7qB3I5CbZ2aBOM0NvmaU7nqI6zYCoEmGh0keq0CnMBlNZhBBAwtiKYqg==", + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.0" + "@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-sdk/credential-provider-sso/node_modules/@aws-sdk/util-body-length-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.46.0.tgz", - "integrity": "sha512-jyD+2c7iaD4Aih93Fm4I183SbdhSy4FNmSlK49PctMVVF+QSpzQxAJvv/nTwq37Kb8orVvs+sgy2FF3lxfOUJg==", + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-buffer-from": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.46.0.tgz", - "integrity": "sha512-e3avbwAUULpPCk4ke9ctrhAwxcXvMv8FYymNJDEN7+9lqZ4XqAjPt+R+IEEFMEbWmIPeZ8TpLw3yuru1Z74iuA==", + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/is-array-buffer": "3.46.0", - "tslib": "^2.3.0" - }, - "engines": { - "node": ">= 12.0.0" + "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-config-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.46.0.tgz", - "integrity": "sha512-KzzusGkvmb1uy3EItl+9YRxOOtjmU6iaAi9pBzHR2fiv13EMVNZrycVFPeGwz6LrsAEumKmTAZjR6c8BRbxtjw==", + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.0" + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-accessanalyzer": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-accessanalyzer/-/client-accessanalyzer-3.888.0.tgz", + "integrity": "sha512-wtyBy3z2sUvuJxEcQhere+ttQWIVx5GauJaYahWAWBRhuZIkqMMebKC0ofJMBSEGTRXL98L3G96pCwoIffFbBw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.888.0", + "@aws-sdk/credential-provider-node": "3.888.0", + "@aws-sdk/middleware-host-header": "3.887.0", + "@aws-sdk/middleware-logger": "3.887.0", + "@aws-sdk/middleware-recursion-detection": "3.887.0", + "@aws-sdk/middleware-user-agent": "3.888.0", + "@aws-sdk/region-config-resolver": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@aws-sdk/util-user-agent-browser": "3.887.0", + "@aws-sdk/util-user-agent-node": "3.888.0", + "@smithy/config-resolver": "^4.2.1", + "@smithy/core": "^3.11.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/hash-node": "^4.1.1", + "@smithy/invalid-dependency": "^4.1.1", + "@smithy/middleware-content-length": "^4.1.1", + "@smithy/middleware-endpoint": "^4.2.1", + "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-serde": "^4.1.1", + "@smithy/middleware-stack": "^4.1.1", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-body-length-node": "^4.1.0", + "@smithy/util-defaults-mode-browser": "^4.1.1", + "@smithy/util-defaults-mode-node": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-retry": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/client-sso": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.888.0.tgz", + "integrity": "sha512-8CLy/ehGKUmekjH+VtZJ4w40PqDg3u0K7uPziq/4P8Q7LLgsy8YQoHNbuY4am7JU3HWrqLXJI9aaz1+vPGPoWA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.888.0", + "@aws-sdk/middleware-host-header": "3.887.0", + "@aws-sdk/middleware-logger": "3.887.0", + "@aws-sdk/middleware-recursion-detection": "3.887.0", + "@aws-sdk/middleware-user-agent": "3.888.0", + "@aws-sdk/region-config-resolver": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@aws-sdk/util-user-agent-browser": "3.887.0", + "@aws-sdk/util-user-agent-node": "3.888.0", + "@smithy/config-resolver": "^4.2.1", + "@smithy/core": "^3.11.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/hash-node": "^4.1.1", + "@smithy/invalid-dependency": "^4.1.1", + "@smithy/middleware-content-length": "^4.1.1", + "@smithy/middleware-endpoint": "^4.2.1", + "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-serde": "^4.1.1", + "@smithy/middleware-stack": "^4.1.1", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-body-length-node": "^4.1.0", + "@smithy/util-defaults-mode-browser": "^4.1.1", + "@smithy/util-defaults-mode-node": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-retry": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/core": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.888.0.tgz", + "integrity": "sha512-L3S2FZywACo4lmWv37Y4TbefuPJ1fXWyWwIJ3J4wkPYFJ47mmtUPqThlVrSbdTHkEjnZgJe5cRfxk0qCLsFh1w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.887.0", + "@aws-sdk/xml-builder": "3.887.0", + "@smithy/core": "^3.11.0", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/property-provider": "^4.0.5", + "@smithy/protocol-http": "^5.2.1", + "@smithy/signature-v4": "^5.1.3", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.888.0.tgz", + "integrity": "sha512-shPi4AhUKbIk7LugJWvNpeZA8va7e5bOHAEKo89S0Ac8WDZt2OaNzbh/b9l0iSL2eEyte8UgIsYGcFxOwIF1VA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-credentials": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-credentials/-/util-credentials-3.46.0.tgz", - "integrity": "sha512-d5bDyCDVYi6ThBY8AntAKooExayFuLUnCXsDkmmWpHlp26JZv9s1/DsXR219ELgu8jIAWiID54HjfEYf8qa6Vw==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.888.0.tgz", + "integrity": "sha512-Jvuk6nul0lE7o5qlQutcqlySBHLXOyoPtiwE6zyKbGc7RVl0//h39Lab7zMeY2drMn8xAnIopL4606Fd8JI/Hw==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "tslib": "^2.3.0" + "@aws-sdk/core": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/property-provider": "^4.0.5", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/util-stream": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-hex-encoding": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.46.0.tgz", - "integrity": "sha512-A831jS32tbdjki4ihS0BIZ3HAi1gv2PtLmAjAW+PHVvBd0S4OpbQApKxKPu0w+NKsp9XQYfkEkeFKCcMqN1zhg==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.888.0.tgz", + "integrity": "sha512-M82ItvS5yq+tO6ZOV1ruaVs2xOne+v8HW85GFCXnz8pecrzYdgxh6IsVqEbbWruryG/mUGkWMbkBZoEsy4MgyA==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.0" + "@aws-sdk/core": "3.888.0", + "@aws-sdk/credential-provider-env": "3.888.0", + "@aws-sdk/credential-provider-http": "3.888.0", + "@aws-sdk/credential-provider-process": "3.888.0", + "@aws-sdk/credential-provider-sso": "3.888.0", + "@aws-sdk/credential-provider-web-identity": "3.888.0", + "@aws-sdk/nested-clients": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@smithy/credential-provider-imds": "^4.0.7", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-uri-escape": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.46.0.tgz", - "integrity": "sha512-drAHEt3YnI6H6NpiTFLFT8e75bOhaO94ZP+kqz/0hluQiKX47Pow3Ar3Diaf/CUMLctH0IX3AaN3T2ve5v19lQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.888.0.tgz", + "integrity": "sha512-KCrQh1dCDC8Y+Ap3SZa6S81kHk+p+yAaOQ5jC3dak4zhHW3RCrsGR/jYdemTOgbEGcA6ye51UbhWfrrlMmeJSA==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.0" + "@aws-sdk/credential-provider-env": "3.888.0", + "@aws-sdk/credential-provider-http": "3.888.0", + "@aws-sdk/credential-provider-ini": "3.888.0", + "@aws-sdk/credential-provider-process": "3.888.0", + "@aws-sdk/credential-provider-sso": "3.888.0", + "@aws-sdk/credential-provider-web-identity": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@smithy/credential-provider-imds": "^4.0.7", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.46.0.tgz", - "integrity": "sha512-wwUh4H6+ur9akctoSgaz41J8JuRrOqey4aY68DmDQ0did3UjhRlbPD3xu0umXoPSgmtqQyl34oMPqCOfA70Z0Q==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.888.0.tgz", + "integrity": "sha512-+aX6piSukPQ8DUS4JAH344GePg8/+Q1t0+kvSHAZHhYvtQ/1Zek3ySOJWH2TuzTPCafY4nmWLcQcqvU1w9+4Lw==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.46.0", - "bowser": "^2.11.0", - "tslib": "^2.3.0" + "@aws-sdk/core": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.46.0.tgz", - "integrity": "sha512-JCY8mKWPic0aXtz7amKXWyjbX8fhdOkRcgsCCnevOHc/7KOxwa97VnDT555GNQ76LO+cEDgYueHklUayV3u+IA==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.888.0.tgz", + "integrity": "sha512-b1ZJji7LJ6E/j1PhFTyvp51in2iCOQ3VP6mj5H6f5OUnqn7efm41iNMoinKr87n0IKZw7qput5ggXVxEdPhouA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/node-config-provider": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@aws-sdk/client-sso": "3.888.0", + "@aws-sdk/core": "3.888.0", + "@aws-sdk/token-providers": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.46.0.tgz", - "integrity": "sha512-zafI5Y7hRVC0vhJ77FPUyBckmpF2v2ZEKFC79AdwdFX11l7XNmq0hY/4CWVYeZ2L0Fyk0UV6eeKyk/TNdce0mg==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.888.0.tgz", + "integrity": "sha512-7P0QNtsDzMZdmBAaY/vY1BsZHwTGvEz3bsn2bm5VSKFAeMmZqsHK1QeYdNsFjLtegnVh+wodxMq50jqLv3LFlA==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.0" + "@aws-sdk/core": "3.888.0", + "@aws-sdk/nested-clients": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-utf8-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.46.0.tgz", - "integrity": "sha512-Uk9hQrWQowU4ymtSxrxiIp7GnBoZfkKGSeWDy2h/1Biaexq9FQclbgwa0ZhA5lKLDj/nUxnXoT/ZcY90mTdzzQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.887.0.tgz", + "integrity": "sha512-ulzqXv6NNqdu/kr0sgBYupWmahISHY+azpJidtK6ZwQIC+vBUk9NdZeqQpy7KVhIk2xd4+5Oq9rxapPwPI21CA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/util-buffer-from": "3.46.0", - "tslib": "^2.3.0" + "@aws-sdk/types": "3.887.0", + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.46.0.tgz", - "integrity": "sha512-VUNTS9HjwLRmS2OQ+i4tqVJBUpk/DjIT0sWUDnKBcC6UCyGOkVmBVisCvUHpwyCLCgYbCvTab1SfrJ8dZsN83w==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/middleware-logger": { + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.887.0.tgz", + "integrity": "sha512-YbbgLI6jKp2qSoAcHnXrQ5jcuc5EYAmGLVFgMVdk8dfCfJLfGGSaOLxF4CXC7QYhO50s+mPPkhBYejCik02Kug==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@aws-sdk/types": "3.887.0", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.887.0.tgz", + "integrity": "sha512-tjrUXFtQnFLo+qwMveq5faxP5MQakoLArXtqieHphSqZTXm21wDJM73hgT4/PQQGTwgYjDKqnqsE1hvk0hcfDw==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" + "@aws-sdk/types": "3.887.0", + "@aws/lambda-invoke-store": "^0.0.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.888.0.tgz", + "integrity": "sha512-ZkcUkoys8AdrNNG7ATjqw2WiXqrhTvT+r4CIK3KhOqIGPHX0p0DQWzqjaIl7ZhSUToKoZ4Ud7MjF795yUr73oA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@smithy/core": "^3.11.0", + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/fetch-http-handler": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.178.0.tgz", - "integrity": "sha512-T/LCNwCihdVNzGn39Dw7tk2U1fMlupFlCsAvDBbO+FOM3h+y9WLHzxmlAVsjPrFXlzdONKf9zd5cuQ+ZW93yAQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/nested-clients": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.888.0.tgz", + "integrity": "sha512-py4o4RPSGt+uwGvSBzR6S6cCBjS4oTX5F8hrHFHfPCdIOMVjyOBejn820jXkCrcdpSj3Qg1yUZXxsByvxc9Lyg==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/querystring-builder": "3.178.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-base64-browser": "3.170.0", - "tslib": "^2.3.1" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.888.0", + "@aws-sdk/middleware-host-header": "3.887.0", + "@aws-sdk/middleware-logger": "3.887.0", + "@aws-sdk/middleware-recursion-detection": "3.887.0", + "@aws-sdk/middleware-user-agent": "3.888.0", + "@aws-sdk/region-config-resolver": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@aws-sdk/util-user-agent-browser": "3.887.0", + "@aws-sdk/util-user-agent-node": "3.888.0", + "@smithy/config-resolver": "^4.2.1", + "@smithy/core": "^3.11.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/hash-node": "^4.1.1", + "@smithy/invalid-dependency": "^4.1.1", + "@smithy/middleware-content-length": "^4.1.1", + "@smithy/middleware-endpoint": "^4.2.1", + "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-serde": "^4.1.1", + "@smithy/middleware-stack": "^4.1.1", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-body-length-node": "^4.1.0", + "@smithy/util-defaults-mode-browser": "^4.1.1", + "@smithy/util-defaults-mode-node": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-retry": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.887.0.tgz", + "integrity": "sha512-VdSMrIqJ3yjJb/fY+YAxrH/lCVv0iL8uA+lbMNfQGtO5tB3Zx6SU9LEpUwBNX8fPK1tUpI65CNE4w42+MY/7Mg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.887.0", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/types": "^4.5.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.1.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/fetch-http-handler/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/token-providers": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.888.0.tgz", + "integrity": "sha512-WA3NF+3W8GEuCMG1WvkDYbB4z10G3O8xuhT7QSjhvLYWQ9CPt3w4VpVIfdqmUn131TCIbhCzD0KN/1VJTjAjyw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.888.0", + "@aws-sdk/nested-clients": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/hash-node": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.178.0.tgz", - "integrity": "sha512-mqYraRQlvPO5egUKTNZ1kP52sfwBlsz7woOewQTHOGomZBDXrh8bl1J+sgaDi1NAwXdZUgxuD3QKxxAKRs9a2Q==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/types": { + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.887.0.tgz", + "integrity": "sha512-fmTEJpUhsPsovQ12vZSpVTEP/IaRoJAMBGQXlQNjtCpkBp6Iq3KQDa/HDaPINE+3xxo6XvTdtibsNOd5zJLV9A==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-buffer-from": "3.170.0", - "tslib": "^2.3.1" + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/hash-node/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/util-endpoints": { + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.887.0.tgz", + "integrity": "sha512-kpegvT53KT33BMeIcGLPA65CQVxLUL/C3gTz9AzlU/SDmeusBHX4nRApAicNzI/ltQ5lxZXbQn18UczzBuwF1w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.887.0", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/invalid-dependency": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.178.0.tgz", - "integrity": "sha512-JJNaiLr3nbRYym6oUAAaoFFYtDnIZ9Scco2p4sG/thT2eyAfXcEdNl1cSD3E/R1J+Ml/YplqTiIY4u1KPAriRw==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.887.0.tgz", + "integrity": "sha512-X71UmVsYc6ZTH4KU6hA5urOzYowSXc3qvroagJNLJYU1ilgZ529lP4J9XOYfEvTXkLR1hPFSRxa43SrwgelMjA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.887.0", + "@smithy/types": "^4.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/invalid-dependency/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.888.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.888.0.tgz", + "integrity": "sha512-rSB3OHyuKXotIGfYEo//9sU0lXAUrTY28SUUnxzOGYuQsAt0XR5iYwBAp+RjV6x8f+Hmtbg0PdCsy1iNAXa0UQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.888.0", + "@aws-sdk/types": "3.887.0", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-sdk/is-array-buffer": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.170.0.tgz", - "integrity": "sha512-yYXqgp8rilBckIvNRs22yAXHKcXb86/g+F+hsTZl38OJintTsLQB//O5v6EQTYhSW7T3wMe1NHDrjZ+hFjAy4Q==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@aws-sdk/xml-builder": { + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.887.0.tgz", + "integrity": "sha512-lMwgWK1kNgUhHGfBvO/5uLe7TKhycwOn3eRCqsKPT9aPCx/HWuTlpcQp8oW2pCRGLS7qzcxqpQulcD+bbUL7XQ==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.1" + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-content-length": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.178.0.tgz", - "integrity": "sha512-p3n3IzU03eRzZivEoQn1HA83LbAKukZwRevsJpya1UfCUtWkXQO3v0jU8rhZE4deGa9k7zuCAEmJ8nCw3QxclQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/abort-controller": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.1.1.tgz", + "integrity": "sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-content-length/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/config-resolver": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.2.2.tgz", + "integrity": "sha512-IT6MatgBWagLybZl1xQcURXRICvqz1z3APSCAI9IqdvfCkrA7RaQIEfgC6G/KvfxnDfQUDqFV+ZlixcuFznGBQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.2.2", + "@smithy/types": "^4.5.0", + "@smithy/util-config-provider": "^4.1.0", + "@smithy/util-middleware": "^4.1.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.178.0.tgz", - "integrity": "sha512-EFc9S63iwCmudVpVSiVPiTnp6WCfsRYUmTrZJJouZzthEhJwcrunwu7Fa9lHYb0zcWLgVFLhzs1Z34J/Er4JoQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/core": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.11.0.tgz", + "integrity": "sha512-Abs5rdP1o8/OINtE49wwNeWuynCu0kme1r4RI3VXVrHr4odVDG7h7mTnw1WXXfN5Il+c25QOnrdL2y56USfxkA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/middleware-serde": "^4.1.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-stream": "^4.3.1", + "@smithy/util-utf8": "^4.1.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-host-header/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/credential-provider-imds": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.1.2.tgz", + "integrity": "sha512-JlYNq8TShnqCLg0h+afqe2wLAwZpuoSgOyzhYvTgbiKBWRov+uUve+vrZEQO6lkdLOWPh7gK5dtb9dS+KGendg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.2.2", + "@smithy/property-provider": "^4.1.1", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.178.0.tgz", - "integrity": "sha512-k4jnB+ryGiAhv6vyNFz2YoaVodldjkbz4mqDlVzhwEn77LT/TcwdBoown3cJD/45LEtiuPqeONoTcNCsuCkRFQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/fetch-http-handler": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.2.1.tgz", + "integrity": "sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/protocol-http": "^5.2.1", + "@smithy/querystring-builder": "^4.1.1", + "@smithy/types": "^4.5.0", + "@smithy/util-base64": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-logger/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/hash-node": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.1.1.tgz", + "integrity": "sha512-H9DIU9WBLhYrvPs9v4sYvnZ1PiAI0oc8CgNQUJ1rpN3pP7QADbTOUjchI2FB764Ub0DstH5xbTqcMJu1pnVqxA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.5.0", + "@smithy/util-buffer-from": "^4.1.0", + "@smithy/util-utf8": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.178.0.tgz", - "integrity": "sha512-dVgSoP2Mer8A0JGaWgpC/f4vPyvHh7laES/u5sTy6RfwrR87oTx+uhKrc6eh+9NkMR2xdRyaNJAMIXwL5bsVzg==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/invalid-dependency": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.1.1.tgz", + "integrity": "sha512-1AqLyFlfrrDkyES8uhINRlJXmHA2FkG+3DY8X+rmLSqmFwk3DJnvhyGzyByPyewh2jbmV+TYQBEfngQax8IFGg==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/is-array-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.1.0.tgz", + "integrity": "sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-retry": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.178.0.tgz", - "integrity": "sha512-glBXpAqt+4KQ7q8y2/kwDX2ujCvCSQok5rlAmUjaQjVPc3cX77QwATIRQTS2nBC4v9tfMc7yL64ZeRbx6n0RAQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/middleware-content-length": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.1.1.tgz", + "integrity": "sha512-9wlfBBgTsRvC2JxLJxv4xDGNBrZuio3AgSl0lSFX7fneW2cGskXTYpFxCdRYD2+5yzmsiTuaAJD1Wp7gWt9y9w==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/service-error-classification": "3.178.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-middleware": "3.178.0", - "tslib": "^2.3.1", - "uuid": "^8.3.2" + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-retry/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/middleware-endpoint": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.2.2.tgz", + "integrity": "sha512-M51KcwD+UeSOFtpALGf5OijWt915aQT5eJhqnMKJt7ZTfDfNcvg2UZgIgTZUoiORawb6o5lk4n3rv7vnzQXgsA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.11.0", + "@smithy/middleware-serde": "^4.1.1", + "@smithy/node-config-provider": "^4.2.2", + "@smithy/shared-ini-file-loader": "^4.2.0", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-middleware": "^4.1.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-serde": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.178.0.tgz", - "integrity": "sha512-TERiu/B4hYi5Jd4iQN9ECTWbt2IZweAgFB010MboM4CAPm6EcszEc/uCB4faLZNdJaksk1BhAR7koURcda8Sew==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/middleware-retry": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.2.2.tgz", + "integrity": "sha512-KZJueEOO+PWqflv2oGx9jICpHdBYXwCI19j7e2V3IMwKgFcXc9D9q/dsTf4B+uCnYxjNoS1jpyv6pGNGRsKOXA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/node-config-provider": "^4.2.2", + "@smithy/protocol-http": "^5.2.1", + "@smithy/service-error-classification": "^4.1.1", + "@smithy/smithy-client": "^4.6.2", + "@smithy/types": "^4.5.0", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-retry": "^4.1.1", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-serde/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/middleware-serde": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.1.1.tgz", + "integrity": "sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-stack": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.178.0.tgz", - "integrity": "sha512-ELYM5Imhlcz2zT1Z4OjVZwO564KvI4L9dMBxuUgO0fwommzjWqxR03yaRGhpGwpCP64d0Op5Koc/RKq5V92Wbw==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/middleware-stack": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.1.1.tgz", + "integrity": "sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.1" + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.178.0.tgz", - "integrity": "sha512-xkKBxrFbs+UwUPpfIGEPuHeBWS2Jgmcd+ipEJUQRR3lY4h1fJ6mPGeyyaVDvwaJp9KgESSI6QTp6V15l8GXXgQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/node-config-provider": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.2.2.tgz", + "integrity": "sha512-SYGTKyPvyCfEzIN5rD8q/bYaOPZprYUPD2f5g9M7OjaYupWOoQFYJ5ho+0wvxIRf471i2SR4GoiZ2r94Jq9h6A==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/property-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-user-agent/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/node-http-handler": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.2.1.tgz", + "integrity": "sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.1.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/querystring-builder": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/node-config-provider": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.178.0.tgz", - "integrity": "sha512-yb5XJcC7SxkZ5oxu3zQ/foBdMkLBKryzx/CVg5BNSsKDjfbouf/ZYPcJDHhc2gzCtZcx18GjFBOnv8cpo/tyXQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/property-provider": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.1.1.tgz", + "integrity": "sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/property-provider": "3.178.0", - "@aws-sdk/shared-ini-file-loader": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/node-config-provider/node_modules/@aws-sdk/property-provider": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.178.0.tgz", - "integrity": "sha512-+Fh1aUANa+Gt/rh4SUHO0yHwKsibyZGk2LLDUcM1+9r0pUZT0qy3h0UCl5Kkj9HUcDJMD73wHTx4UB440xRobw==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/protocol-http": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.2.1.tgz", + "integrity": "sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/node-config-provider/node_modules/@aws-sdk/shared-ini-file-loader": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.178.0.tgz", - "integrity": "sha512-nZGmuhGLDFbXsb7QYDg7PiPMAmsdlSshKJ+AhKSZF/J0SK94kdZgGnGXGUZe52S3G41E3CZIgnLnnsMXq0uErA==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/querystring-builder": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.1.1.tgz", + "integrity": "sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.1" + "@smithy/types": "^4.5.0", + "@smithy/util-uri-escape": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/node-config-provider/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/querystring-parser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.1.1.tgz", + "integrity": "sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/node-http-handler": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.178.0.tgz", - "integrity": "sha512-EtH6YiX1IX0QraQ/+kKBWAEtsFYBnFyxOimTBtlpDYwFpgDzIZ1GFn2wORYomEWALg10kphs8n3E5/7b5t5OWQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/service-error-classification": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.1.1.tgz", + "integrity": "sha512-Iam75b/JNXyDE41UvrlM6n8DNOa/r1ylFyvgruTUx7h2Uk7vDNV9AAwP1vfL1fOL8ls0xArwEGVcGZVd7IO/Cw==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/abort-controller": "3.178.0", - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/querystring-builder": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/types": "^4.5.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/node-http-handler/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.2.0.tgz", + "integrity": "sha512-OQTfmIEp2LLuWdxa8nEEPhZmiOREO6bcB6pjs0AySf4yiZhl6kMOfqmcwcY8BaBPX+0Tb+tG7/Ia/6mwpoZ7Pw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/property-provider": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.37.0.tgz", - "integrity": "sha512-puXV4MIj+n9Pr4KbwpOz6+nK7gmJAgAOZW/yKXxyWH4fTcrCVe9xuo5kqaiI1gb5ojaNt2GuISBFR7bVLumh9Q==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/signature-v4": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.2.1.tgz", + "integrity": "sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.37.0", - "tslib": "^2.3.0" + "@smithy/is-array-buffer": "^4.1.0", + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", + "@smithy/util-hex-encoding": "^4.1.0", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-uri-escape": "^4.1.0", + "@smithy/util-utf8": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/protocol-http": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.178.0.tgz", - "integrity": "sha512-GsnANW60mVYMlE16UGNSOwYZ6TbkoODvmDQi95SEPjM7asf4vihEyDvhxiGS/JvC18UyxRVWT89l/V3hR/SF7w==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/smithy-client": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.6.2.tgz", + "integrity": "sha512-u82cjh/x7MlMat76Z38TRmEcG6JtrrxN4N2CSNG5o2v2S3hfLAxRgSgFqf0FKM3dglH41Evknt/HOX+7nfzZ3g==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/core": "^3.11.0", + "@smithy/middleware-endpoint": "^4.2.2", + "@smithy/middleware-stack": "^4.1.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", + "@smithy/util-stream": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/protocol-http/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/types": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.5.0.tgz", + "integrity": "sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/querystring-builder": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.178.0.tgz", - "integrity": "sha512-vJXlExSshlHtGVvan/U6JihWvzf8t9QwH5I4F6HUY+exxMy5vFDYCnNqGAzbJwq7w/HME1gQWLoXq2k0uODz7g==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/url-parser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.1.1.tgz", + "integrity": "sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-uri-escape": "3.170.0", - "tslib": "^2.3.1" + "@smithy/querystring-parser": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/querystring-builder/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-base64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.1.0.tgz", + "integrity": "sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.1.0", + "@smithy/util-utf8": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/querystring-parser": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.178.0.tgz", - "integrity": "sha512-dp3pLnsOvAcIF7Yn2PY5CIVWX7GvC33nSlWDYeLeCMapccwTbe6zBqreWbScmIGJra4QJTdjccpwo2Yxwhr5QQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-body-length-browser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.1.0.tgz", + "integrity": "sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/querystring-parser/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-body-length-node": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.1.0.tgz", + "integrity": "sha512-BOI5dYjheZdgR9XiEM3HJcEMCXSoqbzu7CzIgYrx0UtmvtC3tC2iDGpJLsSRFffUpy8ymsg2ARMP5fR8mtuUQQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/service-error-classification": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.178.0.tgz", - "integrity": "sha512-tDKTBXxck2N4bhAnQaeokx9ps38V3G70lcDdHS/N9hmqcQQmH5x+1/AMwYWLjUZmOQPBW9sFoG4B3psnl+sefw==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-buffer-from": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.1.0.tgz", + "integrity": "sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/shared-ini-file-loader": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.37.0.tgz", - "integrity": "sha512-+vRBSlfa48R9KL7DpQt3dsu5/+5atjRgoCISblWo3SLpjrx41pKcjKneo7a1u0aP1Xc2oG2TfIyqTWZuOXsmEQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-config-provider": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.1.0.tgz", + "integrity": "sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/signature-v4": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.178.0.tgz", - "integrity": "sha512-8oOx6o0uOqlCDPM0dszfR1WHqd0E1VuFqez8iNItp0DhmhaCuanEwKYYA6HOkVu/MA6CsG6zDIJaFr5ODU2NvQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.1.2.tgz", + "integrity": "sha512-QKrOw01DvNHKgY+3p4r9Ut4u6EHLVZ01u6SkOMe6V6v5C+nRPXJeWh72qCT1HgwU3O7sxAIu23nNh+FOpYVZKA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/is-array-buffer": "3.170.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-hex-encoding": "3.170.0", - "@aws-sdk/util-middleware": "3.178.0", - "@aws-sdk/util-uri-escape": "3.170.0", - "tslib": "^2.3.1" + "@smithy/property-provider": "^4.1.1", + "@smithy/smithy-client": "^4.6.2", + "@smithy/types": "^4.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/signature-v4/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.1.2.tgz", + "integrity": "sha512-l2yRmSfx5haYHswPxMmCR6jGwgPs5LjHLuBwlj9U7nNBMS43YV/eevj+Xq1869UYdiynnMrCKtoOYQcwtb6lKg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.2.2", + "@smithy/credential-provider-imds": "^4.1.2", + "@smithy/node-config-provider": "^4.2.2", + "@smithy/property-provider": "^4.1.1", + "@smithy/smithy-client": "^4.6.2", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/smithy-client": { - "version": "3.180.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.180.0.tgz", - "integrity": "sha512-1vWafiUdn6RvOsD4CyNjMeDtDujuPi4Iq4Db6HrFmVPpJAutOLlCg52Dt7k96KCcIKgxVAs6Br0Waef+pcoGNA==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-endpoints": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.1.2.tgz", + "integrity": "sha512-+AJsaaEGb5ySvf1SKMRrPZdYHRYSzMkCoK16jWnIMpREAnflVspMIDeCVSZJuj+5muZfgGpNpijE3mUNtjv01Q==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-stack": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@smithy/node-config-provider": "^4.2.2", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-hex-encoding": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.1.0.tgz", + "integrity": "sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/types": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.37.0.tgz", - "integrity": "sha512-KwHB06E1uxof5ijfcQXYidyihoCRMnHEFvWCy/VlL+1S54FTlMZ27JOZzQhLiw8NqeNfO33aqpMkxR60TwUZzg==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-middleware": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.1.1.tgz", + "integrity": "sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/url-parser": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.178.0.tgz", - "integrity": "sha512-+Ch29d+IZG6zD1gNDVgFC00huY8ytrPdijAuNJ4DtPBTGP4zbrImw3js0GfvfBjLrQYBnclcAvSx4J1Q/8tqBQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-retry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.1.1.tgz", + "integrity": "sha512-jGeybqEZ/LIordPLMh5bnmnoIgsqnp4IEimmUp5c5voZ8yx+5kAlN5+juyr7p+f7AtZTgvhmInQk4Q0UVbrZ0Q==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/querystring-parser": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - } - }, - "node_modules/@aws-sdk/url-parser/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "@smithy/service-error-classification": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.46.0.tgz", - "integrity": "sha512-MIeqH53/Z5x+uQ/EHs6zqkdBqdpGA8tA6l/mL1j8hRK6bSQEUTAVlPNgjp03qse1Fk94GJbJbjZ4C0R7Z4YM+g==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-stream": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.3.1.tgz", + "integrity": "sha512-khKkW/Jqkgh6caxMWbMuox9+YfGlsk9OnHOYCGVEdYQb/XVzcORXHLYUubHmmda0pubEDncofUrPNniS9d+uAA==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.0" + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/types": "^4.5.0", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-buffer-from": "^4.1.0", + "@smithy/util-hex-encoding": "^4.1.0", + "@smithy/util-utf8": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/util-base64-browser": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.170.0.tgz", - "integrity": "sha512-uLP9Kp74+jc+UWI392LSWIaUj9eXZBhkAiSm8dXAyrr+5GFOKvmEdidFoZKKcFcZ2v3RMonDgFVcDBiZ33w7BQ==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-uri-escape": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.1.0.tgz", + "integrity": "sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/util-base64-node": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.170.0.tgz", - "integrity": "sha512-sjpOmfyW0RWCLXU8Du0ZtwgFoxIuKQIyVygXJ4qxByoa3jIUJXf4U33uSRMy47V3JoogdZuKSpND9hiNk2wU4w==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/@smithy/util-utf8": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.1.0.tgz", + "integrity": "sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/util-buffer-from": "3.170.0", - "tslib": "^2.3.1" + "@smithy/util-buffer-from": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/util-body-length-browser": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.170.0.tgz", - "integrity": "sha512-SqSWA++gsZgHw6tlcEXx9K6R6cVKNYzOq6bca+NR7jXvy1hfqiv9Gx5TZrG4oL4JziP8QA0fTklmI1uQJ4HBRA==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", "dependencies": { - "tslib": "^2.3.1" + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" } }, - "node_modules/@aws-sdk/util-body-length-node": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.170.0.tgz", - "integrity": "sha512-sFb85ngsgfpamwDn22LC/+FkbDTNiddbMHptkajw+CAD2Rb4SJDp2PfXZ6k883BueJWhmxZ9+lApHZqYtgPdzw==", + "node_modules/@aws-sdk/client-accessanalyzer/node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/@aws-sdk/client-api-gateway": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.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-api-gateway": "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-stream": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/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-api-gateway/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" }, - "engines": { - "node": ">= 12.0.0" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/util-buffer-from": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.170.0.tgz", - "integrity": "sha512-3ClE3wgN/Zw0ahfVAY5KQ/y3K2c+SYHwVUQaGSuVQlPOCDInGYjE/XEFwCeGJzncRPHIKDRPEsHCpm1uwgwEqQ==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/is-array-buffer": "3.170.0", - "tslib": "^2.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/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-api-gateway/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": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-config-provider": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.170.0.tgz", - "integrity": "sha512-VV6lfss6Go00TF2hRVJnN8Uf2FOwC++1e8glaeU7fMWluYCBjwl+116mPOPFaxvkJCg0dui2tFroXioslM/rvQ==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.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": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-credentials": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-credentials/-/util-credentials-3.37.0.tgz", - "integrity": "sha512-zcLhSZDKgBLhUjSU5HoQpuQiP3v8oE86NmV/tiZVPEaO6YVULEAB2Cfj1hpM/b/JXWzjSHfT06KXT7QUODKS+A==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/shared-ini-file-loader": "3.37.0", - "tslib": "^2.3.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/util-defaults-mode-browser": { - "version": "3.180.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.180.0.tgz", - "integrity": "sha512-XGq/RUuhqnLlApETBmBWDszNG4TR3FCOSNwFvok1UIPgFXxjh0JOzNc5mAX1St2hfx1IDb9Ja4BmTkYKix7byg==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/property-provider": "3.178.0", - "@aws-sdk/types": "3.178.0", - "bowser": "^2.11.0", - "tslib": "^2.3.1" + "@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": ">= 10.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-defaults-mode-browser/node_modules/@aws-sdk/property-provider": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.178.0.tgz", - "integrity": "sha512-+Fh1aUANa+Gt/rh4SUHO0yHwKsibyZGk2LLDUcM1+9r0pUZT0qy3h0UCl5Kkj9HUcDJMD73wHTx4UB440xRobw==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.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": ">= 12.0.0" - } - }, - "node_modules/@aws-sdk/util-defaults-mode-browser/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", - "engines": { - "node": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-defaults-mode-node": { - "version": "3.180.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.180.0.tgz", - "integrity": "sha512-ch9VR4OKYGEaNbLhX/EyZDpNZst5T+/VYsFyqMA47J0YyMbg7GWyn2FjjhZ7qOV3XU6W8YEBNdd7U/LFFVp8uA==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/config-resolver": "3.178.0", - "@aws-sdk/credential-provider-imds": "3.178.0", - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/property-provider": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/util-defaults-mode-node/node_modules/@aws-sdk/credential-provider-imds": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.178.0.tgz", - "integrity": "sha512-ZvqQTi3+S13LACVgaWNCOKBv5jROIz7rqyZh56QunAkaAUqPbpM4VFODgAGZYPCOSggZbEUUqXOVB9xSnshLnA==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/property-provider": "3.178.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/url-parser": "3.178.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-defaults-mode-node/node_modules/@aws-sdk/property-provider": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.178.0.tgz", - "integrity": "sha512-+Fh1aUANa+Gt/rh4SUHO0yHwKsibyZGk2LLDUcM1+9r0pUZT0qy3h0UCl5Kkj9HUcDJMD73wHTx4UB440xRobw==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-defaults-mode-node/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-api-gateway/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": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-hex-encoding": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.170.0.tgz", - "integrity": "sha512-BDYyMqaxX4/N7rYOIYlqgpZaBuHw3kNXKgOkWtJdzndIZbQX8HnyJ+rF0Pr1aVsOpVDM+fY1prERleFh/ZRTCg==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.1" + "@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": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.37.0.tgz", - "integrity": "sha512-NvDCfOhLLVHp27oGUUs8EVirhz91aX5gdxGS7J/sh5PF0cNN8rwaR1vSLR7BxPmJHMO7NH7i9EwiELfLfYcq6g==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.0" + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-middleware": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.178.0.tgz", - "integrity": "sha512-93WgrJKuwtv3f2r1Q04emzjMiwpYR5hysOHKMkrGOvAVZdDqe1UTjmtuxQadVi3DBr1KOT/d5uP9MjV8LqaUUA==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.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": ">= 12.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-sdk/util-uri-escape": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.170.0.tgz", - "integrity": "sha512-Fof0urZ3Lx6z6LNKSEO6T4DNaNh6sLJaSWFaC6gtVDPux/C3R7wy2RQRDp0baHxE8m1KMB0XnKzHizJNrbDI1w==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.1" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.178.0.tgz", - "integrity": "sha512-LxOrn7Ai88n0i5J5rTb5Bt0TAycPvDYzjdCwmd2mahsPHZGSDLeCeh6KOIxZsEfnzYRl4HGWvIEXdHIYZ3RTug==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.178.0", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", - "engines": { - "node": ">= 12.0.0" + "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.178.0.tgz", - "integrity": "sha512-TrP6v+V4Qnv3E9CNgwR/G+1xiy8fa9j5LAm43qwp9PfJHchNyWOJ0FURD3Ne2sm/388Ybzjb1DRYRZ7B+xbnOw==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.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": ">= 12.0.0" + "node": ">=16.0.0" }, "peerDependencies": { "aws-crt": ">=1.0.0" @@ -1778,24242 +2655,40413 @@ } } }, - "node_modules/@aws-sdk/util-user-agent-node/node_modules/@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.170.0.tgz", - "integrity": "sha512-tJby9krepSwDsBK+KQF5ACacZQ4LH1Aheh5Dy0pghxsN/9IRw7kMWTumuRCnSntLFFphDD7GM494/Dvnl1UCLA==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.3.1" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-utf8-node": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.170.0.tgz", - "integrity": "sha512-52QWGNoNQoyT2CuoQz6LjBKxHQtN/ceMFLW+9J1E0I1ni8XTuTYP52BlMe5484KkmZKsHOm+EWe4xuwwVetTxg==", + "node_modules/@aws-sdk/client-api-gateway/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/util-buffer-from": "3.170.0", - "tslib": "^2.3.1" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-toolkits/telemetry": { - "version": "1.0.87", - "resolved": "https://registry.npmjs.org/@aws-toolkits/telemetry/-/telemetry-1.0.87.tgz", - "integrity": "sha512-rN0s9YhU+SEIVTEojwuVm+Be3CXsTdIsg75PijVM9LdPqwNxzcSY2X/yU4K5R4QNMU160g4/qQ00BI9SdTJxKw==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.12.6", - "fs-extra": "^11.1.0", - "lodash": "^4.17.20", - "prettier": "^2.1.2", - "ts-morph": "^17.0.1", - "typescript": "^4.7.3", - "yargs": "^17.0.1" - } - }, - "node_modules/@aws-toolkits/telemetry/node_modules/fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", - "dev": true, + "@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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/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" }, - "engines": { - "node": ">=14.14" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/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-apprunner/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/highlight": "^7.16.7" + "@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": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/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": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/core": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz", - "integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.7", - "@babel/parser": "^7.17.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/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": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.16.7" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.16.7" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.16.7" + "@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": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.16.7" + "@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": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.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": ">=6.9.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.17.0" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/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": ">=6.9.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helpers": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz", - "integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dev": true, + "node_modules/@aws-sdk/client-apprunner/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "color-convert": "^1.9.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-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-cloudcontrol/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-cloudcontrol/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" }, - "engines": { - "node": ">=4" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.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": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/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": ">=4" + "node": ">=16.0.0" } }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, + "@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": ">=0.8.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/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": ">=4" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "has-flag": "^3.0.0" + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz", - "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==", - "bin": { - "parser": "bin/babel-parser.js" + "node_modules/@aws-sdk/client-cloudcontrol/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": ">=6.0.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@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": ">=6.9.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/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": ">=4" + "node": ">=16.0.0" } }, - "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@bcherny/json-schema-ref-parser": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@bcherny/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz", - "integrity": "sha512-vmEmnJCfpkLdas++9OYg6riIezTYqTHpqUTODJzHLzs5UnXujbOJW9VwcVCnyo1mVRt32FRr23iXBx/sX8YbeQ==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.6", - "call-me-maybe": "^1.0.1", - "js-yaml": "^4.1.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "node_modules/@aws-sdk/client-cloudcontrol/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": ">=0.1.90" + "node": ">=16.0.0" } }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/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": ">= 12" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=16.0.0" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz", - "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", - "dev": true, + "@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": ">=10.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@esbuild/android-arm": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz", - "integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz", - "integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/client-cloudcontrol/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": ">=12" + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", - "dev": true, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=16.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz", - "integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.682.0", + "@aws-sdk/client-sts": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.6", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/client-sso": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" }, - "engines": { - "node": ">=10.10.0" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/client-sts": { + "version": "3.682.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.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/core": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "sprintf-js": "~1.0.2" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-ini": "3.682.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "p-locate": "^4.1.0" + "@aws-sdk/client-sso": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/token-providers": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "p-try": "^2.0.0" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.679.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "p-limit": "^2.2.0" + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-logger": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=16.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.0.0" + "node": ">=16.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/token-providers": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.679.0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/types": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/util-endpoints": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "@smithy/util-endpoints": "^2.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", - "dev": true + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" + "node_modules/@aws-sdk/client-cloudformation/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/@sindresorhus/is": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", - "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", - "engines": { - "node": ">=10" + "node_modules/@aws-sdk/client-cloudformation/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" }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "type-detect": "4.0.8" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.682.0", + "@aws-sdk/client-sts": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/eventstream-serde-browser": "^3.0.10", + "@smithy/eventstream-serde-config-resolver": "^3.0.7", + "@smithy/eventstream-serde-node": "^3.0.9", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sso": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" } }, - "node_modules/@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sts": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@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.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/core": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "defer-to-connect": "^2.0.0" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 6" + "node": ">=16.0.0" } }, - "node_modules/@ts-morph/common": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.18.0.tgz", - "integrity": "sha512-UvWF+oQdMa4qMWhXTOd2JWtAbAJGgkPMNzGHgEcfOwQRIcViKdwsSqXXjSaQCZ4fo+bZMhdfuwQCjlW5bNcqEQ==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "fast-glob": "^3.2.12", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "path-browserify": "^1.0.1" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" } }, - "node_modules/@ts-morph/common/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0" + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-ini": "3.682.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", - "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/@ts-morph/common/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/token-providers": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true - }, - "node_modules/@types/adm-zip": { - "version": "0.4.34", - "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz", - "integrity": "sha512-8ToYLLAYhkRfcmmljrKi22gT2pqu7hGMDtORP1emwIEGmgUTZOsaDjzWFzW5N2frcFRz/50CWt4zA1CxJ73pmQ==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.679.0" } }, - "node_modules/@types/async-lock": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@types/async-lock/-/async-lock-1.1.3.tgz", - "integrity": "sha512-UpeDcjGKsYEQMeqEbfESm8OWJI305I7b9KE4ji3aBjoKWyN5CTdn8izcA1FM1DVDne30R5fNEnIy89vZw5LXJQ==", - "dev": true - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-logger": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-lOGyCnw+2JVPKU3wIV0srU0NyALwTBJlVSx5DfMQOFuuohA8y9S8orImpuIQikZ0uIQ8gehrRjxgQC1rLRi11w==", - "dev": true + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.682.0", + "license": "Apache-2.0", "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/token-providers": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.679.0" } }, - "node_modules/@types/cross-spawn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.2.tgz", - "integrity": "sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/types": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/eslint": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", - "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-endpoints": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "@smithy/util-endpoints": "^2.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.679.0", + "license": "Apache-2.0", "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } }, - "node_modules/@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "license": "Apache-2.0", "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + "node_modules/@aws-sdk/client-codecatalyst": { + "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/token-providers": "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", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/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/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc": { + "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/credential-provider-node": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/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/@types/http-proxy": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", - "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@types/node": "*" + "@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/@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", - "dev": true + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/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/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/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/@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@types/node": "*" + "@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/@types/lodash": { - "version": "4.14.182", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", - "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", - "dev": true - }, - "node_modules/@types/marked": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.2.tgz", - "integrity": "sha512-auNrZ/c0w6wsM9DccwVxWHssrMDezHUAXNesdp2RQrCVCyrQbOiSq7yqdJKrUQQpw9VTm7CGYJH2A/YG7jjrjQ==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true - }, - "node_modules/@types/mime-types": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz", - "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==", - "dev": true - }, - "node_modules/@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", - "dev": true - }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "node_modules/@types/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", - "dev": true - }, - "node_modules/@types/node": { - "version": "14.18.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.5.tgz", - "integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==" - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "node_modules/@types/prettier": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", - "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true - }, - "node_modules/@types/readline-sync": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@types/readline-sync/-/readline-sync-1.4.4.tgz", - "integrity": "sha512-cFjVIoiamX7U6zkO2VPvXyTxbFDdiRo902IarJuPVxBhpDnXhwSaVE86ip+SCuyWBbEioKCkT4C88RNTxBM1Dw==", - "dev": true - }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", - "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.3.9", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", - "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==", - "dev": true - }, - "node_modules/@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/sinon": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.5.tgz", - "integrity": "sha512-BrAUy0yq3n84XOykYGvGbDir9nBIYwQm2NdBNQT0DbtDLqh/5nMUsjz5XfwrefFNLPE9B6g8yLOZREpvw0J40A==", - "dev": true, - "dependencies": { - "@sinonjs/fake-timers": "^7.1.0" - } - }, - "node_modules/@types/sinon/node_modules/@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@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/@types/sinonjs__fake-timers": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true - }, - "node_modules/@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@types/node": "*" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/tcp-port-used": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/tcp-port-used/-/tcp-port-used-1.0.1.tgz", - "integrity": "sha512-6pwWTx8oUtWvsiZUCrhrK/53MzKVLnuNSSaZILPy3uMes9QnTrLMar9BDlJArbMOjDcjb3QXFk6Rz8qmmuySZw==", - "dev": true - }, - "node_modules/@types/uuid": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz", - "integrity": "sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA==", - "dev": true - }, - "node_modules/@types/vscode": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.50.0.tgz", - "integrity": "sha512-QnIeyi4L2DiD9M2bAQKRzT/EQvc80qP9UL6JD5TiLlNRL1khIDg4ej4mDSRbtFrDAsRntFI1RhMvdomUThMsqg==", - "dev": true - }, - "node_modules/@types/vscode-webview": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.0.tgz", - "integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==", - "dev": true - }, - "node_modules/@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-endpoints": { + "version": "3.743.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@types/node": "*" + "@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/@types/xml2js": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.9.tgz", - "integrity": "sha512-CHiCKIihl1pychwR2RNX5mAYmJDACgFVCMT5OArMaO3erzwXVcBqPcusr+Vl8yeeXukxZqtF8mZioqX+mpjjdw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@types/node": "*" + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.0.tgz", - "integrity": "sha512-GgHi/GNuUbTOeoJiEANi0oI6fF3gBQc3bGFYj40nnAPCbhrtEDf2rjBmefFadweBmO1Du1YovHeDP2h5JLhtTQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.38.0", - "@typescript-eslint/type-utils": "5.38.0", - "@typescript-eslint/utils": "5.38.0", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@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": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=18.0.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "aws-crt": ">=1.0.0" }, "peerDependenciesMeta": { - "typescript": { + "aws-crt": { "optional": true } } }, - "node_modules/@typescript-eslint/parser": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.38.0.tgz", - "integrity": "sha512-/F63giJGLDr0ms1Cr8utDAxP2SPiglaD6V+pCOcG35P2jCqdfR7uuEhz1GIC3oy4hkUF8xA1XSXmd9hOh/a5EA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.38.0", - "@typescript-eslint/types": "5.38.0", - "@typescript-eslint/typescript-estree": "5.38.0", - "debug": "^4.3.4" + "@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": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.38.0.tgz", - "integrity": "sha512-ByhHIuNyKD9giwkkLqzezZ9y5bALW8VNY6xXcP+VxoH4JBDKjU5WNnsiD4HJdglHECdV+lyaxhvQjTUbRboiTA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@typescript-eslint/types": "5.38.0", - "@typescript-eslint/visitor-keys": "5.38.0" + "@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": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.38.0.tgz", - "integrity": "sha512-iZq5USgybUcj/lfnbuelJ0j3K9dbs1I3RICAJY9NZZpDgBYXmuUlYQGzftpQA9wC8cKgtS6DASTvF3HrXwwozA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.38.0", - "@typescript-eslint/utils": "5.38.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" + "@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": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.38.0.tgz", - "integrity": "sha512-HHu4yMjJ7i3Cb+8NUuRCdOGu2VMkfmKyIJsOr9PfkBVYLYrtMCK/Ap50Rpov+iKpxDTfnqvDbuPLgBE5FwUNfA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/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" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.0.tgz", - "integrity": "sha512-6P0RuphkR+UuV7Avv7MU3hFoWaGcrgOdi8eTe1NwhMp2/GjUJoODBTRWzlHpZh6lFOaPmSvgxGlROa0Sg5Zbyg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@typescript-eslint/types": "5.38.0", - "@typescript-eslint/visitor-keys": "5.38.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.38.0.tgz", - "integrity": "sha512-6sdeYaBgk9Fh7N2unEXGz+D+som2QCQGPAf1SxrkEr+Z32gMreQ0rparXTNGRRfYUWk/JzbGdcM8NSSd6oqnTA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.38.0", - "@typescript-eslint/types": "5.38.0", - "@typescript-eslint/typescript-estree": "5.38.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.0.tgz", - "integrity": "sha512-MxnrdIyArnTi+XyFLR+kt/uNAcdOnmT+879os7qDRI+EYySR4crXJq9BXPfRzzLGq0wgxkwidrCJ9WCAoacm1w==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@typescript-eslint/types": "5.38.0", - "eslint-visitor-keys": "^3.3.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", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/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": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@vscode/codicons": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.32.tgz", - "integrity": "sha512-3lgSTWhAzzWN/EPURoY4ZDBEA80OPmnaknNujA3qnI4Iu7AONWd9xF3iE4L+4prIe8E3TUnLQ4pxoaFTEEZNwg==", - "dev": true - }, - "node_modules/@vscode/test-electron": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.3.tgz", - "integrity": "sha512-7DmdGYQTqRNaLHKG3j56buc9DkstriY4aV0S3Zj32u0U9/T0L8vwWAC9QGCh1meu1VXDEla1ze27TkqysHGP0Q==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "rimraf": "^3.0.2", - "unzipper": "^0.10.11" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=16" + "node": ">=18.0.0" } }, - "node_modules/@vue/compiler-core": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz", - "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.31", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@vue/compiler-dom": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz", - "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@vue/compiler-core": "3.2.31", - "@vue/shared": "3.2.31" + "@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/@vue/compiler-sfc": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz", - "integrity": "sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.40", - "@vue/compiler-dom": "3.2.40", - "@vue/compiler-ssr": "3.2.40", - "@vue/reactivity-transform": "3.2.40", - "@vue/shared": "3.2.40", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" + "@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": ">=18.0.0" } }, - "node_modules/@vue/compiler-sfc/node_modules/@vue/compiler-core": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz", - "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.40", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@vue/compiler-sfc/node_modules/@vue/compiler-dom": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz", - "integrity": "sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@vue/compiler-core": "3.2.40", - "@vue/shared": "3.2.40" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@vue/compiler-sfc/node_modules/@vue/compiler-ssr": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz", - "integrity": "sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@vue/compiler-dom": "3.2.40", - "@vue/shared": "3.2.40" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@vue/compiler-sfc/node_modules/@vue/shared": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz", - "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==", - "dev": true - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz", - "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@vue/compiler-dom": "3.2.31", - "@vue/shared": "3.2.31" + "@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/@vue/reactivity": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.31.tgz", - "integrity": "sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@vue/shared": "3.2.31" + "@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/@vue/reactivity-transform": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz", - "integrity": "sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.40", - "@vue/shared": "3.2.40", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@vue/reactivity-transform/node_modules/@vue/compiler-core": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz", - "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.40", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@vue/reactivity-transform/node_modules/@vue/shared": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz", - "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==", - "dev": true - }, - "node_modules/@vue/runtime-core": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.31.tgz", - "integrity": "sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@vue/reactivity": "3.2.31", - "@vue/shared": "3.2.31" + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@vue/runtime-dom": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz", - "integrity": "sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@vue/runtime-core": "3.2.31", - "@vue/shared": "3.2.31", - "csstype": "^2.6.8" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@vue/server-renderer": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.31.tgz", - "integrity": "sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@vue/compiler-ssr": "3.2.31", - "@vue/shared": "3.2.31" + "tslib": "^2.6.2" }, - "peerDependencies": { - "vue": "3.2.31" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@vue/shared": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz", - "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ==" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" + "@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/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@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": ">=18.0.0" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@xtuc/long": "4.2.2" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@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/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@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/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", - "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", - "dev": true, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@webpack-cli/info": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", - "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "envinfo": "^7.7.3" + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "webpack-cli": "4.x.x" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@webpack-cli/serve": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", - "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", - "dev": true, - "peerDependencies": { - "webpack-cli": "4.x.x" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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" }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@xmldom/xmldom": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.8.tgz", - "integrity": "sha512-PrJx38EfpitFhwmILRl37jAdBlsww6AZ6rRVK4QS7T7RHLhX7mSs647sTmgr9GIxe3qjXdesmomEgbgaokrVFg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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": ">=10.0.0" + "node": ">=18.0.0" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/a-sync-waterfall": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", - "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true, - "bin": { - "acorn": "bin/acorn" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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": ">=0.4.0" + "node": ">=18.0.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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": ">=0.4.0" + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/adm-zip": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", - "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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": ">=6.0" + "node": ">=18.0.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "debug": "4" + "@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": ">= 6.0.0" + "node": ">=18.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "@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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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/amazon-states-language-service": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/amazon-states-language-service/-/amazon-states-language-service-1.8.0.tgz", - "integrity": "sha512-F5hTDQqAVOYEJCm4PTBf6ZjLQYqzLfnGfyNqxNMTYqilbZk/3T0GQManc0pgt5CbtFw62DB6ovBcEUQQa9xsvA==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "js-yaml": "^3.14.0", - "vscode-json-languageservice": "3.4.9", - "vscode-languageserver": "^6.1.1", - "vscode-languageserver-textdocument": "^1.0.0", - "vscode-languageserver-types": "^3.15.1", - "yaml-language-server": "0.10.0" + "@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/amazon-states-language-service/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "sprintf-js": "~1.0.2" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/amazon-states-language-service/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ansi-wrap": "0.1.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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color-convert": "^2.0.1" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@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": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "default-require-extensions": "^3.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "node_modules/are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.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/are-we-there-yet/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "safe-buffer": "~5.1.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/array-differ": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", - "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, - "node_modules/ast-types": { - "version": "0.9.14", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.14.tgz", - "integrity": "sha512-Ebvx7/0lLboCdyEmAw/4GqwBeKIijPveXNiVGhCGCNxc7z26T5he7DC6ARxu8ByKuzUZZcLog+VP8GMyZrBzJw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/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": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" - }, - "node_modules/async-lock": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.3.0.tgz", - "integrity": "sha512-8A7SkiisnEgME2zEedtDYPxUPzdv3x//E7n5IFktPAtMYSEAV7eNJF0rMwrVyUFj6d/8rgajLantbjcNRQYXIg==" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "engines": { - "node": ">= 0.4" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/aws-sdk": { - "version": "2.1267.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1267.0.tgz", - "integrity": "sha512-ANTtRay26WwNRbYs6eZYN71b3DURNfWaq3AD6BtVNa8fVvnSLn+NNINw2+vLRjDLPZXMAQVHm0qH/TmyBvtjRA==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.4.19" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-sdk/node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "bin": { - "uuid": "dist/bin/uuid" + "node": ">=18.0.0" } }, - "node_modules/aws-sdk/node_modules/xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" + "@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/node_modules/xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4.0" + "node": ">=18.0.0" } }, - "node_modules/aws-ssm-document-language-service": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/aws-ssm-document-language-service/-/aws-ssm-document-language-service-1.0.0.tgz", - "integrity": "sha512-XTkladSKrrD/uIGOwRMyVK7M6eHYVZWobtF0SCXQPocpE0SsHRVY1GflZaEGYgbKgDJTgpNpP8drTsUFzgfL5A==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "vscode-json-languageservice": "3.8.3", - "vscode-languageserver": "^6.1.1", - "yaml": "^1.10.0", - "yaml-language-server": "0.10.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": ">=16.0.0" } }, - "node_modules/aws-ssm-document-language-service/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "sprintf-js": "~1.0.2" + "@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-ssm-document-language-service/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@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" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/aws-ssm-document-language-service/node_modules/jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" - }, - "node_modules/aws-ssm-document-language-service/node_modules/prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", - "optional": true, - "bin": { - "prettier": "bin-prettier.js" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/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": ">=10.13.0" + "node": ">=18.0.0" } }, - "node_modules/aws-ssm-document-language-service/node_modules/vscode-json-languageservice": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.8.3.tgz", - "integrity": "sha512-8yPag/NQHCuTthahyaTtzK0DHT0FKM/xBU0mFBQ8nMo8C1i2P+FCyIVqICoNoHkRI2BTGlXKomPUpsqjSz0TnQ==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "jsonc-parser": "^2.2.1", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "^3.15.1", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.2" + "@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-ssm-document-language-service/node_modules/vscode-jsonrpc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", - "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/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": ">=8.0.0 || >=10.0.0" + "node": ">=18.0.0" } }, - "node_modules/aws-ssm-document-language-service/node_modules/vscode-languageserver-protocol": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz", - "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "vscode-jsonrpc": "^4.0.0", - "vscode-languageserver-types": "3.14.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/aws-ssm-document-language-service/node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", - "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==" - }, - "node_modules/aws-ssm-document-language-service/node_modules/vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - }, - "node_modules/aws-ssm-document-language-service/node_modules/yaml-language-server": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-0.10.1.tgz", - "integrity": "sha512-R9SEt/nWTuZ8weB040L7yyaIVARlZ0ian1Kv6ptu4+xyVlIMobTZXaBTtgyhlMWqcQ3BpsAZu4q/2plRVG3tLQ==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "js-yaml": "^3.13.1", - "jsonc-parser": "^2.2.1", - "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.6.0", - "vscode-languageserver": "^5.2.1", - "vscode-languageserver-types": "^3.15.1", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.1", - "yaml-ast-parser-custom-tags": "0.0.43" - }, - "bin": { - "yaml-language-server": "bin/yaml-language-server" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": "*" - }, - "optionalDependencies": { - "prettier": "2.0.5" + "node": ">=18.0.0" } }, - "node_modules/aws-ssm-document-language-service/node_modules/yaml-language-server/node_modules/vscode-languageserver": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz", - "integrity": "sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "vscode-languageserver-protocol": "3.14.1", - "vscode-uri": "^1.0.6" + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/aws-ssm-document-language-service/node_modules/yaml-language-server/node_modules/vscode-languageserver/node_modules/vscode-uri": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz", - "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==" - }, - "node_modules/azure-devops-node-api": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.0.1.tgz", - "integrity": "sha512-YMdjAw9l5p/6leiyIloxj3k7VIvYThKjvqgiQn88r3nhT93ENwsoDS3A83CyJ4uTWzCZ5f5jCi6c27rTU5Pz+A==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "tunnel": "0.0.6", - "typed-rest-client": "^1.8.4" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/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": ">=0.6" + "node": ">=18.0.0" } }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/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": "*" + "node": ">=18.0.0" } }, - "node_modules/binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" + "@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": "*" + "node": ">=18.0.0" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "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", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/bl": { + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/types": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, + "license": "Apache-2.0", + "peer": true, "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/bl/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", - "dev": true + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=18.0.0" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ms": "2.0.0" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/body-parser/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/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": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/bonjour-service": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.13.tgz", - "integrity": "sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" + "@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/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "fill-range": "^7.0.1" + "@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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", - "escalade": "^3.1.1", - "node-releases": "^2.0.2", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" + "@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": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=18.0.0" } }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.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", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/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": "*" + "node": ">=18.0.0" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10" + "node": ">=18.0.0" } }, - "node_modules/buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/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": ">=0.2.0" + "node": ">=18.0.0" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/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" + }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/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": ">=10.6.0" + "node": ">=18.0.0" } }, - "node_modules/cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "@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" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw==", - "dev": true - }, - "node_modules/caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "callsites": "^2.0.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": ">=4" + "node": ">=18.0.0" } }, - "node_modules/caller-callsite/node_modules/callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "caller-callsite": "^2.0.0" + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=10" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.758.0", + "@aws-sdk/credential-provider-http": "3.758.0", + "@aws-sdk/credential-provider-ini": "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/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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/camelcase-keys/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node/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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/camelcase-keys/node_modules/quick-lru": { + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node/node_modules/@smithy/property-provider": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001317", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz", - "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node/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/chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "traverse": ">=0.3.0 <0.4" + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=18.0.0" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@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": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/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": "*" + "node": ">=18.0.0" } }, - "node_modules/cheerio": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", - "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "cheerio-select": "^1.5.0", - "dom-serializer": "^1.3.2", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/cheerio-select": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", - "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "css-select": "^4.1.3", - "css-what": "^5.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0", - "domutils": "^2.7.0" + "@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" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.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", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": ">=18.0.0" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/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": ">=6.0" + "node": ">=18.0.0" } }, - "node_modules/circular-dependency-plugin": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", - "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "dev": true, - "engines": { - "node": ">=6.0.0" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "webpack": ">=4.0.1" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/cli-color": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", - "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.61", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.10" + "node": ">=18.0.0" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ansi-regex": "^5.0.1" + "@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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "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", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.8" + "node": ">=18.0.0" } }, - "node_modules/clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.10" + "node": ">=18.0.0" } }, - "node_modules/clone-deep": { + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/url-parser": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, + "license": "Apache-2.0", + "peer": true, "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "mimic-response": "^1.0.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "node_modules/cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/cloneable-readable/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/cloneable-readable/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/cloneable-readable/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "safe-buffer": "~5.1.0" + "@aws-sdk/client-sso": "3.758.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/token-providers": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/code-block-writer": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.3.tgz", - "integrity": "sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==", - "dev": true - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "@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/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color-name": "~1.1.4" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=18.0.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", - "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "@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/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/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/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color-name": "1.1.3" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/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": ">= 6" + "node": ">=18.0.0" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "mime-db": ">= 1.43.0 < 2" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">=18.0.0" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true, + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.8" + "node": ">=18.0.0" } }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "safe-buffer": "5.2.1" + "@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": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true, - "engines": { - "node": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/convert-source-map/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, + "@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": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "sprintf-js": "~1.0.2" + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/cosmiconfig/node_modules/import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/cosmiconfig/node_modules/parse-json": { + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/util-utf8": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, + "license": "Apache-2.0", + "peer": true, "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/cosmiconfig/node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/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": ">=4" + "node": ">=16.0.0" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/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": "*" + "node": ">=16.0.0" } }, - "node_modules/css-loader": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", - "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.2.15", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "semver": "^7.3.5" + "@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": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" + "node": ">=16.0.0" } }, - "node_modules/css-select": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", - "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^5.0.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0", - "nth-check": "^2.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/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": ">= 6" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" + "node_modules/@aws-sdk/client-codecatalyst/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": ">=4" + "node": ">=16.0.0" } }, - "node_modules/csstype": { - "version": "2.6.20", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", - "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" - }, - "node_modules/cubic2quad": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/cubic2quad/-/cubic2quad-1.2.1.tgz", - "integrity": "sha512-wT5Y7mO8abrV16gnssKdmIhIbA9wSkeMzhh27jAguKrV82i24wER0vL5TGhUJ9dbJNDcigoRZ0IAHFEEEI4THQ==", - "dev": true - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "ms": "2.1.2" + "@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": ">=6.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" }, "peerDependenciesMeta": { - "supports-color": { + "aws-crt": { "optional": true } } }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" + "node_modules/@aws-sdk/client-codecatalyst/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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/abort-controller/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/decamelize-keys/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, + "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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true, + "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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "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": { - "mimic-response": "^3.1.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" + "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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, + "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": ">=4.0.0" + "node": ">=18.0.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true, + "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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "execa": "^5.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/default-gateway/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/default-gateway/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" + "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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/default-gateway/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, + "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": ">=10.17.0" + "node": ">=18.0.0" } }, - "node_modules/default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, + "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": { - "strip-bom": "^4.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "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": ">=10" + "node": ">=18.0.0" } }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, + "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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "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": { + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true + "node_modules/@aws-sdk/client-codecatalyst/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": ">=18.0.0" + } }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true, + "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": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, + "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" + }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=18.0.0" } }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true, - "bin": { - "detect-libc": "bin/detect-libc.js" + "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": ">=0.10" + "node": ">=18.0.0" } }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true + "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": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, + "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": ">=0.3.1" + "node": ">=18.0.0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, + "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": { - "path-type": "^4.0.0" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/dns-packet": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", - "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", - "dev": true, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" + "node": ">=16.0.0" } }, - "node_modules/doctrine": { + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8/node_modules/@smithy/is-array-buffer": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, + "license": "Apache-2.0", "dependencies": { - "esutils": "^2.0.2" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=16.0.0" } }, - "node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, + "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", "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "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-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": { + "@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-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-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": ">=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==", + "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", + "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==", + "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" }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] + "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-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/domhandler": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", - "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", - "dev": 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": { - "domelementtype": "^2.2.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": ">= 4" + "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==", + "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" }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": 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": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.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" }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": 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/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, + "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": { - "readable-stream": "^2.0.2" + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, + "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": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@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/duplexer2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "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/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/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, + "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": { - "safe-buffer": "~5.1.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/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.4.86", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz", - "integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, + "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/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": ">= 4" + "node": ">=18.0.0" } }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, + "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/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": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "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": { - "once": "^1.4.0" + "@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/enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", - "dev": true, + "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": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "@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": ">=10.13.0" + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "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/envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", - "dev": true, - "bin": { - "envinfo": "dist/cli.js" + "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": { + "@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": ">=4" + "node": ">=18.0.0" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, + "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": { - "is-arrayish": "^0.2.1" + "@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/error-ex/node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/es-abstract": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz", - "integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==", + "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": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.2", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "@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": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0.0" } }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "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": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "@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": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0.0" } }, - "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "dev": true, - "hasInstallScript": true, + "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": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.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": ">=0.10" + "node": ">=18.0.0" } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dev": true, + "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": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "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": { - "es6-promise": "^4.0.3" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dev": true, + "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": { - "d": "^1.0.1", - "ext": "^1.1.2" + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dev": true, + "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": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" + "@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/esbuild": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz", - "integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" + "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" }, "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.15.13", - "@esbuild/linux-loong64": "0.15.13", - "esbuild-android-64": "0.15.13", - "esbuild-android-arm64": "0.15.13", - "esbuild-darwin-64": "0.15.13", - "esbuild-darwin-arm64": "0.15.13", - "esbuild-freebsd-64": "0.15.13", - "esbuild-freebsd-arm64": "0.15.13", - "esbuild-linux-32": "0.15.13", - "esbuild-linux-64": "0.15.13", - "esbuild-linux-arm": "0.15.13", - "esbuild-linux-arm64": "0.15.13", - "esbuild-linux-mips64le": "0.15.13", - "esbuild-linux-ppc64le": "0.15.13", - "esbuild-linux-riscv64": "0.15.13", - "esbuild-linux-s390x": "0.15.13", - "esbuild-netbsd-64": "0.15.13", - "esbuild-openbsd-64": "0.15.13", - "esbuild-sunos-64": "0.15.13", - "esbuild-windows-32": "0.15.13", - "esbuild-windows-64": "0.15.13", - "esbuild-windows-arm64": "0.15.13" + "node": ">=18.0.0" } }, - "node_modules/esbuild-android-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz", - "integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], + "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": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-android-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz", - "integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], + "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": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-darwin-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz", - "integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "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": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz", - "integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "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": { + "@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": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-freebsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz", - "integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "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": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz", - "integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "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.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-linux-32": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz", - "integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "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": { + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-linux-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz", - "integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "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": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-linux-arm": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz", - "integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "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": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-linux-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz", - "integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "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": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz", - "integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "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/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": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz", - "integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "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" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz", - "integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "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": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-linux-s390x": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz", - "integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "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/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-loader": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.20.0.tgz", - "integrity": "sha512-dr+j8O4w5RvqZ7I4PPB4EIyVTd679EBQnMm+JBB7av+vu05Zpje2IpK5N3ld1VWa+WxrInIbNFAg093+E1aRsA==", - "dev": true, + "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": { - "esbuild": "^0.15.6", - "joycon": "^3.0.1", - "json5": "^2.2.0", - "loader-utils": "^2.0.0", - "tapable": "^2.2.0", - "webpack-sources": "^2.2.0" + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/privatenumber/esbuild-loader?sponsor=1" - }, - "peerDependencies": { - "webpack": "^4.40.0 || ^5.0.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/esbuild-netbsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz", - "integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], + "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" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-openbsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz", - "integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], + "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": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-sunos-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz", - "integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], + "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": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-windows-32": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz", - "integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], + "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": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-windows-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz", - "integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], + "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": ">=12" + "node": ">=18.0.0" } }, - "node_modules/esbuild-windows-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz", - "integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], + "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/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": ">=12" + "node": ">=18.0.0" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, + "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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "node_modules/escape-string-regexp": { + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-hex-encoding": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" + "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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/eslint": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", - "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", - "dev": true, + "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": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18.0.0" } }, - "node_modules/eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" + "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/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-header": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz", - "integrity": "sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==", - "dev": true, - "peerDependencies": { - "eslint": ">=7.7.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/eslint-plugin-no-null": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz", - "integrity": "sha512-uRDiz88zCO/2rzGfgG15DBjNsgwWtWiSo4Ezy7zzajUgpnFIqd1TjepKeRmJZHEfBGu58o2a8S0D7vglvvhkVA==", - "dev": true, - "engines": { - "node": ">=5.0.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" }, - "peerDependencies": { - "eslint": ">=3.0.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, + "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": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8.0.0" + "node": ">=18.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.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-datazone": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-datazone/-/client-datazone-3.848.0.tgz", + "integrity": "sha512-m9x9G6oQHUVJvt9JsTdU41/nimz11MMmQLptQVgIJcD6VHoHoVXppvPntK7GUkH0T6+0gw63RugGd7kB+xofBQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/credential-provider-node": "3.848.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.848.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.15", + "@smithy/middleware-retry": "^4.1.16", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@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.23", + "@smithy/util-defaults-mode-node": "^4.0.23", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-stream": "^4.2.3", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/client-sso": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.848.0.tgz", + "integrity": "sha512-mD+gOwoeZQvbecVLGoCmY6pS7kg02BHesbtIxUj+PeBqYoZV5uLvjUOmuGfw1SfoSobKvS11urxC9S7zxU/Maw==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.848.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.15", + "@smithy/middleware-retry": "^4.1.16", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@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.23", + "@smithy/util-defaults-mode-node": "^4.0.23", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/core": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.846.0.tgz", + "integrity": "sha512-7CX0pM906r4WSS68fCTNMTtBCSkTtf3Wggssmx13gD40gcWEZXsU00KzPp1bYheNRyPlAq3rE22xt4wLPXbuxA==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.7.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@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-utf8": "^4.0.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.846.0.tgz", + "integrity": "sha512-QuCQZET9enja7AWVISY+mpFrEIeHzvkx/JEEbHYzHhUkxcnC2Kq2c0bB7hDihGD0AZd3Xsm653hk1O97qu69zg==", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.846.0.tgz", + "integrity": "sha512-Jh1iKUuepdmtreMYozV2ePsPcOF5W9p3U4tWhi3v6nDvz0GsBjzjAROW+BW8XMz9vAD3I9R+8VC3/aq63p5nlw==", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.848.0.tgz", + "integrity": "sha512-r6KWOG+En2xujuMhgZu7dzOZV3/M5U/5+PXrG8dLQ3rdPRB3vgp5tc56KMqLwm/EXKRzAOSuw/UE4HfNOAB8Hw==", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/credential-provider-env": "3.846.0", + "@aws-sdk/credential-provider-http": "3.846.0", + "@aws-sdk/credential-provider-process": "3.846.0", + "@aws-sdk/credential-provider-sso": "3.848.0", + "@aws-sdk/credential-provider-web-identity": "3.848.0", + "@aws-sdk/nested-clients": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@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": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" + "node": ">=18.0.0" } }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.848.0.tgz", + "integrity": "sha512-AblNesOqdzrfyASBCo1xW3uweiSro4Kft9/htdxLeCVU1KVOnFWA5P937MNahViRmIQm2sPBCqL8ZG0u9lnh5g==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.846.0", + "@aws-sdk/credential-provider-http": "3.846.0", + "@aws-sdk/credential-provider-ini": "3.848.0", + "@aws-sdk/credential-provider-process": "3.846.0", + "@aws-sdk/credential-provider-sso": "3.848.0", + "@aws-sdk/credential-provider-web-identity": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@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": ">=10" + "node": ">=18.0.0" } }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.846.0.tgz", + "integrity": "sha512-mEpwDYarJSH+CIXnnHN0QOe0MXI+HuPStD6gsv3z/7Q6ESl8KRWon3weFZCDnqpiJMUVavlDR0PPlAFg2MQoPg==", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.848.0.tgz", + "integrity": "sha512-pozlDXOwJZL0e7w+dqXLgzVDB7oCx4WvtY0sk6l4i07uFliWF/exupb6pIehFWvTUcOvn5aFTTqcQaEzAD5Wsg==", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "@aws-sdk/client-sso": "3.848.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/token-providers": "3.848.0", + "@aws-sdk/types": "3.840.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": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.848.0.tgz", + "integrity": "sha512-D1fRpwPxtVDhcSc/D71exa2gYweV+ocp4D3brF0PgFd//JR3XahZ9W24rVnTQwYEcK9auiBZB89Ltv+WbWN8qw==", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/nested-clients": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.840.0.tgz", + "integrity": "sha512-ub+hXJAbAje94+Ya6c6eL7sYujoE8D4Bumu1NUI8TXjUhVVn0HzVWQjpRLshdLsUp1AW7XyeJaxyajRaJQ8+Xg==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4.0" + "node": ">=18.0.0" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/middleware-logger": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.840.0.tgz", + "integrity": "sha512-lSV8FvjpdllpGaRspywss4CtXV8M7NNNH+2/j86vMH+YCOZ6fu2T/TyFd/tHwZ92vDfHctWkRbQxg0bagqwovA==", "dependencies": { - "is-glob": "^4.0.3" + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10.13.0" + "node": ">=18.0.0" } }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.840.0.tgz", + "integrity": "sha512-Gu7lGDyfddyhIkj1Z1JtrY5NHb5+x/CRiB87GjaSrKxkDaydtX2CU977JIABtt69l9wLbcGDIQ+W0uJ5xPof7g==", "dependencies": { - "ansi-regex": "^5.0.1" + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.848.0.tgz", + "integrity": "sha512-rjMuqSWJEf169/ByxvBqfdei1iaduAnfolTshsZxwcmLIUtbYrFUmts0HrLQqsAG8feGPpDLHA272oPl+NTCCA==", "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@smithy/core": "^3.7.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18.0.0" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/nested-clients": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.848.0.tgz", + "integrity": "sha512-joLsyyo9u61jnZuyYzo1z7kmS7VgWRAkzSGESVzQHfOA1H2PYeUFek6vLT4+c9xMGrX/Z6B0tkRdzfdOPiatLg==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.848.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.15", + "@smithy/middleware-retry": "^4.1.16", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@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.23", + "@smithy/util-defaults-mode-node": "^4.0.23", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.840.0.tgz", + "integrity": "sha512-Qjnxd/yDv9KpIMWr90ZDPtRj0v75AqGC92Lm9+oHXZ8p1MjG5JE2CW0HL8JRgK9iKzgKBL7pPQRXI8FkvEVfrA==", + "dependencies": { + "@aws-sdk/types": "3.840.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": ">=4" + "node": ">=18.0.0" } }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/token-providers": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.848.0.tgz", + "integrity": "sha512-oNPyM4+Di2Umu0JJRFSxDcKQ35+Chl/rAwD47/bS0cDPI8yrao83mLXLeDqpRPHyQW4sXlP763FZcuAibC0+mg==", "dependencies": { - "estraverse": "^5.1.0" + "@aws-sdk/core": "3.846.0", + "@aws-sdk/nested-clients": "3.848.0", + "@aws-sdk/types": "3.840.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": ">=0.10" + "node": ">=18.0.0" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/types": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz", + "integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/util-endpoints": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.848.0.tgz", + "integrity": "sha512-fY/NuFFCq/78liHvRyFKr+aqq1aA/uuVSANjzr5Ym8c+9Z3HRPE9OrExAHoMrZ6zC8tHerQwlsXYYH5XZ7H+ww==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.840.0.tgz", + "integrity": "sha512-JdyZM3EhhL4PqwFpttZu1afDpPJCCc3eyZOLi+srpX11LsGj6sThf47TYQN75HT1CarZ7cCdQHGzP2uy3/xHfQ==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.848.0.tgz", + "integrity": "sha512-Zz1ft9NiLqbzNj/M0jVNxaoxI2F4tGXN0ZbZIj+KJ+PbJo+w5+Jo6d0UDAtbj3AEd79pjcCaP4OA9NTVzItUdw==", "dependencies": { - "estraverse": "^5.2.0" + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4.0" + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@aws-sdk/xml-builder": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", + "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4.0" + "node": ">=18.0.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": ">=4.0" + "node": ">=18.0.0" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { + "@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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/core": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.7.1.tgz", + "integrity": "sha512-ExRCsHnXFtBPnM7MkfKBPcBBdHw1h/QS/cbNw4ho95qnyNHvnpmGbR39MIAv9KggTr5qSPxRSEL+hRXlyGyGQw==", + "dependencies": { + "@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.3", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "d": "1", - "es5-ext": "~0.10.14" + "@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/event-stream": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz", - "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/fetch-http-handler": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.0.tgz", + "integrity": "sha512-mADw7MS0bYe2OGKkHYMaqarOXuDwRbO6ArD91XhHcl2ynjGCFF+hvqf0LyQcYxkA1zaWjefSkU7Ne9mqgApSgQ==", "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "@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": ">=0.4.x" + "node": ">=18.0.0" } }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.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": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.0", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.10.3", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.10.0" + "node": ">=18.0.0" } }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "ms": "2.0.0" + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/express/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.16.tgz", + "integrity": "sha512-plpa50PIGLqzMR2ANKAw2yOW5YKS626KYKqae3atwucbz4Ve4uQ9K9BEZxDLIFmCu7hKLcrq2zmj4a+PfmUV5w==", + "dependencies": { + "@smithy/core": "^3.7.1", + "@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": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true - }, - "node_modules/express/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/middleware-retry": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.17.tgz", + "integrity": "sha512-gsCimeG6BApj0SBecwa1Be+Z+JOJe46iy3B3m3A8jKJHf7eIihP76Is4LwLrbJ1ygoS7Vg73lfqzejmLOrazUA==", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.6", + "@smithy/smithy-client": "^4.4.8", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "type": "^2.5.0" + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", - "dev": true - }, - "node_modules/fancy-log": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.10" + "node": ">=18.0.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "@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": ">=8.6.0" + "node": ">=18.0.0" } }, - "node_modules/fast-json-patch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", - "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/node-http-handler": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.0.tgz", + "integrity": "sha512-vqfSiHz2v8b3TTTrdXi03vNz1KLYYS3bhHCDv36FYDqxT7jvTll1mMnCrkD+gOvgwybuunh/2VmvOMqwBegxEg==", "dependencies": { - "reusify": "^1.0.4" + "@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/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "websocket-driver": ">=0.5.1" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.8.0" + "node": ">=18.0.0" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "pend": "~1.2.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/fecha": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", - "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "flat-cache": "^3.0.4" + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=18.0.0" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "to-regex-range": "^5.0.1" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/service-error-classification": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.6.tgz", + "integrity": "sha512-RRoTDL//7xi4tn5FrN2NzH17jbgmnKidUqd4KvquT0954/i6CXXkh1884jBiunq24g9cGtPBEXlU40W6EpNOOg==", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "@smithy/types": "^4.3.1" }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "ms": "2.0.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/finalhandler/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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/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": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/smithy-client": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.8.tgz", + "integrity": "sha512-pcW691/lx7V54gE+dDGC26nxz8nrvnvRSCJaIYD6XLPpOInEZeKdV/SpSux+wqeQ4Ine7LJQu8uxMvobTIBK0w==", "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + "@smithy/core": "^3.7.1", + "@smithy/middleware-endpoint": "^4.1.16", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.3", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" + "node_modules/@aws-sdk/client-datazone/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/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=18.0.0" } }, - "node_modules/flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", - "dev": true - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "node_modules/follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" + "node_modules/@aws-sdk/client-datazone/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" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "engines": { + "node": ">=18.0.0" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/@aws-sdk/client-datazone/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": { - "is-callable": "^1.1.3" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8.0.0" + "node": ">=18.0.0" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.24", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.24.tgz", + "integrity": "sha512-UkQNgaQ+bidw1MgdgPO1z1k95W/v8Ej/5o/T/Is8PiVUYPspl/ZxV6WO/8DrzZQu5ULnmpB9CDdMSRwgRc21AA==", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.8", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.24", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.24.tgz", + "integrity": "sha512-phvGi/15Z4MpuQibTLOYIumvLdXb+XIJu8TA55voGgboln85jytA3wiD7CkUE8SNcWqkkb+uptZKPiuFouX/7g==", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.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.8", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@aws-sdk/client-datazone/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": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=18.0.0" } }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.6" + "node": ">=18.0.0" } }, - "node_modules/fstream/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "glob": "^7.1.3" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/util-retry": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.6.tgz", + "integrity": "sha512-+YekoF2CaSMv6zKrA6iI/N9yva3Gzn4L6n35Luydweu5MMPYpiGZlWqehPHDHyNbnyaYlz/WJyYAZnC+loBDZg==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "@smithy/service-error-classification": "^4.0.6", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0.0" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@aws-sdk/client-datazone/node_modules/@smithy/util-stream": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.3.tgz", + "integrity": "sha512-cQn412DWHHFNKrQfbHY8vSFI3nTROY1aIKji9N0tpp8gUABRilr7wdf8fqBbSlXresobM+tQFNk6I+0LXK/YZg==", + "dependencies": { + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/node-http-handler": "^4.1.0", + "@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/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, + "node_modules/@aws-sdk/client-datazone/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": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/gauge/node_modules/ansi-regex": { + "node_modules/@aws-sdk/client-datazone/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-datazone/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/client-datazone/node_modules/strnum": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/@aws-sdk/client-ec2": { + "version": "3.695.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-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": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/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-ec2/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-ec2/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": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "number-is-nan": "^1.0.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/gauge/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "ansi-regex": "^2.0.0" + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, + "node_modules/@aws-sdk/client-ec2/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": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/geometry-interfaces": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/geometry-interfaces/-/geometry-interfaces-1.1.4.tgz", - "integrity": "sha512-qD6OdkT6NcES9l4Xx3auTpwraQruU7dARbQPVO71MKvkGYw5/z/oIiGymuFXrRaEQa5Y67EIojUpaLeGEa5hGA==", - "dev": true + "node_modules/@aws-sdk/client-ec2/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/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, + "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": "6.* || 8.* || >= 10.*" + "node": ">=16.0.0" } }, - "node_modules/get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, + "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": ">=8.0.0" + "node": ">=16.0.0" } }, - "node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true, + "node_modules/@aws-sdk/client-ec2/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": ">=10" + "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", + "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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "pump": "^3.0.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": ">=8" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16.0.0" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", - "dev": true + "node_modules/@aws-sdk/client-ec2/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/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "is-glob": "^4.0.1" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 6" + "node": ">=16.0.0" } }, - "node_modules/glob-promise": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-4.2.2.tgz", - "integrity": "sha512-xcUzJ8NWN5bktoTIX7eOclO1Npxd/dyVqUJxlLIDasT4C7KZyqlPIwkdJ0Ypiy3p2ZKahTjK4M9uC3sNSfNMzw==", - "dev": true, + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@types/glob": "^7.1.3" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/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-ecr": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ecr/-/client-ecr-3.693.0.tgz", + "integrity": "sha512-qBI06wo2VaQI/+Pb4GmZRVQMnXFr9B983nWWNhM6xzcYmfJKXbCW29syDVojiwp8/HPMOSqcKJzqIOqCWtN1Ug==", + "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_modules/@aws-sdk/client-ecr/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-ecr/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" }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/ahmadnassri" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-ecr/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-ecr/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-ecr/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-ecr/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": { - "glob": "^7.1.6" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dev": true, + "node_modules/@aws-sdk/client-ecr/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": { - "type-fest": "^0.20.2" + "@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": ">=8" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ecr/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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, + "node_modules/@aws-sdk/client-ecr/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": ">=10" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, + "node_modules/@aws-sdk/client-ecr/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": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16.0.0" } }, - "node_modules/got": { - "version": "11.8.5", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", - "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", + "node_modules/@aws-sdk/client-ecr/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": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10.19.0" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ecr/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" }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true, + "node_modules/@aws-sdk/client-ecr/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": ">=6" + "node": ">=16.0.0" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/@aws-sdk/client-ecr/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": { - "function-bind": "^1.1.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": ">= 0.4.0" + "node": ">=16.0.0" } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@aws-sdk/client-ecr/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/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/@aws-sdk/client-ecr/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": ">=8" + "node": ">=16.0.0" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "node_modules/@aws-sdk/client-ecr/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": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/@aws-sdk/client-ecr/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": ">= 0.4" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/@aws-sdk/client-ecr/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": { - "has-symbols": "^1.0.2" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16.0.0" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "node_modules/hash-sum": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", - "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", - "dev": true - }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, + "node_modules/@aws-sdk/client-ecr/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": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16.0.0" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" + "node_modules/@aws-sdk/client-ecr/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-glue": { + "version": "3.852.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-glue/-/client-glue-3.852.0.tgz", + "integrity": "sha512-5IyZt/gKr0NoUHWGM112ikXrZs+VsA/09bwKDmp4/j250tfaZqgC1zhfBNFkyNisj1JQ0XYjwfzkLnYWlT3Pyw==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/credential-provider-node": "3.848.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.848.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.15", + "@smithy/middleware-retry": "^4.1.16", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@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.23", + "@smithy/util-defaults-mode-node": "^4.0.23", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/client-sso": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.848.0.tgz", + "integrity": "sha512-mD+gOwoeZQvbecVLGoCmY6pS7kg02BHesbtIxUj+PeBqYoZV5uLvjUOmuGfw1SfoSobKvS11urxC9S7zxU/Maw==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.848.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.15", + "@smithy/middleware-retry": "^4.1.16", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@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.23", + "@smithy/util-defaults-mode-node": "^4.0.23", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/core": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.846.0.tgz", + "integrity": "sha512-7CX0pM906r4WSS68fCTNMTtBCSkTtf3Wggssmx13gD40gcWEZXsU00KzPp1bYheNRyPlAq3rE22xt4wLPXbuxA==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.7.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@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-utf8": "^4.0.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.846.0.tgz", + "integrity": "sha512-QuCQZET9enja7AWVISY+mpFrEIeHzvkx/JEEbHYzHhUkxcnC2Kq2c0bB7hDihGD0AZd3Xsm653hk1O97qu69zg==", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.846.0.tgz", + "integrity": "sha512-Jh1iKUuepdmtreMYozV2ePsPcOF5W9p3U4tWhi3v6nDvz0GsBjzjAROW+BW8XMz9vAD3I9R+8VC3/aq63p5nlw==", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.848.0.tgz", + "integrity": "sha512-r6KWOG+En2xujuMhgZu7dzOZV3/M5U/5+PXrG8dLQ3rdPRB3vgp5tc56KMqLwm/EXKRzAOSuw/UE4HfNOAB8Hw==", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/credential-provider-env": "3.846.0", + "@aws-sdk/credential-provider-http": "3.846.0", + "@aws-sdk/credential-provider-process": "3.846.0", + "@aws-sdk/credential-provider-sso": "3.848.0", + "@aws-sdk/credential-provider-web-identity": "3.848.0", + "@aws-sdk/nested-clients": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@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/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.848.0.tgz", + "integrity": "sha512-AblNesOqdzrfyASBCo1xW3uweiSro4Kft9/htdxLeCVU1KVOnFWA5P937MNahViRmIQm2sPBCqL8ZG0u9lnh5g==", "dependencies": { - "lru-cache": "^6.0.0" + "@aws-sdk/credential-provider-env": "3.846.0", + "@aws-sdk/credential-provider-http": "3.846.0", + "@aws-sdk/credential-provider-ini": "3.848.0", + "@aws-sdk/credential-provider-process": "3.846.0", + "@aws-sdk/credential-provider-sso": "3.848.0", + "@aws-sdk/credential-provider-web-identity": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@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": ">=10" + "node": ">=18.0.0" } }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.846.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.846.0.tgz", + "integrity": "sha512-mEpwDYarJSH+CIXnnHN0QOe0MXI+HuPStD6gsv3z/7Q6ESl8KRWon3weFZCDnqpiJMUVavlDR0PPlAFg2MQoPg==", "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.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/hpack.js/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.848.0.tgz", + "integrity": "sha512-pozlDXOwJZL0e7w+dqXLgzVDB7oCx4WvtY0sk6l4i07uFliWF/exupb6pIehFWvTUcOvn5aFTTqcQaEzAD5Wsg==", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@aws-sdk/client-sso": "3.848.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/token-providers": "3.848.0", + "@aws-sdk/types": "3.840.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/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.848.0.tgz", + "integrity": "sha512-D1fRpwPxtVDhcSc/D71exa2gYweV+ocp4D3brF0PgFd//JR3XahZ9W24rVnTQwYEcK9auiBZB89Ltv+WbWN8qw==", "dependencies": { - "safe-buffer": "~5.1.0" + "@aws-sdk/core": "3.846.0", + "@aws-sdk/nested-clients": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/html-entities": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", - "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", - "dev": true - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.840.0.tgz", + "integrity": "sha512-ub+hXJAbAje94+Ya6c6eL7sYujoE8D4Bumu1NUI8TXjUhVVn0HzVWQjpRLshdLsUp1AW7XyeJaxyajRaJQ8+Xg==", "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/middleware-logger": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.840.0.tgz", + "integrity": "sha512-lSV8FvjpdllpGaRspywss4CtXV8M7NNNH+2/j86vMH+YCOZ6fu2T/TyFd/tHwZ92vDfHctWkRbQxg0bagqwovA==", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/http-errors/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.840.0.tgz", + "integrity": "sha512-Gu7lGDyfddyhIkj1Z1JtrY5NHb5+x/CRiB87GjaSrKxkDaydtX2CU977JIABtt69l9wLbcGDIQ+W0uJ5xPof7g==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.848.0.tgz", + "integrity": "sha512-rjMuqSWJEf169/ByxvBqfdei1iaduAnfolTshsZxwcmLIUtbYrFUmts0HrLQqsAG8feGPpDLHA272oPl+NTCCA==", + "dependencies": { + "@aws-sdk/core": "3.846.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@smithy/core": "^3.7.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/nested-clients": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.848.0.tgz", + "integrity": "sha512-joLsyyo9u61jnZuyYzo1z7kmS7VgWRAkzSGESVzQHfOA1H2PYeUFek6vLT4+c9xMGrX/Z6B0tkRdzfdOPiatLg==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.846.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.848.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.15", + "@smithy/middleware-retry": "^4.1.16", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.7", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@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.23", + "@smithy/util-defaults-mode-node": "^4.0.23", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.840.0.tgz", + "integrity": "sha512-Qjnxd/yDv9KpIMWr90ZDPtRj0v75AqGC92Lm9+oHXZ8p1MjG5JE2CW0HL8JRgK9iKzgKBL7pPQRXI8FkvEVfrA==", "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "@aws-sdk/types": "3.840.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": ">=8.0.0" + "node": ">=18.0.0" } }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/token-providers": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.848.0.tgz", + "integrity": "sha512-oNPyM4+Di2Umu0JJRFSxDcKQ35+Chl/rAwD47/bS0cDPI8yrao83mLXLeDqpRPHyQW4sXlP763FZcuAibC0+mg==", "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "@aws-sdk/core": "3.846.0", + "@aws-sdk/nested-clients": "3.848.0", + "@aws-sdk/types": "3.840.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": ">= 6" + "node": ">=18.0.0" } }, - "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/types": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz", + "integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/util-endpoints": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.848.0.tgz", + "integrity": "sha512-fY/NuFFCq/78liHvRyFKr+aqq1aA/uuVSANjzr5Ym8c+9Z3HRPE9OrExAHoMrZ6zC8tHerQwlsXYYH5XZ7H+ww==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.840.0.tgz", + "integrity": "sha512-JdyZM3EhhL4PqwFpttZu1afDpPJCCc3eyZOLi+srpX11LsGj6sThf47TYQN75HT1CarZ7cCdQHGzP2uy3/xHfQ==", "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.848.0.tgz", + "integrity": "sha512-Zz1ft9NiLqbzNj/M0jVNxaoxI2F4tGXN0ZbZIj+KJ+PbJo+w5+Jo6d0UDAtbj3AEd79pjcCaP4OA9NTVzItUdw==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.848.0", + "@aws-sdk/types": "3.840.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" }, "peerDependencies": { - "@types/express": "^4.17.13" + "aws-crt": ">=1.0.0" }, "peerDependenciesMeta": { - "@types/express": { + "aws-crt": { "optional": true } } }, - "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true, - "engines": { - "node": ">=10" + "node_modules/@aws-sdk/client-glue/node_modules/@aws-sdk/xml-builder": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", + "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "node_modules/@aws-sdk/client-glue/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": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10.19.0" + "node": ">=18.0.0" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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": { - "agent-base": "6", - "debug": "4" + "@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": ">= 6" + "node": ">=18.0.0" } }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/core": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.7.2.tgz", + "integrity": "sha512-JoLw59sT5Bm8SAjFCYZyuCGxK8y3vovmoVbZWLDPTH5XpPEIwpFd9m90jjVMwoypDuB/SdVgje5Y4T7w50lJaw==", + "dependencies": { + "@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.3", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8.12.0" + "node": ">=18.0.0" } }, - "node_modules/husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", - "dev": true, - "bin": { - "husky": "lib/bin.js" + "node_modules/@aws-sdk/client-glue/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": { + "@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": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" + "node": ">=18.0.0" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/fetch-http-handler": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.0.tgz", + "integrity": "sha512-mADw7MS0bYe2OGKkHYMaqarOXuDwRbO6ArD91XhHcl2ynjGCFF+hvqf0LyQcYxkA1zaWjefSkU7Ne9mqgApSgQ==", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "@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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" + "node_modules/@aws-sdk/client-glue/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": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "postcss": "^8.1.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 4" + "node": ">=18.0.0" } }, - "node_modules/immutable": { + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/is-array-buffer": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==" - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, + "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": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/import-local": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", - "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.17.tgz", + "integrity": "sha512-S3hSGLKmHG1m35p/MObQCBCdRsrpbPU8B129BVzRqRfDvQqPMQ14iO4LyRw+7LNizYc605COYAcjqgawqi+6jA==", + "dependencies": { + "@smithy/core": "^3.7.2", + "@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": ">=0.8.19" + "node": ">=18.0.0" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/middleware-retry": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.18.tgz", + "integrity": "sha512-bYLZ4DkoxSsPxpdmeapvAKy7rM5+25gR7PGxq2iMiecmbrRGBHj9s75N74Ylg+aBiw9i5jIowC/cLU2NR0qH8w==", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.6", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, + "node_modules/@aws-sdk/client-glue/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": { - "once": "^1.3.0", - "wrappy": "1" + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "node_modules/@aws-sdk/client-glue/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": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" } }, - "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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": ">= 0.10" + "node": ">=18.0.0" } }, - "node_modules/ip-regex": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/node-http-handler": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.0.tgz", + "integrity": "sha512-vqfSiHz2v8b3TTTrdXi03vNz1KLYYS3bhHCDv36FYDqxT7jvTll1mMnCrkD+gOvgwybuunh/2VmvOMqwBegxEg==", + "dependencies": { + "@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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/is": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", - "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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": "*" + "node": ">=18.0.0" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/@aws-sdk/client-glue/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": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0.0" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/@aws-sdk/client-glue/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": { - "has-bigints": "^1.0.1" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/service-error-classification": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.6.tgz", + "integrity": "sha512-RRoTDL//7xi4tn5FrN2NzH17jbgmnKidUqd4KvquT0954/i6CXXkh1884jBiunq24g9cGtPBEXlU40W6EpNOOg==", "dependencies": { - "binary-extensions": "^2.0.0" + "@smithy/types": "^4.3.1" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/@aws-sdk/client-glue/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": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0.0" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "engines": { - "node": ">= 0.4" + "node_modules/@aws-sdk/client-glue/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/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" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/smithy-client": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.9.tgz", + "integrity": "sha512-mbMg8mIUAWwMmb74LoYiArP04zWElPzDoA1jVOp3or0cjlDMgoS6WTC3QXK0Vxoc9I4zdrX0tq6qsOmaIoTWEQ==", "dependencies": { - "has": "^1.0.3" + "@smithy/core": "^3.7.2", + "@smithy/middleware-endpoint": "^4.1.17", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.3", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/@aws-sdk/client-glue/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": { - "has-tostringtag": "^1.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0.0" } }, - "node_modules/is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" + "node_modules/@aws-sdk/client-glue/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/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, + "node_modules/@aws-sdk/client-glue/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" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "node_modules/@aws-sdk/client-glue/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": { - "has-tostringtag": "^1.0.0" + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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": { - "is-extglob": "^2.1.1" + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "engines": { - "node": ">= 0.4" + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.25.tgz", + "integrity": "sha512-pxEWsxIsOPLfKNXvpgFHBGFC3pKYKUFhrud1kyooO9CJai6aaKDHfT10Mi5iiipPXN/JhKAu3qX9o75+X85OdQ==", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.25.tgz", + "integrity": "sha512-+w4n4hKFayeCyELZLfsSQG5mCC3TwSkmRHv4+el5CzFU8ToQpYGhpV7mrRzqlwKkntlPilT1HJy1TVeEvEjWOQ==", + "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.9", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.12.0" + "node": ">=18.0.0" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/@aws-sdk/client-glue/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": { - "has-tostringtag": "^1.0.0" + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-glue/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" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, + "node_modules/@aws-sdk/client-glue/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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/util-retry": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.6.tgz", + "integrity": "sha512-+YekoF2CaSMv6zKrA6iI/N9yva3Gzn4L6n35Luydweu5MMPYpiGZlWqehPHDHyNbnyaYlz/WJyYAZnC+loBDZg==", + "dependencies": { + "@smithy/service-error-classification": "^4.0.6", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, + "node_modules/@aws-sdk/client-glue/node_modules/@smithy/util-stream": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.3.tgz", + "integrity": "sha512-cQn412DWHHFNKrQfbHY8vSFI3nTROY1aIKji9N0tpp8gUABRilr7wdf8fqBbSlXresobM+tQFNk6I+0LXK/YZg==", "dependencies": { - "isobject": "^3.0.1" + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/node-http-handler": "^4.1.0", + "@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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/@aws-sdk/client-glue/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": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-glue/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" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "node_modules/@aws-sdk/client-glue/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "dependencies": { - "call-bind": "^1.0.2" + "strnum": "^2.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "fxparser": "src/cli/cli.js" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" + "node_modules/@aws-sdk/client-glue/node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/@aws-sdk/client-iam": { + "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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/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-iam/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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "has-tostringtag": "^1.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": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/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": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16.0.0" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "has-symbols": "^1.0.2" + "@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": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16.0.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", - "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.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": ">= 0.4" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" + "node_modules/@aws-sdk/client-iam/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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/is-url": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.2" + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, + "node_modules/@aws-sdk/client-iam/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": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "is-docker": "^2.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/is2": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.7.tgz", - "integrity": "sha512-4vBQoURAXC6hnLFxD4VW7uc04XiwTTl/8ydYJxKvPwkWQrSjInkuM5VZVg6BGr1/natq69zDuvO9lGpLClJqvA==", + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "deep-is": "^0.1.3", - "ip-regex": "^4.1.0", - "is-url": "^1.2.4" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=v0.10.0" + "node": ">=16.0.0" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "node_modules/@aws-sdk/client-iam/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/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, + "node_modules/@aws-sdk/client-iam/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": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, + "node_modules/@aws-sdk/client-iam/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": ">=8" + "node": ">=16.0.0" } }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "append-transform": "^2.0.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": ">=8" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node_modules/@aws-sdk/client-iam/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/istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dev": true, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" + "@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": ">=8" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/istanbul-lib-processinfo/node_modules/p-map": { + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/is-array-buffer": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, + "license": "Apache-2.0", "dependencies": { - "aggregate-error": "^3.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/istanbul-lib-processinfo/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" + "node_modules/@aws-sdk/client-iam/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/istanbul-lib-report": { + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-utf8": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, + "license": "Apache-2.0", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, + "node_modules/@aws-sdk/client-iot": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-iot/-/client-iot-3.693.0.tgz", + "integrity": "sha512-0EOKH6CjDHMdE1NSDdtZ8/zov+Xf1MovWvAeQGs76ec4mL2VWP5HvePjjdkGoOo0KC9k/AqOVVc0UOZjK0iCQw==", + "license": "Apache-2.0", "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/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", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iot/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", + "@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-iot/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", + "@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" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", - "dev": true, + "node_modules/@aws-sdk/client-iot/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": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.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": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iot/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", + "@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": ">=8" + "node": ">=16.0.0" } }, - "node_modules/jest-worker": { - "version": "27.3.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.3.1.tgz", - "integrity": "sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==", - "dev": true, + "node_modules/@aws-sdk/client-iot/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": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.13.0" + "node": ">=16.0.0" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "node_modules/@aws-sdk/client-iot/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": { - "has-flag": "^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": ">=10" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "node_modules/@aws-sdk/client-iot/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", + "@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": ">= 0.6.0" + "node": ">=16.0.0" } }, - "node_modules/joycon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.0.1.tgz", - "integrity": "sha512-SJcJNBg32dGgxhPtM0wQqxqV0ax9k/9TaUskGDSJkSFSQOEWWvQ3zzWdGQRIUry2j1zA5+ReH13t0Mf3StuVZA==", - "dev": true, + "node_modules/@aws-sdk/client-iot/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", + "@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": ">=10" + "node": ">=16.0.0" } }, - "node_modules/js-sdsl": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", - "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/@aws-sdk/client-iot/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": { - "argparse": "^2.0.1" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" + "node_modules/@aws-sdk/client-iot/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", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-to-typescript": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-11.0.2.tgz", - "integrity": "sha512-XRyeXBJeo/IH4eTP5D1ptX78vCvH86nMDt2k3AxO28C3uYWEDmy4mgPyMpb8bLJ/pJMElOGuQbnKR5Y6NSh3QQ==", - "dev": true, + "node_modules/@aws-sdk/client-iot/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": { - "@bcherny/json-schema-ref-parser": "9.0.9", - "@types/json-schema": "^7.0.11", - "@types/lodash": "^4.14.182", - "@types/prettier": "^2.6.1", - "cli-color": "^2.0.2", - "get-stdin": "^8.0.0", - "glob": "^7.1.6", - "glob-promise": "^4.2.2", - "is-glob": "^4.0.3", - "lodash": "^4.17.21", - "minimist": "^1.2.6", - "mkdirp": "^1.0.4", - "mz": "^2.7.0", - "prettier": "^2.6.2" - }, - "bin": { - "json2ts": "dist/src/cli.js" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.0.0" } }, - "node_modules/json-schema-to-typescript/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" + "node_modules/@aws-sdk/client-iot/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", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" + "node_modules/@aws-sdk/client-iot/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", + "@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": ">=6" + "node": ">=16.0.0" } }, - "node_modules/jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==" - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/@aws-sdk/client-iot/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": { - "universalify": "^2.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true + "node_modules/@aws-sdk/client-iot/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", + "@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/keytar": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.7.0.tgz", - "integrity": "sha512-YEY9HWqThQc5q5xbXbRwsZTh2PJ36OSYRjSv3NN2xf5s5dpLTjEZnC2YikR29OaVybf9nQ0dJ/80i40RS97t/A==", - "dev": true, - "hasInstallScript": true, + "node_modules/@aws-sdk/client-iot/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": { - "node-addon-api": "^3.0.0", - "prebuild-install": "^6.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "node_modules/@aws-sdk/client-iot/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": { - "json-buffer": "3.0.1" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, + "node_modules/@aws-sdk/client-iot/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", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, + "node_modules/@aws-sdk/client-iot/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6" + "node": ">=16.0.0" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, + "node_modules/@aws-sdk/client-iot/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": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">=16.0.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, + "node_modules/@aws-sdk/client-iot/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": { - "uc.micro": "^1.0.1" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", - "dev": true + "node_modules/@aws-sdk/client-iotsecuretunneling": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-iotsecuretunneling/-/client-iotsecuretunneling-3.693.0.tgz", + "integrity": "sha512-f9p5/TgVQsko0FlYIj9UKAVSfgPF4GgoKGVOI3Gx6XpynYwideGxItq3v0ExoAzpaohq6zRKleqA68o/T1TqXQ==", + "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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iotsecuretunneling/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", + "@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-iotsecuretunneling/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", + "@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/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true, + "node_modules/@aws-sdk/client-iotsecuretunneling/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", + "@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-iotsecuretunneling/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", + "@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": ">=6.11.5" + "node": ">=16.0.0" } }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" + "@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": ">=8.9.0" + "node": ">=16.0.0" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "p-locate": "^5.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": ">=10" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true + "node_modules/@aws-sdk/client-iotsecuretunneling/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", + "@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/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "node_modules/@aws-sdk/client-iotsecuretunneling/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", + "@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/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.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": ">=10" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/logform": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz", - "integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==", + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "@colors/colors": "1.5.0", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "node_modules/@aws-sdk/client-iotsecuretunneling/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", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "yallist": "^4.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "dev": true, + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "es5-ext": "~0.10.2" + "@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/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "sourcemap-codec": "^1.4.8" + "@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/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "semver": "^6.0.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": ">=8" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node_modules/@aws-sdk/client-iotsecuretunneling/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", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "node_modules/@aws-sdk/client-iotsecuretunneling/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", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true, + "node_modules/@aws-sdk/client-iotsecuretunneling/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", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", - "dev": true - }, - "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "tslib": "^2.6.2" }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/marked": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", - "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==", - "dev": true, - "bin": { - "marked": "bin/marked.js" + "node_modules/@aws-sdk/client-iotsecuretunneling/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12" + "node": ">=16.0.0" } }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dev": true, + "node_modules/@aws-sdk/client-iotsecuretunneling/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": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", - "dev": true - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=16.0.0" } }, - "node_modules/memfs": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", - "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", - "dev": true, + "node_modules/@aws-sdk/client-lambda": { + "version": "3.637.0", + "license": "Apache-2.0", "dependencies": { - "fs-monkey": "^1.0.3" + "@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": ">= 4.0.0" + "node": ">=16.0.0" } }, - "node_modules/memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", - "dev": true, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" + "@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/meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dev": true, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16.0.0" } }, - "node_modules/meow/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, + "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": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" + "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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "node_modules/@aws-sdk/client-redshift": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-redshift/-/client-redshift-3.693.0.tgz", + "integrity": "sha512-k+4emXXK7iOOYjTAU+Erj5RVxu68Hi6iI48h5r8iNMhWRUMqUq346tK5qkD4C4x9SzJu5j0WgPWpVUiHu8ufDw==", + "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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-redshift-data": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-redshift-data/-/client-redshift-data-3.693.0.tgz", + "integrity": "sha512-uG5LdlXz80KcauRIucMdiRSQJ2WutewQRHpcTQW4vFUf/kEhUha5fD9FMn+/eJ1NFA2N8hv64vhpzGvu7EiP6Q==", + "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", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-redshift-data/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", + "@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-redshift-data/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", + "@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/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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", + "@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-redshift-data/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", + "@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": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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", + "@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": ">= 0.6" + "node": ">=16.0.0" } }, - "node_modules/microbuffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/microbuffer/-/microbuffer-1.0.0.tgz", - "integrity": "sha512-O/SUXauVN4x6RaEJFqSPcXNtLFL+QzJHKZlyDVYFwcDDRVca3Fa/37QXXC+4zAGGa4YhHrHxKXuuHvLDIQECtA==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "@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": ">=8.6" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" + "node_modules/@aws-sdk/client-redshift-data/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", + "@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": ">=4" + "node": ">=16.0.0" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/@aws-sdk/client-redshift-data/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", + "@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": ">= 0.6" + "node": ">=16.0.0" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/@aws-sdk/client-redshift-data/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": { - "mime-db": "1.52.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": ">= 0.6" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6" + "node": ">=16.0.0" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "node_modules/@aws-sdk/client-redshift-data/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", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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": { - "brace-expansion": "^1.1.7" + "@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": "*" + "node": ">=16.0.0" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" + "@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": ">= 6" + "node": ">=16.0.0" } }, - "node_modules/minimist-options/node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "node_modules/@aws-sdk/client-redshift-data/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": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, - "node_modules/mocha": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", - "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14.0.0" + "node": ">=16.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/mocha-junit-reporter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.0.2.tgz", - "integrity": "sha512-vYwWq5hh3v1lG0gdQCBxwNipBfvDiAM1PHroQRNp96+2l72e9wEUTw+mzoK+O0SudgfQ7WvTQZ9Nh3qkAYAjfg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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": { - "debug": "^2.2.0", - "md5": "^2.1.0", - "mkdirp": "~0.5.1", - "strip-ansi": "^6.0.1", - "xml": "^1.0.0" + "tslib": "^2.6.2" }, - "peerDependencies": { - "mocha": ">=2.2.5" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/mocha-junit-reporter/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/mocha-junit-reporter/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-data/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": { - "ms": "2.0.0" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/mocha-junit-reporter/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "node_modules/@aws-sdk/client-redshift-serverless": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-redshift-serverless/-/client-redshift-serverless-3.693.0.tgz", + "integrity": "sha512-m6Bhw0Xx/x0KGKP9N7c+Jqs5VT6nkZbfwO+QTxllggsuNfAzGwluCw1hoY++/MQ9oFtioEu+ud7xWOlTIK8w/A==", + "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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-redshift-serverless/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", + "@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-redshift-serverless/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", + "@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/mocha-junit-reporter/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "ansi-regex": "^5.0.1" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/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-redshift-serverless/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", + "@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": ">=8" + "node": ">=16.0.0" } }, - "node_modules/mocha-multi-reporters": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/mocha-multi-reporters/-/mocha-multi-reporters-1.5.1.tgz", - "integrity": "sha512-Yb4QJOaGLIcmB0VY7Wif5AjvLMUFAdV57D2TWEva1Y0kU/3LjKpeRVmlMIfuO1SVbauve459kgtIizADqxMWPg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "debug": "^4.1.1", - "lodash": "^4.17.15" + "@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": ">=6.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-redshift-serverless/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", + "@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": { - "mocha": ">=3.1.2" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "balanced-match": "^1.0.0" + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "brace-expansion": "^2.0.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": ">=10" + "node": ">=16.0.0" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "has-flag": "^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": ">=10" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "node_modules/@aws-sdk/client-redshift-serverless/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", + "tslib": "^2.6.2" + }, "engines": { - "node": "*" + "node": ">=16.0.0" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" + "@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" }, - "bin": { - "multicast-dns": "cli.js" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/multimatch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", - "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "@types/minimatch": "^3.0.3", - "array-differ": "^3.0.0", - "array-union": "^2.1.0", - "arrify": "^2.0.1", - "minimatch": "^3.0.4" + "@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": ">=8" + "node": ">=16.0.0" } }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/mvdan-sh": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mvdan-sh/-/mvdan-sh-0.5.0.tgz", - "integrity": "sha512-UWbdl4LHd2fUnaEcOUFVWRdWGLkNoV12cKVIPiirYd8qM5VkCoCTXErlDubevrkEG7kGohvjRxAlTQmOqG80tw==", - "dev": true - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "bin": { - "nanoid": "bin/nanoid.cjs" + "node_modules/@aws-sdk/client-redshift-serverless/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", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=16.0.0" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "dev": true + "node_modules/@aws-sdk/client-redshift-serverless/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", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true + "node_modules/@aws-sdk/client-redshift-serverless/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", + "@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/neatequal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/neatequal/-/neatequal-1.0.0.tgz", - "integrity": "sha512-sVt5awO4a4w24QmAthdrCPiVRW3naB8FGLdyadin01BH+6BzNPEBwGrpwCczQvPlULS6uXTItTe1PJ5P0kYm7A==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/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": { - "varstream": "^0.3.2" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift-serverless/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=16.0.0" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "node_modules/@aws-sdk/client-redshift-serverless/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true - }, - "node_modules/nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-redshift/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", + "@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/node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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": { - "semver": "^5.4.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/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-redshift/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", + "@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/node-abi/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" + "node_modules/@aws-sdk/client-redshift/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", + "@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/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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", + "@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": ">= 6.13.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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": { - "process-on-spawn": "^1.0.0" + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", - "dev": true - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.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": ">=10" + "node": ">=16.0.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "node_modules/@aws-sdk/client-redshift/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", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=10" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-redshift/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", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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": { - "path-key": "^3.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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": { - "boolbase": "^1.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/nunjucks": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", - "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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": { - "a-sync-waterfall": "^1.0.0", - "asap": "^2.0.3", - "commander": "^5.1.0" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" }, - "bin": { - "nunjucks-precompile": "bin/precompile" + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-redshift/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", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-redshift/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", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 6.9.0" + "node": ">=16.0.0" }, "peerDependencies": { - "chokidar": "^3.3.0" + "aws-crt": ">=1.0.0" }, "peerDependenciesMeta": { - "chokidar": { + "aws-crt": { "optional": true } } }, - "node_modules/nunjucks/node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 6" + "node": ">=16.0.0" } }, - "node_modules/nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/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": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8.9" + "node": ">=16.0.0" } }, - "node_modules/nyc/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "node_modules/@aws-sdk/client-redshift/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/nyc/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, + "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", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-stream": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3-control": { + "version": "3.859.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3-control/-/client-s3-control-3.859.0.tgz", + "integrity": "sha512-vzhOtDH4BCdn30+Crg1QxGXbhZIh4Ia84/qNx2EtupkM2UrO6uaZ91qGl175QWU4TcG+mlf/yA/bvrwenhbF6w==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.858.0", + "@aws-sdk/credential-provider-node": "3.859.0", + "@aws-sdk/middleware-bucket-endpoint": "3.840.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-sdk-s3-control": "3.848.0", + "@aws-sdk/middleware-user-agent": "3.858.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.858.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.2", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-blob-browser": "^4.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/hash-stream-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/md5-js": "^4.0.4", + "@smithy/middleware-apply-body-checksum": "^4.1.2", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.17", + "@smithy/middleware-retry": "^4.1.18", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@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.25", + "@smithy/util-defaults-mode-node": "^4.0.25", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/client-sso": { + "version": "3.858.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.858.0.tgz", + "integrity": "sha512-iXuZQs4KH6a3Pwnt0uORalzAZ5EXRPr3lBYAsdNwkP8OYyoUz5/TE3BLyw7ceEh0rj4QKGNnNALYo1cDm0EV8w==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.858.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.858.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.858.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.2", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.17", + "@smithy/middleware-retry": "^4.1.18", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@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.25", + "@smithy/util-defaults-mode-node": "^4.0.25", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/core": { + "version": "3.858.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.858.0.tgz", + "integrity": "sha512-iWm4QLAS+/XMlnecIU1Y33qbBr1Ju+pmWam3xVCPlY4CSptKpVY+2hXOnmg9SbHAX9C005fWhrIn51oDd00c9A==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.7.2", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.9", + "@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-utf8": "^4.0.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.858.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.858.0.tgz", + "integrity": "sha512-kZsGyh2BoSRguzlcGtzdLhw/l/n3KYAC+/l/H0SlsOq3RLHF6tO/cRdsLnwoix2bObChHUp03cex63o1gzdx/Q==", + "dependencies": { + "@aws-sdk/core": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.858.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.858.0.tgz", + "integrity": "sha512-GDnfYl3+NPJQ7WQQYOXEA489B212NinpcIDD7rpsB6IWUPo8yDjT5NceK4uUkIR3MFpNCGt9zd/z6NNLdB2fuQ==", + "dependencies": { + "@aws-sdk/core": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.859.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.859.0.tgz", + "integrity": "sha512-KsccE1T88ZDNhsABnqbQj014n5JMDilAroUErFbGqu5/B3sXqUsYmG54C/BjvGTRUFfzyttK9lB9P9h6ddQ8Cw==", + "dependencies": { + "@aws-sdk/core": "3.858.0", + "@aws-sdk/credential-provider-env": "3.858.0", + "@aws-sdk/credential-provider-http": "3.858.0", + "@aws-sdk/credential-provider-process": "3.858.0", + "@aws-sdk/credential-provider-sso": "3.859.0", + "@aws-sdk/credential-provider-web-identity": "3.858.0", + "@aws-sdk/nested-clients": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.859.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.859.0.tgz", + "integrity": "sha512-ZRDB2xU5aSyTR/jDcli30tlycu6RFvQngkZhBs9Zoh2BiYXrfh2MMuoYuZk+7uD6D53Q2RIEldDHR9A/TPlRuA==", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "@aws-sdk/credential-provider-env": "3.858.0", + "@aws-sdk/credential-provider-http": "3.858.0", + "@aws-sdk/credential-provider-ini": "3.859.0", + "@aws-sdk/credential-provider-process": "3.858.0", + "@aws-sdk/credential-provider-sso": "3.859.0", + "@aws-sdk/credential-provider-web-identity": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@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/nyc/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.858.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.858.0.tgz", + "integrity": "sha512-l5LJWZJMRaZ+LhDjtupFUKEC5hAjgvCRrOvV5T60NCUBOy0Ozxa7Sgx3x+EOwiruuoh3Cn9O+RlbQlJX6IfZIw==", + "dependencies": { + "@aws-sdk/core": "3.858.0", + "@aws-sdk/types": "3.840.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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.859.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.859.0.tgz", + "integrity": "sha512-BwAqmWIivhox5YlFRjManFF8GoTvEySPk6vsJNxDsmGsabY+OQovYxFIYxRCYiHzH7SFjd4Lcd+riJOiXNsvRw==", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@aws-sdk/client-sso": "3.858.0", + "@aws-sdk/core": "3.858.0", + "@aws-sdk/token-providers": "3.859.0", + "@aws-sdk/types": "3.840.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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.858.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.858.0.tgz", + "integrity": "sha512-8iULWsH83iZDdUuiDsRb83M0NqIlXjlDbJUIddVsIrfWp4NmanKw77SV6yOZ66nuJjPsn9j7RDb9bfEPCy5SWA==", "dependencies": { - "p-locate": "^4.1.0" + "@aws-sdk/core": "3.858.0", + "@aws-sdk/nested-clients": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.840.0.tgz", + "integrity": "sha512-+gkQNtPwcSMmlwBHFd4saVVS11In6ID1HczNzpM3MXKXRBfSlbZJbCt6wN//AZ8HMklZEik4tcEOG0qa9UY8SQ==", "dependencies": { - "p-try": "^2.0.0" + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.840.0.tgz", + "integrity": "sha512-ub+hXJAbAje94+Ya6c6eL7sYujoE8D4Bumu1NUI8TXjUhVVn0HzVWQjpRLshdLsUp1AW7XyeJaxyajRaJQ8+Xg==", "dependencies": { - "p-limit": "^2.2.0" + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/middleware-logger": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.840.0.tgz", + "integrity": "sha512-lSV8FvjpdllpGaRspywss4CtXV8M7NNNH+2/j86vMH+YCOZ6fu2T/TyFd/tHwZ92vDfHctWkRbQxg0bagqwovA==", "dependencies": { - "aggregate-error": "^3.0.0" + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.840.0.tgz", + "integrity": "sha512-Gu7lGDyfddyhIkj1Z1JtrY5NHb5+x/CRiB87GjaSrKxkDaydtX2CU977JIABtt69l9wLbcGDIQ+W0uJ5xPof7g==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.858.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.858.0.tgz", + "integrity": "sha512-pC3FT/sRZ6n5NyXiTVu9dpf1D9j3YbJz3XmeOOwJqO/Mib2PZyIQktvNMPgwaC5KMVB1zWqS5bmCwxpMOnq0UQ==", "dependencies": { - "ansi-regex": "^5.0.1" + "@aws-sdk/core": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@smithy/core": "^3.7.2", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/nested-clients": { + "version": "3.858.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.858.0.tgz", + "integrity": "sha512-ChdIj80T2whoWbovmO7o8ICmhEB2S9q4Jes9MBnKAPm69PexcJAK2dQC8yI4/iUP8b3+BHZoUPrYLWjBxIProQ==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.858.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.858.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.858.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.7.2", + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.17", + "@smithy/middleware-retry": "^4.1.18", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.1.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@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.25", + "@smithy/util-defaults-mode-node": "^4.0.25", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.840.0.tgz", + "integrity": "sha512-Qjnxd/yDv9KpIMWr90ZDPtRj0v75AqGC92Lm9+oHXZ8p1MjG5JE2CW0HL8JRgK9iKzgKBL7pPQRXI8FkvEVfrA==", "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "@aws-sdk/types": "3.840.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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/token-providers": { + "version": "3.859.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.859.0.tgz", + "integrity": "sha512-6P2wlvm9KBWOvRNn0Pt8RntnXg8fzOb5kEShvWsOsAocZeqKNaYbihum5/Onq1ZPoVtkdb++8eWDocDnM4k85Q==", "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "@aws-sdk/core": "3.858.0", + "@aws-sdk/nested-clients": "3.858.0", + "@aws-sdk/types": "3.840.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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/types": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz", + "integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/util-arn-parser": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", + "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/util-endpoints": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.848.0.tgz", + "integrity": "sha512-fY/NuFFCq/78liHvRyFKr+aqq1aA/uuVSANjzr5Ym8c+9Z3HRPE9OrExAHoMrZ6zC8tHerQwlsXYYH5XZ7H+ww==", + "dependencies": { + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" } }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.840.0.tgz", + "integrity": "sha512-JdyZM3EhhL4PqwFpttZu1afDpPJCCc3eyZOLi+srpX11LsGj6sThf47TYQN75HT1CarZ7cCdQHGzP2uy3/xHfQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.858.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.858.0.tgz", + "integrity": "sha512-T1m05QlN8hFpx5/5duMjS8uFSK5e6EXP45HQRkZULVkL3DK+jMaxsnh3KLl5LjUoHn/19M4HM0wNUBhYp4Y2Yw==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.858.0", + "@aws-sdk/types": "3.840.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@aws-sdk/xml-builder": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", + "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", "dependencies": { - "ee-first": "1.1.1" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/chunked-blob-reader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", + "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", "dependencies": { - "wrappy": "1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", + "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", "dependencies": { - "fn.name": "1.x.x" + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "mimic-fn": "^2.1.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": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/core": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.7.2.tgz", + "integrity": "sha512-JoLw59sT5Bm8SAjFCYZyuCGxK8y3vovmoVbZWLDPTH5XpPEIwpFd9m90jjVMwoypDuB/SdVgje5Y4T7w50lJaw==", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.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.3", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "@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": ">= 0.8.0" + "node": ">=18.0.0" } }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/fetch-http-handler": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.0.tgz", + "integrity": "sha512-mADw7MS0bYe2OGKkHYMaqarOXuDwRbO6ArD91XhHcl2ynjGCFF+hvqf0LyQcYxkA1zaWjefSkU7Ne9mqgApSgQ==", + "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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/hash-blob-browser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.4.tgz", + "integrity": "sha512-WszRiACJiQV3QG6XMV44i5YWlkrlsM5Yxgz4jvsksuu7LDXA6wAtypfPajtNTadzpJy3KyJPoWehYpmZGKUFIQ==", "dependencies": { - "yocto-queue": "^0.1.0" + "@smithy/chunked-blob-reader": "^5.0.0", + "@smithy/chunked-blob-reader-native": "^4.0.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "p-limit": "^3.0.2" + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/p-retry": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", - "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/hash-stream-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.4.tgz", + "integrity": "sha512-wHo0d8GXyVmpmMh/qOR0R7Y46/G1y6OR8U+bSTB4ppEzRxd1xVAQ9xOE9hOc0bSjhz0ujCPAbfNLkLrpa6cevg==", "dependencies": { - "@types/retry": "^0.12.0", - "retry": "^0.13.1" + "@smithy/types": "^4.3.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/package-hash": { + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/is-array-buffer": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, + "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": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/md5-js": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.4.tgz", + "integrity": "sha512-uGLBVqcOwrLvGh/v/jw423yWHq/ofUGK1W31M2TNspLQbUV1Va0F5kTxtirkoHawODAZcjXTSGi7JwbnPcDPJg==", "dependencies": { - "callsites": "^3.0.0" + "@smithy/types": "^4.3.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.17.tgz", + "integrity": "sha512-S3hSGLKmHG1m35p/MObQCBCdRsrpbPU8B129BVzRqRfDvQqPMQ14iO4LyRw+7LNizYc605COYAcjqgawqi+6jA==", + "dependencies": { + "@smithy/core": "^3.7.2", + "@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": ">= 0.10" + "node": ">=18.0.0" } }, - "node_modules/parse-semver": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", - "integrity": "sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg=", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/middleware-retry": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.18.tgz", + "integrity": "sha512-bYLZ4DkoxSsPxpdmeapvAKy7rM5+25gR7PGxq2iMiecmbrRGBHj9s75N74Ylg+aBiw9i5jIowC/cLU2NR0qH8w==", "dependencies": { - "semver": "^5.1.0" + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.6", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.6", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/parse-semver/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" + "node_modules/@aws-sdk/client-s3-control/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": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "parse5": "^6.0.1" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/node-http-handler": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.0.tgz", + "integrity": "sha512-vqfSiHz2v8b3TTTrdXi03vNz1KLYYS3bhHCDv36FYDqxT7jvTll1mMnCrkD+gOvgwybuunh/2VmvOMqwBegxEg==", + "dependencies": { + "@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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/@aws-sdk/client-s3-control/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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "isarray": "0.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/path-to-regexp/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/service-error-classification": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.6.tgz", + "integrity": "sha512-RRoTDL//7xi4tn5FrN2NzH17jbgmnKidUqd4KvquT0954/i6CXXkh1884jBiunq24g9cGtPBEXlU40W6EpNOOg==", "dependencies": { - "through": "~2.3" + "@smithy/types": "^4.3.1" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, - "engines": { - "node": ">=8.6" + "node_modules/@aws-sdk/client-s3-control/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/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "find-up": "^4.0.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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/smithy-client": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.9.tgz", + "integrity": "sha512-mbMg8mIUAWwMmb74LoYiArP04zWElPzDoA1jVOp3or0cjlDMgoS6WTC3QXK0Vxoc9I4zdrX0tq6qsOmaIoTWEQ==", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@smithy/core": "^3.7.2", + "@smithy/middleware-endpoint": "^4.1.17", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.3", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "p-locate": "^4.1.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "p-try": "^2.0.0" + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "p-limit": "^2.2.0" + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "node_modules/@aws-sdk/client-s3-control/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": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.12.0" + "node": ">=18.0.0" } }, - "node_modules/portfinder/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "node_modules/@aws-sdk/client-s3-control/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": { - "lodash": "^4.17.14" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/@aws-sdk/client-s3-control/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": { - "ms": "^2.1.1" + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/postcss": { - "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "node_modules/@aws-sdk/client-s3-control/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": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" + "tslib": "^2.6.2" }, "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=18.0.0" } }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.25.tgz", + "integrity": "sha512-pxEWsxIsOPLfKNXvpgFHBGFC3pKYKUFhrud1kyooO9CJai6aaKDHfT10Mi5iiipPXN/JhKAu3qX9o75+X85OdQ==", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.9", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "postcss": "^8.1.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.25.tgz", + "integrity": "sha512-+w4n4hKFayeCyELZLfsSQG5mCC3TwSkmRHv4+el5CzFU8ToQpYGhpV7mrRzqlwKkntlPilT1HJy1TVeEvEjWOQ==", "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.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.9", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=18.0.0" } }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "postcss-selector-parser": "^6.0.4" + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=18.0.0" } }, - "node_modules/postcss-modules-values": { + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/util-hex-encoding": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", "dependencies": { - "icss-utils": "^5.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=18.0.0" } }, - "node_modules/postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", - "dev": true - }, - "node_modules/prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/util-retry": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.6.tgz", + "integrity": "sha512-+YekoF2CaSMv6zKrA6iI/N9yva3Gzn4L6n35Luydweu5MMPYpiGZlWqehPHDHyNbnyaYlz/WJyYAZnC+loBDZg==", "dependencies": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" + "@smithy/service-error-classification": "^4.0.6", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/@smithy/util-stream": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.3.tgz", + "integrity": "sha512-cQn412DWHHFNKrQfbHY8vSFI3nTROY1aIKji9N0tpp8gUABRilr7wdf8fqBbSlXresobM+tQFNk6I+0LXK/YZg==", + "dependencies": { + "@smithy/fetch-http-handler": "^5.1.0", + "@smithy/node-http-handler": "^4.1.0", + "@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": ">= 0.8.0" + "node": ">=18.0.0" } }, - "node_modules/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" + "node_modules/@aws-sdk/client-s3-control/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": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/prettier-plugin-sh": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-sh/-/prettier-plugin-sh-0.8.1.tgz", - "integrity": "sha512-tz0g6y+ZaJF0PWaa1F7vhCv4nLgYYl2zYzYU4XJFD1McoY0oHI+l2osvXqv1s5yQdtjdlzKszN6VY7WTaw2Gqw==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/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": { - "mvdan-sh": "^0.5.0" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "prettier": "^2.0.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/pretty-quick": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.1.tgz", - "integrity": "sha512-ZYLGiMoV2jcaas3vTJrLvKAYsxDoXQBUn8OSTxkl67Fyov9lyXivJTl0+2WVh+y6EovGcw7Lm5ThYpH+Sh3XxQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3-control/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "dependencies": { - "chalk": "^3.0.0", - "execa": "^4.0.0", - "find-up": "^4.1.0", - "ignore": "^5.1.4", - "mri": "^1.1.5", - "multimatch": "^4.0.0" + "strnum": "^2.1.0" }, "bin": { - "pretty-quick": "bin/pretty-quick.js" - }, - "engines": { - "node": ">=10.13" + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/client-s3-control/node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/@aws-sdk/client-s3/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-s3/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": { - "prettier": ">=2.0.0" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/pretty-quick/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.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": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/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": ">=8" + "node": ">=16.0.0" } }, - "node_modules/pretty-quick/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/pretty-quick/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "p-locate": "^4.1.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/pretty-quick/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "p-try": "^2.0.0" + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16.0.0" } }, - "node_modules/pretty-quick/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "p-limit": "^2.2.0" + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true, + "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": ">= 0.6" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "fromentries": "^1.2.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.10" + "node": ">=16.0.0" } }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, + "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": ">= 0.10" + "node": ">=16.0.0" } }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "@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/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, + "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": ">=6" + "node": ">=16.0.0" } }, - "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "side-channel": "^1.0.4" + "@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": ">=0.6" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "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": ">=0.4.x" + "node": ">=16.0.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/@aws-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/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "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": ">=10" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=16.0.0" } }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.8" + "node": ">=16.0.0" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, - "bin": { - "rc": "cli.js" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.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==", + "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-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", + "@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==", + "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/read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", - "dev": true, + "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": { - "mute-stream": "~0.0.4" + "@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==", + "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": ">=0.8" + "node": ">=16.0.0" } }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, + "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": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, + "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": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "@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": ">=8" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, + "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": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, + "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": { - "p-locate": "^4.1.0" + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "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": { - "p-try": "^2.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": ">=6" + "node": ">=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, + "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": { - "p-limit": "^2.2.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, + "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": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "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": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 6" + "node": ">=16.0.0" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, + "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": { - "picomatch": "^2.2.1" + "@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": ">=8.10.0" + "node": ">=16.0.0" } }, - "node_modules/readline-sync": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", - "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", - "dev": true, + "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", + "@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": ">= 0.8.0" + "node": ">=16.0.0" } }, - "node_modules/recast": { - "version": "0.11.23", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", - "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", - "dev": true, + "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": { - "ast-types": "0.9.6", - "esprima": "~3.1.0", - "private": "~0.1.5", - "source-map": "~0.5.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": ">= 0.8" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/recast/node_modules/ast-types": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", - "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", - "dev": true, + "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", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.8" + "node": ">=16.0.0" } }, - "node_modules/recast/node_modules/esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" + "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", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/recast/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, + "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", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": 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==", + "license": "Apache-2.0", "dependencies": { - "resolve": "^1.9.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.10" + "node": ">=16.0.0" } }, - "node_modules/redent": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-buffer-from": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, + "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": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "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": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16.0.0" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/@aws-sdk/client-sfn": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sfn/-/client-sfn-3.693.0.tgz", + "integrity": "sha512-B2K3aXGnP7eD1ITEIx4kO43l1N5OLqHdLW4AUbwoopwU5qzicc9jADrthXpGxymJI8AhJz9T2WtLmceBU2EpNg==", + "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", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/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", + "@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-sfn/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", + "@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" }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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", + "@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-sfn/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": { - "es6-error": "^4.0.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": ">=4" + "node": ">=16.0.0" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "node_modules/replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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", + "@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": ">= 0.10" + "node": ">=16.0.0" } }, - "node_modules/request-light": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.2.5.tgz", - "integrity": "sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw==", + "node_modules/@aws-sdk/client-sfn/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": { - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "vscode-nls": "^4.1.1" + "@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/request-light/node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "node_modules/@aws-sdk/client-sfn/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": { - "es6-promisify": "^5.0.0" + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 4.0.0" + "node": ">=16.0.0" } }, - "node_modules/request-light/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "node_modules/@aws-sdk/client-sfn/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": { - "ms": "2.0.0" + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/request-light/node_modules/http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "node_modules/@aws-sdk/client-sfn/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": { - "agent-base": "4", - "debug": "3.1.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": ">= 4.5.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/request-light/node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "node_modules/@aws-sdk/client-sfn/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": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 4.5.0" + "node": ">=16.0.0" } }, - "node_modules/request-light/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/request-light/node_modules/vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.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" }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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": { - "resolve-from": "^5.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "node_modules/@aws-sdk/client-sfn/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": { - "lowercase-keys": "^2.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 4" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, + "node_modules/@aws-sdk/client-sfn/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, + "node_modules/@aws-sdk/client-sfn/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": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/@aws-sdk/client-sfn/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": { - "queue-microtask": "^1.2.2" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/@aws-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/safe-stable-stringify": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", - "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", + "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": ">=10" + "node": ">=16.0.0" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + "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/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@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": ">= 10.13.0" + "node": ">=16.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "node_modules/selfsigned": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", - "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "node-forge": "^1" + "@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": ">=10" + "node": ">=16.0.0" } }, - "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@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": ">=10" + "node": ">=16.0.0" } }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "ms": "2.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/send/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, + "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": ">= 0.8" + "node": ">=16.0.0" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, + "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": ">= 0.8" + "node": ">=16.0.0" } }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "randombytes": "^2.1.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "@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": ">= 0.8.0" + "node": ">=16.0.0" } }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "ms": "2.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.6" + "node": ">=16.0.0" } }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true + "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/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, + "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "kind-of": "^6.0.2" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "shebang-regex": "^3.0.0" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/shebang-regex": { + "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/util-utf8": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/@aws-sdk/client-sso": { + "version": "3.637.0", + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@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/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", - "dev": true + "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/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/@aws-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/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "dev": true, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" + "@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/simple-get/node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "mimic-response": "^2.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/simple-get/node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true, - "engines": { - "node": ">=8" + "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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "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": { - "is-arrayish": "^0.3.1" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/sinon": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", - "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", - "dev": true, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.1.2", - "@sinonjs/samsam": "^6.1.1", - "diff": "^5.0.0", - "nise": "^5.1.1", - "supports-color": "^7.2.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/sinon/node_modules/@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@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/slash": { + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/is-array-buffer": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "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": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, + "node_modules/@aws-sdk/client-sts": { + "version": "3.637.0", + "license": "Apache-2.0", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, + "@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": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "@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/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=16.0.0" } }, - "node_modules/spdy-transport": { + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, + "license": "Apache-2.0", "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, + "node_modules/@aws-sdk/core": { + "version": "3.635.0", + "license": "Apache-2.0", "dependencies": { - "through": "2" + "@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": "*" + "node": ">=16.0.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "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": "*" + "node": ">=18.0.0" } }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true, + "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": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", - "dev": true, + "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": { - "duplexer": "~0.1.1", - "through": "~2.3.4" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "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": { - "safe-buffer": "~5.2.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "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": ">=8" + "node": ">=16.0.0" } }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.635.0", + "license": "Apache-2.0", "dependencies": { - "ansi-regex": "^5.0.1" + "@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": ">=8" + "node": ">=16.0.0" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@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/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ansi-regex": "^4.1.0" + "@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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, + "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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, + "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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, + "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": { - "min-indent": "^1.0.0" + "@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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" + "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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "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": { - "has-flag": "^4.0.0" + "@aws-sdk/client-sso": "3.758.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/token-providers": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" + "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" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/svg-pathdata": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", - "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", - "dev": true, + "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": ">=12.0.0" + "node": ">=18.0.0" } }, - "node_modules/svg2ttf": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/svg2ttf/-/svg2ttf-6.0.3.tgz", - "integrity": "sha512-CgqMyZrbOPpc+WqH7aga4JWkDPso23EgypLsbQ6gN3uoPWwwiLjXvzgrwGADBExvCRJrWFzAeK1bSoSpE7ixSQ==", - "dev": true, + "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": { - "@xmldom/xmldom": "^0.7.2", - "argparse": "^2.0.1", - "cubic2quad": "^1.2.1", - "lodash": "^4.17.10", - "microbuffer": "^1.0.0", - "svgpath": "^2.1.5" + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "bin": { - "svg2ttf": "svg2ttf.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/svgicons2svgfont": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/svgicons2svgfont/-/svgicons2svgfont-10.0.6.tgz", - "integrity": "sha512-fUgQEVg3XwTbOHvlXahHGqCet5Wvfo1bV4DCvbSRvjsOCPCRunYbG4dUJCPegps37BMph3eOrfoobhH5AWuC6A==", - "dev": true, + "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": { - "commander": "^7.2.0", - "geometry-interfaces": "^1.1.4", - "glob": "^7.1.6", - "neatequal": "^1.0.0", - "readable-stream": "^3.4.0", - "sax": "^1.2.4", - "svg-pathdata": "^6.0.0" - }, - "bin": { - "svgicons2svgfont": "bin/svgicons2svgfont.js" + "@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": ">=12.0.0" + "node": ">=18.0.0" } }, - "node_modules/svgicons2svgfont/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, + "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": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/svgicons2svgfont/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "node_modules/svgpath": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/svgpath/-/svgpath-2.5.0.tgz", - "integrity": "sha512-o/vohwqjUO9nDAh4rcjE3KaW/v//At8UJu2LJMybXidf5QLQLVA4bxH0//4YCsr+1H4Gw1Wi/Jc62ynzSBYidw==", - "dev": true - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, + "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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-endpoints": { + "version": "3.743.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/tcp-port-used": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", - "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", + "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": { - "debug": "4.3.1", - "is2": "^2.0.6" + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/tcp-port-used/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "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": { - "ms": "2.1.2" + "@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": ">=6.0" + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" }, "peerDependenciesMeta": { - "supports-color": { + "aws-crt": { "optional": true } } }, - "node_modules/terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=18.0.0" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", - "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "jest-worker": "^27.0.6", - "p-limit": "^3.1.0", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "@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": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "@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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "any-promise": "^1.0.0" + "@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/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "thenify": ">= 3.1.0 < 4" + "@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": ">=0.8" + "node": ">=18.0.0" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "node_modules/time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", - "dev": true, + "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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "rimraf": "^3.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8.17.0" + "node": ">=18.0.0" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, + "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": ">=4" + "node": ">=18.0.0" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "is-number": "^7.0.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", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8.0" + "node": ">=18.0.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, + "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": ">=0.6" + "node": ">=18.0.0" } }, - "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", - "dev": true, + "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": "*" + "node": ">=18.0.0" } }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "node_modules/ts-mockito": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ts-mockito/-/ts-mockito-2.6.1.tgz", - "integrity": "sha512-qU9m/oEBQrKq5hwfbJ7MgmVN5Gu6lFnIGWvpxSjrqq6YYEVv+RwVFWySbZMBgazsWqv6ctAyVBpo9TmAxnOEKw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "lodash": "^4.17.5" + "@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/ts-morph": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-17.0.1.tgz", - "integrity": "sha512-10PkHyXmrtsTvZSL+cqtJLTgFXkU43Gd0JCc0Rw6GchWbqKe0Rwgt1v3ouobTZwQzF1mGhDeAlWYBMGRV7y+3g==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@ts-morph/common": "~0.18.0", - "code-block-writer": "^11.0.3" + "@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": ">=18.0.0" } }, - "node_modules/ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ts-node/node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/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": ">=0.3.1" + "node": ">=18.0.0" } }, - "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "tslib": "^1.8.1" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "node": ">=18.0.0" } }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/ttf2eot": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ttf2eot/-/ttf2eot-2.0.0.tgz", - "integrity": "sha512-U56aG2Ylw7psLOmakjemAzmpqVgeadwENg9oaDjaZG5NYX4WB6+7h74bNPcc+0BXsoU5A/XWiHabDXyzFOmsxQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "argparse": "^1.0.6", - "microbuffer": "^1.0.0" + "@smithy/types": "^4.1.0" }, - "bin": { - "ttf2eot": "ttf2eot.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ttf2eot/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, + "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": { - "sprintf-js": "~1.0.2" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ttf2woff": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ttf2woff/-/ttf2woff-2.0.2.tgz", - "integrity": "sha512-X68badwBjAy/+itU49scLjXUL094up+rHuYk+YAOTTBYSUMOmLZ7VyhZJuqQESj1gnyLAC2/5V8Euv+mExmyPA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "argparse": "^1.0.6", - "microbuffer": "^1.0.0", - "pako": "^1.0.0" + "@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" }, - "bin": { - "ttf2woff": "ttf2woff.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ttf2woff/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "sprintf-js": "~1.0.2" + "@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/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + "node": ">=18.0.0" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "safe-buffer": "^5.0.1" + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=18.0.0" } }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "prelude-ls": "^1.2.1" + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">=18.0.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, + "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": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, + "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": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.6" + "node": ">=18.0.0" } }, - "node_modules/typed-rest-client": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.6.tgz", - "integrity": "sha512-xcQpTEAJw2DP7GqVNECh4dD+riS+C1qndXLfBCJ3xk0kqprtGN491P5KlmrDbKdtuW8NEcP/5ChxiJI3S9WYTA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "qs": "^6.9.1", - "tunnel": "0.0.6", - "underscore": "^1.12.1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, + "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": { - "is-typedarray": "^1.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/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "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": ">=4.2.0" + "node": ">=18.0.0" } }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "node_modules/umd-compat-loader": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/umd-compat-loader/-/umd-compat-loader-2.1.2.tgz", - "integrity": "sha512-RkTlsfrCxUISWqiTtYFFJank7b2Hhl4V2pc29nl0xOEGvvuVkpy1xnufhXfTituxgpW0HSrDk0JHlvPYZxEXKQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ast-types": "^0.9.2", - "loader-utils": "^1.0.3", - "recast": "^0.11.17" + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/umd-compat-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "minimist": "^1.2.0" + "tslib": "^2.6.2" }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/umd-compat-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4.0.0" + "node": ">=18.0.0" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", - "dev": true - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "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": ">= 10.0.0" + "node": ">=18.0.0" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/unzipper": { - "version": "0.10.11", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", - "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "graceful-fs": "^4.2.2", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/unzipper/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.637.0", + "license": "Apache-2.0", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.637.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/unzipper/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/unzipper/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "license": "Apache-2.0", "dependencies": { - "safe-buffer": "~5.1.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" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.637.0", + "license": "Apache-2.0", "dependencies": { - "punycode": "^2.1.0" + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" } }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "license": "Apache-2.0", "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.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/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - }, - "node_modules/util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "license": "Apache-2.0", "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" + "@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/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-node/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": ">= 0.4.0" + "node": ">=16.0.0" } }, - "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-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" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/varstream": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/varstream/-/varstream-0.3.2.tgz", - "integrity": "sha512-OpR3Usr9dGZZbDttlTxdviGdxiURI0prX68+DuaN/JfIDbK9ZOmREKM6PgmelsejMnhgjXmEEEgf+E4NbsSqMg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.637.0", + "license": "Apache-2.0", "dependencies": { - "readable-stream": "^1.0.33" - }, - "bin": { - "json2varstream": "cli/json2varstream.js", - "varstream2json": "cli/varstream2json.js" + "@aws-sdk/client-sso": "3.637.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.10.*" + "node": ">=16.0.0" } }, - "node_modules/varstream/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, - "node_modules/varstream/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/varstream/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true, + "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/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.8" + "node": ">=18.0.0" } }, - "node_modules/vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" + "@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": ">= 0.10" + "node": ">=18.0.0" } }, - "node_modules/vsce": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.6.7.tgz", - "integrity": "sha512-5dEtdi/yzWQbOU7JDUSOs8lmSzzkewBR5P122BUkmXE6A/DEdFsKNsg2773NGXJTwwF1MfsOgUR6QVF3cLLJNQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "azure-devops-node-api": "^11.0.1", - "chalk": "^2.4.2", - "cheerio": "^1.0.0-rc.9", - "commander": "^6.1.0", - "glob": "^7.0.6", - "hosted-git-info": "^4.0.2", - "keytar": "^7.7.0", - "leven": "^3.1.0", - "markdown-it": "^12.3.2", - "mime": "^1.3.4", - "minimatch": "^3.0.3", - "parse-semver": "^1.1.1", - "read": "^1.0.7", - "semver": "^5.1.0", - "tmp": "^0.2.1", - "typed-rest-client": "^1.8.4", - "url-join": "^4.0.1", - "xml2js": "^0.4.23", - "yauzl": "^2.3.1", - "yazl": "^2.2.2" - }, - "bin": { - "vsce": "vsce" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14" + "node": ">=18.0.0" } }, - "node_modules/vsce/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color-convert": "^1.9.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/vsce/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@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": ">=4" + "node": ">=18.0.0" } }, - "node_modules/vsce/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color-name": "1.1.3" + "@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/vsce/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/vsce/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, + "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": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.8.0" + "node": ">=18.0.0" } }, - "node_modules/vsce/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/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": ">=4" + "node": ">=18.0.0" } }, - "node_modules/vsce/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" + "node_modules/@aws-sdk/credential-provider-web-identity/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/vsce/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "has-flag": "^3.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/vscode-json-languageservice": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.4.9.tgz", - "integrity": "sha512-4VCpZ9ooea/Zc/MTnj1ccc9C7rqcoinKVQLhLoi6jw6yueSf4y4tg/YIUiPPVMlEAG7ZCPS+NVmqxisQ+mOsSw==", + "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": { - "jsonc-parser": "^2.2.0", - "vscode-languageserver-textdocument": "^1.0.0-next.4", - "vscode-languageserver-types": "^3.15.0-next.6", - "vscode-nls": "^4.1.1", - "vscode-uri": "^2.1.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/vscode-json-languageservice/node_modules/jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" - }, - "node_modules/vscode-json-languageservice/node_modules/vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - }, - "node_modules/vscode-jsonrpc": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", - "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==", + "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/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": ">=8.0.0 || >=10.0.0" + "node": ">=18.0.0" } }, - "node_modules/vscode-languageclient": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.1.4.tgz", - "integrity": "sha512-EUOU+bJu6axmt0RFNo3nrglQLPXMfanbYViJee3Fbn2VuQoX0ZOI4uTYhSRvYLP2vfwTP/juV62P/mksCdTZMA==", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "semver": "^6.3.0", - "vscode-languageserver-protocol": "3.15.3" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "vscode": "^1.41.0" + "node": ">=18.0.0" } }, - "node_modules/vscode-languageclient/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" + "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/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vscode-languageserver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz", - "integrity": "sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ==", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "vscode-languageserver-protocol": "^3.15.3" + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.15.3", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", - "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "vscode-jsonrpc": "^5.0.1", - "vscode-languageserver-types": "3.15.1" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", - "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz", - "integrity": "sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A==" - }, - "node_modules/vscode-languageserver-types": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", - "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" - }, - "node_modules/vscode-nls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.0.tgz", - "integrity": "sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==" - }, - "node_modules/vscode-nls-dev": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/vscode-nls-dev/-/vscode-nls-dev-3.3.2.tgz", - "integrity": "sha512-/YJY/LegZ0jsWFd8BforDmXpwWKprM7L3rL0kLEvjQxOJw6qtmnoUJorLIv0ZXjebeyhI3mc8hjmQr479ykLIQ==", - "dev": true, + "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": { - "ansi-colors": "^3.2.3", - "clone": "^2.1.1", - "event-stream": "^3.3.4", - "fancy-log": "^1.3.3", - "glob": "^7.1.2", - "iconv-lite": "^0.4.19", - "is": "^3.2.1", - "source-map": "^0.6.1", - "typescript": "^3.9.5", - "vinyl": "^2.1.0", - "xml2js": "^0.4.19", - "yargs": "^13.2.4" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "bin": { - "vscl": "lib/vscl.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true, + "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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color-convert": "^1.9.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": ">=4" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "color-name": "1.1.3" + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/vscode-nls-dev/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, + "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": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/vscode-nls-dev/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, + "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": { - "locate-path": "^3.0.0" + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, + "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": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "p-try": "^2.0.0" + "@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": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, + "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": { - "p-limit": "^2.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, + "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/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "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": { + "@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-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": { + "@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-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": { + "@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-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-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": { + "@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": ">=4" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": 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": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "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": ">=4.2.0" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": 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": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.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": ">=6" + "node": ">=18.0.0" } }, - "node_modules/vscode-nls-dev/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/vscode-nls-dev/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": 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": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "@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/vscode-nls-dev/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": 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": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.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/vscode-uri": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", - "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==" - }, - "node_modules/vue": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.31.tgz", - "integrity": "sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==", + "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": { - "@vue/compiler-dom": "3.2.31", - "@vue/compiler-sfc": "3.2.31", - "@vue/runtime-dom": "3.2.31", - "@vue/server-renderer": "3.2.31", - "@vue/shared": "3.2.31" + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vue-loader": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.1.tgz", - "integrity": "sha512-V53TJbHmzjBhCG5OYI2JWy/aYDspz4oVHKxS43Iy212GjGIG1T3EsB3+GWXFm/1z5VwjdjLmdZUFYM70y77vtQ==", - "dev": 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": { - "chalk": "^4.1.0", - "hash-sum": "^2.0.0", - "loader-utils": "^2.0.0" + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "webpack": "^4.1.0 || ^5.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vue-style-loader": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", - "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", - "dev": 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": { - "hash-sum": "^1.0.2", - "loader-utils": "^1.0.2" + "@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/vue-style-loader/node_modules/hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "node_modules/vue-style-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": 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": { - "minimist": "^1.2.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" }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vue-style-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": 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": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "@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": ">=4.0.0" + "node": ">=18.0.0" } }, - "node_modules/vue/node_modules/@vue/compiler-sfc": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz", - "integrity": "sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==", + "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": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.31", - "@vue/compiler-dom": "3.2.31", - "@vue/compiler-ssr": "3.2.31", - "@vue/reactivity-transform": "3.2.31", - "@vue/shared": "3.2.31", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" + "@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-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/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/vue/node_modules/@vue/reactivity-transform": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz", - "integrity": "sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==", + "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": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.31", - "@vue/shared": "3.2.31", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" + "@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-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": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", - "dev": true, + "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": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" + "@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": ">=10.13.0" + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/wawoff2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wawoff2/-/wawoff2-2.0.1.tgz", - "integrity": "sha512-r0CEmvpH63r4T15ebFqeOjGqU4+EgTx4I510NtK35EMciSdcTxCw3Byy3JnBonz7iyIFZ0AbVo0bbFpEVuhCYA==", - "dev": true, + "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": { - "argparse": "^2.0.1" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "bin": { - "woff2_compress.js": "bin/woff2_compress.js", - "woff2_decompress.js": "bin/woff2_decompress.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, + "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": { - "minimalistic-assert": "^1.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/webfont": { - "version": "11.2.26", - "resolved": "https://registry.npmjs.org/webfont/-/webfont-11.2.26.tgz", - "integrity": "sha512-ms9abzcJGMBj5yVTpNfAcyQB0SNzmi10aBlKLC7kAt1TQ5eZqieRhvhxN1BrXaizWV0nksp6vLqBfaJmBWmF7Q==", - "dev": true, + "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": { - "cosmiconfig": "^5.2.0", - "deepmerge": "^4.2.2", - "globby": "^11.0.0", - "meow": "^9.0.0", - "nunjucks": "^3.2.3", - "p-limit": "^3.1.0", - "parse-json": "^5.2.0", - "resolve-from": "^5.0.0", - "svg2ttf": "^6.0.2", - "svgicons2svgfont": "^10.0.3", - "ttf2eot": "^2.0.0", - "ttf2woff": "^2.0.2", - "wawoff2": "^2.0.0", - "xml2js": "^0.4.23" - }, - "bin": { - "webfont": "dist/cli.js" + "@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": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/webfont/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, + "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": { + "@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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/webpack": { - "version": "5.65.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", - "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", - "dev": true, + "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": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", - "webpack-sources": "^3.2.2" - }, - "bin": { - "webpack": "bin/webpack.js" + "@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": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/webpack-cli": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", - "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", - "dev": true, + "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": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.1.0", - "@webpack-cli/info": "^1.4.0", - "@webpack-cli/serve": "^1.6.0", - "colorette": "^2.0.14", - "commander": "^7.0.0", - "execa": "^5.0.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "@webpack-cli/migrate": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, + "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": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/webpack-cli/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, + "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": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/webpack-cli/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" + "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": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/webpack-cli/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, "engines": { - "node": ">=10.17.0" + "node": ">=18.0.0" } }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", - "dev": true, + "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": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^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": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "node": ">=18.0.0" } }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": 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": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.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" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": 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": { - "fast-deep-equal": "^3.1.3" + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "ajv": "^8.8.2" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": 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": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=18.0.0" } }, - "node_modules/webpack-dev-server": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.2.tgz", - "integrity": "sha512-H95Ns95dP24ZsEzO6G9iT+PNw4Q7ltll1GfJHV4fKphuHWgKFzGHWi4alTlTnpk1SPPk41X+l2RB7rLfIhnB9Q==", - "dev": 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": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" + "@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": ">= 12.13.0" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", - "dev": 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": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "@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" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/webpack-dev-server/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": 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": { - "fast-deep-equal": "^3.1.3" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "ajv": "^8.8.2" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": 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": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=18.0.0" } }, - "node_modules/webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "dev": 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": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, - "node_modules/webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", - "dev": 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": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10.13.0" + "node": ">=18.0.0" } }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "peerDependencies": { - "acorn": "^8" + "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.3.1" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/webpack/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": 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/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.8.x" + "node": ">=18.0.0" } }, - "node_modules/webpack/node_modules/webpack-sources": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz", - "integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==", - "dev": 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/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": ">=10.13.0" + "node": ">=18.0.0" } }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": 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": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" + "@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": ">=0.8.0" + "node": ">=18.0.0" } }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": 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": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.8.0" + "node": ">=18.0.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "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": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "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": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", - "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "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": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.9" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0.0" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": 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": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, - "node_modules/winston": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.1.tgz", - "integrity": "sha512-qKLMQVWMOvY0h9H4BA8Sfh79+KdnKi8gsyCSvfQgc+6teSlq92j82WK+zAJc6fLbAA2jwQuqkANLpQeOFA4Kug==", - "deprecated": "Please use version 3.6.0 or >3.7.1 when available due to https://github.com/winstonjs/winston/issues/2103", + "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": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.4.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.5.0" + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/winston-transport": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", - "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "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": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", - "triple-beam": "^1.3.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 6.4.0" + "node": ">=18.0.0" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": 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/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": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": 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": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.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": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": 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": ">=8" + "node": ">=18.0.0" } }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/@aws-sdk/credential-providers/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": { - "ansi-regex": "^5.0.1" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": 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": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ws": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", - "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "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/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "engines": { + "node": ">=18.0.0" } }, - "node_modules/xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", - "dev": 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.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/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "node_modules/@aws-sdk/credential-providers/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": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=4.0.0" + "node": ">=18.0.0" } }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "node_modules/@aws-sdk/credential-providers/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": ">=4.0" + "node": ">=18.0.0" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, + "node_modules/@aws-sdk/lib-storage": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.7", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/smithy-client": "^3.4.3", + "buffer": "5.6.0", + "events": "3.3.0", + "stream-browserify": "3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=10" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.693.0" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "node_modules/@aws-sdk/lib-storage/node_modules/buffer": { + "version": "5.6.0", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "node_modules/@aws-sdk/lib-storage/node_modules/events": { + "version": "3.3.0", + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=0.8.x" } }, - "node_modules/yaml-ast-parser-custom-tags": { - "version": "0.0.43", - "resolved": "https://registry.npmjs.org/yaml-ast-parser-custom-tags/-/yaml-ast-parser-custom-tags-0.0.43.tgz", - "integrity": "sha512-R5063FF/JSAN6qXCmylwjt9PcDH6M0ExEme/nJBzLspc6FJDmHHIqM7xh2WfEmsTJqClF79A9VkXjkAqmZw9SQ==" - }, - "node_modules/yaml-cfn": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/yaml-cfn/-/yaml-cfn-0.3.1.tgz", - "integrity": "sha512-8uEuOFPZFI06cQA+E37oRd9zHEPZVpkVjrBjXxWSt0Hy8hil/KnCcskpR7jwx6ejzfejIi5uzaoQgHTl6qzaNw==", + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "js-yaml": "^4.0.0" + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-arn-parser": "3.693.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "tslib": "^2.6.2" }, - "bin": { - "yaml-cfn": "cli.js" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/yaml-language-server": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-0.10.0.tgz", - "integrity": "sha512-d2/7eGgonEIRcnW9kK+k+ERG4gTOk5BXHr9KjTVv8gEarXKa62Kk+nyFE4AXgMDZ0LXTu8nTuN/AdboJiGN+pQ==", + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "js-yaml": "^3.13.1", - "jsonc-parser": "^2.2.1", - "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.6.0", - "vscode-languageserver": "^5.2.1", - "vscode-languageserver-types": "^3.15.1", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.1", - "yaml-ast-parser-custom-tags": "0.0.43" - }, - "bin": { - "yaml-language-server": "bin/yaml-language-server" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": "*" - }, - "optionalDependencies": { - "prettier": "2.0.5" + "node": ">=16.0.0" } }, - "node_modules/yaml-language-server/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "sprintf-js": "~1.0.2" + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-stream": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/yaml-language-server/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/yaml-language-server/node_modules/jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" - }, - "node_modules/yaml-language-server/node_modules/prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", - "optional": true, - "bin": { - "prettier": "bin-prettier.js" + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, "engines": { - "node": ">=10.13.0" + "node": ">=16.0.0" } }, - "node_modules/yaml-language-server/node_modules/vscode-json-languageservice": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.11.0.tgz", - "integrity": "sha512-QxI+qV97uD7HHOCjh3MrM1TfbdwmTXrMckri5Tus1/FQiG3baDZb2C9Y0y8QThs7PwHYBIQXcAc59ZveCRZKPA==", + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "3.16.0-next.2", - "vscode-nls": "^5.0.0", - "vscode-uri": "^2.1.2" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/yaml-language-server/node_modules/vscode-json-languageservice/node_modules/jsonc-parser": { + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/util-utf8": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==" - }, - "node_modules/yaml-language-server/node_modules/vscode-json-languageservice/node_modules/vscode-languageserver-types": { - "version": "3.16.0-next.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz", - "integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==" - }, - "node_modules/yaml-language-server/node_modules/vscode-json-languageservice/node_modules/vscode-nls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.0.tgz", - "integrity": "sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==" - }, - "node_modules/yaml-language-server/node_modules/vscode-jsonrpc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", - "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8.0.0 || >=10.0.0" + "node": ">=16.0.0" } }, - "node_modules/yaml-language-server/node_modules/vscode-languageserver": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz", - "integrity": "sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A==", + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "license": "Apache-2.0", "dependencies": { - "vscode-languageserver-protocol": "3.14.1", - "vscode-uri": "^1.0.6" + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/yaml-language-server/node_modules/vscode-languageserver-protocol": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz", - "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==", + "node_modules/@aws-sdk/middleware-host-header/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", "dependencies": { - "vscode-jsonrpc": "^4.0.0", - "vscode-languageserver-types": "3.14.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/yaml-language-server/node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", - "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==" - }, - "node_modules/yaml-language-server/node_modules/vscode-languageserver/node_modules/vscode-uri": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz", - "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==" - }, - "node_modules/yaml-language-server/node_modules/vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - }, - "node_modules/yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", - "dev": true, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=16.0.0" } }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, + "node_modules/@aws-sdk/middleware-logger/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=16.0.0" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, + "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/yazl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", - "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", - "dev": true, + "node_modules/@aws-sdk/middleware-sdk-api-gateway": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "buffer-crc32": "~0.2.3" + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "node_modules/@aws-sdk/middleware-sdk-ec2": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-format-url": "3.693.0", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6" + "node": ">=16.0.0" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-arn-parser": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@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-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-stream": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.0" + "engines": { + "node": ">=16.0.0" } }, - "@aws-crypto/ie11-detection": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", - "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", - "requires": { - "tslib": "^1.11.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3-control/-/middleware-sdk-s3-control-3.848.0.tgz", + "integrity": "sha512-1zozD+IKFzFE9RLOCBOGPjhi+jUj0bLxf0ntqBMBJKX9Cf5zqvVuck7mCY19+m0/B+GuSAoiQm2yPV6dcgN17g==", "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "@aws-sdk/middleware-bucket-endpoint": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@aws-sdk/util-endpoints": "3.848.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-crypto/sha256-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", - "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", - "requires": { - "@aws-crypto/ie11-detection": "^2.0.0", - "@aws-crypto/sha256-js": "^2.0.0", - "@aws-crypto/supports-web-crypto": "^2.0.0", - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.840.0.tgz", + "integrity": "sha512-+gkQNtPwcSMmlwBHFd4saVVS11In6ID1HczNzpM3MXKXRBfSlbZJbCt6wN//AZ8HMklZEik4tcEOG0qa9UY8SQ==", "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-crypto/sha256-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", - "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", - "requires": { - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", - "tslib": "^1.11.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/node_modules/@aws-sdk/types": { + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz", + "integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==", "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-crypto/supports-web-crypto": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", - "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", - "requires": { - "tslib": "^1.11.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/node_modules/@aws-sdk/util-arn-parser": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", + "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-crypto/util": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.1.tgz", - "integrity": "sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ==", - "requires": { - "@aws-sdk/types": "^3.1.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/node_modules/@aws-sdk/util-endpoints": { + "version": "3.848.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.848.0.tgz", + "integrity": "sha512-fY/NuFFCq/78liHvRyFKr+aqq1aA/uuVSANjzr5Ym8c+9Z3HRPE9OrExAHoMrZ6zC8tHerQwlsXYYH5XZ7H+ww==", "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "@aws-sdk/types": "3.840.0", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-sdk/abort-controller": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.178.0.tgz", - "integrity": "sha512-ptDkCB06BJrYdhKzamM9yI15LxcGkPczY80hzKAY/aecm09alnW27uCt5HJJx2nCd18IUH28ZO1sc7DTLOWb3A==", - "requires": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/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/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@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" } }, - "@aws-sdk/client-sso": { - "version": "3.181.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.181.0.tgz", - "integrity": "sha512-p/HsYVAO7fgjFfn4LqlnlpSKPXGPegAwjPpY+SqyK3/Hj1OtED4whG8LTgxZSTtYwNIkHEjrHnXTiymyXh34Aw==", - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.178.0", - "@aws-sdk/fetch-http-handler": "3.178.0", - "@aws-sdk/hash-node": "3.178.0", - "@aws-sdk/invalid-dependency": "3.178.0", - "@aws-sdk/middleware-content-length": "3.178.0", - "@aws-sdk/middleware-host-header": "3.178.0", - "@aws-sdk/middleware-logger": "3.178.0", - "@aws-sdk/middleware-recursion-detection": "3.178.0", - "@aws-sdk/middleware-retry": "3.178.0", - "@aws-sdk/middleware-serde": "3.178.0", - "@aws-sdk/middleware-stack": "3.178.0", - "@aws-sdk/middleware-user-agent": "3.178.0", - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/node-http-handler": "3.178.0", - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/smithy-client": "3.180.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/url-parser": "3.178.0", - "@aws-sdk/util-base64-browser": "3.170.0", - "@aws-sdk/util-base64-node": "3.170.0", - "@aws-sdk/util-body-length-browser": "3.170.0", - "@aws-sdk/util-body-length-node": "3.170.0", - "@aws-sdk/util-defaults-mode-browser": "3.180.0", - "@aws-sdk/util-defaults-mode-node": "3.180.0", - "@aws-sdk/util-user-agent-browser": "3.178.0", - "@aws-sdk/util-user-agent-node": "3.178.0", - "@aws-sdk/util-utf8-browser": "3.170.0", - "@aws-sdk/util-utf8-node": "3.170.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/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/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-sdk/client-sso-oidc": { - "version": "3.181.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.181.0.tgz", - "integrity": "sha512-cMrEVAgLDM4AlGuMS6uzWSCYohIth2FcqWq4102lWve2kPQK5E6bl3SdWFLOIZc75ladkbwUXivBYtjNtLeRQA==", - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.178.0", - "@aws-sdk/fetch-http-handler": "3.178.0", - "@aws-sdk/hash-node": "3.178.0", - "@aws-sdk/invalid-dependency": "3.178.0", - "@aws-sdk/middleware-content-length": "3.178.0", - "@aws-sdk/middleware-host-header": "3.178.0", - "@aws-sdk/middleware-logger": "3.178.0", - "@aws-sdk/middleware-recursion-detection": "3.178.0", - "@aws-sdk/middleware-retry": "3.178.0", - "@aws-sdk/middleware-serde": "3.178.0", - "@aws-sdk/middleware-stack": "3.178.0", - "@aws-sdk/middleware-user-agent": "3.178.0", - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/node-http-handler": "3.178.0", - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/smithy-client": "3.180.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/url-parser": "3.178.0", - "@aws-sdk/util-base64-browser": "3.170.0", - "@aws-sdk/util-base64-node": "3.170.0", - "@aws-sdk/util-body-length-browser": "3.170.0", - "@aws-sdk/util-body-length-node": "3.170.0", - "@aws-sdk/util-defaults-mode-browser": "3.180.0", - "@aws-sdk/util-defaults-mode-node": "3.180.0", - "@aws-sdk/util-user-agent-browser": "3.178.0", - "@aws-sdk/util-user-agent-node": "3.178.0", - "@aws-sdk/util-utf8-browser": "3.170.0", - "@aws-sdk/util-utf8-node": "3.170.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/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/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-sdk/config-resolver": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.178.0.tgz", - "integrity": "sha512-8xL98TGMaVULIN7HRWV2q1o0Y2p38QuweehzM8yXCZrrLOyHgWo3waP2RNVeddOB7MrSwwU/gw9rXSv7YHLZ6w==", - "requires": { - "@aws-sdk/signature-v4": "3.178.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-config-provider": "3.170.0", - "@aws-sdk/util-middleware": "3.178.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/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": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-sdk/credential-provider-env": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.46.0.tgz", - "integrity": "sha512-dBCyVBJ1nVi+lvM1jV6LFw8FpGjdeCglLMTmZUxJLBMh/Lp+GWtnGxd7u38WnH5gxKC4xLnYj9zP1t0ha1tGzQ==", - "requires": { - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/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/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==" - } + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-sdk/credential-provider-imds": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.46.0.tgz", - "integrity": "sha512-nQidNDq6mjas/wFOi9XvHkvNmzM/XdSB/eRh6CH+wQeb8RjAlGm2Ivg0mpz/iIxjPXDIduW8aI/gFU3+3um6CQ==", - "requires": { - "@aws-sdk/node-config-provider": "3.46.0", - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/url-parser": "3.46.0", - "tslib": "^2.3.0" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/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/node-config-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.46.0.tgz", - "integrity": "sha512-Hzz860d1GNZSSX74TywfUu125l8BT6JkJuKG0QDhuC+9xklNfC1hgziihldHu6xL7DzY5UKgjyzdNBQfqCqLbw==", - "requires": { - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/querystring-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.46.0.tgz", - "integrity": "sha512-xxTnIXLbx4Jq16Utza7wh4HpPfVyCL0c+6NU2t+kXZ2sgOWhx2XAhShcZVbEkA/61UAMEIhyNBVE+z9OFz6X5g==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/shared-ini-file-loader": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.46.0.tgz", - "integrity": "sha512-Rp7Z1X23kvyRCziOxXu2PYCCPT5CQ5t8O4WoKrEkMT9Vqm2gluXOcCnL4iOpRkSRGEZT7lfe5OCM8ApNRTIHpQ==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==" - }, - "@aws-sdk/url-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.46.0.tgz", - "integrity": "sha512-foMB0AC3QDy+KfvRxsMXvJQZXr9CMzdupcNIXwKRZog82tEEc09dVeUjuJrO4H+A2eK84SyawRfy+ow+LRqvqw==", - "requires": { - "@aws-sdk/querystring-parser": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - } + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-sdk/credential-provider-ini": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.46.0.tgz", - "integrity": "sha512-D+3YLWzCaFUUcbLHAsJIoaI8AhoSpIl3c0a3spVN64f+V1XtwunbJO6VXsIF78RLe0kTP7IA/Rey86ydQVeqcw==", - "requires": { - "@aws-sdk/credential-provider-env": "3.46.0", - "@aws-sdk/credential-provider-imds": "3.46.0", - "@aws-sdk/credential-provider-sso": "3.46.0", - "@aws-sdk/credential-provider-web-identity": "3.46.0", - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-credentials": "3.46.0", - "tslib": "^2.3.0" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/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/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/shared-ini-file-loader": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.46.0.tgz", - "integrity": "sha512-Rp7Z1X23kvyRCziOxXu2PYCCPT5CQ5t8O4WoKrEkMT9Vqm2gluXOcCnL4iOpRkSRGEZT7lfe5OCM8ApNRTIHpQ==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==" - }, - "@aws-sdk/util-credentials": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-credentials/-/util-credentials-3.46.0.tgz", - "integrity": "sha512-d5bDyCDVYi6ThBY8AntAKooExayFuLUnCXsDkmmWpHlp26JZv9s1/DsXR219ELgu8jIAWiID54HjfEYf8qa6Vw==", - "requires": { - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "tslib": "^2.3.0" - } - } + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-sdk/credential-provider-process": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.37.0.tgz", - "integrity": "sha512-VOfWtUBbICb7xEHRFN7+fRA+move/3HT4mZt7C5KBXIaILT3b8hrK1mT/fRQ3dx9dF56PEGj/WkACOBjMzIcdg==", - "requires": { - "@aws-sdk/property-provider": "3.37.0", - "@aws-sdk/shared-ini-file-loader": "3.37.0", - "@aws-sdk/types": "3.37.0", - "@aws-sdk/util-credentials": "3.37.0", - "tslib": "^2.3.0" + "node_modules/@aws-sdk/middleware-sdk-s3-control/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" } }, - "@aws-sdk/credential-provider-sso": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.46.0.tgz", - "integrity": "sha512-y3zv6FtaEu1cjtun6vQ1S/2aya70cPjCcoQhSrsH9TDYXp/ZRk4PN6xdVGGpkZX2kZhowGU5DvhOGK48IqrNZg==", - "requires": { - "@aws-sdk/client-sso": "3.46.0", - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-credentials": "3.46.0", - "tslib": "^2.3.0" - }, + "node_modules/@aws-sdk/middleware-sdk-s3-control/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": { - "@aws-sdk/abort-controller": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.46.0.tgz", - "integrity": "sha512-XrCocRwajh3jkI/Y2PCZjYUZcJfmCa4DYM5nnW2+w4o7ez7vXEQ1j5FCI+/ogJIqfccnmEIlLZGlfzmc6vVbJw==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/client-sso": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.46.0.tgz", - "integrity": "sha512-n+dHT9azUW4pFA7a98gV/qFYdNwoMQ/4Y4tvPE28s9CKx8O0OIDlOwLPrhSBETCuRJNfnug1vNnFIzvOapfCkg==", - "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.46.0", - "@aws-sdk/fetch-http-handler": "3.46.0", - "@aws-sdk/hash-node": "3.46.0", - "@aws-sdk/invalid-dependency": "3.46.0", - "@aws-sdk/middleware-content-length": "3.46.0", - "@aws-sdk/middleware-host-header": "3.46.0", - "@aws-sdk/middleware-logger": "3.46.0", - "@aws-sdk/middleware-retry": "3.46.0", - "@aws-sdk/middleware-serde": "3.46.0", - "@aws-sdk/middleware-stack": "3.46.0", - "@aws-sdk/middleware-user-agent": "3.46.0", - "@aws-sdk/node-config-provider": "3.46.0", - "@aws-sdk/node-http-handler": "3.46.0", - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/smithy-client": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/url-parser": "3.46.0", - "@aws-sdk/util-base64-browser": "3.46.0", - "@aws-sdk/util-base64-node": "3.46.0", - "@aws-sdk/util-body-length-browser": "3.46.0", - "@aws-sdk/util-body-length-node": "3.46.0", - "@aws-sdk/util-user-agent-browser": "3.46.0", - "@aws-sdk/util-user-agent-node": "3.46.0", - "@aws-sdk/util-utf8-browser": "3.46.0", - "@aws-sdk/util-utf8-node": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/config-resolver": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.46.0.tgz", - "integrity": "sha512-R7YGDhvVE1VIS7uyjG3rZE1nrRu/+YVBq/pPlq5f4Tis3EoUooPfr5yYOVAuZI1CGsgycbCi6jnaqLGIfxUFmQ==", - "requires": { - "@aws-sdk/signature-v4": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-config-provider": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/fetch-http-handler": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.46.0.tgz", - "integrity": "sha512-uOfdwbUCG+2LQ4iMkxD/izlzjnIrB5P5HtH7L5w1EFIsdxDXeFnnql0FaEcOvaEEg2rs9z0t+oLwMJZnNNtqAg==", - "requires": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/querystring-builder": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-base64-browser": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/hash-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.46.0.tgz", - "integrity": "sha512-rABF9k5uSJdqmwBeYnu+2+iWEmPNVsoBy9bwLvEmGfh557wAwh3dL5IDf+NiIFrd8GTOF/2HV1477XXBl15C0g==", - "requires": { - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-buffer-from": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/invalid-dependency": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.46.0.tgz", - "integrity": "sha512-KumWtDstAKpKRQbHA95AeHpBNtxCXHVbUk+nFAiXcBP281yEalUbyK0W5Q2bDl26L3z6zHodg3OJllHYavJKMg==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/is-array-buffer": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.46.0.tgz", - "integrity": "sha512-HcQtJgZDQgo7ivD79GF82pTf+zYGjsgzKG7lkUBEetSfkV0W8h6XfhN6DmuYQuCcu1Pt9IkN7haYNPiPdfDhvg==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/middleware-content-length": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.46.0.tgz", - "integrity": "sha512-Vf17vKAZ9n2ZlkMoHmCXMHAJegw3djC8qxe2sGdHSGyozfJNpA77ec32ldLvBtQ82LPmSaqdhcbP0/oYCalnzA==", - "requires": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.46.0.tgz", - "integrity": "sha512-I+WsUzpyzS9l5Dt64kyp7v+9KeYPOCviYmVw2kM1EZRdAeo+jiCRxU5LnDJ9ORxfRwGcEmQV7xb4UpqXcn2N6Q==", - "requires": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.46.0.tgz", - "integrity": "sha512-xkB98tfZc1pSeks0a7jagYIVHxJfoxHX7wcASzBa3IjyodZpSqDW392edF9c3kSCv6G6PGRbG+F1F6j7ZTVpRQ==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/middleware-retry": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.46.0.tgz", - "integrity": "sha512-YkNNs2dUcriLwy4pYG7nfa850tD8dtFUeE/IQ+YBMbWDedT31UFkCfHUdjBK1GFbIv+G1N+ZVGBCkWq1OuhKXw==", - "requires": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/service-error-classification": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0", - "uuid": "^8.3.2" - } - }, - "@aws-sdk/middleware-serde": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.46.0.tgz", - "integrity": "sha512-5M56VUm/stsSabauHxFrv1BSoH0VPyMB1V4vewAD3cp5YGiUpChYxjhcBbzi0QvI65HLxa6nLedwrE+g1uVJ1Q==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/middleware-stack": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.46.0.tgz", - "integrity": "sha512-+3SmpYo12i9Gn7L/HEJqAv5+OieZL9zfXungFKr96rTpcvYDZWQblTP3tugBtvGV6V4tzvebMkUTWxBB6p+dhQ==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.46.0.tgz", - "integrity": "sha512-n9VEWlIXxbJXCr2IpocNJQUW7dhCdAcPKmxV0T5LZ/AygKsLvbWy40u2Qm9/eB1MYpqiheeb5MsY3UXxHgnOlg==", - "requires": { - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/node-config-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.46.0.tgz", - "integrity": "sha512-Hzz860d1GNZSSX74TywfUu125l8BT6JkJuKG0QDhuC+9xklNfC1hgziihldHu6xL7DzY5UKgjyzdNBQfqCqLbw==", - "requires": { - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/node-http-handler": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.46.0.tgz", - "integrity": "sha512-SqeKskt47u/ZIeN5b6lmjOAx0yLiY/WDQ6N9Z6LRJCYiSZ7oHflA1jPWkX20qWOKioa2iHBVTNNX2lu8yFkWbg==", - "requires": { - "@aws-sdk/abort-controller": "3.46.0", - "@aws-sdk/protocol-http": "3.46.0", - "@aws-sdk/querystring-builder": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/protocol-http": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.46.0.tgz", - "integrity": "sha512-hKqHYEp/JDfOl5kZKp0CRgvbsg+c52Ss4KwuRoU9vA56VZ5TpfgHznajdme97xedsE40hnZeitv2BKEMbkYCqg==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/querystring-builder": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.46.0.tgz", - "integrity": "sha512-YYGRK291ro+KR3TX0jjyGRdMGHn6D2CBD89oXj8tAV3djeMIpFSGDrEL+NKeJvp7aBNlEnQ9kSfzyQuzQVvJWA==", - "requires": { - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-uri-escape": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/querystring-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.46.0.tgz", - "integrity": "sha512-xxTnIXLbx4Jq16Utza7wh4HpPfVyCL0c+6NU2t+kXZ2sgOWhx2XAhShcZVbEkA/61UAMEIhyNBVE+z9OFz6X5g==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/service-error-classification": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.46.0.tgz", - "integrity": "sha512-hFDh/qtvKX9xUPxjGiDvumKsoO/3+eL4hi6X3qWN8lHg49wixjwcwlCEPn9jhdFJ9TRXc20CgPxWv4+V96Yf/A==" - }, - "@aws-sdk/shared-ini-file-loader": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.46.0.tgz", - "integrity": "sha512-Rp7Z1X23kvyRCziOxXu2PYCCPT5CQ5t8O4WoKrEkMT9Vqm2gluXOcCnL4iOpRkSRGEZT7lfe5OCM8ApNRTIHpQ==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/signature-v4": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.46.0.tgz", - "integrity": "sha512-qtI1t0CrEhVCxaezmmBpMe1WmQxdxho8oPiMEKWIUkkXQFg78Eg3jnXlLhjL4+MGHMqBB3mV7nGO6k8qu8H9rA==", - "requires": { - "@aws-sdk/is-array-buffer": "3.46.0", - "@aws-sdk/types": "3.46.0", - "@aws-sdk/util-hex-encoding": "3.46.0", - "@aws-sdk/util-uri-escape": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/smithy-client": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.46.0.tgz", - "integrity": "sha512-Dzx4CR+rOkr5hXbLhnOfnrPWmSs4O9BTjFWD+4oh+RTXq0It8g+fWZxPcdvRCDU4GjS9Gtbkw0f0pN3FMCEszQ==", - "requires": { - "@aws-sdk/middleware-stack": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==" - }, - "@aws-sdk/url-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.46.0.tgz", - "integrity": "sha512-foMB0AC3QDy+KfvRxsMXvJQZXr9CMzdupcNIXwKRZog82tEEc09dVeUjuJrO4H+A2eK84SyawRfy+ow+LRqvqw==", - "requires": { - "@aws-sdk/querystring-parser": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-base64-browser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.46.0.tgz", - "integrity": "sha512-oDlExDHYVOXsHFwFCA+CxZlGiHWeO53l0xoohpTIwGV6u48jED/4GrNM6iWVT6Vwd4skqtRMM41IHXjtiCtp/g==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-base64-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.46.0.tgz", - "integrity": "sha512-/ruNBm21Ptk+IGhwTphs8j5oDCjNIrUSipDoRtUuMGQR9TnNzup0e+sJDqP0BrKKM+tcvqEUhz+MScxbwJrwmg==", - "requires": { - "@aws-sdk/util-buffer-from": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-body-length-browser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.46.0.tgz", - "integrity": "sha512-OJgMlBv4gEdmHCdZO9htysz9GMw0mS7qB3I5CbZ2aBOM0NvmaU7nqI6zYCoEmGh0keq0CnMBlNZhBBAwtiKYqg==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-body-length-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.46.0.tgz", - "integrity": "sha512-jyD+2c7iaD4Aih93Fm4I183SbdhSy4FNmSlK49PctMVVF+QSpzQxAJvv/nTwq37Kb8orVvs+sgy2FF3lxfOUJg==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-buffer-from": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.46.0.tgz", - "integrity": "sha512-e3avbwAUULpPCk4ke9ctrhAwxcXvMv8FYymNJDEN7+9lqZ4XqAjPt+R+IEEFMEbWmIPeZ8TpLw3yuru1Z74iuA==", - "requires": { - "@aws-sdk/is-array-buffer": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-config-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.46.0.tgz", - "integrity": "sha512-KzzusGkvmb1uy3EItl+9YRxOOtjmU6iaAi9pBzHR2fiv13EMVNZrycVFPeGwz6LrsAEumKmTAZjR6c8BRbxtjw==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-credentials": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-credentials/-/util-credentials-3.46.0.tgz", - "integrity": "sha512-d5bDyCDVYi6ThBY8AntAKooExayFuLUnCXsDkmmWpHlp26JZv9s1/DsXR219ELgu8jIAWiID54HjfEYf8qa6Vw==", - "requires": { - "@aws-sdk/shared-ini-file-loader": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-hex-encoding": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.46.0.tgz", - "integrity": "sha512-A831jS32tbdjki4ihS0BIZ3HAi1gv2PtLmAjAW+PHVvBd0S4OpbQApKxKPu0w+NKsp9XQYfkEkeFKCcMqN1zhg==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-uri-escape": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.46.0.tgz", - "integrity": "sha512-drAHEt3YnI6H6NpiTFLFT8e75bOhaO94ZP+kqz/0hluQiKX47Pow3Ar3Diaf/CUMLctH0IX3AaN3T2ve5v19lQ==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.46.0.tgz", - "integrity": "sha512-wwUh4H6+ur9akctoSgaz41J8JuRrOqey4aY68DmDQ0did3UjhRlbPD3xu0umXoPSgmtqQyl34oMPqCOfA70Z0Q==", - "requires": { - "@aws-sdk/types": "3.46.0", - "bowser": "^2.11.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.46.0.tgz", - "integrity": "sha512-JCY8mKWPic0aXtz7amKXWyjbX8fhdOkRcgsCCnevOHc/7KOxwa97VnDT555GNQ76LO+cEDgYueHklUayV3u+IA==", - "requires": { - "@aws-sdk/node-config-provider": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-utf8-browser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.46.0.tgz", - "integrity": "sha512-zafI5Y7hRVC0vhJ77FPUyBckmpF2v2ZEKFC79AdwdFX11l7XNmq0hY/4CWVYeZ2L0Fyk0UV6eeKyk/TNdce0mg==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@aws-sdk/util-utf8-node": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.46.0.tgz", - "integrity": "sha512-Uk9hQrWQowU4ymtSxrxiIp7GnBoZfkKGSeWDy2h/1Biaexq9FQclbgwa0ZhA5lKLDj/nUxnXoT/ZcY90mTdzzQ==", - "requires": { - "@aws-sdk/util-buffer-from": "3.46.0", - "tslib": "^2.3.0" - } - } + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.46.0.tgz", - "integrity": "sha512-VUNTS9HjwLRmS2OQ+i4tqVJBUpk/DjIT0sWUDnKBcC6UCyGOkVmBVisCvUHpwyCLCgYbCvTab1SfrJ8dZsN83w==", - "requires": { - "@aws-sdk/property-provider": "3.46.0", - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/property-provider": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.46.0.tgz", - "integrity": "sha512-e3Jcds7G1Hg5VDvwLox0HlQq4G2fvmkO1BRPvM8WfRGvxRNK40dqoelm2NMtbNK0KgFPIpKsGeX1UhZDt9Od9w==", - "requires": { - "@aws-sdk/types": "3.46.0", - "tslib": "^2.3.0" - } - }, - "@aws-sdk/types": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.46.0.tgz", - "integrity": "sha512-yhrkVVyv4RUt3KqDDyEayjBM5dRBtuS486THeqtSghUYNV7M/cW18TA3gdMC0pRGgUqfKrOysdBZjCyPrYNvuA==" - } + "@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" } }, - "@aws-sdk/fetch-http-handler": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.178.0.tgz", - "integrity": "sha512-T/LCNwCihdVNzGn39Dw7tk2U1fMlupFlCsAvDBbO+FOM3h+y9WLHzxmlAVsjPrFXlzdONKf9zd5cuQ+ZW93yAQ==", - "requires": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/querystring-builder": "3.178.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-base64-browser": "3.170.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@aws-sdk/hash-node": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.178.0.tgz", - "integrity": "sha512-mqYraRQlvPO5egUKTNZ1kP52sfwBlsz7woOewQTHOGomZBDXrh8bl1J+sgaDi1NAwXdZUgxuD3QKxxAKRs9a2Q==", - "requires": { - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-buffer-from": "3.170.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@aws-sdk/invalid-dependency": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.178.0.tgz", - "integrity": "sha512-JJNaiLr3nbRYym6oUAAaoFFYtDnIZ9Scco2p4sG/thT2eyAfXcEdNl1cSD3E/R1J+Ml/YplqTiIY4u1KPAriRw==", - "requires": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@aws-sdk/is-array-buffer": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.170.0.tgz", - "integrity": "sha512-yYXqgp8rilBckIvNRs22yAXHKcXb86/g+F+hsTZl38OJintTsLQB//O5v6EQTYhSW7T3wMe1NHDrjZ+hFjAy4Q==", - "requires": { - "tslib": "^2.3.1" + "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" } }, - "@aws-sdk/middleware-content-length": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.178.0.tgz", - "integrity": "sha512-p3n3IzU03eRzZivEoQn1HA83LbAKukZwRevsJpya1UfCUtWkXQO3v0jU8rhZE4deGa9k7zuCAEmJ8nCw3QxclQ==", - "requires": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/middleware-host-header": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.178.0.tgz", - "integrity": "sha512-EFc9S63iwCmudVpVSiVPiTnp6WCfsRYUmTrZJJouZzthEhJwcrunwu7Fa9lHYb0zcWLgVFLhzs1Z34J/Er4JoQ==", - "requires": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/middleware-logger": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.178.0.tgz", - "integrity": "sha512-k4jnB+ryGiAhv6vyNFz2YoaVodldjkbz4mqDlVzhwEn77LT/TcwdBoown3cJD/45LEtiuPqeONoTcNCsuCkRFQ==", - "requires": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@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" } }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.178.0.tgz", - "integrity": "sha512-dVgSoP2Mer8A0JGaWgpC/f4vPyvHh7laES/u5sTy6RfwrR87oTx+uhKrc6eh+9NkMR2xdRyaNJAMIXwL5bsVzg==", - "requires": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" } }, - "@aws-sdk/middleware-retry": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.178.0.tgz", - "integrity": "sha512-glBXpAqt+4KQ7q8y2/kwDX2ujCvCSQok5rlAmUjaQjVPc3cX77QwATIRQTS2nBC4v9tfMc7yL64ZeRbx6n0RAQ==", - "requires": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/service-error-classification": "3.178.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-middleware": "3.178.0", - "tslib": "^2.3.1", - "uuid": "^8.3.2" - }, + "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": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" } }, - "@aws-sdk/middleware-serde": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.178.0.tgz", - "integrity": "sha512-TERiu/B4hYi5Jd4iQN9ECTWbt2IZweAgFB010MboM4CAPm6EcszEc/uCB4faLZNdJaksk1BhAR7koURcda8Sew==", - "requires": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/middleware-stack": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.178.0.tgz", - "integrity": "sha512-ELYM5Imhlcz2zT1Z4OjVZwO564KvI4L9dMBxuUgO0fwommzjWqxR03yaRGhpGwpCP64d0Op5Koc/RKq5V92Wbw==", - "requires": { - "tslib": "^2.3.1" + "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" } }, - "@aws-sdk/middleware-user-agent": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.178.0.tgz", - "integrity": "sha512-xkKBxrFbs+UwUPpfIGEPuHeBWS2Jgmcd+ipEJUQRR3lY4h1fJ6mPGeyyaVDvwaJp9KgESSI6QTp6V15l8GXXgQ==", - "requires": { - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/node-config-provider": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.178.0.tgz", - "integrity": "sha512-yb5XJcC7SxkZ5oxu3zQ/foBdMkLBKryzx/CVg5BNSsKDjfbouf/ZYPcJDHhc2gzCtZcx18GjFBOnv8cpo/tyXQ==", - "requires": { - "@aws-sdk/property-provider": "3.178.0", - "@aws-sdk/shared-ini-file-loader": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/property-provider": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.178.0.tgz", - "integrity": "sha512-+Fh1aUANa+Gt/rh4SUHO0yHwKsibyZGk2LLDUcM1+9r0pUZT0qy3h0UCl5Kkj9HUcDJMD73wHTx4UB440xRobw==", - "requires": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/shared-ini-file-loader": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.178.0.tgz", - "integrity": "sha512-nZGmuhGLDFbXsb7QYDg7PiPMAmsdlSshKJ+AhKSZF/J0SK94kdZgGnGXGUZe52S3G41E3CZIgnLnnsMXq0uErA==", - "requires": { - "tslib": "^2.3.1" - } - }, - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/node-http-handler": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.178.0.tgz", - "integrity": "sha512-EtH6YiX1IX0QraQ/+kKBWAEtsFYBnFyxOimTBtlpDYwFpgDzIZ1GFn2wORYomEWALg10kphs8n3E5/7b5t5OWQ==", - "requires": { - "@aws-sdk/abort-controller": "3.178.0", - "@aws-sdk/protocol-http": "3.178.0", - "@aws-sdk/querystring-builder": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/property-provider": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.37.0.tgz", - "integrity": "sha512-puXV4MIj+n9Pr4KbwpOz6+nK7gmJAgAOZW/yKXxyWH4fTcrCVe9xuo5kqaiI1gb5ojaNt2GuISBFR7bVLumh9Q==", - "requires": { - "@aws-sdk/types": "3.37.0", - "tslib": "^2.3.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" } }, - "@aws-sdk/protocol-http": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.178.0.tgz", - "integrity": "sha512-GsnANW60mVYMlE16UGNSOwYZ6TbkoODvmDQi95SEPjM7asf4vihEyDvhxiGS/JvC18UyxRVWT89l/V3hR/SF7w==", - "requires": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/querystring-builder": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.178.0.tgz", - "integrity": "sha512-vJXlExSshlHtGVvan/U6JihWvzf8t9QwH5I4F6HUY+exxMy5vFDYCnNqGAzbJwq7w/HME1gQWLoXq2k0uODz7g==", - "requires": { - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-uri-escape": "3.170.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/is-array-buffer": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/querystring-parser": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.178.0.tgz", - "integrity": "sha512-dp3pLnsOvAcIF7Yn2PY5CIVWX7GvC33nSlWDYeLeCMapccwTbe6zBqreWbScmIGJra4QJTdjccpwo2Yxwhr5QQ==", - "requires": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/service-error-classification": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.178.0.tgz", - "integrity": "sha512-tDKTBXxck2N4bhAnQaeokx9ps38V3G70lcDdHS/N9hmqcQQmH5x+1/AMwYWLjUZmOQPBW9sFoG4B3psnl+sefw==" - }, - "@aws-sdk/shared-ini-file-loader": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.37.0.tgz", - "integrity": "sha512-+vRBSlfa48R9KL7DpQt3dsu5/+5atjRgoCISblWo3SLpjrx41pKcjKneo7a1u0aP1Xc2oG2TfIyqTWZuOXsmEQ==", - "requires": { - "tslib": "^2.3.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" } }, - "@aws-sdk/signature-v4": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.178.0.tgz", - "integrity": "sha512-8oOx6o0uOqlCDPM0dszfR1WHqd0E1VuFqez8iNItp0DhmhaCuanEwKYYA6HOkVu/MA6CsG6zDIJaFr5ODU2NvQ==", - "requires": { - "@aws-sdk/is-array-buffer": "3.170.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/util-hex-encoding": "3.170.0", - "@aws-sdk/util-middleware": "3.178.0", - "@aws-sdk/util-uri-escape": "3.170.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/smithy-client": { - "version": "3.180.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.180.0.tgz", - "integrity": "sha512-1vWafiUdn6RvOsD4CyNjMeDtDujuPi4Iq4Db6HrFmVPpJAutOLlCg52Dt7k96KCcIKgxVAs6Br0Waef+pcoGNA==", - "requires": { - "@aws-sdk/middleware-stack": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "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": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/types": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.37.0.tgz", - "integrity": "sha512-KwHB06E1uxof5ijfcQXYidyihoCRMnHEFvWCy/VlL+1S54FTlMZ27JOZzQhLiw8NqeNfO33aqpMkxR60TwUZzg==" - }, - "@aws-sdk/url-parser": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.178.0.tgz", - "integrity": "sha512-+Ch29d+IZG6zD1gNDVgFC00huY8ytrPdijAuNJ4DtPBTGP4zbrImw3js0GfvfBjLrQYBnclcAvSx4J1Q/8tqBQ==", - "requires": { - "@aws-sdk/querystring-parser": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@aws-sdk/util-arn-parser": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.46.0.tgz", - "integrity": "sha512-MIeqH53/Z5x+uQ/EHs6zqkdBqdpGA8tA6l/mL1j8hRK6bSQEUTAVlPNgjp03qse1Fk94GJbJbjZ4C0R7Z4YM+g==", - "requires": { - "tslib": "^2.3.0" + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.342.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@aws-sdk/util-base64-browser": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.170.0.tgz", - "integrity": "sha512-uLP9Kp74+jc+UWI392LSWIaUj9eXZBhkAiSm8dXAyrr+5GFOKvmEdidFoZKKcFcZ2v3RMonDgFVcDBiZ33w7BQ==", - "requires": { - "tslib": "^2.3.1" + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@aws-sdk/util-base64-node": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.170.0.tgz", - "integrity": "sha512-sjpOmfyW0RWCLXU8Du0ZtwgFoxIuKQIyVygXJ4qxByoa3jIUJXf4U33uSRMy47V3JoogdZuKSpND9hiNk2wU4w==", - "requires": { - "@aws-sdk/util-buffer-from": "3.170.0", - "tslib": "^2.3.1" + "node_modules/@aws-sdk/middleware-user-agent/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" } }, - "@aws-sdk/util-body-length-browser": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.170.0.tgz", - "integrity": "sha512-SqSWA++gsZgHw6tlcEXx9K6R6cVKNYzOq6bca+NR7jXvy1hfqiv9Gx5TZrG4oL4JziP8QA0fTklmI1uQJ4HBRA==", - "requires": { - "tslib": "^2.3.1" + "node_modules/@aws-sdk/nested-clients": { + "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/nested-clients/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" } }, - "@aws-sdk/util-body-length-node": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.170.0.tgz", - "integrity": "sha512-sFb85ngsgfpamwDn22LC/+FkbDTNiddbMHptkajw+CAD2Rb4SJDp2PfXZ6k883BueJWhmxZ9+lApHZqYtgPdzw==", - "requires": { - "tslib": "^2.3.1" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@aws-sdk/util-buffer-from": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.170.0.tgz", - "integrity": "sha512-3ClE3wgN/Zw0ahfVAY5KQ/y3K2c+SYHwVUQaGSuVQlPOCDInGYjE/XEFwCeGJzncRPHIKDRPEsHCpm1uwgwEqQ==", - "requires": { - "@aws-sdk/is-array-buffer": "3.170.0", - "tslib": "^2.3.1" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@aws-sdk/util-config-provider": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.170.0.tgz", - "integrity": "sha512-VV6lfss6Go00TF2hRVJnN8Uf2FOwC++1e8glaeU7fMWluYCBjwl+116mPOPFaxvkJCg0dui2tFroXioslM/rvQ==", - "requires": { - "tslib": "^2.3.1" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@aws-sdk/util-credentials": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-credentials/-/util-credentials-3.37.0.tgz", - "integrity": "sha512-zcLhSZDKgBLhUjSU5HoQpuQiP3v8oE86NmV/tiZVPEaO6YVULEAB2Cfj1hpM/b/JXWzjSHfT06KXT7QUODKS+A==", - "requires": { - "@aws-sdk/shared-ini-file-loader": "3.37.0", - "tslib": "^2.3.0" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@aws-sdk/util-defaults-mode-browser": { - "version": "3.180.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.180.0.tgz", - "integrity": "sha512-XGq/RUuhqnLlApETBmBWDszNG4TR3FCOSNwFvok1UIPgFXxjh0JOzNc5mAX1St2hfx1IDb9Ja4BmTkYKix7byg==", - "requires": { - "@aws-sdk/property-provider": "3.178.0", - "@aws-sdk/types": "3.178.0", - "bowser": "^2.11.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/property-provider": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.178.0.tgz", - "integrity": "sha512-+Fh1aUANa+Gt/rh4SUHO0yHwKsibyZGk2LLDUcM1+9r0pUZT0qy3h0UCl5Kkj9HUcDJMD73wHTx4UB440xRobw==", - "requires": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@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" } }, - "@aws-sdk/util-defaults-mode-node": { - "version": "3.180.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.180.0.tgz", - "integrity": "sha512-ch9VR4OKYGEaNbLhX/EyZDpNZst5T+/VYsFyqMA47J0YyMbg7GWyn2FjjhZ7qOV3XU6W8YEBNdd7U/LFFVp8uA==", - "requires": { - "@aws-sdk/config-resolver": "3.178.0", - "@aws-sdk/credential-provider-imds": "3.178.0", - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/property-provider": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/credential-provider-imds": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.178.0.tgz", - "integrity": "sha512-ZvqQTi3+S13LACVgaWNCOKBv5jROIz7rqyZh56QunAkaAUqPbpM4VFODgAGZYPCOSggZbEUUqXOVB9xSnshLnA==", - "requires": { - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/property-provider": "3.178.0", - "@aws-sdk/types": "3.178.0", - "@aws-sdk/url-parser": "3.178.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/property-provider": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.178.0.tgz", - "integrity": "sha512-+Fh1aUANa+Gt/rh4SUHO0yHwKsibyZGk2LLDUcM1+9r0pUZT0qy3h0UCl5Kkj9HUcDJMD73wHTx4UB440xRobw==", - "requires": { - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - } - }, - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@aws-sdk/util-hex-encoding": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.170.0.tgz", - "integrity": "sha512-BDYyMqaxX4/N7rYOIYlqgpZaBuHw3kNXKgOkWtJdzndIZbQX8HnyJ+rF0Pr1aVsOpVDM+fY1prERleFh/ZRTCg==", - "requires": { - "tslib": "^2.3.1" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@aws-sdk/util-locate-window": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.37.0.tgz", - "integrity": "sha512-NvDCfOhLLVHp27oGUUs8EVirhz91aX5gdxGS7J/sh5PF0cNN8rwaR1vSLR7BxPmJHMO7NH7i9EwiELfLfYcq6g==", - "requires": { - "tslib": "^2.3.0" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@aws-sdk/util-middleware": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.178.0.tgz", - "integrity": "sha512-93WgrJKuwtv3f2r1Q04emzjMiwpYR5hysOHKMkrGOvAVZdDqe1UTjmtuxQadVi3DBr1KOT/d5uP9MjV8LqaUUA==", - "requires": { - "tslib": "^2.3.1" + "node_modules/@aws-sdk/nested-clients/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 + } } }, - "@aws-sdk/util-uri-escape": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.170.0.tgz", - "integrity": "sha512-Fof0urZ3Lx6z6LNKSEO6T4DNaNh6sLJaSWFaC6gtVDPux/C3R7wy2RQRDp0baHxE8m1KMB0XnKzHizJNrbDI1w==", - "requires": { - "tslib": "^2.3.1" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.178.0.tgz", - "integrity": "sha512-LxOrn7Ai88n0i5J5rTb5Bt0TAycPvDYzjdCwmd2mahsPHZGSDLeCeh6KOIxZsEfnzYRl4HGWvIEXdHIYZ3RTug==", - "requires": { - "@aws-sdk/types": "3.178.0", - "bowser": "^2.11.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@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" } }, - "@aws-sdk/util-user-agent-node": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.178.0.tgz", - "integrity": "sha512-TrP6v+V4Qnv3E9CNgwR/G+1xiy8fa9j5LAm43qwp9PfJHchNyWOJ0FURD3Ne2sm/388Ybzjb1DRYRZ7B+xbnOw==", - "requires": { - "@aws-sdk/node-config-provider": "3.178.0", - "@aws-sdk/types": "3.178.0", - "tslib": "^2.3.1" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/types": { - "version": "3.178.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.178.0.tgz", - "integrity": "sha512-CrHxHzXSEr/Z3NLFvJgSGHGcD9tYUZ0Rhp8tFCSpD3TpBo3/Y7RIvqaEPvECsL52UEloeBhQf65AO8590YkVmQ==" - } + "@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" } }, - "@aws-sdk/util-utf8-browser": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.170.0.tgz", - "integrity": "sha512-tJby9krepSwDsBK+KQF5ACacZQ4LH1Aheh5Dy0pghxsN/9IRw7kMWTumuRCnSntLFFphDD7GM494/Dvnl1UCLA==", - "requires": { - "tslib": "^2.3.1" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@aws-sdk/util-utf8-node": { - "version": "3.170.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.170.0.tgz", - "integrity": "sha512-52QWGNoNQoyT2CuoQz6LjBKxHQtN/ceMFLW+9J1E0I1ni8XTuTYP52BlMe5484KkmZKsHOm+EWe4xuwwVetTxg==", - "requires": { - "@aws-sdk/util-buffer-from": "3.170.0", - "tslib": "^2.3.1" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@aws-toolkits/telemetry": { - "version": "1.0.87", - "resolved": "https://registry.npmjs.org/@aws-toolkits/telemetry/-/telemetry-1.0.87.tgz", - "integrity": "sha512-rN0s9YhU+SEIVTEojwuVm+Be3CXsTdIsg75PijVM9LdPqwNxzcSY2X/yU4K5R4QNMU160g4/qQ00BI9SdTJxKw==", - "dev": true, - "requires": { - "ajv": "^6.12.6", - "fs-extra": "^11.1.0", - "lodash": "^4.17.20", - "prettier": "^2.1.2", - "ts-morph": "^17.0.1", - "typescript": "^4.7.3", - "yargs": "^17.0.1" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - } + "@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" } }, - "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.7" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", - "dev": true - }, - "@babel/core": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz", - "integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.7", - "@babel/parser": "^7.17.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "@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" } }, - "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-stack": { + "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" } }, - "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" + "node_modules/@aws-sdk/nested-clients/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" + }, + "engines": { + "node": ">=18.0.0" } }, - "@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true - }, - "@babel/helpers": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz", - "integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" } }, - "@babel/parser": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz", - "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==" - }, - "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" + "node_modules/@aws-sdk/nested-clients/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" } }, - "@bcherny/json-schema-ref-parser": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@bcherny/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz", - "integrity": "sha512-vmEmnJCfpkLdas++9OYg6riIezTYqTHpqUTODJzHLzs5UnXujbOJW9VwcVCnyo1mVRt32FRr23iXBx/sX8YbeQ==", - "dev": true, - "requires": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.6", - "call-me-maybe": "^1.0.1", - "js-yaml": "^4.1.0" + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" - }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" + "node_modules/@aws-sdk/nested-clients/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/nested-clients/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@discoveryjs/json-ext": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz", - "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", - "dev": true + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@esbuild/android-arm": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz", - "integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==", - "dev": true, - "optional": true + "node_modules/@aws-sdk/nested-clients/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" + } }, - "@esbuild/linux-loong64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz", - "integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==", - "dev": true, - "optional": true + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@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" } }, - "@humanwhocodes/config-array": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz", - "integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "node_modules/@aws-sdk/nested-clients/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": ">=18.0.0" } }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-middleware": { + "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" + } }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@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/nested-clients/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "@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" } }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true + "node_modules/@aws-sdk/property-provider": { + "version": "3.46.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.46.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true + "node_modules/@aws-sdk/property-provider/node_modules/@aws-sdk/types": { + "version": "3.46.0", + "license": "Apache-2.0", + "engines": { + "node": ">= 12.0.0" + } }, - "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "node_modules/@aws-sdk/protocol-http": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true + "node_modules/@aws-sdk/protocol-http/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "node_modules/@aws-sdk/protocol-http/node_modules/@smithy/types": { + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", - "dev": true + "node_modules/@aws-sdk/region-config-resolver/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" + } }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-format-url": "3.693.0", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@sindresorhus/is": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", - "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==" - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" + "node_modules/@aws-sdk/smithy-client": { + "version": "3.342.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-stack": "3.342.0", + "@aws-sdk/types": "3.342.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" + "node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types": { + "version": "3.342.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" } }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "requires": { - "defer-to-connect": "^2.0.0" + "node_modules/@aws-sdk/token-providers/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" } }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "@ts-morph/common": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.18.0.tgz", - "integrity": "sha512-UvWF+oQdMa4qMWhXTOd2JWtAbAJGgkPMNzGHgEcfOwQRIcViKdwsSqXXjSaQCZ4fo+bZMhdfuwQCjlW5bNcqEQ==", - "dev": true, - "requires": { - "fast-glob": "^3.2.12", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "path-browserify": "^1.0.1" - }, + "node_modules/@aws-sdk/types": { + "version": "3.692.0", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", - "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "@types/adm-zip": { - "version": "0.4.34", - "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz", - "integrity": "sha512-8ToYLLAYhkRfcmmljrKi22gT2pqu7hGMDtORP1emwIEGmgUTZOsaDjzWFzW5N2frcFRz/50CWt4zA1CxJ73pmQ==", - "dev": true, - "requires": { - "@types/node": "*" + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@types/async-lock": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@types/async-lock/-/async-lock-1.1.3.tgz", - "integrity": "sha512-UpeDcjGKsYEQMeqEbfESm8OWJI305I7b9KE4ji3aBjoKWyN5CTdn8izcA1FM1DVDne30R5fNEnIy89vZw5LXJQ==", - "dev": true + "node_modules/@aws-sdk/util-endpoints/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" + } }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" + "node_modules/@aws-sdk/util-format-url": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/querystring-builder": "^3.0.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dev": true, - "requires": { - "@types/node": "*" + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.37.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "@types/bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-lOGyCnw+2JVPKU3wIV0srU0NyALwTBJlVSx5DfMQOFuuohA8y9S8orImpuIQikZ0uIQ8gehrRjxgQC1rLRi11w==", - "dev": true + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } }, - "@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" + "node_modules/@aws-sdk/util-user-agent-browser/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" } }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "requires": { - "@types/node": "*" + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", - "dev": true, - "requires": { - "@types/express-serve-static-core": "*", - "@types/node": "*" + "node_modules/@aws-sdk/util-user-agent-node/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" } }, - "@types/cross-spawn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.2.tgz", - "integrity": "sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==", - "dev": true, - "requires": { - "@types/node": "*" + "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" } }, - "@types/eslint": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", - "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" + "node_modules/@aws-sdk/xml-builder": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "@types/eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", + "node_modules/@aws-toolkits/telemetry": { + "version": "1.0.329", + "resolved": "https://registry.npmjs.org/@aws-toolkits/telemetry/-/telemetry-1.0.329.tgz", + "integrity": "sha512-zMkljZDtIAxuZzPTLL5zIxn+zGmk767sbqGIc2ZYuv0sSU+UoYgB3tqwV5KVV2oDPKs5593nwJC97NVHJqzowQ==", "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" + "license": "Apache-2.0", + "dependencies": { + "ajv": "^6.12.6", + "cross-spawn": "^7.0.6", + "fs-extra": "^11.1.0", + "lodash": "^4.17.20", + "prettier": "^3.3.2", + "ts-morph": "^23.0.0", + "yargs": "^17.0.1" } }, - "@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true - }, - "@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "node_modules/@aws/chat-client": { + "version": "0.1.4", "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" + "license": "Apache-2.0", + "dependencies": { + "@aws/chat-client-ui-types": "^0.1.12", + "@aws/language-server-runtimes-types": "^0.1.10", + "@aws/mynah-ui": "^4.28.0" } }, - "@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "node_modules/@aws/chat-client-ui-types": { + "version": "0.1.47", + "resolved": "https://registry.npmjs.org/@aws/chat-client-ui-types/-/chat-client-ui-types-0.1.47.tgz", + "integrity": "sha512-Pu6UnAImpweLMcAmhNdw/NrajB25Ymzp1Om1V9NEVQJRMO/KJCDiErmbOYTYBXvgNoR10kObqiL1P/Tk/Fpu3g==", "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" + "license": "Apache-2.0", + "dependencies": { + "@aws/language-server-runtimes-types": "^0.1.41" } }, - "@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, - "requires": { - "@types/node": "*" + "node_modules/@aws/lambda-invoke-store": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz", + "integrity": "sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" } }, - "@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" + "node_modules/@aws/language-server-runtimes": { + "version": "0.2.128", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.128.tgz", + "integrity": "sha512-C666VAvY2PQ8CQkDzjL/+N9rfcFzY6vuGe733drMwwRVHt8On0B0PQPjy31ZjxHUUcjVp78Nb9vmSUEVBfxGTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws/language-server-runtimes-types": "^0.1.56", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api-logs": "^0.200.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.200.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.200.0", + "@opentelemetry/resources": "^2.0.1", + "@opentelemetry/sdk-logs": "^0.200.0", + "@opentelemetry/sdk-metrics": "^2.0.1", + "@smithy/node-http-handler": "^4.0.4", + "ajv": "^8.17.1", + "aws-sdk": "^2.1692.0", + "hpagent": "^1.2.0", + "jose": "^5.9.6", + "mac-ca": "^3.1.1", + "registry-js": "^1.16.1", + "rxjs": "^7.8.2", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-uri": "^3.1.0", + "win-ca": "^3.5.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/language-server-runtimes-types": { + "version": "0.1.56", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.56.tgz", + "integrity": "sha512-Md/L750JShCHUsCQUJva51Ofkn/GDBEX8PpZnWUIVqkpddDR00SLQS2smNf4UHtKNJ2fefsfks/Kqfuatjkjvg==", + "license": "Apache-2.0", + "dependencies": { + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5" } }, - "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + "node_modules/@aws/language-server-runtimes/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } }, - "@types/http-proxy": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", - "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", - "dev": true, - "requires": { - "@types/node": "*" + "node_modules/@aws/language-server-runtimes/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", - "dev": true + "node_modules/@aws/language-server-runtimes/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/abort-controller": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.2.tgz", + "integrity": "sha512-Sl/78VDtgqKxN2+1qduaVE140XF+Xg+TafkncspwM4jFP/LHr76ZHmIY/y3V1M0mMLNk+Je6IGbzxy23RSToMw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", - "requires": { - "@types/node": "*" + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/node-http-handler": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.4.tgz", + "integrity": "sha512-/mdqabuAT3o/ihBGjL94PUbTSPSRJ0eeVTdgADzow0wRJ0rN4A27EOrtlK56MYiO1fDvlO3jVTCxQtQmK9dZ1g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@types/lodash": { - "version": "4.14.182", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", - "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", - "dev": true + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/protocol-http": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.0.tgz", + "integrity": "sha512-KxAOL1nUNw2JTYrtviRRjEnykIDhxc84qMBzxvu1MUfQfHTuBlCG7PA6EdVwqpJjH7glw7FqQoFxUJSyBQgu7g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@types/marked": { + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/querystring-builder": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.2.tgz", - "integrity": "sha512-auNrZ/c0w6wsM9DccwVxWHssrMDezHUAXNesdp2RQrCVCyrQbOiSq7yqdJKrUQQpw9VTm7CGYJH2A/YG7jjrjQ==", - "dev": true - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true - }, - "@types/mime-types": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz", - "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==", - "dev": true - }, - "@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "@types/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", - "dev": true + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.2.tgz", + "integrity": "sha512-NTOs0FwHw1vimmQM4ebh+wFQvOwkEf/kQL6bSM1Lock+Bv4I89B3hGYoUEPkmvYPkDKyp5UdXJYu+PoTQ3T31Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@types/node": { - "version": "14.18.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.5.tgz", - "integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==" + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/types": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.2.0.tgz", + "integrity": "sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true + "node_modules/@aws/language-server-runtimes/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==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@types/prettier": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", - "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", - "dev": true + "node_modules/@aws/language-server-runtimes/node_modules/ajv": { + "version": "8.17.1", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true + "node_modules/@aws/language-server-runtimes/node_modules/jose": { + "version": "5.10.0", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true + "node_modules/@aws/language-server-runtimes/node_modules/json-schema-traverse": { + "version": "1.0.0", + "license": "MIT" }, - "@types/readline-sync": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@types/readline-sync/-/readline-sync-1.4.4.tgz", - "integrity": "sha512-cFjVIoiamX7U6zkO2VPvXyTxbFDdiRo902IarJuPVxBhpDnXhwSaVE86ip+SCuyWBbEioKCkT4C88RNTxBM1Dw==", - "dev": true + "node_modules/@aws/language-server-runtimes/node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==" }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "requires": { - "@types/node": "*" + "node_modules/@aws/mynah-ui": { + "version": "4.35.4", + "resolved": "https://registry.npmjs.org/@aws/mynah-ui/-/mynah-ui-4.35.4.tgz", + "integrity": "sha512-LuOexbuMSKYCl/Qa7zj9d4/ueTLK3ltoYHeA0I7gOpPC/vYACxqjVqX6HPhNCE+L5zBKNMN2Z+FUaox+fYhvAQ==", + "hasInstallScript": true, + "dependencies": { + "escape-html": "^1.0.3", + "highlight.js": "^11.11.0", + "just-clone": "^6.2.0", + "marked": "^14.1.0", + "sanitize-html": "^2.12.1", + "unescape-html": "^1.1.0" + }, + "peerDependencies": { + "escape-html": "^1.0.3", + "highlight.js": "^11.11.0", + "just-clone": "^6.2.0", + "marked": "^14.1.0", + "sanitize-html": "^2.12.1", + "unescape-html": "^1.1.0" } }, - "@types/retry": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", - "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", - "dev": true - }, - "@types/semver": { - "version": "7.3.9", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", - "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==", - "dev": true + "node_modules/@aws/mynah-ui/node_modules/marked": { + "version": "14.1.4", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } }, - "@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "node_modules/@babel/code-frame": { + "version": "7.23.5", "dev": true, - "requires": { - "@types/express": "*" + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", "dev": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "@types/sinon": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.5.tgz", - "integrity": "sha512-BrAUy0yq3n84XOykYGvGbDir9nBIYwQm2NdBNQT0DbtDLqh/5nMUsjz5XfwrefFNLPE9B6g8yLOZREpvw0J40A==", + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", "dev": true, - "requires": { - "@sinonjs/fake-timers": "^7.1.0" - }, + "license": "MIT", "dependencies": { - "@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - } + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "@types/sinonjs__fake-timers": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true - }, - "@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", "dev": true, - "requires": { - "@types/node": "*" + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" } }, - "@types/tcp-port-used": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/tcp-port-used/-/tcp-port-used-1.0.1.tgz", - "integrity": "sha512-6pwWTx8oUtWvsiZUCrhrK/53MzKVLnuNSSaZILPy3uMes9QnTrLMar9BDlJArbMOjDcjb3QXFk6Rz8qmmuySZw==", - "dev": true - }, - "@types/uuid": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz", - "integrity": "sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA==", - "dev": true - }, - "@types/vscode": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.50.0.tgz", - "integrity": "sha512-QnIeyi4L2DiD9M2bAQKRzT/EQvc80qP9UL6JD5TiLlNRL1khIDg4ej4mDSRbtFrDAsRntFI1RhMvdomUThMsqg==", - "dev": true - }, - "@types/vscode-webview": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.0.tgz", - "integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==", - "dev": true - }, - "@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", "dev": true, - "requires": { - "@types/node": "*" - } + "license": "MIT" }, - "@types/xml2js": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.9.tgz", - "integrity": "sha512-CHiCKIihl1pychwR2RNX5mAYmJDACgFVCMT5OArMaO3erzwXVcBqPcusr+Vl8yeeXukxZqtF8mZioqX+mpjjdw==", + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", "dev": true, - "requires": { - "@types/node": "*" + "license": "MIT", + "engines": { + "node": ">=0.8.0" } }, - "@typescript-eslint/eslint-plugin": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.0.tgz", - "integrity": "sha512-GgHi/GNuUbTOeoJiEANi0oI6fF3gBQc3bGFYj40nnAPCbhrtEDf2rjBmefFadweBmO1Du1YovHeDP2h5JLhtTQ==", + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.38.0", - "@typescript-eslint/type-utils": "5.38.0", - "@typescript-eslint/utils": "5.38.0", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "@typescript-eslint/parser": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.38.0.tgz", - "integrity": "sha512-/F63giJGLDr0ms1Cr8utDAxP2SPiglaD6V+pCOcG35P2jCqdfR7uuEhz1GIC3oy4hkUF8xA1XSXmd9hOh/a5EA==", + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.38.0", - "@typescript-eslint/types": "5.38.0", - "@typescript-eslint/typescript-estree": "5.38.0", - "debug": "^4.3.4" + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "@typescript-eslint/scope-manager": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.38.0.tgz", - "integrity": "sha512-ByhHIuNyKD9giwkkLqzezZ9y5bALW8VNY6xXcP+VxoH4JBDKjU5WNnsiD4HJdglHECdV+lyaxhvQjTUbRboiTA==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", "dev": true, - "requires": { - "@typescript-eslint/types": "5.38.0", - "@typescript-eslint/visitor-keys": "5.38.0" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@typescript-eslint/type-utils": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.38.0.tgz", - "integrity": "sha512-iZq5USgybUcj/lfnbuelJ0j3K9dbs1I3RICAJY9NZZpDgBYXmuUlYQGzftpQA9wC8cKgtS6DASTvF3HrXwwozA==", + "node_modules/@babel/highlight": { + "version": "7.23.4", "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.38.0", - "@typescript-eslint/utils": "5.38.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@typescript-eslint/types": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.38.0.tgz", - "integrity": "sha512-HHu4yMjJ7i3Cb+8NUuRCdOGu2VMkfmKyIJsOr9PfkBVYLYrtMCK/Ap50Rpov+iKpxDTfnqvDbuPLgBE5FwUNfA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.0.tgz", - "integrity": "sha512-6P0RuphkR+UuV7Avv7MU3hFoWaGcrgOdi8eTe1NwhMp2/GjUJoODBTRWzlHpZh6lFOaPmSvgxGlROa0Sg5Zbyg==", + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", "dev": true, - "requires": { - "@typescript-eslint/types": "5.38.0", - "@typescript-eslint/visitor-keys": "5.38.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "@typescript-eslint/utils": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.38.0.tgz", - "integrity": "sha512-6sdeYaBgk9Fh7N2unEXGz+D+som2QCQGPAf1SxrkEr+Z32gMreQ0rparXTNGRRfYUWk/JzbGdcM8NSSd6oqnTA==", + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.38.0", - "@typescript-eslint/types": "5.38.0", - "@typescript-eslint/typescript-estree": "5.38.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "@typescript-eslint/visitor-keys": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.0.tgz", - "integrity": "sha512-MxnrdIyArnTi+XyFLR+kt/uNAcdOnmT+879os7qDRI+EYySR4crXJq9BXPfRzzLGq0wgxkwidrCJ9WCAoacm1w==", + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", "dev": true, - "requires": { - "@typescript-eslint/types": "5.38.0", - "eslint-visitor-keys": "^3.3.0" - }, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - } + "color-name": "1.1.3" } }, - "@vscode/codicons": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.32.tgz", - "integrity": "sha512-3lgSTWhAzzWN/EPURoY4ZDBEA80OPmnaknNujA3qnI4Iu7AONWd9xF3iE4L+4prIe8E3TUnLQ4pxoaFTEEZNwg==", - "dev": true - }, - "@vscode/test-electron": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.3.tgz", - "integrity": "sha512-7DmdGYQTqRNaLHKG3j56buc9DkstriY4aV0S3Zj32u0U9/T0L8vwWAC9QGCh1meu1VXDEla1ze27TkqysHGP0Q==", + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", "dev": true, - "requires": { - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "rimraf": "^3.0.2", - "unzipper": "^0.10.11" - } + "license": "MIT" }, - "@vue/compiler-core": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz", - "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.31", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" } }, - "@vue/compiler-dom": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz", - "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==", - "requires": { - "@vue/compiler-core": "3.2.31", - "@vue/shared": "3.2.31" + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, - "@vue/compiler-sfc": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz", - "integrity": "sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==", + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", "dev": true, - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.40", - "@vue/compiler-dom": "3.2.40", - "@vue/compiler-ssr": "3.2.40", - "@vue/reactivity-transform": "3.2.40", - "@vue/shared": "3.2.40", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" - }, + "license": "MIT", "dependencies": { - "@vue/compiler-core": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz", - "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==", - "dev": true, - "requires": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.40", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" - } - }, - "@vue/compiler-dom": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz", - "integrity": "sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==", - "dev": true, - "requires": { - "@vue/compiler-core": "3.2.40", - "@vue/shared": "3.2.40" - } - }, - "@vue/compiler-ssr": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz", - "integrity": "sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==", - "dev": true, - "requires": { - "@vue/compiler-dom": "3.2.40", - "@vue/shared": "3.2.40" - } - }, - "@vue/shared": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz", - "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==", - "dev": true - } + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "@vue/compiler-ssr": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz", - "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==", - "requires": { - "@vue/compiler-dom": "3.2.31", - "@vue/shared": "3.2.31" + "node_modules/@babel/parser": { + "version": "7.23.6", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "@vue/reactivity": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.31.tgz", - "integrity": "sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==", - "requires": { - "@vue/shared": "3.2.31" + "node_modules/@bcherny/json-schema-ref-parser": { + "version": "10.0.5-fork", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" } }, - "@vue/reactivity-transform": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz", - "integrity": "sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", "dev": true, - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.40", - "@vue/shared": "3.2.40", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" - }, - "dependencies": { - "@vue/compiler-core": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz", - "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==", - "dev": true, - "requires": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.40", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" - } - }, - "@vue/shared": { - "version": "3.2.40", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz", - "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==", - "dev": true - } - } + "license": "MIT" }, - "@vue/runtime-core": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.31.tgz", - "integrity": "sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==", - "requires": { - "@vue/reactivity": "3.2.31", - "@vue/shared": "3.2.31" + "node_modules/@colors/colors": { + "version": "1.5.0", + "license": "MIT", + "engines": { + "node": ">=0.1.90" } }, - "@vue/runtime-dom": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz", - "integrity": "sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==", - "requires": { - "@vue/runtime-core": "3.2.31", - "@vue/shared": "3.2.31", - "csstype": "^2.6.8" + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" } }, - "@vue/server-renderer": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.31.tgz", - "integrity": "sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==", - "requires": { - "@vue/compiler-ssr": "3.2.31", - "@vue/shared": "3.2.31" + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "@vue/shared": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz", - "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ==" + "node_modules/@dabh/diagnostics": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.5", "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "license": "MIT", + "engines": { + "node": ">=10.0.0" } }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "node_modules/@eslint/js": { + "version": "8.56.0", "dev": true, - "requires": { - "@xtuc/long": "4.2.2" + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" + "node_modules/@gerhobbelt/gitignore-parser": { + "version": "0.2.0-9", + "license": "Apache License, Version 2.0", + "engines": { + "node": ">=10" } }, - "@webpack-cli/configtest": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", - "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", "dev": true, - "requires": {} + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } }, - "@webpack-cli/info": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", - "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", "dev": true, - "requires": { - "envinfo": "^7.7.3" + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "@webpack-cli/serve": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", - "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", "dev": true, - "requires": {} - }, - "@xmldom/xmldom": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.8.tgz", - "integrity": "sha512-PrJx38EfpitFhwmILRl37jAdBlsww6AZ6rRVK4QS7T7RHLhX7mSs647sTmgr9GIxe3qjXdesmomEgbgaokrVFg==", - "dev": true + "license": "BSD-3-Clause" }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "node_modules/@iarna/toml": { + "version": "2.2.5", + "license": "ISC" }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } }, - "a-sync-waterfall": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", - "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==", - "dev": true + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "license": "MIT" }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } }, - "adm-zip": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", - "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==" + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", "dev": true, - "requires": { - "debug": "4" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "license": "MIT", + "engines": { + "node": ">=6.0.0" } }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", "dev": true, - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } + "license": "MIT", + "engines": { + "node": ">=6.0.0" } }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", "dev": true, - "requires": {} - }, - "amazon-states-language-service": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/amazon-states-language-service/-/amazon-states-language-service-1.8.0.tgz", - "integrity": "sha512-F5hTDQqAVOYEJCm4PTBf6ZjLQYqzLfnGfyNqxNMTYqilbZk/3T0GQManc0pgt5CbtFw62DB6ovBcEUQQa9xsvA==", - "requires": { - "js-yaml": "^3.14.0", - "vscode-json-languageservice": "3.4.9", - "vscode-languageserver": "^6.1.1", - "vscode-languageserver-textdocument": "^1.0.0", - "vscode-languageserver-types": "^3.15.1", - "yaml-language-server": "0.10.0" - }, + "license": "MIT", "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - } + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "license": "MIT" }, - "ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", "dev": true, - "requires": { - "ansi-wrap": "0.1.0" + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "dev": true, + "license": "MIT" }, - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" + "node_modules/@koa/cors": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 14.0.0" + } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@koa/router": { + "version": "13.1.0", "dev": true, - "requires": { - "color-convert": "^2.0.1" + "license": "MIT", + "dependencies": { + "http-errors": "^2.0.0", + "koa-compose": "^4.1.0", + "path-to-regexp": "^6.3.0" + }, + "engines": { + "node": ">= 18" } }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true + "node_modules/@koa/router/node_modules/path-to-regexp": { + "version": "6.3.0", + "dev": true, + "license": "MIT" }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "dev": true, + "license": "MIT" }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", "dev": true, - "requires": { - "default-require-extensions": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">= 8" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, + "license": "MIT", "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "node_modules/@opentelemetry/api-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.200.0.tgz", + "integrity": "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } }, - "array-differ": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", - "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", - "dev": true - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, - "ast-types": { - "version": "0.9.14", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.14.tgz", - "integrity": "sha512-Ebvx7/0lLboCdyEmAw/4GqwBeKIijPveXNiVGhCGCNxc7z26T5he7DC6ARxu8ByKuzUZZcLog+VP8GMyZrBzJw==", - "dev": true - }, - "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" - }, - "async-lock": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.3.0.tgz", - "integrity": "sha512-8A7SkiisnEgME2zEedtDYPxUPzdv3x//E7n5IFktPAtMYSEAV7eNJF0rMwrVyUFj6d/8rgajLantbjcNRQYXIg==" - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" - }, - "aws-sdk": { - "version": "2.1267.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1267.0.tgz", - "integrity": "sha512-ANTtRay26WwNRbYs6eZYN71b3DURNfWaq3AD6BtVNa8fVvnSLn+NNINw2+vLRjDLPZXMAQVHm0qH/TmyBvtjRA==", - "requires": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.4.19" - }, + "node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "license": "Apache-2.0", "dependencies": { - "uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" - } + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "aws-ssm-document-language-service": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/aws-ssm-document-language-service/-/aws-ssm-document-language-service-1.0.0.tgz", - "integrity": "sha512-XTkladSKrrD/uIGOwRMyVK7M6eHYVZWobtF0SCXQPocpE0SsHRVY1GflZaEGYgbKgDJTgpNpP8drTsUFzgfL5A==", - "requires": { - "vscode-json-languageservice": "3.8.3", - "vscode-languageserver": "^6.1.1", - "yaml": "^1.10.0", - "yaml-language-server": "0.10.1" - }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.200.0.tgz", + "integrity": "sha512-KfWw49htbGGp9s8N4KI8EQ9XuqKJ0VG+yVYVYFiCYSjEV32qpQ5qZ9UZBzOZ6xRb+E16SXOSCT3RkqBVSABZ+g==", + "license": "Apache-2.0", "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" - }, - "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", - "optional": true - }, - "vscode-json-languageservice": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.8.3.tgz", - "integrity": "sha512-8yPag/NQHCuTthahyaTtzK0DHT0FKM/xBU0mFBQ8nMo8C1i2P+FCyIVqICoNoHkRI2BTGlXKomPUpsqjSz0TnQ==", - "requires": { - "jsonc-parser": "^2.2.1", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "^3.15.1", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.2" - } - }, - "vscode-jsonrpc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", - "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==" - }, - "vscode-languageserver-protocol": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz", - "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==", - "requires": { - "vscode-jsonrpc": "^4.0.0", - "vscode-languageserver-types": "3.14.0" - }, - "dependencies": { - "vscode-languageserver-types": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", - "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==" - } - } - }, - "vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - }, - "yaml-language-server": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-0.10.1.tgz", - "integrity": "sha512-R9SEt/nWTuZ8weB040L7yyaIVARlZ0ian1Kv6ptu4+xyVlIMobTZXaBTtgyhlMWqcQ3BpsAZu4q/2plRVG3tLQ==", - "requires": { - "js-yaml": "^3.13.1", - "jsonc-parser": "^2.2.1", - "prettier": "2.0.5", - "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.6.0", - "vscode-languageserver": "^5.2.1", - "vscode-languageserver-types": "^3.15.1", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.1", - "yaml-ast-parser-custom-tags": "0.0.43" - }, - "dependencies": { - "vscode-languageserver": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz", - "integrity": "sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A==", - "requires": { - "vscode-languageserver-protocol": "3.14.1", - "vscode-uri": "^1.0.6" - }, - "dependencies": { - "vscode-uri": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz", - "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==" - } - } - } - } - } + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/sdk-logs": "0.200.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "azure-devops-node-api": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.0.1.tgz", - "integrity": "sha512-YMdjAw9l5p/6leiyIloxj3k7VIvYThKjvqgiQn88r3nhT93ENwsoDS3A83CyJ4uTWzCZ5f5jCi6c27rTU5Pz+A==", - "dev": true, - "requires": { - "tunnel": "0.0.6", - "typed-rest-client": "^1.8.4" + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.200.0.tgz", + "integrity": "sha512-5BiR6i8yHc9+qW7F6LqkuUnIzVNA7lt0qRxIKcKT+gq3eGUPHZ3DY29sfxI3tkvnwMgtnHDMNze5DdxW39HsAw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "dev": true - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", - "dev": true, - "requires": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.200.0.tgz", + "integrity": "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-transformer": "0.200.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.200.0.tgz", + "integrity": "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-logs": "0.200.0", + "@opentelemetry/sdk-metrics": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.200.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.200.0.tgz", + "integrity": "sha512-VZG870063NLfObmQQNtCVcdXXLzI3vOjjrRENmU37HYiPFa0ZXpXVDsTD02Nh3AT3xYJzQaWKl2X2lQ2l7TWJA==", + "license": "Apache-2.0", "dependencies": { - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - } + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", - "dev": true + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", + "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } }, - "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", + "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.0.tgz", + "integrity": "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA==", + "license": "Apache-2.0", "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, - "bonjour-service": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.13.tgz", - "integrity": "sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA==", - "dev": true, - "requires": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.0.tgz", + "integrity": "sha512-qQnYdX+ZCkonM7tA5iU4fSRsVxbFGml8jbxOgipRGMFHKaXKHQ30js03rTobYjKjIfnOsZSbHKWF0/0v0OQGfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.33.0.tgz", + "integrity": "sha512-TIpZvE8fiEILFfTlfPnltpBaD3d9/+uQHVCyC3vfdh6WfCXKhNFzoP5RyDDIndfvZC5GrA4pyEDNyjPloJud+w==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } }, - "bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@pkgr/core": { + "version": "0.1.1", "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@playwright/browser-chromium": { + "version": "1.49.1", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.49.1" + }, + "engines": { + "node": ">=18" } }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" }, - "browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", - "escalade": "^3.1.1", - "node-releases": "^2.0.2", - "picocolors": "^1.0.0" - } + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } }, - "buffer-indexof-polyfill": { + "node_modules/@protobufjs/float": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "dev": true + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" }, - "buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", - "dev": true + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" }, - "cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" }, - "cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@sindresorhus/is": { + "version": "4.2.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "node_modules/@sinonjs/commons": { + "version": "1.8.3", "dev": true, - "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" } }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^2.0.0" } }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw==", - "dev": true - }, - "caller-callsite": { + "node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", "dev": true, - "requires": { - "callsites": "^2.0.0" - }, + "license": "BSD-3-Clause", "dependencies": { - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", - "dev": true - } + "type-detect": "4.0.8" } }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "node_modules/@sinonjs/samsam": { + "version": "6.1.1", "dev": true, - "requires": { - "caller-callsite": "^2.0.0" + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" } }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, + "license": "(Unlicense OR Apache-2.0)" + }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.9", + "license": "Apache-2.0", "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - } + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "caniuse-lite": { - "version": "1.0.30001317", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz", - "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ==", - "dev": true - }, - "chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", - "dev": true, - "requires": { - "traverse": ">=0.3.0 <0.4" + "node_modules/@smithy/chunked-blob-reader": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "3.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" } }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", - "dev": true + "node_modules/@smithy/config-resolver": { + "version": "3.0.13", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "cheerio": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", - "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", - "dev": true, - "requires": { - "cheerio-select": "^1.5.0", - "dom-serializer": "^1.3.2", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" + "node_modules/@smithy/core": { + "version": "2.5.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "cheerio-select": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", - "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", - "dev": true, - "requires": { - "css-select": "^4.1.3", - "css-what": "^5.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0", - "domutils": "^2.7.0" + "node_modules/@smithy/core/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "node_modules/@smithy/core/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" } }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true + "node_modules/@smithy/core/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" + } }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "circular-dependency-plugin": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", - "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "dev": true, - "requires": {} + "node_modules/@smithy/eventstream-codec": { + "version": "3.1.10", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.7.2", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" + } }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true + "node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.14", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.13", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "cli-color": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", - "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", - "dev": true, - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.61", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.13", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.13", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.13", + "license": "Apache-2.0", "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } + "@smithy/eventstream-codec": "^3.1.10", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true + "node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true + "node_modules/@smithy/hash-blob-browser": { + "version": "3.1.10", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^4.0.0", + "@smithy/chunked-blob-reader-native": "^3.0.1", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" + "node_modules/@smithy/hash-node": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" + "node_modules/@smithy/hash-node/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true + "node_modules/@smithy/hash-node/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" + } }, - "cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "node_modules/@smithy/hash-node/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/@smithy/hash-stream-node": { + "version": "3.1.10", + "license": "Apache-2.0", "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "@smithy/types": "^3.7.2", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "code-block-writer": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.3.tgz", - "integrity": "sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==", - "dev": true + "node_modules/@smithy/hash-stream-node/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "node_modules/@smithy/hash-stream-node/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" + } }, - "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "requires": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "node_modules/@smithy/hash-stream-node/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/@smithy/invalid-dependency": { + "version": "3.0.11", + "license": "Apache-2.0", "dependencies": { - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - } + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" + "node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "node_modules/@smithy/md5-js": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } }, - "color-string": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", - "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "node_modules/@smithy/md5-js/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true + "node_modules/@smithy/md5-js/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" + } }, - "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true + "node_modules/@smithy/md5-js/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" + } }, - "colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "requires": { - "color": "^3.1.3", - "text-hex": "1.0.x" + "node_modules/@smithy/middleware-apply-body-checksum": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-apply-body-checksum/-/middleware-apply-body-checksum-4.1.2.tgz", + "integrity": "sha512-YK7yIjjW67Fat8uk2CsUDaQwfcvA1RPaoLKKDZycf7QZ3QlmPUuLLDsMVrJWPy/2mahJjpcaAfzZnK7cXDlVAQ==", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" + "node_modules/@smithy/middleware-apply-body-checksum/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": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, + "node_modules/@smithy/middleware-apply-body-checksum/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": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "requires": { - "safe-buffer": "5.2.1" + "node_modules/@smithy/middleware-apply-body-checksum/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" } }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.13", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" + "node_modules/@smithy/middleware-endpoint": { + "version": "3.2.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" } }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true + "node_modules/@smithy/middleware-serde": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true + "node_modules/@smithy/middleware-stack": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "node_modules/@smithy/node-config-provider": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" + "node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.11", + "license": "Apache-2.0", "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", - "dev": true - } + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "node_modules/@smithy/querystring-builder": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", - "dev": true + "node_modules/@smithy/querystring-parser": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "css-loader": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", - "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", - "dev": true, - "requires": { - "icss-utils": "^5.1.0", - "postcss": "^8.2.15", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "semver": "^7.3.5" + "node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "css-select": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", - "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^5.0.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0", - "nth-check": "^2.0.0" + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", - "dev": true + "node_modules/@smithy/signature-v4": { + "version": "4.2.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "cssesc": { + "node_modules/@smithy/signature-v4/node_modules/@smithy/is-array-buffer": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "csstype": { - "version": "2.6.20", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", - "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" - }, - "cubic2quad": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/cubic2quad/-/cubic2quad-1.2.1.tgz", - "integrity": "sha512-wT5Y7mO8abrV16gnssKdmIhIbA9wSkeMzhh27jAguKrV82i24wER0vL5TGhUJ9dbJNDcigoRZ0IAHFEEEI4THQ==", - "dev": true - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" + "node_modules/@smithy/signature-v4/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" } }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==", - "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, + "node_modules/@smithy/signature-v4/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true - } + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "requires": { - "mimic-response": "^3.1.0" - }, + "node_modules/@smithy/smithy-client": { + "version": "3.7.0", + "license": "Apache-2.0", "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - } + "@smithy/core": "^2.5.7", + "@smithy/middleware-endpoint": "^3.2.8", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "node_modules/@smithy/types": { + "version": "3.7.2", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true + "node_modules/@smithy/url-parser": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } }, - "default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "requires": { - "execa": "^5.0.0" + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-base64/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - } + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "default-require-extensions": { + "node_modules/@smithy/util-base64/node_modules/@smithy/util-buffer-from": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "requires": { - "strip-bom": "^4.0.0" + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + "node_modules/@smithy/util-base64/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" + } }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true + "node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" } }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.13", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } }, - "dns-packet": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", - "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", - "dev": true, - "requires": { - "@leichtgewicht/ip-codec": "^2.0.1" + "node_modules/@smithy/util-endpoints": { + "version": "2.1.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "doctrine": { + "node_modules/@smithy/util-hex-encoding": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "node_modules/@smithy/util-middleware": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", - "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" + "node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "node_modules/@smithy/util-stream": { + "version": "3.3.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^4.1.3", + "@smithy/node-http-handler": "^3.3.3", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "node_modules/@smithy/util-stream/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.4.86", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz", - "integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "node_modules/@smithy/util-stream/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" + } }, - "emojis-list": { + "node_modules/@smithy/util-stream/node_modules/@smithy/util-utf8": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true + "node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" + "node_modules/@smithy/util-waiter": { + "version": "3.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", + "node_modules/@stylistic/eslint-plugin": { + "version": "2.11.0", "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.13.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" } }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - }, - "envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "8.16.0", "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - }, + "license": "MIT", "dependencies": { - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - } + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "es-abstract": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz", - "integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==", - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.2", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.16.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "8.16.0", "dev": true, - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.16.0", "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" + "node_modules/@stylistic/eslint-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.0", "dev": true, - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "node_modules/@stylistic/eslint-plugin/node_modules/espree": { + "version": "10.3.0", "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "esbuild": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz", - "integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==", + "node_modules/@stylistic/eslint-plugin/node_modules/estraverse": { + "version": "5.3.0", "dev": true, - "requires": { - "@esbuild/android-arm": "0.15.13", - "@esbuild/linux-loong64": "0.15.13", - "esbuild-android-64": "0.15.13", - "esbuild-android-arm64": "0.15.13", - "esbuild-darwin-64": "0.15.13", - "esbuild-darwin-arm64": "0.15.13", - "esbuild-freebsd-64": "0.15.13", - "esbuild-freebsd-arm64": "0.15.13", - "esbuild-linux-32": "0.15.13", - "esbuild-linux-64": "0.15.13", - "esbuild-linux-arm": "0.15.13", - "esbuild-linux-arm64": "0.15.13", - "esbuild-linux-mips64le": "0.15.13", - "esbuild-linux-ppc64le": "0.15.13", - "esbuild-linux-riscv64": "0.15.13", - "esbuild-linux-s390x": "0.15.13", - "esbuild-netbsd-64": "0.15.13", - "esbuild-openbsd-64": "0.15.13", - "esbuild-sunos-64": "0.15.13", - "esbuild-windows-32": "0.15.13", - "esbuild-windows-64": "0.15.13", - "esbuild-windows-arm64": "0.15.13" + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, - "esbuild-android-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz", - "integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==", + "node_modules/@stylistic/eslint-plugin/node_modules/minimatch": { + "version": "9.0.5", "dev": true, - "optional": true + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "esbuild-android-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz", - "integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==", + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.2", "dev": true, - "optional": true + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "esbuild-darwin-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz", - "integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==", - "dev": true, - "optional": true + "node_modules/@svgdotjs/svg.js": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.js/-/svg.js-3.2.4.tgz", + "integrity": "sha512-BjJ/7vWNowlX3Z8O4ywT58DqbNRyYlkk6Yz/D13aB7hGmfQTvGX4Tkgtm/ApYlu9M7lCQi15xUEidqMUmdMYwg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Fuzzyma" + } }, - "esbuild-darwin-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz", - "integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==", - "dev": true, - "optional": true + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } }, - "esbuild-freebsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz", - "integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==", - "dev": true, - "optional": true + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } }, - "esbuild-freebsd-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz", - "integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==", + "node_modules/@tootallnate/once": { + "version": "1.1.2", "dev": true, - "optional": true + "license": "MIT", + "engines": { + "node": ">= 6" + } }, - "esbuild-linux-32": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz", - "integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==", + "node_modules/@ts-morph/common": { + "version": "0.24.0", "dev": true, - "optional": true + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.2", + "minimatch": "^9.0.4", + "mkdirp": "^3.0.1", + "path-browserify": "^1.0.1" + } }, - "esbuild-linux-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz", - "integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==", + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "2.0.1", "dev": true, - "optional": true + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "esbuild-linux-arm": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz", - "integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==", + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.5", "dev": true, - "optional": true + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "esbuild-linux-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz", - "integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==", + "node_modules/@ts-morph/common/node_modules/mkdirp": { + "version": "3.0.1", "dev": true, - "optional": true + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "esbuild-linux-mips64le": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz", - "integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==", + "node_modules/@tsconfig/node10": { + "version": "1.0.8", "dev": true, - "optional": true + "license": "MIT" }, - "esbuild-linux-ppc64le": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz", - "integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==", + "node_modules/@tsconfig/node12": { + "version": "1.0.9", "dev": true, - "optional": true + "license": "MIT" }, - "esbuild-linux-riscv64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz", - "integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==", + "node_modules/@tsconfig/node14": { + "version": "1.0.3", "dev": true, - "optional": true + "license": "MIT" }, - "esbuild-linux-s390x": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz", - "integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==", + "node_modules/@tsconfig/node16": { + "version": "1.0.2", "dev": true, - "optional": true + "license": "MIT" }, - "esbuild-loader": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.20.0.tgz", - "integrity": "sha512-dr+j8O4w5RvqZ7I4PPB4EIyVTd679EBQnMm+JBB7av+vu05Zpje2IpK5N3ld1VWa+WxrInIbNFAg093+E1aRsA==", + "node_modules/@types/adm-zip": { + "version": "0.4.34", "dev": true, - "requires": { - "esbuild": "^0.15.6", - "joycon": "^3.0.1", - "json5": "^2.2.0", - "loader-utils": "^2.0.0", - "tapable": "^2.2.0", - "webpack-sources": "^2.2.0" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "esbuild-netbsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz", - "integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==", + "node_modules/@types/async-lock": { + "version": "1.4.0", "dev": true, - "optional": true + "license": "MIT" }, - "esbuild-openbsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz", - "integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==", + "node_modules/@types/body-parser": { + "version": "1.19.2", "dev": true, - "optional": true + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } }, - "esbuild-sunos-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz", - "integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==", + "node_modules/@types/bonjour": { + "version": "3.5.10", "dev": true, - "optional": true + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, - "esbuild-windows-32": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz", - "integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==", + "node_modules/@types/bytes": { + "version": "3.1.1", "dev": true, - "optional": true + "license": "MIT" }, - "esbuild-windows-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz", - "integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==", - "dev": true, - "optional": true + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } }, - "esbuild-windows-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz", - "integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==", + "node_modules/@types/circular-dependency-plugin": { + "version": "5.0.8", "dev": true, - "optional": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "license": "MIT", + "dependencies": { + "@types/node": "*", + "webpack": "^5.1.0" + } }, - "eslint": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", - "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", + "node_modules/@types/connect": { + "version": "3.4.35", "dev": true, - "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, + "license": "MIT", "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } + "@types/node": "*" } }, - "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", "dev": true, - "requires": {} + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } }, - "eslint-plugin-header": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz", - "integrity": "sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==", + "node_modules/@types/cross-spawn": { + "version": "6.0.6", "dev": true, - "requires": {} + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, - "eslint-plugin-no-null": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz", - "integrity": "sha512-uRDiz88zCO/2rzGfgG15DBjNsgwWtWiSo4Ezy7zzajUgpnFIqd1TjepKeRmJZHEfBGu58o2a8S0D7vglvvhkVA==", + "node_modules/@types/diff": { + "version": "5.0.7", "dev": true, - "requires": {} + "license": "MIT" }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/@types/eslint": { + "version": "8.56.12", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", + "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/@types/estree": { + "version": "1.0.5", "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } + "license": "MIT" }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "node_modules/@types/express": { + "version": "4.17.13", "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - } + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, + "license": "MIT", "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" } }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/@types/glob": { + "version": "8.1.0", "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, + "license": "MIT", "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } + "@types/minimatch": "^5.1.2", + "@types/node": "*" } }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + "node_modules/@types/glob/node_modules/@types/minimatch": { + "version": "5.1.2", + "dev": true, + "license": "MIT" }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "node_modules/@types/he": { + "version": "1.2.3", + "dev": true, + "license": "MIT" }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "license": "MIT" }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "node_modules/@types/http-proxy": { + "version": "1.17.9", "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "event-stream": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz", - "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", "dev": true, - "requires": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + "license": "MIT" }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "node_modules/@types/jaro-winkler": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@types/jaro-winkler/-/jaro-winkler-0.2.4.tgz", + "integrity": "sha512-TNVu6vL0Z3h+hYcW78IRloINA0y0MTVJ1PFVtVpBSgk+ejmaH5aVfcVghzNXZ0fa6gXe4zapNMQtMGWOJKTLig==", "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } + "license": "MIT" }, - "expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true + "node_modules/@types/js-yaml": { + "version": "4.0.5", + "dev": true, + "license": "MIT" }, - "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "node_modules/@types/jsdom": { + "version": "21.1.6", "dev": true, - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.0", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.10.3", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, + "license": "MIT", "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" } }, - "ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "node_modules/@types/json-schema": { + "version": "7.0.15", "dev": true, - "requires": { - "type": "^2.5.0" - }, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.3", + "license": "MIT", "dependencies": { - "type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", - "dev": true - } + "@types/node": "*" } }, - "fancy-log": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "node_modules/@types/linkify-it": { + "version": "3.0.3", "dev": true, - "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" - } + "license": "MIT" }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "node_modules/@types/lodash": { + "version": "4.14.182", + "dev": true, + "license": "MIT" }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "node_modules/@types/lokijs": { + "version": "1.5.14", "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "13.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" } }, - "fast-json-patch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", - "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==" + "node_modules/@types/mdurl": { + "version": "1.0.3", + "dev": true, + "license": "MIT" }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "node_modules/@types/mime": { + "version": "1.3.2", + "dev": true, + "license": "MIT" }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "node_modules/@types/mime-types": { + "version": "2.1.4", + "dev": true, + "license": "MIT" }, - "fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true + "node_modules/@types/minimatch": { + "version": "3.0.5", + "dev": true, + "license": "MIT" }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "node_modules/@types/minimist": { + "version": "1.2.2", "dev": true, - "requires": { - "reusify": "^1.0.4" - } + "license": "MIT" }, - "faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "node_modules/@types/mocha": { + "version": "10.0.6", "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.8.4", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" } }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "node_modules/@types/node-fetch": { + "version": "2.6.9", "dev": true, - "requires": { - "pend": "~1.2.0" + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" } }, - "fecha": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", - "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "dev": true, + "license": "MIT" }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/@types/prettier": { + "version": "2.7.0", "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } + "license": "MIT" }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/@types/prismjs": { + "version": "1.26.3", "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } + "license": "MIT" }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "node_modules/@types/proper-lockfile": { + "version": "4.1.4", "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, + "license": "MIT", "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } + "@types/retry": "*" } }, - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "node_modules/@types/qs": { + "version": "6.9.7", "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } + "license": "MIT" }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/@types/range-parser": { + "version": "1.2.4", "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true + "license": "MIT" }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/@types/readline-sync": { + "version": "1.4.8", "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "license": "MIT" + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", - "dev": true + "node_modules/@types/retry": { + "version": "0.12.1", + "dev": true, + "license": "MIT" }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + "node_modules/@types/semver": { + "version": "7.5.0", + "dev": true, + "license": "MIT" }, - "follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "dev": true + "node_modules/@types/serve-index": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "requires": { - "is-callable": "^1.1.3" + "node_modules/@types/serve-static": { + "version": "1.13.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "node_modules/@types/sinon": { + "version": "10.0.5", "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" + "license": "MIT", + "dependencies": { + "@sinonjs/fake-timers": "^7.1.0" } }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "node_modules/@types/sinon/node_modules/@sinonjs/fake-timers": { + "version": "7.1.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.7.0" } }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.2", "dev": true, - "optional": true + "license": "MIT" }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } + "node_modules/@types/sockjs": { + "version": "0.3.33", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "node_modules/@types/stream-buffers": { + "version": "3.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + "node_modules/@types/svgdom": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/svgdom/-/svgdom-0.1.2.tgz", + "integrity": "sha512-ZFwX8cDhbz6jiv3JZdMVYq8SSWHOUchChPmRoMwdIu3lz89aCu/gVK9TdR1eeb0ARQ8+5rtjUKrk1UR8hh0dhQ==", + "dev": true, + "license": "MIT" }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "node_modules/@types/tcp-port-used": { + "version": "1.0.1", "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } + "license": "MIT" }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "dev": true, + "license": "MIT" }, - "geometry-interfaces": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/geometry-interfaces/-/geometry-interfaces-1.1.4.tgz", - "integrity": "sha512-qD6OdkT6NcES9l4Xx3auTpwraQruU7dARbQPVO71MKvkGYw5/z/oIiGymuFXrRaEQa5Y67EIojUpaLeGEa5hGA==", - "dev": true + "node_modules/@types/uuid": { + "version": "9.0.8", + "license": "MIT" }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "node_modules/@types/vscode": { + "version": "1.83.0", + "dev": true, + "license": "MIT" }, - "get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } + "node_modules/@types/vscode-webview": { + "version": "1.57.1", + "dev": true, + "license": "MIT" }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "dev": true, + "license": "MIT" }, - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true + "node_modules/@types/webpack-env": { + "version": "1.18.5", + "dev": true, + "license": "MIT" }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" + "node_modules/@types/whatwg-url": { + "version": "11.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" } }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "node_modules/@types/ws": { + "version": "8.5.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "node_modules/@types/xml2js": { + "version": "0.4.11", "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.14.1", "dev": true, - "requires": { - "is-glob": "^4.0.1" + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/type-utils": "7.14.1", + "@typescript-eslint/utils": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "glob-promise": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-4.2.2.tgz", - "integrity": "sha512-xcUzJ8NWN5bktoTIX7eOclO1Npxd/dyVqUJxlLIDasT4C7KZyqlPIwkdJ0Ypiy3p2ZKahTjK4M9uC3sNSfNMzw==", + "node_modules/@typescript-eslint/parser": { + "version": "7.14.1", "dev": true, - "requires": { - "@types/glob": "^7.1.3" + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.14.1", "dev": true, - "requires": { - "type-fest": "^0.20.2" + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1" }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.14.1", + "dev": true, + "license": "MIT", "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/utils": "7.14.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/@typescript-eslint/types": { + "version": "7.14.1", "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "got": { - "version": "11.8.5", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", - "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", - "requires": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.14.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "handle-thing": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "requires": { - "get-intrinsic": "^1.1.1" + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { - "has-symbols": "^1.0.2" + "node_modules/@typescript-eslint/utils": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "hash-sum": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", - "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", - "dev": true - }, - "hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.14.1", "dev": true, - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "he": { + "node_modules/@ungap/structured-clone": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true + "dev": true, + "license": "ISC" }, - "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "node_modules/@vscode/codicons": { + "version": "0.0.33", "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "license": "CC-BY-4.0" }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "node_modules/@vscode/debugprotocol": { + "version": "1.64.0", + "license": "MIT" + }, + "node_modules/@vscode/test-electron": { + "version": "2.3.8", "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - }, + "license": "MIT", "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jszip": "^3.10.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=16" } }, - "html-entities": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", - "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "node_modules/@vscode/test-web": { + "version": "0.0.65", "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" + "license": "MIT", + "dependencies": { + "@koa/cors": "^5.0.0", + "@koa/router": "^13.1.0", + "@playwright/browser-chromium": "^1.49.0", + "glob": "^11.0.0", + "gunzip-maybe": "^1.4.2", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "koa": "^2.15.3", + "koa-morgan": "^1.0.1", + "koa-mount": "^4.0.0", + "koa-static": "^5.0.0", + "minimist": "^1.2.8", + "playwright": "^1.49.0", + "tar-fs": "^3.0.6", + "vscode-uri": "^3.0.8" + }, + "bin": { + "vscode-test-web": "out/server/index.js" + }, + "engines": { + "node": ">=16" } }, - "http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true + "node_modules/@vscode/test-web/node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/@vscode/test-web/node_modules/brace-expansion": { + "version": "2.0.1", "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, + "license": "MIT", "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } + "balanced-match": "^1.0.0" } }, - "http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true + "node_modules/@vscode/test-web/node_modules/glob": { + "version": "11.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "node_modules/@vscode/test-web/node_modules/http-proxy-agent": { + "version": "7.0.2", "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "node_modules/@vscode/test-web/node_modules/https-proxy-agent": { + "version": "7.0.6", "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", "debug": "4" + }, + "engines": { + "node": ">= 14" } }, - "http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "node_modules/@vscode/test-web/node_modules/jackspeak": { + "version": "4.0.2", "dev": true, - "requires": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, + "license": "BlueOak-1.0.0", "dependencies": { - "is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true - } + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" + "node_modules/@vscode/test-web/node_modules/lru-cache": { + "version": "11.0.2", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" } }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "node_modules/@vscode/test-web/node_modules/minimatch": { + "version": "10.0.1", "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - }, - "husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/@vscode/test-web/node_modules/path-scurry": { + "version": "2.0.0", "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "node_modules/@vscode/test-web/node_modules/tar-fs": { + "version": "3.0.6", "dev": true, - "requires": {} - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true + "node_modules/@vscode/test-web/node_modules/tar-stream": { + "version": "3.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } }, - "immutable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==" + "node_modules/@vscode/test-web/node_modules/vscode-uri": { + "version": "3.0.8", + "dev": true, + "license": "MIT" }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/@vscode/vsce": { + "version": "2.19.0", "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "license": "MIT", + "dependencies": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^5.1.0", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 14" + }, + "optionalDependencies": { + "keytar": "^7.7.0" } }, - "import-local": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", - "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "node_modules/@vscode/vsce/node_modules/ansi-styles": { + "version": "3.2.1", "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "node_modules/@vscode/vsce/node_modules/chalk": { + "version": "2.4.2", "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "node_modules/@vscode/vsce/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "node_modules/@vscode/vsce/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "node_modules/@vscode/vsce/node_modules/entities": { + "version": "2.1.0", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true + "node_modules/@vscode/vsce/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } }, - "ip-regex": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==" - }, - "ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", - "dev": true - }, - "is": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", - "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", - "dev": true - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "requires": { - "has-bigints": "^1.0.1" + "node_modules/@vscode/vsce/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/@vscode/vsce/node_modules/has-flag": { + "version": "3.0.0", "dev": true, - "requires": { - "binary-extensions": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "node_modules/@vscode/vsce/node_modules/linkify-it": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" } }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" - }, - "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "node_modules/@vscode/vsce/node_modules/markdown-it": { + "version": "12.3.2", "dev": true, - "requires": { - "has": "^1.0.3" + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" } }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "requires": { - "has-tostringtag": "^1.0.0" + "node_modules/@vscode/vsce/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", - "dev": true - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "requires": { - "has-tostringtag": "^1.0.0" + "node_modules/@vscode/vsce/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/@vscode/vsce/node_modules/xml2js": { + "version": "0.5.0", "dev": true, - "requires": { - "is-extglob": "^2.1.1" + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" } }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "requires": { - "has-tostringtag": "^1.0.0" + "node_modules/@vue/compiler-core": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.21.3", + "@vue/shared": "3.3.4", + "estree-walker": "^2.0.2", + "source-map-js": "^1.0.2" } }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true + "node_modules/@vue/compiler-dom": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.3.4", + "@vue/shared": "3.3.4" + } }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" + "node_modules/@vue/compiler-sfc": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.15", + "@vue/compiler-core": "3.3.4", + "@vue/compiler-dom": "3.3.4", + "@vue/compiler-ssr": "3.3.4", + "@vue/reactivity-transform": "3.3.4", + "@vue/shared": "3.3.4", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.0", + "postcss": "^8.1.10", + "source-map-js": "^1.0.2" } }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true + "node_modules/@vue/compiler-ssr": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.3.4", + "@vue/shared": "3.3.4" + } }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "node_modules/@vue/reactivity": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.3.4" } }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "requires": { - "call-bind": "^1.0.2" + "node_modules/@vue/reactivity-transform": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.15", + "@vue/compiler-core": "3.3.4", + "@vue/shared": "3.3.4", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.0" } }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + "node_modules/@vue/runtime-core": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.3.4", + "@vue/shared": "3.3.4" + } }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "requires": { - "has-tostringtag": "^1.0.0" + "node_modules/@vue/runtime-dom": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/runtime-core": "3.3.4", + "@vue/shared": "3.3.4", + "csstype": "^3.1.1" } }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "requires": { - "has-symbols": "^1.0.2" + "node_modules/@vue/server-renderer": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.3.4", + "@vue/shared": "3.3.4" + }, + "peerDependencies": { + "vue": "3.3.4" } }, - "is-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", - "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0" + "node_modules/@vue/shared": { + "version": "3.3.4", + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "dev": true, + "license": "MIT" }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "dev": true, + "license": "MIT" }, - "is-url": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "dev": true, + "license": "MIT" }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "requires": { - "call-bind": "^1.0.2" + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "dev": true, + "license": "MIT" }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", "dev": true, - "requires": { - "is-docker": "^2.0.0" + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" } }, - "is2": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.7.tgz", - "integrity": "sha512-4vBQoURAXC6hnLFxD4VW7uc04XiwTTl/8ydYJxKvPwkWQrSjInkuM5VZVg6BGr1/natq69zDuvO9lGpLClJqvA==", - "requires": { - "deep-is": "^0.1.3", - "ip-regex": "^4.1.0", - "is-url": "^1.2.4" + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "dev": true, + "license": "MIT" }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", "dev": true, - "requires": { - "append-transform": "^2.0.0" + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, + "license": "MIT", "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", "dev": true, - "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, + "license": "MIT", "dependencies": { - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" } }, - "istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "jest-worker": { - "version": "27.3.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.3.1.tgz", - "integrity": "sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==", + "node_modules/@webpack-cli/info": { + "version": "2.0.2", "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "license": "MIT", + "engines": { + "node": ">=14.15.0" }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" - }, - "joycon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.0.1.tgz", - "integrity": "sha512-SJcJNBg32dGgxhPtM0wQqxqV0ax9k/9TaUskGDSJkSFSQOEWWvQ3zzWdGQRIUry2j1zA5+ReH13t0Mf3StuVZA==", - "dev": true - }, - "js-sdsl": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", - "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-to-typescript": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-11.0.2.tgz", - "integrity": "sha512-XRyeXBJeo/IH4eTP5D1ptX78vCvH86nMDt2k3AxO28C3uYWEDmy4mgPyMpb8bLJ/pJMElOGuQbnKR5Y6NSh3QQ==", + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", "dev": true, - "requires": { - "@bcherny/json-schema-ref-parser": "9.0.9", - "@types/json-schema": "^7.0.11", - "@types/lodash": "^4.14.182", - "@types/prettier": "^2.6.1", - "cli-color": "^2.0.2", - "get-stdin": "^8.0.0", - "glob": "^7.1.6", - "glob-promise": "^4.2.2", - "is-glob": "^4.0.3", - "lodash": "^4.17.21", - "minimist": "^1.2.6", - "mkdirp": "^1.0.4", - "mz": "^2.7.0", - "prettier": "^2.6.2" + "license": "MIT", + "engines": { + "node": ">=14.15.0" }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true } } }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==" - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" + "node_modules/@xmldom/xmldom": { + "version": "0.7.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" } }, - "just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "dev": true, + "license": "BSD-3-Clause" }, - "keytar": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.7.0.tgz", - "integrity": "sha512-YEY9HWqThQc5q5xbXbRwsZTh2PJ36OSYRjSv3NN2xf5s5dpLTjEZnC2YikR29OaVybf9nQ0dJ/80i40RS97t/A==", + "node_modules/@xtuc/long": { + "version": "4.2.2", "dev": true, - "requires": { - "node-addon-api": "^3.0.0", - "prebuild-install": "^6.0.0" - } + "license": "Apache-2.0" }, - "keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", - "requires": { - "json-buffer": "3.0.1" + "node_modules/@zip.js/zip.js": { + "version": "2.7.41", + "license": "BSD-3-Clause", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" } }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/a-sync-waterfall": { + "version": "1.0.1", "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "license": "MIT" }, - "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "node_modules/accepts": { + "version": "1.3.8", "dev": true, - "requires": { - "uc.micro": "^1.0.1" + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" } }, - "listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", - "dev": true - }, - "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "node_modules/acorn": { + "version": "8.14.0", "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", "dev": true, - "requires": { - "p-locate": "^5.0.0" + "license": "MIT", + "peerDependencies": { + "acorn": "^8" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/acorn-jsx": { + "version": "5.3.2", "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "logform": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz", - "integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==", - "requires": { - "@colors/colors": "1.5.0", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" + "node_modules/acorn-walk": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" } }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" + "node_modules/adm-zip": { + "version": "0.5.10", + "license": "MIT", + "engines": { + "node": ">=6.0" } }, - "lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "node_modules/agent-base": { + "version": "6.0.2", "dev": true, - "requires": { - "es5-ext": "~0.10.2" + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" } }, - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "requires": { - "sourcemap-codec": "^1.4.8" + "node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/ajv-formats": { + "version": "2.1.1", "dev": true, - "requires": { - "semver": "^6.0.0" - }, + "license": "MIT", "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true } } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true - }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", - "dev": true - }, - "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.8.2", "dev": true, - "requires": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, + "license": "MIT", "dependencies": { - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true - } + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "marked": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", - "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==", - "dev": true + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" }, - "md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "node_modules/ajv-keywords": { + "version": "3.5.2", "dev": true, - "requires": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" } }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true + "node_modules/amazon-q-vscode": { + "resolved": "packages/amazonq", + "link": true }, - "memfs": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", - "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", - "dev": true, - "requires": { - "fs-monkey": "^1.0.3" + "node_modules/amazon-states-language-service": { + "version": "1.16.1", + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.0", + "jsonata": "2.0.5", + "lodash": "^4.17.21", + "vscode-json-languageservice": "3.4.9", + "vscode-languageserver": "^9.0.0", + "vscode-languageserver-textdocument": "^1.0.0", + "vscode-languageserver-types": "^3.17.5", + "yaml-language-server": "0.15.0" } }, - "memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", - "dev": true, - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" + "node_modules/ansi-colors": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=6" } }, - "meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "license": "MIT", "dependencies": { - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - } + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true + "node_modules/ansi-html-community": { + "version": "0.0.8", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "node_modules/ansi-regex": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true + "node_modules/ansi-wrap": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "microbuffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/microbuffer/-/microbuffer-1.0.0.tgz", - "integrity": "sha512-O/SUXauVN4x6RaEJFqSPcXNtLFL+QzJHKZlyDVYFwcDDRVca3Fa/37QXXC+4zAGGa4YhHrHxKXuuHvLDIQECtA==", - "dev": true + "node_modules/any-promise": { + "version": "1.3.0", + "dev": true, + "license": "MIT" }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/anymatch": { + "version": "3.1.2", "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "license": "ISC" }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true + "node_modules/argparse": { + "version": "2.0.1", + "license": "Python-2.0" }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/array-flatten": { + "version": "2.1.2", "dev": true, - "requires": { - "brace-expansion": "^1.1.7" + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "node_modules/asap": { + "version": "2.0.6", + "dev": true, + "license": "MIT" }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "node_modules/asn1.js": { + "version": "5.4.1", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/assert": { + "version": "2.1.0", "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, + "license": "MIT", "dependencies": { - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true - } + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" + "node_modules/ast-types": { + "version": "0.9.14", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true + "node_modules/async": { + "version": "3.2.5", + "license": "MIT" }, - "mocha": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", - "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "node_modules/async-lock": { + "version": "1.4.0", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-core-vscode": { + "resolved": "packages/core", + "link": true + }, + "node_modules/aws-sdk": { + "version": "2.1692.0", + "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" } }, - "mocha-junit-reporter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.0.2.tgz", - "integrity": "sha512-vYwWq5hh3v1lG0gdQCBxwNipBfvDiAM1PHroQRNp96+2l72e9wEUTw+mzoK+O0SudgfQ7WvTQZ9Nh3qkAYAjfg==", + "node_modules/aws-sdk-client-mock": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/aws-sdk-client-mock/-/aws-sdk-client-mock-4.1.0.tgz", + "integrity": "sha512-h/tOYTkXEsAcV3//6C1/7U4ifSpKyJvb6auveAepqqNJl6TdZaPFEtKjBQNf8UxQdDP850knB2i/whq4zlsxJw==", "dev": true, - "requires": { - "debug": "^2.2.0", - "md5": "^2.1.0", - "mkdirp": "~0.5.1", - "strip-ansi": "^6.0.1", - "xml": "^1.0.0" - }, + "license": "MIT", "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } + "@types/sinon": "^17.0.3", + "sinon": "^18.0.1", + "tslib": "^2.1.0" } }, - "mocha-multi-reporters": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/mocha-multi-reporters/-/mocha-multi-reporters-1.5.1.tgz", - "integrity": "sha512-Yb4QJOaGLIcmB0VY7Wif5AjvLMUFAdV57D2TWEva1Y0kU/3LjKpeRVmlMIfuO1SVbauve459kgtIizADqxMWPg==", + "node_modules/aws-sdk-client-mock/node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, - "requires": { - "debug": "^4.1.1", - "lodash": "^4.17.15" + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" } }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true + "node_modules/aws-sdk-client-mock/node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/aws-sdk-client-mock/node_modules/@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + } }, - "multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "node_modules/aws-sdk-client-mock/node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, - "requires": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "multimatch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", - "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "node_modules/aws-sdk-client-mock/node_modules/@types/sinon": { + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz", + "integrity": "sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==", "dev": true, - "requires": { - "@types/minimatch": "^3.0.3", - "array-differ": "^3.0.0", - "array-union": "^2.1.0", - "arrify": "^2.0.1", - "minimatch": "^3.0.4" + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" } }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true + "node_modules/aws-sdk-client-mock/node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" }, - "mvdan-sh": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mvdan-sh/-/mvdan-sh-0.5.0.tgz", - "integrity": "sha512-UWbdl4LHd2fUnaEcOUFVWRdWGLkNoV12cKVIPiirYd8qM5VkCoCTXErlDubevrkEG7kGohvjRxAlTQmOqG80tw==", - "dev": true + "node_modules/aws-sdk-client-mock/node_modules/nise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", + "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.1", + "@sinonjs/text-encoding": "^0.7.3", + "just-extend": "^6.2.0", + "path-to-regexp": "^8.1.0" + } }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "node_modules/aws-sdk-client-mock/node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" } }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==" + "node_modules/aws-sdk-client-mock/node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, - "napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "dev": true + "node_modules/aws-sdk-client-mock/node_modules/sinon": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-18.0.1.tgz", + "integrity": "sha512-a2N2TDY1uGviajJ6r4D1CyRAkzE9NNVlYOV1wX5xQDuAk0ONgzgRl0EjCQuRCPxOwp13ghsMwt9Gdldujs39qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.2.0", + "nise": "^6.0.0", + "supports-color": "^7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "neatequal": { + "node_modules/aws-ssm-document-language-service": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/neatequal/-/neatequal-1.0.0.tgz", - "integrity": "sha512-sVt5awO4a4w24QmAthdrCPiVRW3naB8FGLdyadin01BH+6BzNPEBwGrpwCczQvPlULS6uXTItTe1PJ5P0kYm7A==", - "dev": true, - "requires": { - "varstream": "^0.3.2" + "license": "Apache-2.0", + "dependencies": { + "vscode-json-languageservice": "3.8.3", + "vscode-languageserver": "^6.1.1", + "yaml": "^1.10.0", + "yaml-language-server": "0.10.1" } }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true + "node_modules/aws-ssm-document-language-service/node_modules/argparse": { + "version": "1.0.10", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "node_modules/aws-ssm-document-language-service/node_modules/js-yaml": { + "version": "3.14.1", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true + "node_modules/aws-ssm-document-language-service/node_modules/jsonc-parser": { + "version": "2.3.1", + "license": "MIT" }, - "nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" + "node_modules/aws-ssm-document-language-service/node_modules/prettier": { + "version": "2.0.5", + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" } }, - "node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "dev": true, - "requires": { - "semver": "^5.4.1" + "node_modules/aws-ssm-document-language-service/node_modules/vscode-json-languageservice": { + "version": "3.8.3", + "license": "MIT", + "dependencies": { + "jsonc-parser": "^2.2.1", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.2" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/vscode-jsonrpc": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8.0.0 || >=10.0.0" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/vscode-languageserver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz", + "integrity": "sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "^3.15.3" }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/vscode-languageserver-protocol": { + "version": "3.14.1", + "license": "MIT", "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "vscode-jsonrpc": "^4.0.0", + "vscode-languageserver-types": "3.14.0" } }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true + "node_modules/aws-ssm-document-language-service/node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { + "version": "3.14.0", + "license": "MIT" }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true + "node_modules/aws-ssm-document-language-service/node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "requires": { - "process-on-spawn": "^1.0.0" + "node_modules/aws-ssm-document-language-service/node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" } }, - "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", - "dev": true + "node_modules/aws-ssm-document-language-service/node_modules/vscode-nls": { + "version": "4.1.2", + "license": "MIT" }, - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "node_modules/aws-ssm-document-language-service/node_modules/yaml-language-server": { + "version": "0.10.1", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "jsonc-parser": "^2.2.1", + "request-light": "^0.2.4", + "vscode-json-languageservice": "^3.6.0", + "vscode-languageserver": "^5.2.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.1", + "yaml-ast-parser-custom-tags": "0.0.43" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + }, + "engines": { + "node": "*" + }, + "optionalDependencies": { + "prettier": "2.0.5" } }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "node_modules/aws-ssm-document-language-service/node_modules/yaml-language-server/node_modules/vscode-languageserver": { + "version": "5.2.1", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.14.1", + "vscode-uri": "^1.0.6" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + "node_modules/aws-ssm-document-language-service/node_modules/yaml-language-server/node_modules/vscode-languageserver/node_modules/vscode-uri": { + "version": "1.0.8", + "license": "MIT" }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/aws-toolkit-vscode": { + "resolved": "packages/toolkit", + "link": true + }, + "node_modules/azure-devops-node-api": { + "version": "11.2.0", "dev": true, - "requires": { - "path-key": "^3.0.0" + "license": "MIT", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" } }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "node_modules/b4a": { + "version": "1.6.4", "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } + "license": "ISC" }, - "nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.2.2", "dev": true, - "requires": { - "boolbase": "^1.0.0" + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.0", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^1.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "node_modules/bare-os": { + "version": "2.3.0", + "dev": true, + "license": "Apache-2.0", + "optional": true }, - "nunjucks": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", - "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "node_modules/bare-path": { + "version": "2.1.2", "dev": true, - "requires": { - "a-sync-waterfall": "^1.0.0", - "asap": "^2.0.3", - "commander": "^5.1.0" - }, + "license": "Apache-2.0", + "optional": true, "dependencies": { - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true - } + "bare-os": "^2.1.0" } }, - "nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "node_modules/bare-stream": { + "version": "1.0.0", "dev": true, - "requires": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, + "license": "Apache-2.0", + "optional": true, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "streamx": "^2.16.1" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "requires": { - "call-bind": "^1.0.2", + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/batch": { + "version": "0.6.1", + "dev": true, + "license": "MIT" + }, + "node_modules/big.js": { + "version": "5.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.0.13", + "dev": true, + "license": "MIT", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/bowser": { + "version": "2.11.0", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.2", + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.4", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/browserify-zlib": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "~0.2.0" + } + }, + "node_modules/browserify-zlib/node_modules/pako": { + "version": "0.2.9", + "dev": true, + "license": "MIT" + }, + "node_modules/browserslist": { + "version": "4.23.1", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.16" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c8": { + "version": "9.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/c8/node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/cache-content-type": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys/node_modules/quick-lru": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001638", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "license": "ISC" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "4.0.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/circular-dependency-plugin": { + "version": "5.2.2", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.1" + } + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cli-color": { + "version": "2.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.61", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/cloneable-readable/node_modules/readable-stream": { + "version": "2.3.7", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/cloneable-readable/node_modules/safe-buffer": { + "version": "5.1.2", + "license": "MIT" + }, + "node_modules/cloneable-readable/node_modules/string_decoder": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.16", + "dev": true, + "license": "MIT" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/cookies": { + "version": "0.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cookies/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/core-js-compat": { + "version": "3.37.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/cosmiconfig/node_modules/import-fresh": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/cosmiconfig/node_modules/parse-json": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/resolve-from": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "license": "MIT", + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-loader": { + "version": "6.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.4", + "postcss-modules-scope": "^3.1.1", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "license": "MIT" + }, + "node_modules/cubic2quad": { + "version": "1.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/d": { + "version": "1.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "dev": true, + "license": "MIT" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-equal": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/depd": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "license": "MIT" + }, + "node_modules/duplexify": { + "version": "3.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexify/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexify/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.812", + "dev": true, + "license": "ISC" + }, + "node_modules/elliptic": { + "version": "6.6.1", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/enabled": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/envinfo": { + "version": "7.11.1", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/es-abstract": { + "version": "1.20.2", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.2", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "license": "MIT" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/esbuild": { + "version": "0.15.13", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.15.13", + "@esbuild/linux-loong64": "0.15.13", + "esbuild-android-64": "0.15.13", + "esbuild-android-arm64": "0.15.13", + "esbuild-darwin-64": "0.15.13", + "esbuild-darwin-arm64": "0.15.13", + "esbuild-freebsd-64": "0.15.13", + "esbuild-freebsd-arm64": "0.15.13", + "esbuild-linux-32": "0.15.13", + "esbuild-linux-64": "0.15.13", + "esbuild-linux-arm": "0.15.13", + "esbuild-linux-arm64": "0.15.13", + "esbuild-linux-mips64le": "0.15.13", + "esbuild-linux-ppc64le": "0.15.13", + "esbuild-linux-riscv64": "0.15.13", + "esbuild-linux-s390x": "0.15.13", + "esbuild-netbsd-64": "0.15.13", + "esbuild-openbsd-64": "0.15.13", + "esbuild-sunos-64": "0.15.13", + "esbuild-windows-32": "0.15.13", + "esbuild-windows-64": "0.15.13", + "esbuild-windows-arm64": "0.15.13" + } + }, + "node_modules/esbuild-loader": { + "version": "2.20.0", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.15.6", + "joycon": "^3.0.1", + "json5": "^2.2.0", + "loader-utils": "^2.0.0", + "tapable": "^2.2.0", + "webpack-sources": "^2.2.0" + }, + "funding": { + "url": "https://github.com/privatenumber/esbuild-loader?sponsor=1" + }, + "peerDependencies": { + "webpack": "^4.40.0 || ^5.0.0" + } + }, + "node_modules/esbuild/node_modules/esbuild-darwin-arm64": { + "version": "0.15.13", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-aws-toolkits": { + "resolved": "plugins/eslint-plugin-aws-toolkits", + "link": true + }, + "node_modules/eslint-plugin-header": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=7.7.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-security-node": { + "version": "1.1.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "54.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "@eslint-community/eslint-utils": "^4.4.0", + "@eslint/eslintrc": "^3.0.2", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.37.0", + "esquery": "^1.5.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.6.1", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=18.18" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.56.0" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/espree": { + "version": "10.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/globals": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.3", + "dev": true, + "license": "ISC" + }, + "node_modules/espree": { + "version": "9.6.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/event-stream": { + "version": "3.3.5", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.12", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ext": { + "version": "1.6.0", + "dev": true, + "license": "ISC", + "dependencies": { + "type": "^2.5.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "dev": true, + "license": "ISC" + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.13.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.1", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/finalhandler/node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.2", + "dev": true, + "license": "ISC" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "license": "MIT" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/geometry-interfaces": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stdin": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "license": "MIT" + }, + "node_modules/glob": { + "version": "10.3.10", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.3", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.5", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gunzip-maybe": { + "version": "1.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-zlib": "^0.1.4", + "is-deflate": "^1.0.0", + "is-gzip": "^1.0.0", + "peek-stream": "^1.1.0", + "pumpify": "^1.3.3", + "through2": "^2.0.3" + }, + "bin": { + "gunzip-maybe": "bin.js" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, + "node_modules/hash-base": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-sum": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/hash.js": { + "version": "1.1.7", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hosted-git-info": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/hpagent": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/html-entities": { + "version": "2.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-assert": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-assert/node_modules/http-errors": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "license": "BSD-2-Clause" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http2": { + "version": "3.3.6", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "9.0.7", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/i18n-ts": { + "version": "1.0.5", + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/immutable": { + "version": "4.3.0", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is": { + "version": "3.3.0", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "dev": true, + "license": "MIT" + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-deflate": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-directory": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-electron": { + "version": "2.2.2", + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-gzip": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-nan": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-promise": { + "version": "2.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.9", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "license": "MIT" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is2": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "ip-regex": "^4.1.0", + "is-url": "^1.2.4" + }, + "engines": { + "node": ">=v0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jaro-winkler": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/jaro-winkler/-/jaro-winkler-0.2.8.tgz", + "integrity": "sha512-yr+mElb6dWxA1mzFu0+26njV5DWAQRNTi5pB6fFMm79zHrfAs3d0qjhe/IpZI4AHIUJkzvu5QXQRWOw2O0GQyw==", + "license": "MIT" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jmespath": { + "version": "0.16.0", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jose": { + "version": "5.4.1", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/joycon": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "23.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^3.0.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.7", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.14.2", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/http-proxy-agent": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-to-typescript": { + "version": "13.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcherny/json-schema-ref-parser": "10.0.5-fork", + "@types/json-schema": "^7.0.11", + "@types/lodash": "^4.14.182", + "@types/prettier": "^2.6.1", + "cli-color": "^2.0.2", + "get-stdin": "^8.0.0", + "glob": "^7.1.6", + "glob-promise": "^4.2.2", + "is-glob": "^4.0.3", + "lodash": "^4.17.21", + "minimist": "^1.2.6", + "mkdirp": "^1.0.4", + "mz": "^2.7.0", + "prettier": "^2.6.2" + }, + "bin": { + "json2ts": "dist/src/cli.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/json-schema-to-typescript/node_modules/@types/glob": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/json-schema-to-typescript/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/json-schema-to-typescript/node_modules/glob-promise": { + "version": "4.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/ahmadnassri" + }, + "peerDependencies": { + "glob": "^7.1.6" + } + }, + "node_modules/json-schema-to-typescript/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/json-schema-to-typescript/node_modules/prettier": { + "version": "2.8.8", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonata": { + "version": "2.0.5", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/just-clone": { + "version": "6.2.0", + "license": "MIT" + }, + "node_modules/just-extend": { + "version": "4.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/keygrip": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/koa": { + "version": "2.15.3", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.9.0", + "debug": "^4.3.2", + "delegates": "^1.0.0", + "depd": "^2.0.0", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^2.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "engines": { + "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + } + }, + "node_modules/koa-compose": { + "version": "4.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/koa-convert": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "co": "^4.6.0", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/koa-morgan": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "morgan": "^1.6.1" + } + }, + "node_modules/koa-mount": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.1", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 7.6.0" + } + }, + "node_modules/koa-send": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "http-errors": "^1.7.3", + "resolve-path": "^1.4.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/koa-send/node_modules/http-errors": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa-static": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "koa-send": "^5.0.0" + }, + "engines": { + "node": ">= 7.6.0" + } + }, + "node_modules/koa-static/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/koa/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/koa/node_modules/http-errors": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/launch-editor": { + "version": "2.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.4.0", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.5.0", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, + "node_modules/lokijs": { + "version": "1.5.12", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==", + "license": "Apache-2.0" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, + "node_modules/mac-ca": { + "version": "3.1.1", + "license": "BSD-3-Clause", + "dependencies": { + "node-forge": "^1.3.1", + "undici": "^6.16.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.0", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/map-obj": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/map-stream": { + "version": "0.0.7", + "license": "MIT" + }, + "node_modules/markdown-it": { + "version": "13.0.2", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "3.0.1", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/marked": { + "version": "13.0.1", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.7", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoizee": { + "version": "0.4.15", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/microbuffer": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/mime": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/arrify": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "license": "MIT" + }, + "node_modules/mocha": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha-junit-reporter": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "md5": "^2.3.0", + "mkdirp": "^3.0.0", + "strip-ansi": "^6.0.1", + "xml": "^1.0.1" + }, + "peerDependencies": { + "mocha": ">=2.2.5" + } + }, + "node_modules/mocha-junit-reporter/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha-junit-reporter/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha-junit-reporter/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha-multi-reporters": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "lodash": "^4.17.15" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "mocha": ">=3.1.2" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "5.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/morgan": { + "version": "1.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "license": "ISC" + }, + "node_modules/mvdan-sh": { + "version": "0.10.1", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/mz": { + "version": "2.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/neatequal": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "varstream": "^0.3.2" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/nise": { + "version": "5.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/node-abi": { + "version": "3.45.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "dev": true, + "license": "MIT" + }, + "node_modules/noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ==", + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nunjucks": { + "version": "3.2.4", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/nunjucks/node_modules/commander": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/nwsapi": { + "version": "2.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/only": { + "version": "0.0.2", + "dev": true + }, + "node_modules/open": { + "version": "8.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "license": "MIT" + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "license": "ISC", + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/parse5": { + "version": "7.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.1.0", + "license": "ISC", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-to-regexp": { + "version": "1.9.0", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-to-regexp/node_modules/isarray": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "license": [ + "MIT", + "Apache2" + ], + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "license": "MIT", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/peek-stream": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "duplexify": "^3.5.0", + "through2": "^2.0.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.0.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.49.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.1", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/portfinder": { + "version": "1.0.32", + "license": "MIT", + "dependencies": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/async": { + "version": "2.6.4", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/postcss": { + "version": "8.4.33", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.15", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prettier-plugin-sh": { + "version": "0.14.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mvdan-sh": "^0.10.1", + "sh-syntax": "^0.4.1" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + }, + "peerDependencies": { + "prettier": "^3.0.3" + } + }, + "node_modules/pretty-quick": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "find-up": "^5.0.0", + "ignore": "^5.3.0", + "mri": "^1.2.0", + "picocolors": "^1.0.0", + "picomatch": "^3.0.1", + "tslib": "^2.6.2" + }, + "bin": { + "pretty-quick": "lib/cli.mjs" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/pretty-quick/node_modules/picomatch": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/private": { + "version": "0.1.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "dev": true, + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readline-sync": { + "version": "1.4.10", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/recast": { + "version": "0.11.23", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/recast/node_modules/ast-types": { + "version": "0.9.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/recast/node_modules/esprima": { + "version": "3.1.3", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "dev": true, + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/registry-js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/registry-js/-/registry-js-1.16.1.tgz", + "integrity": "sha512-pQ2kD36lh+YNtpaXm6HCCb0QZtV/zQEeKnkfEIj5FDSpF/oFts7pwizEUkWSvP8IbGb4A4a5iBhhS9eUearMmQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^3.2.1", + "prebuild-install": "^5.3.5" + } + }, + "node_modules/registry-js/node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "license": "MIT", + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/registry-js/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/registry-js/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/registry-js/node_modules/node-abi": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "license": "MIT", + "dependencies": { + "semver": "^5.4.1" + } + }, + "node_modules/registry-js/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "license": "MIT" + }, + "node_modules/registry-js/node_modules/prebuild-install": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz", + "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==", + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/registry-js/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/registry-js/node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "license": "MIT", + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/regjsparser": { + "version": "0.10.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "license": "ISC" + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/request-light": { + "version": "0.2.5", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.3", + "vscode-nls": "^4.1.1" + } + }, + "node_modules/request-light/node_modules/agent-base": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/request-light/node_modules/debug": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/request-light/node_modules/http-proxy-agent": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "agent-base": "4", + "debug": "3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/request-light/node_modules/https-proxy-agent": { + "version": "2.2.4", + "license": "MIT", + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/request-light/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/request-light/node_modules/vscode-nls": { + "version": "4.1.2", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "license": "MIT" + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-path": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "http-errors": "~1.6.2", + "path-is-absolute": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/resolve-path/node_modules/http-errors": { + "version": "1.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/resolve-path/node_modules/inherits": { + "version": "2.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve-path/node_modules/setprototypeof": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/responselike": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "license": "MIT" + }, + "node_modules/retry": { + "version": "0.13.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/sanitize-html": { + "version": "2.13.0", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "node_modules/sanitize-html/node_modules/is-plain-object": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sass": { + "version": "1.69.5", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "16.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.1", + "license": "ISC" + }, + "node_modules/saxes": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/sh-syntax": { + "version": "0.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/sha.js": { + "version": "2.4.11", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sinon": { + "version": "14.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^9.1.2", + "@sinonjs/samsam": "^6.1.1", + "diff": "^5.0.0", + "nise": "^5.1.1", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "dev": true, + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdy": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "license": "BSD-3-Clause" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-buffers": { + "version": "3.0.2", + "license": "Unlicense", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-combiner": { + "version": "0.2.2", + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/streamx": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "license": "MIT" + }, + "node_modules/style-loader": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/svg2ttf": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.7.2", + "argparse": "^2.0.1", + "cubic2quad": "^1.2.1", + "lodash": "^4.17.10", + "microbuffer": "^1.0.0", + "svgpath": "^2.1.5" + }, + "bin": { + "svg2ttf": "svg2ttf.js" + } + }, + "node_modules/svgdom": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/svgdom/-/svgdom-0.1.21.tgz", + "integrity": "sha512-PrMx2aEzjRgyK9nbff6/NOzNmGcRnkjwO9p3JnHISmqPTMGtBPi4uFp59fVhI9PqRp8rVEWgmXFbkgYRsTnapg==", + "license": "MIT", + "dependencies": { + "fontkit": "^2.0.4", + "image-size": "^1.2.1", + "sax": "^1.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Fuzzyma" + } + }, + "node_modules/svgdom/node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" + }, + "node_modules/svgicons2svgfont": { + "version": "10.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^7.2.0", + "geometry-interfaces": "^1.1.4", + "glob": "^7.1.6", + "neatequal": "^1.0.0", + "readable-stream": "^3.4.0", + "sax": "^1.2.4", + "svg-pathdata": "^6.0.0" + }, + "bin": { + "svgicons2svgfont": "bin/svgicons2svgfont.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/svgicons2svgfont/node_modules/commander": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/svgicons2svgfont/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/svgicons2svgfont/node_modules/sax": { + "version": "1.2.4", + "dev": true, + "license": "ISC" + }, + "node_modules/svgpath": { + "version": "2.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/synckit": { + "version": "0.8.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tcp-port-used": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "debug": "4.3.1", + "is2": "^2.0.6" + } + }, + "node_modules/tcp-port-used/node_modules/debug": { + "version": "4.3.1", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/terser": { + "version": "5.31.6", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timers-ext": { + "version": "0.1.7", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/triple-beam": { + "version": "1.3.0", + "license": "MIT" + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-morph": { + "version": "23.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.24.0", + "code-block-writer": "^13.0.1" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/ttf2eot": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.6", + "microbuffer": "^1.0.0" + }, + "bin": { + "ttf2eot": "ttf2eot.js" + } + }, + "node_modules/ttf2eot/node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/ttf2woff": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.6", + "microbuffer": "^1.0.0", + "pako": "^1.0.0" + }, + "bin": { + "ttf2woff": "ttf2woff.js" + } + }, + "node_modules/ttf2woff/node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "dev": true, + "license": "MIT", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/umd-compat-loader": { + "version": "2.1.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "ast-types": "^0.9.2", + "loader-utils": "^1.0.3", + "recast": "^0.11.17" + } + }, + "node_modules/umd-compat-loader/node_modules/json5": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/umd-compat-loader/node_modules/loader-utils": { + "version": "1.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/underscore": { + "version": "1.13.6", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "6.21.2", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "license": "MIT" + }, + "node_modules/unescape-html": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.16", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url": { + "version": "0.10.3", + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/url-parse": { + "version": "1.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "license": "MIT" + }, + "node_modules/util": { + "version": "0.12.5", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/varstream": { + "version": "0.3.2", + "dev": true, + "dependencies": { + "readable-stream": "^1.0.33" + }, + "bin": { + "json2varstream": "cli/json2varstream.js", + "varstream2json": "cli/varstream2json.js" + }, + "engines": { + "node": ">=0.10.*" + } + }, + "node_modules/varstream/node_modules/isarray": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/varstream/node_modules/readable-stream": { + "version": "1.1.14", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/varstream/node_modules/string_decoder": { + "version": "0.10.31", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vinyl": { + "version": "2.2.1", + "license": "MIT", + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "3.4.9", + "license": "MIT", + "dependencies": { + "jsonc-parser": "^2.2.0", + "vscode-languageserver-textdocument": "^1.0.0-next.4", + "vscode-languageserver-types": "^3.15.0-next.6", + "vscode-nls": "^4.1.1", + "vscode-uri": "^2.1.0" + } + }, + "node_modules/vscode-json-languageservice/node_modules/jsonc-parser": { + "version": "2.3.1", + "license": "MIT" + }, + "node_modules/vscode-json-languageservice/node_modules/vscode-nls": { + "version": "4.1.2", + "license": "MIT" + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "license": "MIT", + "dependencies": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "engines": { + "vscode": "^1.82.0" + } + }, + "node_modules/vscode-languageclient/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/vscode-languageclient/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "license": "MIT" + }, + "node_modules/vscode-nls-dev": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "clone": "^2.1.2", + "event-stream": "^3.3.4", + "fancy-log": "^1.3.3", + "glob": "^7.2.0", + "iconv-lite": "^0.6.3", + "is": "^3.3.0", + "source-map": "^0.6.1", + "typescript": "^4.5.4", + "vinyl": "^2.2.1", + "xml2js": "^0.5.0", + "yargs": "^17.3.0" + }, + "bin": { + "vscl": "lib/vscl.js" + } + }, + "node_modules/vscode-nls-dev/node_modules/glob": { + "version": "7.2.3", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vscode-nls-dev/node_modules/iconv-lite": { + "version": "0.6.3", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vscode-nls-dev/node_modules/typescript": { + "version": "4.9.5", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/vscode-nls-dev/node_modules/xml2js": { + "version": "0.5.0", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vscode-uri": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.3.4", + "@vue/compiler-sfc": "3.3.4", + "@vue/runtime-dom": "3.3.4", + "@vue/server-renderer": "3.3.4", + "@vue/shared": "3.3.4" + } + }, + "node_modules/vue-loader": { + "version": "17.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "watchpack": "^2.4.0" + }, + "peerDependencies": { + "webpack": "^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-style-loader/node_modules/hash-sum": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-style-loader/node_modules/json5": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vue-style-loader/node_modules/loader-utils": { + "version": "1.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wawoff2": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "woff2_compress.js": "bin/woff2_compress.js", + "woff2_decompress.js": "bin/woff2_decompress.js" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "dev": true, + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-toolkit": { + "resolved": "packages/core/src/web", + "link": true + }, + "node_modules/web-tree-sitter": { + "version": "0.20.8", + "license": "MIT" + }, + "node_modules/webfont": { + "version": "11.2.26", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^5.2.0", + "deepmerge": "^4.2.2", + "globby": "^11.0.0", + "meow": "^9.0.0", + "nunjucks": "^3.2.3", + "p-limit": "^3.1.0", + "parse-json": "^5.2.0", + "resolve-from": "^5.0.0", + "svg2ttf": "^6.0.2", + "svgicons2svgfont": "^10.0.3", + "ttf2eot": "^2.0.0", + "ttf2woff": "^2.0.2", + "wawoff2": "^2.0.0", + "xml2js": "^0.4.23" + }, + "bin": { + "webfont": "dist/cli.js" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/webfont/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/webfont/node_modules/xml2js": { + "version": "0.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.95.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.11.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.8.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/events": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/webpack/node_modules/webpack-sources": { + "version": "3.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.0.0", + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.8", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/win-ca": { + "version": "3.5.1", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "is-electron": "^2.2.0", + "make-dir": "^1.3.0", + "node-forge": "^1.2.1", + "split": "^1.0.1" + } + }, + "node_modules/win-ca/node_modules/make-dir": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/winston": { + "version": "3.11.0", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.6.0", + "license": "MIT", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston/node_modules/@colors/colors": { + "version": "1.6.0", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/xtend": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.2", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yaml-ast-parser-custom-tags": { + "version": "0.0.43", + "license": "Apache-2.0" + }, + "node_modules/yaml-cfn": { + "version": "0.3.2", + "license": "Apache-2.0", + "dependencies": { + "js-yaml": "^4.0.0" + }, + "bin": { + "yaml-cfn": "cli.js" + } + }, + "node_modules/yaml-language-server": { + "version": "0.15.0", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "jsonc-parser": "^2.2.1", + "request-light": "^0.2.4", + "vscode-json-languageservice": "^3.10.0", + "vscode-languageserver": "^5.2.1", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.1", + "yaml-language-server-parser": "0.1.2" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + }, + "engines": { + "node": "*" + }, + "optionalDependencies": { + "prettier": "2.0.5" + } + }, + "node_modules/yaml-language-server-parser": { + "version": "0.1.2", + "license": "Apache-2.0" + }, + "node_modules/yaml-language-server/node_modules/argparse": { + "version": "1.0.10", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/yaml-language-server/node_modules/js-yaml": { + "version": "3.14.1", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/yaml-language-server/node_modules/jsonc-parser": { + "version": "2.3.1", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/prettier": { + "version": "2.0.5", + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-json-languageservice": { + "version": "3.11.0", + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "3.16.0-next.2", + "vscode-nls": "^5.0.0", + "vscode-uri": "^2.1.2" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-json-languageservice/node_modules/jsonc-parser": { + "version": "3.2.0", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-json-languageservice/node_modules/vscode-languageserver-types": { + "version": "3.16.0-next.2", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-json-languageservice/node_modules/vscode-nls": { + "version": "5.2.0", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-jsonrpc": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8.0.0 || >=10.0.0" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver": { + "version": "5.2.1", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.14.1", + "vscode-uri": "^1.0.6" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver-protocol": { + "version": "3.14.1", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "^4.0.0", + "vscode-languageserver-types": "3.14.0" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { + "version": "3.14.0", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver/node_modules/vscode-uri": { + "version": "1.0.8", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-nls": { + "version": "4.1.2", + "license": "MIT" + }, + "node_modules/yargs": { + "version": "17.7.2", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/cliui": { + "version": "8.0.1", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/ylru": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/amazonq": { + "name": "amazon-q-vscode", + "version": "1.103.0-SNAPSHOT", + "license": "Apache-2.0", + "dependencies": { + "aws-core-vscode": "file:../core/" + }, + "engines": { + "npm": "^10.1.0", + "vscode": "^1.83.0" + } + }, + "packages/core": { + "name": "aws-core-vscode", + "version": "1.0.0", + "hasInstallScript": true, + "license": "Apache-2.0", + "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-accessanalyzer": "^3.888.0", + "@aws-sdk/client-api-gateway": "<3.731.0", + "@aws-sdk/client-apprunner": "<3.731.0", + "@aws-sdk/client-cloudcontrol": "<3.731.0", + "@aws-sdk/client-cloudformation": "<3.731.0", + "@aws-sdk/client-cloudwatch-logs": "<3.731.0", + "@aws-sdk/client-codecatalyst": "<3.731.0", + "@aws-sdk/client-cognito-identity": "<3.731.0", + "@aws-sdk/client-datazone": "^3.848.0", + "@aws-sdk/client-docdb": "<3.731.0", + "@aws-sdk/client-docdb-elastic": "<3.731.0", + "@aws-sdk/client-ec2": "<3.731.0", + "@aws-sdk/client-ecr": "~3.693.0", + "@aws-sdk/client-ecs": "~3.693.0", + "@aws-sdk/client-glue": "^3.852.0", + "@aws-sdk/client-iam": "<3.731.0", + "@aws-sdk/client-iot": "~3.693.0", + "@aws-sdk/client-iotsecuretunneling": "~3.693.0", + "@aws-sdk/client-lambda": "<3.731.0", + "@aws-sdk/client-redshift": "~3.693.0", + "@aws-sdk/client-redshift-data": "~3.693.0", + "@aws-sdk/client-redshift-serverless": "~3.693.0", + "@aws-sdk/client-s3": "<3.731.0", + "@aws-sdk/client-s3-control": "^3.830.0", + "@aws-sdk/client-sagemaker": "<3.696.0", + "@aws-sdk/client-schemas": "~3.693.0", + "@aws-sdk/client-secrets-manager": "~3.693.0", + "@aws-sdk/client-sfn": "<3.731.0", + "@aws-sdk/client-ssm": "<3.731.0", + "@aws-sdk/client-sso": "<3.731.0", + "@aws-sdk/client-sso-oidc": "<3.731.0", + "@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", + "@aws-sdk/s3-request-presigner": "<3.731.0", + "@aws-sdk/smithy-client": "<3.731.0", + "@aws-sdk/util-arn-parser": "<3.731.0", + "@aws/mynah-ui": "^4.35.4", + "@gerhobbelt/gitignore-parser": "^0.2.0-9", + "@iarna/toml": "^2.2.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/util-retry": "^4.0.1", + "@svgdotjs/svg.js": "^3.0.16", + "@vscode/debugprotocol": "^1.57.0", + "@zip.js/zip.js": "^2.7.41", + "adm-zip": "^0.5.10", + "amazon-states-language-service": "^1.16.1", + "async-lock": "^1.4.0", + "aws-sdk": "^2.1692.0", + "aws-ssm-document-language-service": "^1.0.0", + "bytes": "^3.1.2", + "cross-fetch": "^4.0.0", + "cross-spawn": "^7.0.5", + "diff": "^5.1.0", + "fast-json-patch": "^3.1.1", + "glob": "^10.3.10", + "got": "^11.8.5", + "highlight.js": "^11.9.0", + "http2": "^3.3.6", + "i18n-ts": "^1.0.5", + "immutable": "^4.3.0", + "jaro-winkler": "^0.2.8", + "jose": "5.4.1", + "js-yaml": "^4.1.0", + "jsonc-parser": "^3.2.0", + "lodash": "^4.17.21", + "lokijs": "^1.5.12", + "markdown-it": "^13.0.2", + "mime-types": "^2.1.32", + "node-fetch": "^2.7.0", + "portfinder": "^1.0.32", + "protobufjs": "^7.2.6", + "semver": "^7.5.4", + "stream-buffers": "^3.0.2", + "strip-ansi": "^5.2.0", + "svgdom": "^0.1.0", + "tcp-port-used": "^1.0.1", + "vscode-languageclient": "^9.0.1", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.15.3", + "vscode-languageserver-textdocument": "^1.0.8", + "vue": "^3.3.4", + "web-tree-sitter": "^0.20.8", + "whatwg-url": "^14.0.0", + "winston": "^3.11.0", + "winston-transport": "^4.6.0", + "ws": "^8.16.0", + "xml2js": "^0.6.1", + "yaml-cfn": "^0.3.2" + }, + "devDependencies": { + "@aws-sdk/types": "^3.13.1", + "@aws/chat-client": "^0.1.4", + "@aws/chat-client-ui-types": "^0.1.47", + "@aws/language-server-runtimes": "^0.2.119", + "@aws/language-server-runtimes-types": "^0.1.47", + "@cspotcode/source-map-support": "^0.8.1", + "@sinonjs/fake-timers": "^10.0.2", + "@types/adm-zip": "^0.4.34", + "@types/async-lock": "^1.4.0", + "@types/bytes": "^3.1.0", + "@types/circular-dependency-plugin": "^5.0.8", + "@types/cross-spawn": "^6.0.6", + "@types/diff": "^5.0.7", + "@types/glob": "^8.1.0", + "@types/js-yaml": "^4.0.5", + "@types/jsdom": "^21.1.6", + "@types/lodash": "^4.14.180", + "@types/lokijs": "^1.5.14", + "@types/markdown-it": "^13.0.2", + "@types/mime-types": "^2.1.4", + "@types/mocha": "^10.0.6", + "@types/node": "^16.18.95", + "@types/node-fetch": "^2.6.8", + "@types/prismjs": "^1.26.0", + "@types/proper-lockfile": "^4.1.4", + "@types/readline-sync": "^1.4.8", + "@types/semver": "^7.5.0", + "@types/sinon": "^10.0.5", + "@types/sinonjs__fake-timers": "^8.1.2", + "@types/stream-buffers": "^3.0.7", + "@types/svgdom": "^0.1.2", + "@types/tcp-port-used": "^1.0.1", + "@types/uuid": "^9.0.1", + "@types/whatwg-url": "^11.0.4", + "@types/xml2js": "^0.4.11", + "@vue/compiler-sfc": "^3.3.2", + "aws-sdk-client-mock": "^4.1.0", + "c8": "^9.0.0", + "circular-dependency-plugin": "^5.2.2", + "css-loader": "^6.10.0", + "esbuild-loader": "2.20.0", + "file-loader": "^6.2.0", + "jsdom": "^23.0.1", + "json-schema-to-typescript": "^13.1.1", + "marked": "^13.0.1", + "mocha": "^10.1.0", + "mocha-junit-reporter": "^2.2.1", + "mocha-multi-reporters": "^1.5.1", + "readline-sync": "^1.4.9", + "sass": "^1.49.8", + "sass-loader": "^16.0.2", + "sinon": "^14.0.0", + "style-loader": "^3.3.1", + "ts-node": "^10.9.1", + "typescript": "^5.0.4", + "umd-compat-loader": "^2.1.2", + "vue-loader": "^17.2.2", + "vue-style-loader": "^4.1.3", + "webfont": "^11.2.26" + }, + "engines": { + "npm": "^10.1.0", + "vscode": "^1.83.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb": { + "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-sdk-rds": "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" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic": { + "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", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-ecs": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ecs/-/client-ecs-3.693.0.tgz", + "integrity": "sha512-HbMtxh+gBtdHS4v0lZk7mb/E9PtjK9m2mDxiqyTXcZkdYPnq3MGACgUNUt8Siv+BgzQJTP8jikflCeMQ4ECHmw==", + "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" + } + }, + "packages/core/node_modules/@aws-sdk/client-ecs/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", + "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-ecs/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", + "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-ecs/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", + "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-ecs/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-ecs/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-ecs/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-schemas": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-schemas/-/client-schemas-3.693.0.tgz", + "integrity": "sha512-a6B9z2hBlO67c8k6WMJNhFP26VCYEaL7aAo3oe/IbT1sncD6cSoROF5L0o9ebsosA+81Xkkvjj2zeF/+ohdAng==", + "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-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" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-schemas/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", + "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-schemas/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", + "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-schemas/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", + "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-schemas/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-schemas/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-schemas/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-secrets-manager": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.693.0.tgz", + "integrity": "sha512-PiXkl64LYhwZQ2zPQhxwpnLwGS7Lw8asFCj29SxEaYRnYra3ajE5d+Yvv68qC+diUNkeZh6k6zn7nEOZ4rWEwA==", + "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", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-secrets-manager/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", + "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-secrets-manager/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", + "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-secrets-manager/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", + "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-secrets-manager/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-secrets-manager/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-secrets-manager/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/core/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-node/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-host-header/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-recursion-detection/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-sdk-rds": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-format-url": "3.693.0", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-sdk-rds/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-user-agent/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@aws-sdk/token-providers/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/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" + } + }, + "packages/core/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 + } + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/querystring-builder": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry": { + "version": "4.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/service-error-classification": "^4.0.2", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.2", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/core": { + "version": "3.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.3", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-stream": "^4.2.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.2.0", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", + "@smithy/util-middleware": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/middleware-serde": { + "version": "4.0.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/middleware-stack": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/node-config-provider": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/property-provider": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/smithy-client": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.2.0", + "@smithy/middleware-endpoint": "^4.1.0", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-stream": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/url-parser": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-middleware": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-stream": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/types": "^4.2.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" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/node-http-handler": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/node-http-handler/node_modules/@smithy/abort-controller": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/node-http-handler/node_modules/@smithy/querystring-builder": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/node-http-handler/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/protocol-http": { + "version": "5.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/protocol-http/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/querystring-parser": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/querystring-parser/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/service-error-classification": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/service-error-classification/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/shared-ini-file-loader/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/util-retry": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/util-retry/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/@types/node": { + "version": "16.18.95", + "dev": true, + "license": "MIT" + }, + "packages/core/src/web": { + "name": "web-toolkit", + "license": "Apache-2.0", + "dependencies": { + "crypto-browserify": "^3.12.0", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "stream-browserify": "^3.0.0" + }, + "devDependencies": { + "assert": "^2.1.0" + } + }, + "packages/toolkit": { + "name": "aws-toolkit-vscode", + "version": "3.83.0-SNAPSHOT", + "license": "Apache-2.0", + "dependencies": { + "aws-core-vscode": "file:../core/" + }, + "devDependencies": {}, + "engines": { + "npm": "^10.1.0", + "vscode": "^1.83.0" + } + }, + "plugins/eslint-plugin-aws-toolkits": { + "version": "1.0.0", + "license": "Apache-2.0", + "devDependencies": { + "@types/eslint": "^8.56.0", + "mocha": "^10.1.0" + }, + "engines": { + "npm": "^10.1.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client": { + "version": "1.0.0", + "hasInstallScript": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/credential-provider-node": "3.731.1", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/eventstream-serde-browser": "^4.0.1", + "@smithy/eventstream-serde-config-resolver": "^4.0.1", + "@smithy/eventstream-serde-node": "^4.0.1", + "@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-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@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.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "devDependencies": { + "@tsconfig/node18": "18.2.4", + "@types/node": "^18.19.69", + "concurrently": "7.0.0", + "downlevel-dts": "0.10.1", + "rimraf": "^3.0.0", + "typescript": "~5.2.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/client-sso": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/core": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/credential-provider-env": "3.731.0", + "@aws-sdk/credential-provider-http": "3.731.0", + "@aws-sdk/credential-provider-process": "3.731.0", + "@aws-sdk/credential-provider-sso": "3.731.1", + "@aws-sdk/credential-provider-web-identity": "3.731.1", + "@aws-sdk/nested-clients": "3.731.1", + "@aws-sdk/types": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.731.0", + "@aws-sdk/credential-provider-http": "3.731.0", + "@aws-sdk/credential-provider-ini": "3.731.1", + "@aws-sdk/credential-provider-process": "3.731.0", + "@aws-sdk/credential-provider-sso": "3.731.1", + "@aws-sdk/credential-provider-web-identity": "3.731.1", + "@aws-sdk/types": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.731.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/token-providers": "3.731.1", + "@aws-sdk/types": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/nested-clients": "3.731.1", + "@aws-sdk/types": "3.731.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/middleware-logger": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/nested-clients": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/token-providers": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.731.1", + "@aws-sdk/types": "3.731.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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/types": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/util-endpoints": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/util-locate-window": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/types": "3.731.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 + } + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@babel/runtime": { + "version": "7.26.9", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-codec": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", + "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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "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" } }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "requires": { - "ee-first": "1.1.1" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "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" + }, + "engines": { + "node": ">=18.0.0" } }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" } }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "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" + } }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "p-retry": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", - "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", - "dev": true, - "requires": { - "@types/retry": "^0.12.0", - "retry": "^0.13.1" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "package-hash": { + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-body-length-browser": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "dependencies": { + "@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" + } }, - "parse-semver": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", - "integrity": "sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg=", - "dev": true, - "requires": { - "semver": "^5.1.0" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", + "license": "Apache-2.0", + "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": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "requires": { - "parse5": "^6.0.1" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", + "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": ">=18.0.0" + } }, - "path-exists": { + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-uri-escape": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@tsconfig/node18": { + "version": "18.2.4", + "dev": true, + "license": "MIT" }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@types/node": { + "version": "18.19.80", "dev": true, - "requires": { - "isarray": "0.0.1" - }, + "license": "MIT", "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } + "undici-types": "~5.26.4" } }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@types/uuid": { + "version": "9.0.8", + "license": "MIT" }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/ansi-regex": { + "version": "5.0.1", "dev": true, - "requires": { - "through": "~2.3" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/ansi-styles": { + "version": "4.3.0", "dev": true, - "requires": { - "find-up": "^4.0.0" - }, + "license": "MIT", "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" + "color-convert": "^2.0.1" }, - "dependencies": { - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "requires": { - "lodash": "^4.17.14" - } - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "postcss": { - "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", - "requires": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/balanced-match": { + "version": "1.0.2", "dev": true, - "requires": {} + "license": "MIT" }, - "postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/bowser": { + "version": "2.11.0", + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/brace-expansion": { + "version": "1.1.11", "dev": true, - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/chalk": { + "version": "4.1.2", "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", "dev": true, - "requires": { - "icss-utils": "^5.0.0" + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/cliui": { + "version": "7.0.4", "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", - "dev": true - }, - "prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/color-convert": { + "version": "2.0.1", "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" }, - "prettier-plugin-sh": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-sh/-/prettier-plugin-sh-0.8.1.tgz", - "integrity": "sha512-tz0g6y+ZaJF0PWaa1F7vhCv4nLgYYl2zYzYU4XJFD1McoY0oHI+l2osvXqv1s5yQdtjdlzKszN6VY7WTaw2Gqw==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/concat-map": { + "version": "0.0.1", "dev": true, - "requires": { - "mvdan-sh": "^0.5.0" - } + "license": "MIT" }, - "pretty-quick": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.1.tgz", - "integrity": "sha512-ZYLGiMoV2jcaas3vTJrLvKAYsxDoXQBUn8OSTxkl67Fyov9lyXivJTl0+2WVh+y6EovGcw7Lm5ThYpH+Sh3XxQ==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/concurrently": { + "version": "7.0.0", "dev": true, - "requires": { - "chalk": "^3.0.0", - "execa": "^4.0.0", - "find-up": "^4.1.0", - "ignore": "^5.1.4", - "mri": "^1.1.5", - "multimatch": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.0 || >=16.0.0" } }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/date-fns": { + "version": "2.30.0", "dev": true, - "requires": { - "fromentries": "^1.2.0" + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" } }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/downlevel-dts": { + "version": "0.10.1", "dev": true, - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, + "license": "MIT", "dependencies": { - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - } + "semver": "^7.3.2", + "shelljs": "^0.8.3", + "typescript": "next" + }, + "bin": { + "downlevel-dts": "index.js" } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/downlevel-dts/node_modules/typescript": { + "version": "5.9.0-dev.20250324", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/escalade": { + "version": "3.2.0", "dev": true, - "requires": { - "side-channel": "^1.0.4" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/fast-xml-parser": { + "version": "4.4.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/function-bind": { + "version": "1.1.2", "dev": true, - "requires": { - "safe-buffer": "^5.1.0" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/get-caller-file": { + "version": "2.0.5", "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/glob": { + "version": "7.2.3", "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, + "license": "ISC", "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - } + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/has-flag": { + "version": "4.0.0", "dev": true, - "requires": { - "mute-stream": "~0.0.4" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/hasown": { + "version": "2.0.2", "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, + "license": "MIT", "dependencies": { - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/inflight": { + "version": "1.0.6", "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, + "license": "ISC", "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } + "once": "^1.3.0", + "wrappy": "1" } }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/interpret": { + "version": "1.4.0", "dev": true, - "requires": { - "picomatch": "^2.2.1" + "license": "MIT", + "engines": { + "node": ">= 0.10" } }, - "readline-sync": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", - "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", - "dev": true - }, - "recast": { - "version": "0.11.23", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", - "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/is-core-module": { + "version": "2.16.1", "dev": true, - "requires": { - "ast-types": "0.9.6", - "esprima": "~3.1.0", - "private": "~0.1.5", - "source-map": "~0.5.0" - }, + "license": "MIT", "dependencies": { - "ast-types": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", - "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", - "dev": true - }, - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", "dev": true, - "requires": { - "resolve": "^1.9.0" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/lodash": { + "version": "4.17.21", "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } + "license": "MIT" }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/path-is-absolute": { + "version": "1.0.1", "dev": true, - "requires": { - "es6-error": "^4.0.1" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" }, - "request-light": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.2.5.tgz", - "integrity": "sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw==", - "requires": { - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "vscode-nls": "^4.1.1" - }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/rechoir": { + "version": "0.6.2", + "dev": true, "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "requires": { - "agent-base": "4", - "debug": "3.1.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - } + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/regenerator-runtime": { + "version": "0.14.1", + "dev": true, + "license": "MIT" }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/resolve": { + "version": "1.22.10", "dev": true, - "requires": { - "is-core-module": "^2.9.0", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/rimraf": { + "version": "3.0.2", "dev": true, - "requires": { - "resolve-from": "^5.0.0" + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/rxjs": { + "version": "6.6.7", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" } }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" }, - "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", - "requires": { - "lowercase-keys": "^2.0.0" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/semver": { + "version": "7.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/shelljs": { + "version": "0.8.5", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/spawn-command": { + "version": "0.0.2", "dev": true }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/string-width": { + "version": "4.2.3", "dev": true, - "requires": { - "glob": "^7.1.3" + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/strip-ansi": { + "version": "6.0.1", "dev": true, - "requires": { - "queue-microtask": "^1.2.2" + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safe-stable-stringify": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", - "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/strnum": { + "version": "1.1.2", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } }, - "selfsigned": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", - "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/typescript": { + "version": "5.2.2", "dev": true, - "requires": { - "node-forge": "^1" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "requires": { - "lru-cache": "^6.0.0" + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/undici-types": { + "version": "5.26.5", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/wrap-ansi": { + "version": "7.0.0", "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, + "license": "MIT", "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/wrappy": { + "version": "1.0.2", "dev": true, - "requires": { - "randombytes": "^2.1.0" + "license": "ISC" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" } }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/yargs": { + "version": "16.2.0", "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, + "license": "MIT", "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - } + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/yargs-parser": { + "version": "20.2.9", "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" + "license": "ISC", + "engines": { + "node": ">=10" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming": { + "version": "1.0.0", + "hasInstallScript": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/token-providers": "3.731.1", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/eventstream-serde-browser": "^4.0.1", + "@smithy/eventstream-serde-config-resolver": "^4.0.1", + "@smithy/eventstream-serde-node": "^4.0.1", + "@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-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@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.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "devDependencies": { + "@tsconfig/node18": "18.2.4", + "@types/node": "^18.19.69", + "concurrently": "7.0.0", + "downlevel-dts": "0.10.1", + "rimraf": "^3.0.0", + "typescript": "~5.2.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/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" + } }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/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" + } }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "src.gen/@amzn/codewhisperer-streaming/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" } }, - "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } }, - "simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "dev": true, - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", "dependencies": { - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, - "requires": { - "mimic-response": "^2.0.0" - } - }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true - } + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "sinon": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", - "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.1.2", - "@sinonjs/samsam": "^6.1.1", - "diff": "^5.0.0", - "nise": "^5.1.1", - "supports-color": "^7.2.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/core": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.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" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.731.0", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - } + "@aws-sdk/types": "3.731.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/middleware-logger": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "requires": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.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" + } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/nested-clients": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.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" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.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" + } }, - "source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/token-providers": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.731.1", + "@aws-sdk/types": "3.731.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" + } }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/types": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/util-endpoints": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "requires": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/util-locate-window": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/types": "3.731.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 + } + } }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/@babel/runtime": { + "version": "7.26.9", "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "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" } }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "requires": { - "through": "2" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "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" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "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" + } }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-codec": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "through": "~2.3.4" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.1", + "license": "Apache-2.0", "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } + "@smithy/eventstream-codec": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "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" } }, - "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "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" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "strip-bom": { + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/is-array-buffer": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "svg-pathdata": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", - "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", - "dev": true - }, - "svg2ttf": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/svg2ttf/-/svg2ttf-6.0.3.tgz", - "integrity": "sha512-CgqMyZrbOPpc+WqH7aga4JWkDPso23EgypLsbQ6gN3uoPWwwiLjXvzgrwGADBExvCRJrWFzAeK1bSoSpE7ixSQ==", - "dev": true, - "requires": { - "@xmldom/xmldom": "^0.7.2", - "argparse": "^2.0.1", - "cubic2quad": "^1.2.1", - "lodash": "^4.17.10", - "microbuffer": "^1.0.0", - "svgpath": "^2.1.5" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "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" } }, - "svgicons2svgfont": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/svgicons2svgfont/-/svgicons2svgfont-10.0.6.tgz", - "integrity": "sha512-fUgQEVg3XwTbOHvlXahHGqCet5Wvfo1bV4DCvbSRvjsOCPCRunYbG4dUJCPegps37BMph3eOrfoobhH5AWuC6A==", - "dev": true, - "requires": { - "commander": "^7.2.0", - "geometry-interfaces": "^1.1.4", - "glob": "^7.1.6", - "neatequal": "^1.0.0", - "readable-stream": "^3.4.0", - "sax": "^1.2.4", - "svg-pathdata": "^6.0.0" - }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - } + "@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" } }, - "svgpath": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/svgpath/-/svgpath-2.5.0.tgz", - "integrity": "sha512-o/vohwqjUO9nDAh4rcjE3KaW/v//At8UJu2LJMybXidf5QLQLVA4bxH0//4YCsr+1H4Gw1Wi/Jc62ynzSBYidw==", - "dev": true - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "tcp-port-used": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", - "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", - "requires": { - "debug": "4.3.1", - "is2": "^2.0.6" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "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" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - } + "@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": ">=18.0.0" } }, - "terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", - "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "terser-webpack-plugin": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", - "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", - "dev": true, - "requires": { - "jest-worker": "^27.0.6", - "p-limit": "^3.1.0", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "requires": { - "any-promise": "^1.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "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" } }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", - "dev": true, - "requires": { - "thenify": ">= 3.1.0 < 4" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "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", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "dev": true, - "requires": { - "es5-ext": "~0.10.46", - "next-tick": "1" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "dependencies": { + "@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" + } }, - "traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", + "license": "Apache-2.0", + "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": ">=18.0.0" + } }, - "trim-newlines": { + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-endpoints": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "ts-mockito": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ts-mockito/-/ts-mockito-2.6.1.tgz", - "integrity": "sha512-qU9m/oEBQrKq5hwfbJ7MgmVN5Gu6lFnIGWvpxSjrqq6YYEVv+RwVFWySbZMBgazsWqv6ctAyVBpo9TmAxnOEKw==", - "dev": true, - "requires": { - "lodash": "^4.17.5" + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "ts-morph": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-17.0.1.tgz", - "integrity": "sha512-10PkHyXmrtsTvZSL+cqtJLTgFXkU43Gd0JCc0Rw6GchWbqKe0Rwgt1v3ouobTZwQzF1mGhDeAlWYBMGRV7y+3g==", - "dev": true, - "requires": { - "@ts-morph/common": "~0.18.0", - "code-block-writer": "^11.0.3" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", - "yn": "3.1.1" - }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", "dependencies": { - "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, - "requires": { - "@cspotcode/source-map-consumer": "0.8.0" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - } + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } + "@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" } }, - "ttf2eot": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ttf2eot/-/ttf2eot-2.0.0.tgz", - "integrity": "sha512-U56aG2Ylw7psLOmakjemAzmpqVgeadwENg9oaDjaZG5NYX4WB6+7h74bNPcc+0BXsoU5A/XWiHabDXyzFOmsxQ==", - "dev": true, - "requires": { - "argparse": "^1.0.6", - "microbuffer": "^1.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - } + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "ttf2woff": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ttf2woff/-/ttf2woff-2.0.2.tgz", - "integrity": "sha512-X68badwBjAy/+itU49scLjXUL094up+rHuYk+YAOTTBYSUMOmLZ7VyhZJuqQESj1gnyLAC2/5V8Euv+mExmyPA==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/@tsconfig/node18": { + "version": "18.2.4", "dev": true, - "requires": { - "argparse": "^1.0.6", - "microbuffer": "^1.0.0", - "pako": "^1.0.0" - }, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@types/node": { + "version": "18.19.80", + "dev": true, + "license": "MIT", "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - } + "undici-types": "~5.26.4" } }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/@types/uuid": { + "version": "9.0.8", + "license": "MIT" }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "src.gen/@amzn/codewhisperer-streaming/node_modules/ansi-regex": { + "version": "5.0.1", "dev": true, - "requires": { - "safe-buffer": "^5.0.1" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/ansi-styles": { + "version": "4.3.0", "dev": true, - "requires": { - "prelude-ls": "^1.2.1" + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/bowser": { + "version": "2.11.0", + "license": "MIT" }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/brace-expansion": { + "version": "1.1.11", "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "typed-rest-client": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.6.tgz", - "integrity": "sha512-xcQpTEAJw2DP7GqVNECh4dD+riS+C1qndXLfBCJ3xk0kqprtGN491P5KlmrDbKdtuW8NEcP/5ChxiJI3S9WYTA==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/chalk": { + "version": "4.1.2", "dev": true, - "requires": { - "qs": "^6.9.1", - "tunnel": "0.0.6", - "underscore": "^1.12.1" + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", "dev": true, - "requires": { - "is-typedarray": "^1.0.0" + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==" - }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "umd-compat-loader": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/umd-compat-loader/-/umd-compat-loader-2.1.2.tgz", - "integrity": "sha512-RkTlsfrCxUISWqiTtYFFJank7b2Hhl4V2pc29nl0xOEGvvuVkpy1xnufhXfTituxgpW0HSrDk0JHlvPYZxEXKQ==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/cliui": { + "version": "7.0.4", "dev": true, - "requires": { - "ast-types": "^0.9.2", - "loader-utils": "^1.0.3", - "recast": "^0.11.17" - }, + "license": "ISC", "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "src.gen/@amzn/codewhisperer-streaming/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", - "dev": true - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + "src.gen/@amzn/codewhisperer-streaming/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" }, - "unzipper": { - "version": "0.10.11", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", - "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", - "dev": true, - "requires": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "graceful-fs": "^4.2.2", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "src.gen/@amzn/codewhisperer-streaming/node_modules/concurrently": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.0 || >=16.0.0" } }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/date-fns": { + "version": "2.30.0", "dev": true, - "requires": { - "punycode": "^2.1.0" + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" } }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/downlevel-dts": { + "version": "0.10.1", + "dev": true, + "license": "MIT", "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } + "semver": "^7.3.2", + "shelljs": "^0.8.3", + "typescript": "next" + }, + "bin": { + "downlevel-dts": "index.js" } }, - "url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", - "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" + "src.gen/@amzn/codewhisperer-streaming/node_modules/downlevel-dts/node_modules/typescript": { + "version": "5.9.0-dev.20250324", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/escalade": { + "version": "3.2.0", "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "varstream": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/varstream/-/varstream-0.3.2.tgz", - "integrity": "sha512-OpR3Usr9dGZZbDttlTxdviGdxiURI0prX68+DuaN/JfIDbK9ZOmREKM6PgmelsejMnhgjXmEEEgf+E4NbsSqMg==", - "dev": true, - "requires": { - "readable-stream": "^1.0.33" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } + "src.gen/@amzn/codewhisperer-streaming/node_modules/fast-xml-parser": { + "version": "4.4.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" } }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" }, - "vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/function-bind": { + "version": "1.1.2", "dev": true, - "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "vsce": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.6.7.tgz", - "integrity": "sha512-5dEtdi/yzWQbOU7JDUSOs8lmSzzkewBR5P122BUkmXE6A/DEdFsKNsg2773NGXJTwwF1MfsOgUR6QVF3cLLJNQ==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/get-caller-file": { + "version": "2.0.5", "dev": true, - "requires": { - "azure-devops-node-api": "^11.0.1", - "chalk": "^2.4.2", - "cheerio": "^1.0.0-rc.9", - "commander": "^6.1.0", - "glob": "^7.0.6", - "hosted-git-info": "^4.0.2", - "keytar": "^7.7.0", - "leven": "^3.1.0", - "markdown-it": "^12.3.2", - "mime": "^1.3.4", - "minimatch": "^3.0.3", - "parse-semver": "^1.1.1", - "read": "^1.0.7", - "semver": "^5.1.0", - "tmp": "^0.2.1", - "typed-rest-client": "^1.8.4", - "url-join": "^4.0.1", - "xml2js": "^0.4.23", - "yauzl": "^2.3.1", - "yazl": "^2.2.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "vscode-json-languageservice": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.4.9.tgz", - "integrity": "sha512-4VCpZ9ooea/Zc/MTnj1ccc9C7rqcoinKVQLhLoi6jw6yueSf4y4tg/YIUiPPVMlEAG7ZCPS+NVmqxisQ+mOsSw==", - "requires": { - "jsonc-parser": "^2.2.0", - "vscode-languageserver-textdocument": "^1.0.0-next.4", - "vscode-languageserver-types": "^3.15.0-next.6", - "vscode-nls": "^4.1.1", - "vscode-uri": "^2.1.0" - }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", "dependencies": { - "jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" - }, - "vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - } + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "vscode-jsonrpc": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", - "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==" - }, - "vscode-languageclient": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.1.4.tgz", - "integrity": "sha512-EUOU+bJu6axmt0RFNo3nrglQLPXMfanbYViJee3Fbn2VuQoX0ZOI4uTYhSRvYLP2vfwTP/juV62P/mksCdTZMA==", - "requires": { - "semver": "^6.3.0", - "vscode-languageserver-protocol": "3.15.3" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } + "src.gen/@amzn/codewhisperer-streaming/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, - "vscode-languageserver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz", - "integrity": "sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ==", - "requires": { - "vscode-languageserver-protocol": "^3.15.3" + "src.gen/@amzn/codewhisperer-streaming/node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "vscode-languageserver-protocol": { - "version": "3.15.3", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", - "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", - "requires": { - "vscode-jsonrpc": "^5.0.1", - "vscode-languageserver-types": "3.15.1" - }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", "dependencies": { - "vscode-languageserver-types": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", - "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" - } + "once": "^1.3.0", + "wrappy": "1" } }, - "vscode-languageserver-textdocument": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz", - "integrity": "sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A==" - }, - "vscode-languageserver-types": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", - "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" - }, - "vscode-nls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.0.tgz", - "integrity": "sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==" + "src.gen/@amzn/codewhisperer-streaming/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" }, - "vscode-nls-dev": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/vscode-nls-dev/-/vscode-nls-dev-3.3.2.tgz", - "integrity": "sha512-/YJY/LegZ0jsWFd8BforDmXpwWKprM7L3rL0kLEvjQxOJw6qtmnoUJorLIv0ZXjebeyhI3mc8hjmQr479ykLIQ==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/interpret": { + "version": "1.4.0", "dev": true, - "requires": { - "ansi-colors": "^3.2.3", - "clone": "^2.1.1", - "event-stream": "^3.3.4", - "fancy-log": "^1.3.3", - "glob": "^7.1.2", - "iconv-lite": "^0.4.19", - "is": "^3.2.1", - "source-map": "^0.6.1", - "typescript": "^3.9.5", - "vinyl": "^2.1.0", - "xml2js": "^0.4.19", - "yargs": "^13.2.4" - }, - "dependencies": { - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "license": "MIT", + "engines": { + "node": ">= 0.10" } }, - "vscode-uri": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", - "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==" - }, - "vue": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.31.tgz", - "integrity": "sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==", - "requires": { - "@vue/compiler-dom": "3.2.31", - "@vue/compiler-sfc": "3.2.31", - "@vue/runtime-dom": "3.2.31", - "@vue/server-renderer": "3.2.31", - "@vue/shared": "3.2.31" - }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", "dependencies": { - "@vue/compiler-sfc": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz", - "integrity": "sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.31", - "@vue/compiler-dom": "3.2.31", - "@vue/compiler-ssr": "3.2.31", - "@vue/reactivity-transform": "3.2.31", - "@vue/shared": "3.2.31", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" - } - }, - "@vue/reactivity-transform": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz", - "integrity": "sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.31", - "@vue/shared": "3.2.31", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" - } - } + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "vue-loader": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.1.tgz", - "integrity": "sha512-V53TJbHmzjBhCG5OYI2JWy/aYDspz4oVHKxS43Iy212GjGIG1T3EsB3+GWXFm/1z5VwjdjLmdZUFYM70y77vtQ==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", "dev": true, - "requires": { - "chalk": "^4.1.0", - "hash-sum": "^2.0.0", - "loader-utils": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "vue-style-loader": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", - "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/lodash": { + "version": "4.17.21", "dev": true, - "requires": { - "hash-sum": "^1.0.2", - "loader-utils": "^1.0.2" - }, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", "dependencies": { - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/once": { + "version": "1.4.0", "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" + "license": "ISC", + "dependencies": { + "wrappy": "1" } }, - "wawoff2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wawoff2/-/wawoff2-2.0.1.tgz", - "integrity": "sha512-r0CEmvpH63r4T15ebFqeOjGqU4+EgTx4I510NtK35EMciSdcTxCw3Byy3JnBonz7iyIFZ0AbVo0bbFpEVuhCYA==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/path-is-absolute": { + "version": "1.0.1", "dev": true, - "requires": { - "argparse": "^2.0.1" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/path-parse": { + "version": "1.0.7", "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } + "license": "MIT" }, - "webfont": { - "version": "11.2.26", - "resolved": "https://registry.npmjs.org/webfont/-/webfont-11.2.26.tgz", - "integrity": "sha512-ms9abzcJGMBj5yVTpNfAcyQB0SNzmi10aBlKLC7kAt1TQ5eZqieRhvhxN1BrXaizWV0nksp6vLqBfaJmBWmF7Q==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/rechoir": { + "version": "0.6.2", "dev": true, - "requires": { - "cosmiconfig": "^5.2.0", - "deepmerge": "^4.2.2", - "globby": "^11.0.0", - "meow": "^9.0.0", - "nunjucks": "^3.2.3", - "p-limit": "^3.1.0", - "parse-json": "^5.2.0", - "resolve-from": "^5.0.0", - "svg2ttf": "^6.0.2", - "svgicons2svgfont": "^10.0.3", - "ttf2eot": "^2.0.0", - "ttf2woff": "^2.0.2", - "wawoff2": "^2.0.0", - "xml2js": "^0.4.23" - }, "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" } }, - "webpack": { - "version": "5.65.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", - "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/regenerator-runtime": { + "version": "0.14.1", "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", - "webpack-sources": "^3.2.2" - }, - "dependencies": { - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, - "webpack-sources": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz", - "integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==", - "dev": true - } + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "webpack-cli": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", - "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/resolve": { + "version": "1.22.10", "dev": true, - "requires": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.1.0", - "@webpack-cli/info": "^1.4.0", - "@webpack-cli/serve": "^1.6.0", - "colorette": "^2.0.14", - "commander": "^7.0.0", - "execa": "^5.0.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", - "webpack-merge": "^5.7.3" - }, + "license": "MIT", "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - } + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/rimraf": { + "version": "3.0.2", "dev": true, - "requires": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, + "license": "ISC", "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "webpack-dev-server": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.2.tgz", - "integrity": "sha512-H95Ns95dP24ZsEzO6G9iT+PNw4Q7ltll1GfJHV4fKphuHWgKFzGHWi4alTlTnpk1SPPk41X+l2RB7rLfIhnB9Q==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/rxjs": { + "version": "6.6.7", "dev": true, - "requires": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" - }, + "license": "Apache-2.0", "dependencies": { - "ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" } }, - "webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - } + "license": "0BSD" }, - "webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/semver": { + "version": "7.7.1", "dev": true, - "requires": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/shelljs": { + "version": "0.8.5", "dev": true, - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" + "license": "BSD-3-Clause", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" } }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/spawn-command": { + "version": "0.0.2", "dev": true }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "src.gen/@amzn/codewhisperer-streaming/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/strnum": { + "version": "1.1.2", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" }, - "which-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", - "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.9" + "src.gen/@amzn/codewhisperer-streaming/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", "dev": true, - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, - "winston": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.1.tgz", - "integrity": "sha512-qKLMQVWMOvY0h9H4BA8Sfh79+KdnKi8gsyCSvfQgc+6teSlq92j82WK+zAJc6fLbAA2jwQuqkANLpQeOFA4Kug==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.4.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.5.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" } }, - "winston-transport": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", - "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", - "requires": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", - "triple-beam": "^1.3.0" + "src.gen/@amzn/codewhisperer-streaming/node_modules/typescript": { + "version": "5.2.2", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/undici-types": { + "version": "5.26.5", + "dev": true, + "license": "MIT" }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true + "src.gen/@amzn/codewhisperer-streaming/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "wrap-ansi": { + "src.gen/@amzn/codewhisperer-streaming/node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "src.gen/@amzn/codewhisperer-streaming/node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", - "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==", "dev": true, - "requires": {} - }, - "xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", - "dev": true - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + "license": "ISC" }, - "y18n": { + "src.gen/@amzn/codewhisperer-streaming/node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - }, - "yaml-ast-parser-custom-tags": { - "version": "0.0.43", - "resolved": "https://registry.npmjs.org/yaml-ast-parser-custom-tags/-/yaml-ast-parser-custom-tags-0.0.43.tgz", - "integrity": "sha512-R5063FF/JSAN6qXCmylwjt9PcDH6M0ExEme/nJBzLspc6FJDmHHIqM7xh2WfEmsTJqClF79A9VkXjkAqmZw9SQ==" - }, - "yaml-cfn": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/yaml-cfn/-/yaml-cfn-0.3.1.tgz", - "integrity": "sha512-8uEuOFPZFI06cQA+E37oRd9zHEPZVpkVjrBjXxWSt0Hy8hil/KnCcskpR7jwx6ejzfejIi5uzaoQgHTl6qzaNw==", - "requires": { - "js-yaml": "^4.0.0" - } - }, - "yaml-language-server": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-0.10.0.tgz", - "integrity": "sha512-d2/7eGgonEIRcnW9kK+k+ERG4gTOk5BXHr9KjTVv8gEarXKa62Kk+nyFE4AXgMDZ0LXTu8nTuN/AdboJiGN+pQ==", - "requires": { - "js-yaml": "^3.13.1", - "jsonc-parser": "^2.2.1", - "prettier": "2.0.5", - "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.6.0", - "vscode-languageserver": "^5.2.1", - "vscode-languageserver-types": "^3.15.1", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.1", - "yaml-ast-parser-custom-tags": "0.0.43" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" - }, - "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", - "optional": true - }, - "vscode-json-languageservice": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.11.0.tgz", - "integrity": "sha512-QxI+qV97uD7HHOCjh3MrM1TfbdwmTXrMckri5Tus1/FQiG3baDZb2C9Y0y8QThs7PwHYBIQXcAc59ZveCRZKPA==", - "requires": { - "jsonc-parser": "^3.0.0", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "3.16.0-next.2", - "vscode-nls": "^5.0.0", - "vscode-uri": "^2.1.2" - }, - "dependencies": { - "jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==" - }, - "vscode-languageserver-types": { - "version": "3.16.0-next.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz", - "integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==" - }, - "vscode-nls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.0.tgz", - "integrity": "sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==" - } - } - }, - "vscode-jsonrpc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", - "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==" - }, - "vscode-languageserver": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz", - "integrity": "sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A==", - "requires": { - "vscode-languageserver-protocol": "3.14.1", - "vscode-uri": "^1.0.6" - }, - "dependencies": { - "vscode-uri": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz", - "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==" - } - } - }, - "vscode-languageserver-protocol": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz", - "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==", - "requires": { - "vscode-jsonrpc": "^4.0.0", - "vscode-languageserver-types": "3.14.0" - }, - "dependencies": { - "vscode-languageserver-types": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", - "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==" - } - } - }, - "vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - } + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" } }, - "yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/yargs": { + "version": "16.2.0", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.3", + "string-width": "^4.2.0", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^20.2.2" }, - "dependencies": { - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "engines": { + "node": ">=10" } }, - "yazl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", - "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "src.gen/@amzn/codewhisperer-streaming/node_modules/yargs-parser": { + "version": "20.2.9", "dev": true, - "requires": { - "buffer-crc32": "~0.2.3" + "license": "ISC", + "engines": { + "node": ">=10" } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true } } } diff --git a/package.json b/package.json index 9b38eaef846..dd196da079f 100644 --- a/package.json +++ b/package.json @@ -1,3624 +1,13 @@ { - "name": "aws-toolkit-vscode", - "displayName": "AWS Toolkit", - "description": "Amazon Web Services toolkit for browsing and updating cloud resources", - "version": "1.63.0-SNAPSHOT", - "extensionKind": [ - "workspace" + "name": "root", + "workspaces": [ + "packages/core/src/web", + "packages/*", + "plugins/*" ], - "publisher": "amazonwebservices", + "version": "0.0.1", + "private": true, "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "https://github.com/aws/aws-toolkit-vscode" - }, - "engines": { - "vscode": "^1.50.1" - }, - "icon": "resources/marketplace/aws-icon-256x256.png", - "bugs": { - "url": "https://github.com/aws/aws-toolkit-vscode/issues" - }, - "galleryBanner": { - "color": "#FF9900", - "theme": "light" - }, - "categories": [ - "Debuggers", - "Linters", - "Other" - ], - "keywords": [ - "AWS", - "CodeCatalyst", - "CodeWhisperer", - "Lambda", - "Serverless" - ], - "preview": false, - "qna": "https://github.com/aws/aws-toolkit-vscode/issues", - "activationEvents": [ - "onStartupFinished", - "onDebugResolve:aws-sam", - "onCommand:aws.login", - "onCommand:aws.credentials.profile.create", - "onCommand:aws.credentials.edit", - "onCommand:aws.logout", - "onCommand:aws.createIssueOnGitHub", - "onCommand:aws.submitFeedback", - "onCommand:aws.showRegion", - "onView:aws.explorer", - "onView:aws.codeWhisperer.securityPanel", - "onCommand:aws.deploySamApplication", - "onCommand:aws.samcli.detect", - "onCommand:aws.lambda.createNewSamApp", - "onDebugInitialConfigurations", - "onCommand:aws.viewLogs", - "onCommand:aws.quickStart", - "onCommand:aws.help", - "onCommand:aws.github", - "onCommand:aws.previewStateMachine", - "onCommand:aws.stepfunctions.createStateMachineFromTemplate", - "onCommand:aws.stepfunctions.publishStateMachine", - "onView:aws.developerTools", - "onCommand:aws.cdk.refresh", - "onCommand:aws.cdk.renderStateMachineGraph", - "onCommand:aws.aboutToolkit", - "onCommand:aws.cloudWatchLogs.viewLogStream", - "onCommand:aws.codecatalyst.listCommands", - "onCommand:aws.codecatalyst.openOrg", - "onCommand:aws.codecatalyst.openProject", - "onCommand:aws.codecatalyst.openRepo", - "onCommand:aws.codecatalyst.openDevEnv", - "onCommand:aws.codecatalyst.cloneRepo", - "onCommand:aws.codecatalyst.createDevEnv", - "onLanguage:asl", - "onLanguage:asl-yaml", - "onLanguage:ssm-json", - "onLanguage:ssm-yaml", - "onCommand:aws.ssmDocument.createLocalDocument", - "onCommand:aws.ssmDocument.openLocalDocument", - "onCommand:aws.ssmDocument.openLocalDocumentJson", - "onCommand:aws.ssmDocument.openLocalDocumentYaml", - "onCommand:aws.ssmDocument.deleteDocument", - "onCommand:aws.ssmDocument.publishDocument", - "onCommand:aws.ssmDocument.updateDocumentVersion", - "onLanguage:javascript", - "onLanguage:java", - "onLanguage:python", - "onLanguage:csharp", - "onLanguage:yaml", - "onCommand:aws.launchConfigForm", - "onCommand:aws.toggleSamCodeLenses", - "onCommand:aws.addSamDebugConfig", - "onCommand:aws.s3.uploadFile", - "onCommand:aws.cloudFormation.newTemplate", - "onCommand:aws.sam.newTemplate", - "onFileSystem:s3", - "onFileSystem:s3-readonly", - "onCommand:aws.codeWhisperer.accept", - "onCommand:aws.codeWhisperer", - "onCommand:aws.uploadLambda" - ], - "main": "./dist/src/main", - "contributes": { - "configuration": { - "type": "object", - "title": "%AWS.productName%", - "cloud9": { - "cn": { - "title": "%AWS.productName.cn%" - } - }, - "properties": { - "aws.profile": { - "type": "string", - "deprecationMessage": "The current profile is now stored internally by the Toolkit.", - "description": "%AWS.configuration.profileDescription%" - }, - "aws.ecs.openTerminalCommand": { - "type": "string", - "default": "/bin/sh", - "markdownDescription": "%AWS.configuration.description.ecs.openTerminalCommand%" - }, - "aws.iot.maxItemsPerPage": { - "type": "number", - "default": 100, - "minimum": 1, - "maximum": 250, - "markdownDescription": "%AWS.configuration.description.iot.maxItemsPerPage%" - }, - "aws.s3.maxItemsPerPage": { - "type": "number", - "default": 300, - "minimum": 3, - "maximum": 1000, - "markdownDescription": "%AWS.configuration.description.s3.maxItemsPerPage%" - }, - "aws.samcli.location": { - "type": "string", - "scope": "machine", - "default": "", - "markdownDescription": "%AWS.configuration.description.samcli.location%" - }, - "aws.samcli.lambdaTimeout": { - "type": "number", - "default": 90000, - "markdownDescription": "%AWS.configuration.description.samcli.lambdaTimeout%" - }, - "aws.samcli.legacyDeploy": { - "type": "boolean", - "default": false, - "markdownDescription": "%AWS.configuration.description.samcli.legacyDeploy%" - }, - "aws.logLevel": { - "type": "string", - "default": "info", - "enum": [ - "error", - "warn", - "info", - "verbose", - "debug" - ], - "enumDescriptions": [ - "Errors Only", - "Errors and Warnings", - "Errors, Warnings, and Info", - "Errors, Warnings, Info, and Verbose", - "Errors, Warnings, Info, Verbose, and Debug" - ], - "markdownDescription": "%AWS.configuration.description.logLevel%", - "cloud9": { - "cn": { - "markdownDescription": "%AWS.configuration.description.logLevel.cn%" - } - } - }, - "aws.telemetry": { - "type": "boolean", - "default": true, - "markdownDescription": "%AWS.configuration.description.telemetry%", - "cloud9": { - "cn": { - "markdownDescription": "%AWS.configuration.description.telemetry.cn%" - } - } - }, - "aws.stepfunctions.asl.format.enable": { - "type": "boolean", - "scope": "window", - "default": true, - "description": "%AWS.stepFunctions.asl.format.enable.desc%" - }, - "aws.stepfunctions.asl.maxItemsComputed": { - "type": "number", - "default": 5000, - "description": "%AWS.stepFunctions.asl.maxItemsComputed.desc%" - }, - "aws.ssmDocument.ssm.maxItemsComputed": { - "type": "number", - "default": 5000, - "description": "%AWS.ssmDocument.ssm.maxItemsComputed.desc%" - }, - "aws.cloudWatchLogs.limit": { - "type": "number", - "default": 10000, - "description": "%AWS.cloudWatchLogs.limit.desc%", - "maximum": 10000 - }, - "aws.samcli.manuallySelectedBuckets": { - "type": "object", - "description": "%AWS.samcli.deploy.bucket.recentlyUsed%", - "default": [] - }, - "aws.samcli.enableCodeLenses": { - "type": "boolean", - "description": "%AWS.configuration.enableCodeLenses%", - "default": false - }, - "aws.suppressPrompts": { - "type": "object", - "description": "%AWS.configuration.description.suppressPrompts%", - "default": {}, - "properties": { - "apprunnerNotifyPricing": { - "type": "boolean", - "default": false - }, - "apprunnerNotifyPause": { - "type": "boolean", - "default": false - }, - "ecsRunCommand": { - "type": "boolean", - "default": false - }, - "ecsRunCommandEnable": { - "type": "boolean", - "default": false - }, - "ecsRunCommandDisable": { - "type": "boolean", - "default": false - }, - "regionAddAutomatically": { - "type": "boolean", - "default": false - }, - "yamlExtPrompt": { - "type": "boolean", - "default": false - }, - "fileViewerEdit": { - "type": "boolean", - "default": false - }, - "createCredentialsProfile": { - "type": "boolean", - "default": false - }, - "samcliConfirmDevStack": { - "type": "boolean", - "default": false - }, - "remoteConnected": { - "type": "boolean", - "default": false - } - }, - "additionalProperties": false - }, - "aws.experiments": { - "type": "object", - "markdownDescription": "%AWS.configuration.description.experiments%", - "default": { - "jsonResourceModification": false, - "samSyncCode": false - }, - "properties": { - "jsonResourceModification": { - "type": "boolean", - "default": false - }, - "samSyncCode": { - "type": "boolean", - "default": false - } - }, - "additionalProperties": false - }, - "aws.codeWhisperer.includeSuggestionsWithCodeReferences": { - "type": "boolean", - "description": "%AWS.configuration.description.codewhisperer%", - "default": true - }, - "aws.codeWhisperer.shareCodeWhispererContentWithAWS": { - "type": "boolean", - "markdownDescription": "%AWS.configuration.description.codewhisperer.shareCodeWhispererContentWithAWS%", - "default": true - }, - "aws.codeWhisperer.javaCompilationOutput": { - "type": "string", - "default": "", - "description": "Provide the ABSOLUTE path which is used to store java project compilation results." - }, - "aws.resources.enabledResources": { - "type": "array", - "description": "%AWS.configuration.description.resources.enabledResources%", - "items": { - "type": "string" - } - }, - "aws.lambda.recentlyUploaded": { - "type": "object", - "description": "%AWS.configuration.description.lambda.recentlyUploaded%", - "default": [] - } - } - }, - "debuggers": [ - { - "type": "aws-sam", - "label": "%AWS.configuration.description.awssam.debug.label%", - "configurationAttributes": { - "direct-invoke": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AwsSamDebuggerConfiguration", - "additionalProperties": false, - "properties": { - "aws": { - "title": "AWS Connection", - "description": "%AWS.configuration.description.awssam.debug.aws%", - "properties": { - "credentials": { - "description": "%AWS.configuration.description.awssam.debug.credentials%", - "type": "string", - "cloud9": { - "cn": { - "description": "%AWS.configuration.description.awssam.debug.credentials.cn%" - } - } - }, - "region": { - "description": "%AWS.configuration.description.awssam.debug.region%", - "type": "string" - } - }, - "additionalProperties": false, - "type": "object" - }, - "invokeTarget": { - "oneOf": [ - { - "title": "Template Target Properties", - "description": "%AWS.configuration.description.awssam.debug.invokeTarget%", - "properties": { - "templatePath": { - "description": "%AWS.configuration.description.awssam.debug.templatePath%", - "type": "string" - }, - "logicalId": { - "description": "%AWS.configuration.description.awssam.debug.logicalId%", - "type": "string" - }, - "target": { - "description": "%AWS.configuration.description.awssam.debug.target%", - "type": "string", - "enum": [ - "template" - ] - } - }, - "additionalProperties": false, - "required": [ - "templatePath", - "logicalId", - "target" - ], - "type": "object" - }, - { - "title": "Code Target Properties", - "description": "%AWS.configuration.description.awssam.debug.invokeTarget%", - "properties": { - "lambdaHandler": { - "description": "%AWS.configuration.description.awssam.debug.lambdaHandler%", - "type": "string" - }, - "projectRoot": { - "description": "%AWS.configuration.description.awssam.debug.projectRoot%", - "type": "string" - }, - "target": { - "description": "%AWS.configuration.description.awssam.debug.target%", - "type": "string", - "enum": [ - "code" - ] - }, - "architecture": { - "description": "%AWS.configuration.description.awssam.debug.architecture%", - "type": "string", - "enum": [ - "x86_64", - "arm64" - ] - } - }, - "additionalProperties": false, - "required": [ - "lambdaHandler", - "projectRoot", - "target" - ], - "type": "object" - }, - { - "title": "API Target Properties", - "description": "%AWS.configuration.description.awssam.debug.invokeTarget%", - "properties": { - "templatePath": { - "description": "%AWS.configuration.description.awssam.debug.templatePath%", - "type": "string" - }, - "logicalId": { - "description": "%AWS.configuration.description.awssam.debug.logicalId%", - "type": "string" - }, - "target": { - "description": "%AWS.configuration.description.awssam.debug.target%", - "type": "string", - "enum": [ - "api" - ] - } - }, - "additionalProperties": false, - "required": [ - "templatePath", - "logicalId", - "target" - ], - "type": "object" - } - ] - }, - "lambda": { - "title": "Lambda Properties", - "description": "%AWS.configuration.description.awssam.debug.lambda%", - "properties": { - "environmentVariables": { - "description": "%AWS.configuration.description.awssam.debug.envvars%", - "additionalProperties": { - "type": [ - "string" - ] - }, - "type": "object" - }, - "payload": { - "description": "%AWS.configuration.description.awssam.debug.event%", - "properties": { - "json": { - "description": "%AWS.configuration.description.awssam.debug.event.json%", - "type": "object" - }, - "path": { - "description": "%AWS.configuration.description.awssam.debug.event.path%", - "type": "string" - } - }, - "additionalProperties": false, - "type": "object" - }, - "memoryMb": { - "description": "%AWS.configuration.description.awssam.debug.memoryMb%", - "type": "number" - }, - "runtime": { - "description": "%AWS.configuration.description.awssam.debug.runtime%", - "type": "string" - }, - "timeoutSec": { - "description": "%AWS.configuration.description.awssam.debug.timeout%", - "type": "number" - }, - "pathMappings": { - "type:": "array", - "items": { - "title": "Path Mapping", - "type": "object", - "properties": { - "localRoot": { - "type": "string" - }, - "remoteRoot": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "localRoot", - "remoteRoot" - ] - } - } - }, - "additionalProperties": false, - "type": "object" - }, - "sam": { - "title": "SAM CLI Properties", - "description": "%AWS.configuration.description.awssam.debug.sam%", - "properties": { - "buildArguments": { - "description": "%AWS.configuration.description.awssam.debug.buildArguments%", - "type": "array", - "items": { - "type": "string" - } - }, - "buildDir": { - "description": "%AWS.configuration.description.awssam.debug.buildDir%", - "type": "string" - }, - "containerBuild": { - "description": "%AWS.configuration.description.awssam.debug.containerBuild%", - "type": "boolean" - }, - "dockerNetwork": { - "description": "%AWS.configuration.description.awssam.debug.dockerNetwork%", - "type": "string" - }, - "localArguments": { - "description": "%AWS.configuration.description.awssam.debug.localArguments%", - "type": "array", - "items": { - "type": "string" - } - }, - "skipNewImageCheck": { - "description": "%AWS.configuration.description.awssam.debug.skipNewImageCheck%", - "type": "boolean" - }, - "template": { - "description": "%AWS.configuration.description.awssam.debug.template%", - "properties": { - "parameters": { - "description": "%AWS.configuration.description.awssam.debug.templateParameters%", - "additionalProperties": { - "type": [ - "string", - "number" - ] - }, - "type": "object" - } - }, - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false, - "type": "object" - }, - "api": { - "title": "API Gateway Properties", - "description": "%AWS.configuration.description.awssam.debug.api%", - "properties": { - "path": { - "description": "%AWS.configuration.description.awssam.debug.api.path%", - "type": "string" - }, - "httpMethod": { - "description": "%AWS.configuration.description.awssam.debug.api.httpMethod%", - "type": "string", - "enum": [ - "delete", - "get", - "head", - "options", - "patch", - "post", - "put" - ] - }, - "payload": { - "description": "%AWS.configuration.description.awssam.debug.event%", - "properties": { - "json": { - "description": "%AWS.configuration.description.awssam.debug.event.json%", - "type": "object" - }, - "path": { - "description": "%AWS.configuration.description.awssam.debug.event.path%", - "type": "string" - } - }, - "additionalProperties": false, - "type": "object" - }, - "headers": { - "description": "%AWS.configuration.description.awssam.debug.api.headers%", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "querystring": { - "description": "%AWS.configuration.description.awssam.debug.api.queryString%", - "type": "string" - }, - "stageVariables": { - "description": "%AWS.configuration.description.awssam.debug.api.stageVariables%", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "clientCertificateId": { - "description": "%AWS.configuration.description.awssam.debug.api.clientCertId%", - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "path", - "httpMethod" - ], - "type": "object" - } - }, - "required": [ - "invokeTarget" - ], - "type": "object" - } - }, - "configurationSnippets": [ - { - "label": "%AWS.configuration.description.awssam.debug.snippets.lambdaCode.label%", - "description": "%AWS.configuration.description.awssam.debug.snippets.lambdaCode.description%", - "body": { - "type": "aws-sam", - "request": "direct-invoke", - "name": "${3:Invoke Lambda}", - "invokeTarget": { - "target": "code", - "lambdaHandler": "${1:Function Handler}", - "projectRoot": "^\"\\${workspaceFolder}\"" - }, - "lambda": { - "runtime": "${2:Lambda Runtime}", - "payload": { - "json": {} - } - } - }, - "cloud9": { - "cn": { - "label": "%AWS.configuration.description.awssam.debug.snippets.lambdaCode.label.cn%", - "description": "%AWS.configuration.description.awssam.debug.snippets.lambdaCode.description.cn%" - } - } - }, - { - "label": "%AWS.configuration.description.awssam.debug.snippets.lambdaTemplate.label%", - "description": "%AWS.configuration.description.awssam.debug.snippets.lambdaTemplate.description%", - "body": { - "type": "aws-sam", - "request": "direct-invoke", - "name": "${3:Invoke Lambda}", - "invokeTarget": { - "target": "template", - "templatePath": "${1:Template Location}", - "logicalId": "${2:Function Logical ID}" - }, - "lambda": { - "payload": { - "json": {} - } - } - }, - "cloud9": { - "cn": { - "label": "%AWS.configuration.description.awssam.debug.snippets.lambdaTemplate.label.cn%", - "description": "%AWS.configuration.description.awssam.debug.snippets.lambdaTemplate.description.cn%" - } - } - }, - { - "label": "%AWS.configuration.description.awssam.debug.snippets.api.label%", - "description": "%AWS.configuration.description.awssam.debug.snippets.api.description%", - "body": { - "type": "aws-sam", - "request": "direct-invoke", - "name": "${5:Invoke Lambda with API Gateway}", - "invokeTarget": { - "target": "api", - "templatePath": "${1:Template Location}", - "logicalId": "${2:Function Logical ID}" - }, - "api": { - "path": "${3:Path}", - "httpMethod": "${4:Method}", - "payload": { - "json": {} - } - } - }, - "cloud9": { - "cn": { - "label": "%AWS.configuration.description.awssam.debug.snippets.api.label.cn%", - "description": "%AWS.configuration.description.awssam.debug.snippets.api.description.cn%" - } - } - } - ] - } - ], - "viewsContainers": { - "activitybar": [ - { - "id": "aws-explorer", - "title": "%AWS.title%", - "icon": "resources/aws-logo.svg", - "cloud9": { - "cn": { - "title": "%AWS.title.cn%", - "icon": "resources/aws-cn-logo.svg" - } - } - } - ], - "panel": [ - { - "id": "aws-codewhisperer-reference-log", - "title": "CodeWhisperer Reference Log", - "icon": "media/aws-logo.svg" - } - ] - }, - "views": { - "aws-explorer": [ - { - "id": "aws.explorer", - "name": "%AWS.lambda.explorerTitle%" - }, - { - "id": "aws.developerTools", - "name": "%AWS.developerTools.explorerTitle%" - } - ], - "aws-codewhisperer-reference-log": [ - { - "type": "webview", - "id": "aws.codeWhisperer.referenceLog", - "name": "" - } - ] - }, - "submenus": [ - { - "id": "aws.auth", - "label": "%AWS.submenu.auth.title%", - "icon": "$(ellipsis)" - } - ], - "menus": { - "commandPalette": [ - { - "command": "aws.apig.copyUrl", - "when": "false" - }, - { - "command": "aws.apig.invokeRemoteRestApi", - "when": "false" - }, - { - "command": "aws.deleteCloudFormation", - "when": "false" - }, - { - "command": "aws.downloadStateMachineDefinition", - "when": "false" - }, - { - "command": "aws.ecr.createRepository", - "when": "false" - }, - { - "command": "aws.executeStateMachine", - "when": "false" - }, - { - "command": "aws.copyArn", - "when": "false" - }, - { - "command": "aws.copyName", - "when": "false" - }, - { - "command": "aws.listCommands", - "when": "false" - }, - { - "command": "aws.codecatalyst.listCommands", - "when": "false" - }, - { - "command": "aws.codecatalyst.openDevEnv", - "when": "!isCloud9" - }, - { - "command": "aws.codecatalyst.createDevEnv", - "when": "!isCloud9" - }, - { - "command": "aws.codecatalyst.removeConnection", - "when": "false" - }, - { - "command": "aws.codeWhisperer.removeConnection", - "when": "false" - }, - { - "command": "aws.downloadSchemaItemCode", - "when": "false" - }, - { - "command": "aws.deleteLambda", - "when": "false" - }, - { - "command": "aws.downloadLambda", - "when": "false" - }, - { - "command": "aws.invokeLambda", - "when": "false" - }, - { - "command": "aws.copyLambdaUrl", - "when": "false" - }, - { - "command": "aws.viewSchemaItem", - "when": "false" - }, - { - "command": "aws.searchSchema", - "when": "false" - }, - { - "command": "aws.searchSchemaPerRegistry", - "when": "false" - }, - { - "command": "aws.refreshAwsExplorer", - "when": "false" - }, - { - "command": "aws.cdk.refresh", - "when": "false" - }, - { - "command": "aws.cdk.viewDocs", - "when": "false" - }, - { - "command": "aws.ssmDocument.openLocalDocument", - "when": "false" - }, - { - "command": "aws.ssmDocument.openLocalDocumentJson", - "when": "false" - }, - { - "command": "aws.ssmDocument.openLocalDocumentYaml", - "when": "false" - }, - { - "command": "aws.ssmDocument.deleteDocument", - "when": "false" - }, - { - "command": "aws.ssmDocument.updateDocumentVersion", - "when": "false" - }, - { - "command": "aws.copyLogStreamName", - "when": "resourceScheme == awsCloudWatchLogs" - }, - { - "command": "aws.saveCurrentLogStreamContent", - "when": "resourceScheme == awsCloudWatchLogs" - }, - { - "command": "aws.s3.editFile", - "when": "resourceScheme == s3-readonly" - }, - { - "command": "aws.cloudWatchLogs.viewLogStream", - "when": "false" - }, - { - "command": "aws.ecr.deleteRepository", - "when": "false" - }, - { - "command": "aws.ecr.copyTagUri", - "when": "false" - }, - { - "command": "aws.ecr.copyRepositoryUri", - "when": "false" - }, - { - "command": "aws.ecr.deleteTag", - "when": "false" - }, - { - "command": "aws.iot.createThing", - "when": "false" - }, - { - "command": "aws.iot.deleteThing", - "when": "false" - }, - { - "command": "aws.iot.createCert", - "when": "false" - }, - { - "command": "aws.iot.deleteCert", - "when": "false" - }, - { - "command": "aws.iot.attachCert", - "when": "false" - }, - { - "command": "aws.iot.attachPolicy", - "when": "false" - }, - { - "command": "aws.iot.activateCert", - "when": "false" - }, - { - "command": "aws.iot.deactivateCert", - "when": "false" - }, - { - "command": "aws.iot.revokeCert", - "when": "false" - }, - { - "command": "aws.iot.createPolicy", - "when": "false" - }, - { - "command": "aws.iot.deletePolicy", - "when": "false" - }, - { - "command": "aws.iot.createPolicyVersion", - "when": "false" - }, - { - "command": "aws.iot.deletePolicyVersion", - "when": "false" - }, - { - "command": "aws.iot.detachCert", - "when": "false" - }, - { - "command": "aws.iot.detachPolicy", - "when": "false" - }, - { - "command": "aws.iot.viewPolicyVersion", - "when": "false" - }, - { - "command": "aws.iot.setDefaultPolicy", - "when": "false" - }, - { - "command": "aws.iot.copyEndpoint", - "when": "false" - }, - { - "command": "aws.deploySamApplication", - "when": "config.aws.samcli.legacyDeploy" - }, - { - "command": "aws.samcli.sync", - "when": "!config.aws.samcli.legacyDeploy" - }, - { - "command": "aws.samcli.syncCode", - "when": "config.aws.experiments.samSyncCode && !config.aws.samcli.legacyDeploy" - }, - { - "command": "aws.s3.copyPath", - "when": "false" - }, - { - "command": "aws.s3.createBucket", - "when": "false" - }, - { - "command": "aws.s3.createFolder", - "when": "false" - }, - { - "command": "aws.s3.deleteBucket", - "when": "false" - }, - { - "command": "aws.s3.deleteFile", - "when": "false" - }, - { - "command": "aws.s3.downloadFileAs", - "when": "false" - }, - { - "command": "aws.s3.openFile", - "when": "false" - }, - { - "command": "aws.s3.editFile", - "when": "false" - }, - { - "command": "aws.s3.uploadFileToParent", - "when": "false" - }, - { - "command": "aws.apprunner.startDeployment", - "when": "false" - }, - { - "command": "aws.apprunner.createService", - "when": "false" - }, - { - "command": "aws.apprunner.pauseService", - "when": "false" - }, - { - "command": "aws.apprunner.resumeService", - "when": "false" - }, - { - "command": "aws.apprunner.copyServiceUrl", - "when": "false" - }, - { - "command": "aws.apprunner.open", - "when": "false" - }, - { - "command": "aws.apprunner.deleteService", - "when": "false" - }, - { - "command": "aws.apprunner.createServiceFromEcr", - "when": "false" - }, - { - "command": "aws.resources.copyIdentifier", - "when": "false" - }, - { - "command": "aws.resources.openResourcePreview", - "when": "false" - }, - { - "command": "aws.resources.createResource", - "when": "false" - }, - { - "command": "aws.resources.deleteResource", - "when": "false" - }, - { - "command": "aws.resources.updateResource", - "when": "false" - }, - { - "command": "aws.resources.updateResourceInline", - "when": "false" - }, - { - "command": "aws.resources.saveResource", - "when": "false" - }, - { - "command": "aws.resources.closeResource", - "when": "false" - }, - { - "command": "aws.resources.viewDocs", - "when": "false" - }, - { - "command": "aws.ecs.runCommandInContainer", - "when": "false" - }, - { - "command": "aws.ecs.openTaskInTerminal", - "when": "false" - }, - { - "command": "aws.ecs.enableEcsExec", - "when": "false" - }, - { - "command": "aws.ecs.disableEcsExec", - "when": "false" - }, - { - "command": "aws.ecs.viewDocumentation", - "when": "false" - }, - { - "command": "aws.renderStateMachineGraph", - "when": "false" - }, - { - "command": "aws.auth.addConnection", - "when": "false" - }, - { - "command": "aws.auth.switchConnections", - "when": "false" - }, - { - "command": "aws.auth.signout", - "when": "false" - }, - { - "command": "aws.auth.help", - "when": "false" - }, - { - "command": "aws.dev.openMenu", - "when": "aws.isDevMode || isCloud9" - } - ], - "editor/title": [ - { - "command": "aws.previewStateMachine", - "when": "editorLangId == asl || editorLangId == asl-yaml", - "group": "navigation" - }, - { - "command": "aws.saveCurrentLogStreamContent", - "when": "resourceScheme == awsCloudWatchLogs", - "group": "navigation" - }, - { - "command": "aws.s3.editFile", - "when": "resourceScheme == s3-readonly", - "group": "navigation" - }, - { - "command": "aws.ssmDocument.publishDocument", - "when": "editorLangId =~ /^(ssm-yaml|ssm-json)$/", - "group": "navigation" - }, - { - "command": "aws.resources.updateResourceInline", - "when": "resourceScheme == awsResource && !isCloud9 && config.aws.experiments.jsonResourceModification", - "group": "navigation" - }, - { - "command": "aws.resources.closeResource", - "when": "resourcePath =~ /^.+(awsResource.json)$/", - "group": "navigation" - }, - { - "command": "aws.resources.saveResource", - "when": "resourcePath =~ /^.+(awsResource.json)$/", - "group": "navigation" - } - ], - "editor/title/context": [ - { - "command": "aws.copyLogStreamName", - "when": "resourceScheme == awsCloudWatchLogs", - "group": "1_cutcopypaste@1" - } - ], - "view/title": [ - { - "command": "aws.submitFeedback", - "when": "view == aws.explorer", - "group": "navigation@6" - }, - { - "command": "aws.refreshAwsExplorer", - "when": "view == aws.explorer", - "group": "navigation@5" - }, - { - "command": "aws.login", - "when": "view == aws.explorer", - "group": "1_account@1" - }, - { - "command": "aws.showRegion", - "when": "view == aws.explorer", - "group": "1_account@2" - }, - { - "command": "aws.listCommands", - "when": "view == aws.explorer && !isCloud9", - "group": "1_account@3" - }, - { - "command": "aws.lambda.createNewSamApp", - "when": "view == aws.explorer", - "group": "3_lambda@1" - }, - { - "command": "aws.launchConfigForm", - "when": "view == aws.explorer", - "group": "3_lambda@2" - }, - { - "command": "aws.deploySamApplication", - "when": "config.aws.samcli.legacyDeploy && view == aws.explorer", - "group": "3_lambda@3" - }, - { - "command": "aws.samcli.sync", - "when": "!config.aws.samcli.legacyDeploy && view == aws.explorer", - "group": "3_lambda@3" - }, - { - "command": "aws.samcli.syncCode", - "when": "config.aws.experiments.samSyncCode && !config.aws.samcli.legacyDeploy && view == aws.explorer", - "group": "3_lambda@4" - }, - { - "command": "aws.quickStart", - "when": "view == aws.explorer", - "group": "y_toolkitMeta@1" - }, - { - "command": "aws.help", - "when": "view == aws.explorer || !aws.explorer.visible && view =~ /^aws/", - "group": "y_toolkitMeta@2" - }, - { - "command": "aws.github", - "when": "view == aws.explorer || !aws.explorer.visible && view =~ /^aws/", - "group": "y_toolkitMeta@3" - }, - { - "command": "aws.createIssueOnGitHub", - "when": "view == aws.explorer || !aws.explorer.visible && view =~ /^aws/", - "group": "y_toolkitMeta@4" - }, - { - "command": "aws.submitFeedback", - "when": "view == aws.explorer || !aws.explorer.visible && view =~ /^aws/", - "group": "y_toolkitMeta@5" - }, - { - "command": "aws.aboutToolkit", - "when": "view == aws.explorer || !aws.explorer.visible && view =~ /^aws/", - "group": "z_about@1" - }, - { - "command": "aws.viewLogs", - "when": "view == aws.explorer || !aws.explorer.visible && view =~ /^aws/", - "group": "z_about@1" - }, - { - "command": "aws.codecatalyst.cloneRepo", - "when": "view == aws.codecatalyst && !isCloud9", - "group": "1_codeCatalyst@1" - }, - { - "command": "aws.codecatalyst.createDevEnv", - "when": "view == aws.codecatalyst && !isCloud9", - "group": "1_codeCatalyst@1" - }, - { - "command": "aws.codecatalyst.listCommands", - "when": "view == aws.codecatalyst && !isCloud9", - "group": "1_codeCatalyst@1" - }, - { - "command": "aws.codecatalyst.openDevEnv", - "when": "view == aws.codecatalyst && !isCloud9", - "group": "1_codeCatalyst@1" - } - ], - "explorer/context": [ - { - "command": "aws.deploySamApplication", - "when": "config.aws.samcli.legacyDeploy && isFileSystemResource && resourceFilename =~ /^template\\.(json|yml|yaml)$/", - "group": "z_aws@1" - }, - { - "command": "aws.samcli.sync", - "when": "!config.aws.samcli.legacyDeploy && isFileSystemResource && resourceFilename =~ /^(template\\.(json|yml|yaml))|(samconfig\\.toml)$/", - "group": "z_aws@1" - }, - { - "command": "aws.samcli.syncCode", - "when": "config.aws.experiments.samSyncCode && !config.aws.samcli.legacyDeploy && isFileSystemResource && resourceFilename =~ /^(template\\.(json|yml|yaml))|(samconfig\\.toml)$/", - "group": "z_aws@2" - }, - { - "command": "aws.uploadLambda", - "when": "explorerResourceIsFolder || isFileSystemResource && resourceFilename =~ /^template\\.(json|yml|yaml)$/", - "group": "z_aws@3" - } - ], - "view/item/context": [ - { - "command": "aws.apig.invokeRemoteRestApi", - "when": "view == aws.explorer && viewItem =~ /^(awsApiGatewayNode)$/", - "group": "0@1" - }, - { - "command": "aws.codecatalyst.removeConnection", - "when": "viewItem == awsCodeCatalystNodeSaved", - "group": "0@1" - }, - { - "command": "aws.codecatalyst.removeConnection", - "when": "viewItem == awsCodeCatalystNodeSaved", - "group": "inline@1" - }, - { - "command": "aws.ecr.createRepository", - "when": "view == aws.explorer && viewItem == awsEcrNode", - "group": "inline@1" - }, - { - "command": "aws.iot.createThing", - "when": "view == aws.explorer && viewItem == awsIotThingsNode", - "group": "inline@1" - }, - { - "command": "aws.iot.createCert", - "when": "view == aws.explorer && viewItem == awsIotCertsNode", - "group": "inline@1" - }, - { - "command": "aws.iot.createPolicy", - "when": "view == aws.explorer && viewItem == awsIotPoliciesNode", - "group": "inline@1" - }, - { - "command": "aws.iot.attachCert", - "when": "view == aws.explorer && viewItem == awsIotThingNode", - "group": "inline@1" - }, - { - "command": "aws.iot.attachPolicy", - "when": "view == aws.explorer && viewItem =~ /^awsIotCertificateNode.(Things|Policies)/", - "group": "inline@1" - }, - { - "command": "aws.s3.openFile", - "when": "view == aws.explorer && viewItem == awsS3FileNode && !isCloud9", - "group": "0@1" - }, - { - "command": "aws.s3.editFile", - "when": "view == aws.explorer && viewItem == awsS3FileNode && !isCloud9", - "group": "inline@1" - }, - { - "command": "aws.s3.downloadFileAs", - "when": "view == aws.explorer && viewItem == awsS3FileNode", - "group": "inline@2" - }, - { - "command": "aws.s3.createBucket", - "when": "view == aws.explorer && viewItem == awsS3Node", - "group": "inline@1" - }, - { - "command": "aws.s3.createFolder", - "when": "view == aws.explorer && viewItem =~ /^(awsS3BucketNode|awsS3FolderNode)$/", - "group": "inline@1" - }, - { - "command": "aws.ssmDocument.openLocalDocument", - "when": "view == aws.explorer && viewItem =~ /^(awsDocumentItemNode|awsDocumentItemNodeWriteable)$/", - "group": "inline@1" - }, - { - "command": "aws.s3.uploadFile", - "when": "view == aws.explorer && viewItem =~ /^(awsS3BucketNode|awsS3FolderNode)$/", - "group": "inline@2" - }, - { - "command": "aws.showRegion", - "when": "view == aws.explorer && viewItem == awsRegionNode", - "group": "0@1" - }, - { - "command": "aws.lambda.createNewSamApp", - "when": "view == aws.explorer && viewItem == awsLambdaNode || viewItem == awsRegionNode", - "group": "1@1" - }, - { - "command": "aws.launchConfigForm", - "when": "view == aws.explorer && viewItem == awsLambdaNode || viewItem == awsRegionNode || viewItem == awsCloudFormationRootNode", - "group": "1@1" - }, - { - "command": "aws.deploySamApplication", - "when": "config.aws.samcli.legacyDeploy && view == aws.explorer && viewItem =~ /^(awsLambdaNode|awsRegionNode|awsCloudFormationRootNode)$/", - "group": "1@2" - }, - { - "command": "aws.samcli.sync", - "when": "!config.aws.samcli.legacyDeploy && view == aws.explorer && viewItem =~ /^(awsLambdaNode|awsRegionNode|awsCloudFormationRootNode)$/", - "group": "1@2" - }, - { - "command": "aws.samcli.syncCode", - "when": "config.aws.experiments.samSyncCode && !config.aws.samcli.legacyDeploy && view == aws.explorer && viewItem =~ /^(awsLambdaNode|awsRegionNode|awsCloudFormationRootNode)$/", - "group": "1@3" - }, - { - "command": "aws.ecr.copyTagUri", - "when": "view == aws.explorer && viewItem == awsEcrTagNode", - "group": "2@1" - }, - { - "command": "aws.ecr.deleteTag", - "when": "view == aws.explorer && viewItem == awsEcrTagNode", - "group": "3@1" - }, - { - "command": "aws.ecr.copyRepositoryUri", - "when": "view == aws.explorer && viewItem == awsEcrRepositoryNode", - "group": "2@1" - }, - { - "command": "aws.ecr.createRepository", - "when": "view == aws.explorer && viewItem == awsEcrNode", - "group": "0@1" - }, - { - "command": "aws.ecr.deleteRepository", - "when": "view == aws.explorer && viewItem == awsEcrRepositoryNode", - "group": "3@1" - }, - { - "command": "aws.invokeLambda", - "when": "view == aws.explorer && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable|awsCloudFormationFunctionNode)$/", - "group": "0@1" - }, - { - "command": "aws.downloadLambda", - "when": "view == aws.explorer && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable)$/", - "group": "0@2" - }, - { - "command": "aws.uploadLambda", - "when": "view == aws.explorer && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable)$/", - "group": "1@1" - }, - { - "command": "aws.deleteLambda", - "when": "view == aws.explorer && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable)$/", - "group": "4@1" - }, - { - "command": "aws.copyLambdaUrl", - "when": "view == aws.explorer && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable)$/", - "group": "2@0" - }, - { - "command": "aws.deleteCloudFormation", - "when": "view == aws.explorer && viewItem == awsCloudFormationNode", - "group": "3@5" - }, - { - "command": "aws.searchSchema", - "when": "view == aws.explorer && viewItem == awsSchemasNode", - "group": "0@1" - }, - { - "command": "aws.searchSchemaPerRegistry", - "when": "view == aws.explorer && viewItem == awsRegistryItemNode", - "group": "0@1" - }, - { - "command": "aws.viewSchemaItem", - "when": "view == aws.explorer && viewItem == awsSchemaItemNode", - "group": "0@1" - }, - { - "command": "aws.stepfunctions.createStateMachineFromTemplate", - "when": "view == aws.explorer && viewItem == awsStepFunctionsNode", - "group": "0@1" - }, - { - "command": "aws.downloadStateMachineDefinition", - "when": "view == aws.explorer && viewItem == awsStateMachineNode", - "group": "0@1" - }, - { - "command": "aws.renderStateMachineGraph", - "when": "view == aws.explorer && viewItem == awsStateMachineNode", - "group": "0@2" - }, - { - "command": "aws.cdk.renderStateMachineGraph", - "when": "viewItem == awsCdkStateMachineNode", - "group": "inline@1" - }, - { - "command": "aws.cdk.renderStateMachineGraph", - "when": "viewItem == awsCdkStateMachineNode", - "group": "0@1" - }, - { - "command": "aws.executeStateMachine", - "when": "view == aws.explorer && viewItem == awsStateMachineNode", - "group": "0@3" - }, - { - "command": "aws.iot.createThing", - "when": "view == aws.explorer && viewItem == awsIotThingsNode", - "group": "0@1" - }, - { - "command": "aws.iot.createCert", - "when": "view == aws.explorer && viewItem == awsIotCertsNode", - "group": "0@1" - }, - { - "command": "aws.iot.createPolicy", - "when": "view == aws.explorer && viewItem == awsIotPoliciesNode", - "group": "0@1" - }, - { - "command": "aws.iot.createPolicyVersion", - "when": "view == aws.explorer && viewItem == awsIotPolicyNode.WithVersions", - "group": "0@1" - }, - { - "command": "aws.iot.viewPolicyVersion", - "when": "view == aws.explorer && viewItem =~ /^awsIotPolicyVersionNode./", - "group": "0@1" - }, - { - "command": "aws.iot.attachCert", - "when": "view == aws.explorer && viewItem == awsIotThingNode", - "group": "0@1" - }, - { - "command": "aws.iot.attachPolicy", - "when": "view == aws.explorer && viewItem =~ /^awsIotCertificateNode.(Things|Policies)/", - "group": "0@1" - }, - { - "command": "aws.s3.createBucket", - "when": "view == aws.explorer && viewItem == awsS3Node", - "group": "0@1" - }, - { - "command": "aws.s3.downloadFileAs", - "when": "view == aws.explorer && viewItem == awsS3FileNode", - "group": "0@1" - }, - { - "command": "aws.s3.uploadFile", - "when": "view == aws.explorer && viewItem =~ /^(awsS3BucketNode|awsS3FolderNode)$/", - "group": "0@1" - }, - { - "command": "aws.s3.uploadFileToParent", - "when": "view == aws.explorer && viewItem == awsS3FileNode", - "group": "1@1" - }, - { - "command": "aws.s3.createFolder", - "when": "view == aws.explorer && viewItem =~ /^(awsS3BucketNode|awsS3FolderNode)$/", - "group": "1@1" - }, - { - "command": "aws.iot.deactivateCert", - "when": "view == aws.explorer && viewItem =~ /^awsIotCertificateNode.(Things|Policies).ACTIVE$/", - "group": "1@1" - }, - { - "command": "aws.iot.activateCert", - "when": "view == aws.explorer && viewItem =~ /^awsIotCertificateNode.(Things|Policies).INACTIVE$/", - "group": "1@1" - }, - { - "command": "aws.iot.revokeCert", - "when": "view == aws.explorer && viewItem =~ /^awsIotCertificateNode.(Things|Policies).(ACTIVE|INACTIVE)$/", - "group": "1@2" - }, - { - "command": "aws.iot.setDefaultPolicy", - "when": "view == aws.explorer && viewItem == awsIotPolicyVersionNode.NONDEFAULT", - "group": "1@1" - }, - { - "command": "aws.iot.copyEndpoint", - "when": "view == aws.explorer && viewItem == awsIotNode", - "group": "2@1" - }, - { - "command": "aws.copyName", - "when": "view == aws.explorer && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable|awsCloudFormationFunctionNode|awsStateMachineNode|awsCloudFormationNode|awsS3BucketNode|awsS3FolderNode|awsS3FileNode|awsApiGatewayNode|awsIotThingNode)$|^(awsAppRunnerServiceNode|awsIotCertificateNode|awsIotPolicyNode|awsIotPolicyVersionNode)/", - "group": "2@1" - }, - { - "command": "aws.copyArn", - "when": "view == aws.explorer && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable|awsCloudFormationFunctionNode|awsStateMachineNode|awsCloudFormationNode|awsCloudWatchLogNode|awsS3BucketNode|awsS3FolderNode|awsS3FileNode|awsApiGatewayNode|awsEcrRepositoryNode|awsIotThingNode)$|^(awsAppRunnerServiceNode|awsEcsServiceNode|awsIotCertificateNode|awsIotPolicyNode|awsIotPolicyVersionNode|awsMdeInstanceNode)/", - "group": "2@2" - }, - { - "command": "aws.apig.copyUrl", - "when": "view == aws.explorer && viewItem =~ /^(awsApiGatewayNode)$/", - "group": "2@0" - }, - { - "command": "aws.s3.copyPath", - "when": "view == aws.explorer && viewItem =~ /^(awsS3FolderNode|awsS3FileNode)$/", - "group": "2@3" - }, - { - "command": "aws.s3.presignedURL", - "when": "view == aws.explorer && viewItem =~ /^(awsS3FileNode)$/", - "group": "2@4" - }, - { - "command": "aws.iot.detachCert", - "when": "view == aws.explorer && viewItem =~ /^(awsIotCertificateNode.Things)/", - "group": "3@1" - }, - { - "command": "aws.iot.detachPolicy", - "when": "view == aws.explorer && viewItem == awsIotPolicyNode.Certificates", - "group": "3@1" - }, - { - "command": "aws.iot.deleteThing", - "when": "view == aws.explorer && viewItem == awsIotThingNode", - "group": "3@1" - }, - { - "command": "aws.iot.deleteCert", - "when": "view == aws.explorer && viewItem =~ /^awsIotCertificateNode.Policies/", - "group": "3@1" - }, - { - "command": "aws.iot.deletePolicy", - "when": "view == aws.explorer && viewItem == awsIotPolicyNode.WithVersions", - "group": "3@1" - }, - { - "command": "aws.iot.deletePolicyVersion", - "when": "view == aws.explorer && viewItem == awsIotPolicyVersionNode.NONDEFAULT", - "group": "3@1" - }, - { - "command": "aws.s3.deleteBucket", - "when": "view == aws.explorer && viewItem == awsS3BucketNode", - "group": "3@1" - }, - { - "command": "aws.s3.deleteFile", - "when": "view == aws.explorer && viewItem == awsS3FileNode", - "group": "3@1" - }, - { - "command": "aws.downloadSchemaItemCode", - "when": "view == aws.explorer && viewItem == awsSchemaItemNode", - "group": "1@1" - }, - { - "command": "aws.cloudWatchLogs.viewLogStream", - "group": "0@1", - "when": "view == aws.explorer && viewItem == awsCloudWatchLogNode" - }, - { - "command": "aws.ssmDocument.openLocalDocumentYaml", - "group": "0@1", - "when": "view == aws.explorer && viewItem =~ /^(awsDocumentItemNode|awsDocumentItemNodeWriteable)$/" - }, - { - "command": "aws.ssmDocument.openLocalDocumentJson", - "group": "0@2", - "when": "view == aws.explorer && viewItem =~ /^(awsDocumentItemNode|awsDocumentItemNodeWriteable)$/" - }, - { - "command": "aws.ssmDocument.updateDocumentVersion", - "group": "2@1", - "when": "view == aws.explorer && viewItem == awsDocumentItemNodeWriteable" - }, - { - "command": "aws.ssmDocument.deleteDocument", - "group": "3@2", - "when": "view == aws.explorer && viewItem == awsDocumentItemNodeWriteable" - }, - { - "command": "aws.ecs.runCommandInContainer", - "group": "0@1", - "when": "view == aws.explorer && viewItem =~ /^(awsEcsContainerNodeExec)(.*)$/" - }, - { - "command": "aws.ecs.openTaskInTerminal", - "group": "0@2", - "when": "view == aws.explorer && viewItem =~ /^(awsEcsContainerNodeExec)(.*)$/ && !isCloud9" - }, - { - "command": "aws.ecs.enableEcsExec", - "group": "0@2", - "when": "view == aws.explorer && viewItem == awsEcsServiceNode.DISABLED" - }, - { - "command": "aws.ecs.disableEcsExec", - "group": "0@2", - "when": "view == aws.explorer && viewItem == awsEcsServiceNode.ENABLED" - }, - { - "command": "aws.ecs.viewDocumentation", - "group": "1@3", - "when": "view == aws.explorer && viewItem =~ /^(awsEcsClusterNode|awsEcsContainerNode)$|^awsEcsServiceNode/" - }, - { - "command": "aws.resources.configure", - "when": "view == aws.explorer && viewItem == resourcesRootNode", - "group": "1@1" - }, - { - "command": "aws.resources.configure", - "when": "view == aws.explorer && viewItem == resourcesRootNode", - "group": "inline@1" - }, - { - "command": "aws.resources.openResourcePreview", - "when": "view == aws.explorer && viewItem =~ /^(.*)(ResourceNode)$/", - "group": "1@1" - }, - { - "command": "aws.resources.copyIdentifier", - "when": "view == aws.explorer && viewItem =~ /^(.*)(ResourceNode)$/", - "group": "1@1" - }, - { - "command": "aws.resources.viewDocs", - "when": "view == aws.explorer && viewItem =~ /^(.*)(Documented)(.*)(ResourceTypeNode)$/", - "group": "1@1" - }, - { - "command": "aws.resources.createResource", - "when": "view == aws.explorer && viewItem =~ /^(.*)(Creatable)(.*)(ResourceTypeNode)$/ && !isCloud9 && config.aws.experiments.jsonResourceModification", - "group": "2@1" - }, - { - "command": "aws.resources.createResource", - "when": "view == aws.explorer && viewItem =~ /^(.*)(Creatable)(.*)(ResourceTypeNode)$/ && !isCloud9 && config.aws.experiments.jsonResourceModification", - "group": "inline@1" - }, - { - "command": "aws.resources.updateResource", - "when": "view == aws.explorer && viewItem =~ /^(.*)(Updatable)(.*)(ResourceNode)$/ && !isCloud9 && config.aws.experiments.jsonResourceModification", - "group": "2@1" - }, - { - "command": "aws.resources.deleteResource", - "when": "view == aws.explorer && viewItem =~ /^(.*)(Deletable)(.*)(ResourceNode)$/ && !isCloud9 && config.aws.experiments.jsonResourceModification", - "group": "2@2" - }, - { - "command": "aws.apprunner.createServiceFromEcr", - "group": "0@2", - "when": "view == aws.explorer && viewItem =~ /awsEcrTagNode|awsEcrRepositoryNode/" - }, - { - "command": "aws.apprunner.startDeployment", - "group": "0@1", - "when": "view == aws.explorer && viewItem == awsAppRunnerServiceNode.RUNNING" - }, - { - "command": "aws.apprunner.createService", - "group": "0@2", - "when": "view == aws.explorer && viewItem == awsAppRunnerNode" - }, - { - "command": "aws.apprunner.pauseService", - "group": "0@3", - "when": "view == aws.explorer && viewItem == awsAppRunnerServiceNode.RUNNING" - }, - { - "command": "aws.apprunner.resumeService", - "group": "0@3", - "when": "view == aws.explorer && viewItem == awsAppRunnerServiceNode.PAUSED" - }, - { - "command": "aws.apprunner.copyServiceUrl", - "group": "1@1", - "when": "view == aws.explorer && viewItem == awsAppRunnerServiceNode.RUNNING" - }, - { - "command": "aws.apprunner.open", - "group": "1@2", - "when": "view == aws.explorer && viewItem == awsAppRunnerServiceNode.RUNNING" - }, - { - "command": "aws.apprunner.deleteService", - "group": "3@1", - "when": "view == aws.explorer && viewItem =~ /awsAppRunnerServiceNode.[RUNNING|PAUSED|CREATE_FAILED]/" - }, - { - "command": "aws.cloudFormation.newTemplate", - "group": "0@1", - "when": "view == aws.explorer && viewItem == awsCloudFormationRootNode" - }, - { - "command": "aws.sam.newTemplate", - "group": "0@2", - "when": "view == aws.explorer && viewItem == awsCloudFormationRootNode" - }, - { - "command": "aws.codeWhisperer.configure", - "when": "viewItem =~ /^awsCodeWhispererNode/ && !isCloud9", - "group": "inline@2" - }, - { - "command": "aws.codeWhisperer.introduction", - "when": "viewItem =~ /^awsCodeWhispererNode/ && !isCloud9 && CODEWHISPERER_TERMS_ACCEPTED", - "group": "inline@1" - }, - { - "command": "aws.codeWhisperer.removeConnection", - "when": "viewItem == awsCodeWhispererNodeSaved", - "group": "0@1" - }, - { - "command": "aws.codeWhisperer.removeConnection", - "when": "viewItem == awsCodeWhispererNodeSaved", - "group": "inline@3" - }, - { - "command": "aws.cdk.refresh", - "when": "viewItem == awsCdkRootNode", - "group": "inline@1" - }, - { - "command": "aws.cdk.refresh", - "when": "viewItem == awsCdkRootNode", - "group": "0@1" - }, - { - "command": "aws.cdk.viewDocs", - "when": "viewItem == awsCdkRootNode", - "group": "0@2" - }, - { - "command": "aws.auth.addConnection", - "when": "viewItem == awsAuthNode", - "group": "0@1" - }, - { - "command": "aws.auth.switchConnections", - "when": "viewItem == awsAuthNode", - "group": "0@2" - }, - { - "command": "aws.auth.signout", - "when": "viewItem == awsAuthNode && !isCloud9", - "group": "0@3" - }, - { - "command": "aws.auth.help", - "when": "viewItem == awsAuthNode", - "group": "inline@1" - }, - { - "submenu": "aws.auth", - "when": "viewItem == awsAuthNode", - "group": "inline@2" - } - ], - "aws.auth": [ - { - "command": "aws.auth.addConnection", - "group": "0@1" - }, - { - "command": "aws.auth.switchConnections", - "group": "0@2" - }, - { - "command": "aws.auth.signout", - "enablement": "!isCloud9", - "group": "0@3" - } - ] - }, - "commands": [ - { - "command": "aws.launchConfigForm", - "title": "%AWS.command.launchConfigForm.title%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apig.copyUrl", - "title": "%AWS.command.apig.copyUrl%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apig.invokeRemoteRestApi", - "title": "%AWS.command.apig.invokeRemoteRestApi%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%", - "title": "%AWS.command.apig.invokeRemoteRestApi.cn%" - } - } - }, - { - "command": "aws.lambda.createNewSamApp", - "title": "%AWS.command.createNewSamApp%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.login", - "title": "%AWS.command.login%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "title": "%AWS.command.login.cn%", - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.credentials.profile.create", - "title": "%AWS.command.credentials.profile.create%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.credentials.edit", - "title": "%AWS.command.credentials.edit%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.codecatalyst.openOrg", - "title": "%AWS.command.codecatalyst.openOrg%", - "category": "AWS" - }, - { - "command": "aws.codecatalyst.openProject", - "title": "%AWS.command.codecatalyst.openProject%", - "category": "AWS" - }, - { - "command": "aws.codecatalyst.openRepo", - "title": "%AWS.command.codecatalyst.openRepo%", - "category": "AWS" - }, - { - "command": "aws.codecatalyst.openDevEnv", - "title": "%AWS.command.codecatalyst.openDevEnv%", - "category": "AWS", - "enablement": "!isCloud9" - }, - { - "command": "aws.codecatalyst.listCommands", - "title": "%AWS.command.codecatalyst.listCommands%", - "category": "AWS", - "enablement": "!isCloud9" - }, - { - "command": "aws.codecatalyst.cloneRepo", - "title": "%AWS.command.codecatalyst.cloneRepo%", - "category": "AWS", - "enablement": "!isCloud9" - }, - { - "command": "aws.codecatalyst.createDevEnv", - "title": "%AWS.command.codecatalyst.createDevEnv%", - "category": "AWS", - "enablement": "!isCloud9" - }, - { - "command": "aws.codecatalyst.removeConnection", - "title": "%AWS.command.codecatalyst.removeConnection%", - "category": "AWS", - "icon": "$(debug-disconnect)" - }, - { - "command": "aws.logout", - "title": "%AWS.command.logout%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.auth.addConnection", - "title": "%AWS.command.auth.addConnection%", - "category": "%AWS.title%" - }, - { - "command": "aws.auth.switchConnections", - "title": "%AWS.command.auth.switchConnections%", - "category": "%AWS.title%" - }, - { - "command": "aws.auth.signout", - "title": "%AWS.command.auth.signout%", - "category": "%AWS.title%", - "enablement": "!isCloud9" - }, - { - "command": "aws.auth.help", - "title": "%AWS.generic.viewDocs%", - "category": "%AWS.title%", - "icon": "$(question)" - }, - { - "command": "aws.createIssueOnGitHub", - "title": "%AWS.command.createIssueOnGitHub%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecr.copyTagUri", - "title": "%AWS.command.ecr.copyTagUri%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecr.deleteTag", - "title": "%AWS.command.ecr.deleteTag%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecr.copyRepositoryUri", - "title": "%AWS.command.ecr.copyRepositoryUri%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecr.createRepository", - "title": "%AWS.command.ecr.createRepository%", - "category": "%AWS.title%", - "icon": "$(add)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecr.deleteRepository", - "title": "%AWS.command.ecr.deleteRepository%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.showRegion", - "title": "%AWS.command.showRegion%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.createThing", - "title": "%AWS.command.iot.createThing%", - "category": "%AWS.title%", - "icon": "$(add)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.deleteThing", - "title": "%AWS.generic.promptDelete%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.createCert", - "title": "%AWS.command.iot.createCert%", - "category": "%AWS.title%", - "icon": "$(add)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.deleteCert", - "title": "%AWS.generic.promptDelete%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.attachCert", - "title": "%AWS.command.iot.attachCert%", - "category": "%AWS.title%", - "icon": "$(aws-generic-attach-file)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.attachPolicy", - "title": "%AWS.command.iot.attachPolicy%", - "category": "%AWS.title%", - "icon": "$(aws-generic-attach-file)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.activateCert", - "title": "%AWS.command.iot.activateCert%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.deactivateCert", - "title": "%AWS.command.iot.deactivateCert%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.revokeCert", - "title": "%AWS.command.iot.revokeCert%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.createPolicy", - "title": "%AWS.command.iot.createPolicy%", - "category": "%AWS.title%", - "icon": "$(add)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.deletePolicy", - "title": "%AWS.generic.promptDelete%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.createPolicyVersion", - "title": "%AWS.command.iot.createPolicyVersion%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.deletePolicyVersion", - "title": "%AWS.generic.promptDelete%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.detachCert", - "title": "%AWS.command.iot.detachCert%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.detachPolicy", - "title": "%AWS.command.iot.detachCert%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.viewPolicyVersion", - "title": "%AWS.command.iot.viewPolicyVersion%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.setDefaultPolicy", - "title": "%AWS.command.iot.setDefaultPolicy%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.iot.copyEndpoint", - "title": "%AWS.command.iot.copyEndpoint%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.s3.presignedURL", - "title": "%AWS.command.s3.presignedURL%", - "category": "%AWS.title%" - }, - { - "command": "aws.s3.copyPath", - "title": "%AWS.command.s3.copyPath%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.s3.downloadFileAs", - "title": "%AWS.command.s3.downloadFileAs%", - "category": "%AWS.title%", - "icon": "$(cloud-download)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.s3.openFile", - "title": "%AWS.command.s3.openFile%", - "category": "%AWS.title%", - "icon": "$(open-preview)" - }, - { - "command": "aws.s3.editFile", - "title": "%AWS.command.s3.editFile%", - "category": "%AWS.title%", - "icon": "$(edit)" - }, - { - "command": "aws.s3.uploadFile", - "title": "%AWS.command.s3.uploadFile%", - "category": "%AWS.title%", - "icon": "$(cloud-upload)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.s3.uploadFileToParent", - "title": "%AWS.command.s3.uploadFileToParent%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.s3.createFolder", - "title": "%AWS.command.s3.createFolder%", - "category": "%AWS.title%", - "icon": "$(new-folder)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.s3.createBucket", - "title": "%AWS.command.s3.createBucket%", - "category": "%AWS.title%", - "icon": "$(aws-s3-create-bucket)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.s3.deleteBucket", - "title": "%AWS.generic.promptDelete%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.s3.deleteFile", - "title": "%AWS.generic.promptDelete%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.invokeLambda", - "title": "%AWS.command.invokeLambda%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "title": "%AWS.command.invokeLambda.cn%", - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.downloadLambda", - "title": "%AWS.command.downloadLambda%", - "category": "%AWS.title%", - "enablement": "viewItem == awsRegionFunctionNodeDownloadable", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.uploadLambda", - "title": "%AWS.command.uploadLambda%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.deleteLambda", - "title": "%AWS.generic.promptDelete%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.copyLambdaUrl", - "title": "%AWS.generic.copyUrl%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.deploySamApplication", - "title": "%AWS.command.deploySamApplication%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.submitFeedback", - "title": "%AWS.command.submitFeedback%", - "category": "%AWS.title%", - "icon": "$(comment)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.refreshAwsExplorer", - "title": "%AWS.command.refreshAwsExplorer%", - "category": "%AWS.title%", - "icon": { - "dark": "resources/icons/vscode/dark/refresh.svg", - "light": "resources/icons/vscode/light/refresh.svg" - } - }, - { - "command": "aws.samcli.detect", - "title": "%AWS.command.samcli.detect%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.deleteCloudFormation", - "title": "%AWS.command.deleteCloudFormation%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.downloadStateMachineDefinition", - "title": "%AWS.command.downloadStateMachineDefinition%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.executeStateMachine", - "title": "%AWS.command.executeStateMachine%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.renderStateMachineGraph", - "title": "%AWS.command.renderStateMachineGraph%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.copyArn", - "title": "%AWS.command.copyArn%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.copyName", - "title": "%AWS.command.copyName%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.listCommands", - "title": "%AWS.command.listCommands%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "title": "%AWS.command.listCommands.cn%", - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.viewSchemaItem", - "title": "%AWS.command.viewSchemaItem%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.searchSchema", - "title": "%AWS.command.searchSchema%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.searchSchemaPerRegistry", - "title": "%AWS.command.searchSchemaPerRegistry%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.downloadSchemaItemCode", - "title": "%AWS.command.downloadSchemaItemCode%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.viewLogs", - "title": "%AWS.command.viewLogs%", - "category": "%AWS.title%" - }, - { - "command": "aws.help", - "title": "%AWS.command.help%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.github", - "title": "%AWS.command.github%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.quickStart", - "title": "%AWS.command.quickStart%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.cdk.refresh", - "title": "%AWS.command.refreshCdkExplorer%", - "category": "%AWS.title%", - "icon": { - "dark": "resources/icons/vscode/dark/refresh.svg", - "light": "resources/icons/vscode/light/refresh.svg" - }, - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.cdk.viewDocs", - "title": "%AWS.generic.viewDocs%", - "category": "%AWS.title%" - }, - { - "command": "aws.stepfunctions.createStateMachineFromTemplate", - "title": "%AWS.command.stepFunctions.createStateMachineFromTemplate%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.stepfunctions.publishStateMachine", - "title": "%AWS.command.stepFunctions.publishStateMachine%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.previewStateMachine", - "title": "%AWS.command.stepFunctions.previewStateMachine%", - "category": "%AWS.title%", - "icon": "$(aws-stepfunctions-preview)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.cdk.renderStateMachineGraph", - "title": "%AWS.command.cdk.previewStateMachine%", - "category": "AWS", - "icon": "$(aws-stepfunctions-preview)" - }, - { - "command": "aws.aboutToolkit", - "title": "%AWS.command.aboutToolkit%", - "category": "%AWS.title%" - }, - { - "command": "aws.cloudWatchLogs.viewLogStream", - "title": "%AWS.command.viewLogStream%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ssmDocument.createLocalDocument", - "title": "%AWS.command.ssmDocument.createLocalDocument%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ssmDocument.openLocalDocument", - "title": "%AWS.command.ssmDocument.openLocalDocument%", - "category": "%AWS.title%", - "icon": "$(cloud-download)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ssmDocument.openLocalDocumentJson", - "title": "%AWS.command.ssmDocument.openLocalDocumentJson%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ssmDocument.openLocalDocumentYaml", - "title": "%AWS.command.ssmDocument.openLocalDocumentYaml%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ssmDocument.deleteDocument", - "title": "%AWS.command.ssmDocument.deleteDocument%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ssmDocument.publishDocument", - "title": "%AWS.command.ssmDocument.publishDocument%", - "category": "%AWS.title%", - "icon": "$(cloud-upload)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ssmDocument.updateDocumentVersion", - "title": "%AWS.command.ssmDocument.updateDocumentVersion%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.copyLogStreamName", - "title": "%AWS.command.copyLogStreamName%", - "category": "%AWS.title%", - "icon": "$(files)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.saveCurrentLogStreamContent", - "title": "%AWS.command.saveCurrentLogStreamContent%", - "category": "%AWS.title%", - "icon": "$(save-as)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.addSamDebugConfig", - "title": "%AWS.command.addSamDebugConfig%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.toggleSamCodeLenses", - "title": "%AWS.command.toggleSamCodeLenses%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecs.runCommandInContainer", - "title": "%AWS.ecs.runCommandInContainer%", - "category": "%AWS.title%", - "enablement": "viewItem == awsEcsContainerNodeExecEnabled", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecs.openTaskInTerminal", - "title": "%AWS.ecs.openTaskInTerminal%", - "category": "%AWS.title%", - "enablement": "viewItem == awsEcsContainerNodeExecEnabled", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecs.enableEcsExec", - "title": "%AWS.ecs.enableEcsExec%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecs.viewDocumentation", - "title": "%AWS.generic.viewDocs%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.copyIdentifier", - "title": "%AWS.command.resources.copyIdentifier%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.openResourcePreview", - "title": "%AWS.generic.preview%", - "category": "%AWS.title%", - "icon": "$(open-preview)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.createResource", - "title": "%AWS.generic.create%", - "category": "%AWS.title%", - "icon": "$(add)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.deleteResource", - "title": "%AWS.generic.promptDelete%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.updateResource", - "title": "%AWS.generic.promptUpdate%", - "category": "%AWS.title%", - "icon": "$(pencil)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.updateResourceInline", - "title": "%AWS.generic.promptUpdate%", - "category": "%AWS.title%", - "icon": "$(pencil)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.saveResource", - "title": "%AWS.generic.save%", - "category": "%AWS.title%", - "icon": "$(save)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.closeResource", - "title": "%AWS.generic.close%", - "category": "%AWS.title%", - "icon": "$(close)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.viewDocs", - "title": "%AWS.generic.viewDocs%", - "category": "%AWS.title%", - "icon": "$(book)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.resources.configure", - "title": "%AWS.command.resources.configure%", - "category": "%AWS.title%", - "icon": "$(gear)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apprunner.createService", - "title": "%AWS.command.apprunner.createService%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.ecs.disableEcsExec", - "title": "%AWS.ecs.disableEcsExec%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apprunner.createServiceFromEcr", - "title": "%AWS.command.apprunner.createServiceFromEcr%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apprunner.pauseService", - "title": "%AWS.command.apprunner.pauseService%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apprunner.resumeService", - "title": "%AWS.command.apprunner.resumeService%", - "category": "AWS", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apprunner.copyServiceUrl", - "title": "%AWS.command.apprunner.copyServiceUrl%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apprunner.open", - "title": "%AWS.command.apprunner.open%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apprunner.deleteService", - "title": "%AWS.generic.promptDelete%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.apprunner.startDeployment", - "title": "%AWS.command.apprunner.startDeployment%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.cloudFormation.newTemplate", - "title": "%AWS.command.cloudFormation.newTemplate%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.sam.newTemplate", - "title": "%AWS.command.sam.newTemplate%", - "category": "%AWS.title%", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.samcli.sync", - "title": "%AWS.command.samcli.sync%", - "category": "%AWS.title%" - }, - { - "command": "aws.samcli.syncCode", - "title": "%AWS.command.samcli.syncCode%", - "category": "%AWS.title%" - }, - { - "command": "aws.codeWhisperer", - "title": "%AWS.command.codewhisperer.title%", - "category": "%AWS.title%" - }, - { - "command": "aws.codeWhisperer.configure", - "title": "%AWS.command.codewhisperer.configure%", - "category": "%AWS.title%", - "icon": "$(gear)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.codeWhisperer.introduction", - "title": "%AWS.command.codewhisperer.introduction%", - "category": "%AWS.title%", - "icon": "$(question)", - "cloud9": { - "cn": { - "category": "%AWS.title.cn%" - } - } - }, - { - "command": "aws.codeWhisperer.removeConnection", - "title": "%AWS.command.codewhisperer.removeConnection%", - "category": "%AWS.title%", - "icon": "$(debug-disconnect)" - }, - { - "command": "aws.dev.openMenu", - "title": "Open Developer Menu", - "category": "AWS (Developer)", - "enablement": "aws.isDevMode" - } - ], - "jsonValidation": [ - { - "fileMatch": ".aws/templates.json", - "url": "./dist/src/templates/templates.json" - }, - { - "fileMatch": "*ecs-task-def.json", - "url": "https://ecs-intellisense.s3-us-west-2.amazonaws.com/task-definition/schema.json" - } - ], - "languages": [ - { - "id": "asl", - "extensions": [ - ".asl.json", - ".asl" - ], - "aliases": [ - "Amazon States Language" - ] - }, - { - "id": "asl-yaml", - "aliases": [ - "Amazon States Language (YAML)" - ], - "extensions": [ - ".asl.yaml", - ".asl.yml" - ] - }, - { - "id": "ssm-json", - "extensions": [ - ".ssm.json" - ], - "aliases": [ - "AWS Systems Manager Document (JSON)" - ] - }, - { - "id": "ssm-yaml", - "extensions": [ - ".ssm.yaml", - ".ssm.yml" - ], - "aliases": [ - "AWS Systems Manager Document (YAML)" - ] - } - ], - "keybindings": [ - { - "command": "aws.previewStateMachine", - "key": "ctrl+shift+v", - "mac": "cmd+shift+v", - "when": "editorTextFocus && editorLangId == asl || editorTextFocus && editorLangId == asl-yaml" - }, - { - "command": "aws.codeWhisperer", - "key": "alt+c", - "mac": "alt+c", - "when": "editorTextFocus" - }, - { - "command": "aws.codeWhisperer.nextCodeSuggestion", - "key": "right", - "mac": "right", - "when": "editorTextFocus && CODEWHISPERER_SERVICE_ACTIVE" - }, - { - "command": "aws.codeWhisperer.previousCodeSuggestion", - "key": "left", - "mac": "left", - "when": "editorTextFocus && CODEWHISPERER_SERVICE_ACTIVE" - }, - { - "command": "aws.codeWhisperer.rejectCodeSuggestion", - "key": "escape", - "mac": "escape", - "when": "editorTextFocus && CODEWHISPERER_SERVICE_ACTIVE" - }, - { - "command": "aws.codeWhisperer.rejectCodeSuggestion", - "key": "backspace", - "mac": "backspace", - "when": "editorTextFocus && CODEWHISPERER_SERVICE_ACTIVE" - }, - { - "command": "aws.codeWhisperer.rejectCodeSuggestion", - "key": "up", - "mac": "up", - "when": "editorTextFocus && CODEWHISPERER_SERVICE_ACTIVE", - "args": "up" - }, - { - "command": "aws.codeWhisperer.rejectCodeSuggestion", - "key": "down", - "mac": "down", - "when": "editorTextFocus && CODEWHISPERER_SERVICE_ACTIVE", - "args": "down" - }, - { - "command": "aws.codeWhisperer.acceptCodeSuggestion", - "key": "tab", - "mac": "tab", - "when": "editorTextFocus && CODEWHISPERER_SERVICE_ACTIVE" - }, - { - "key": "right", - "command": "editor.action.inlineSuggest.showNext", - "when": "inlineSuggestionVisible && !editorReadonly && CODEWHISPERER_ENABLED" - }, - { - "key": "left", - "command": "editor.action.inlineSuggest.showPrevious", - "when": "inlineSuggestionVisible && !editorReadonly && CODEWHISPERER_ENABLED" - } - ], - "grammars": [ - { - "language": "asl", - "scopeName": "source.asl", - "path": "./syntaxes/ASL.tmLanguage" - }, - { - "language": "asl-yaml", - "scopeName": "source.asl.yaml", - "path": "./syntaxes/asl-yaml.tmLanguage.json" - }, - { - "language": "ssm-json", - "scopeName": "source.ssmjson", - "path": "./syntaxes/SSMJSON.tmLanguage" - }, - { - "language": "ssm-yaml", - "scopeName": "source.ssmyaml", - "path": "./syntaxes/SSMYAML.tmLanguage" - } - ], - "resourceLabelFormatters": [ - { - "scheme": "awsCloudWatchLogs", - "formatting": { - "label": "${path}", - "separator": "\\" - } - }, - { - "scheme": "s3*", - "formatting": { - "label": "[S3] ${path}", - "separator": "/" - } - } - ], - "walkthroughs": [ - { - "id": "getStarted", - "title": "%AWS.walkthrough.gettingStarted.title%", - "description": "%AWS.walkthrough.gettingStarted.description%", - "cloud9": { - "cn": { - "description": "%AWS.walkthrough.gettingStarted.description.cn%" - } - }, - "steps": [ - { - "id": "connect", - "title": "%AWS.walkthrough.gettingStarted.connect%", - "media": { - "markdown": "resources/walkthrough/setup-connect.md" - }, - "completionEvents": [ - "onCommand:aws.login", - "onCommand:aws.credentials.profile.create" - ] - }, - { - "id": "changeRegions", - "title": "%AWS.walkthrough.gettingStarted.changeRegions%", - "media": { - "markdown": "resources/walkthrough/setup-region.md" - }, - "completionEvents": [ - "onCommand:aws.showRegion" - ] - }, - { - "id": "setupToolchain", - "title": "%AWS.walkthrough.gettingStarted.setupToolchain%", - "media": { - "markdown": "resources/walkthrough/setup-toolchain.md" - } - } - ] - } - ], - "icons": { - "aws-apprunner-service": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1a5" - } - }, - "aws-cdk-logo": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1a6" - } - }, - "aws-cloudformation-stack": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1a7" - } - }, - "aws-cloudwatch-log-group": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1a8" - } - }, - "aws-codecatalyst-logo": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1a9" - } - }, - "aws-ecr-registry": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1aa" - } - }, - "aws-ecs-cluster": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1ab" - } - }, - "aws-ecs-container": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1ac" - } - }, - "aws-ecs-service": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1ad" - } - }, - "aws-generic-attach-file": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1ae" - } - }, - "aws-iot-certificate": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1af" - } - }, - "aws-iot-policy": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1b0" - } - }, - "aws-iot-thing": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1b1" - } - }, - "aws-lambda-function": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1b2" - } - }, - "aws-s3-bucket": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1b3" - } - }, - "aws-s3-create-bucket": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1b4" - } - }, - "aws-schemas-registry": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1b5" - } - }, - "aws-schemas-schema": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1b6" - } - }, - "aws-stepfunctions-preview": { - "description": "AWS Contributed Icon", - "default": { - "fontPath": "./resources/fonts/aws-toolkit-icons.woff", - "fontCharacter": "\\e1b7" - } - } - } - }, - "scripts": { - "vscode:prepublish": "npm run clean && npm run buildScripts && npm run lint && webpack --mode production && npm run copyFiles -- --webpacked", - "clean": "ts-node ./scripts/clean.ts dist", - "reset": "npm run clean -- node_modules && npm install", - "copyFiles": "ts-node ./scripts/build/copyFiles.ts", - "buildScripts": "npm run generateClients && npm run generatePackage && npm run generateNonCodeFiles && npm run copyFiles", - "compile": "npm run clean && npm run buildScripts && webpack --mode development && npm run copyFiles -- --webpacked", - "watch": "npm run clean && npm run buildScripts && tsc -watch -p ./", - "postinstall": "npm run generateTelemetry && npm run generateConfigurationAttributes", - "testCompile": "npm run buildScripts && tsc -p ./ && npm run instrument", - "test": "npm run testCompile && ts-node ./scripts/test/test.ts && npm run report", - "integrationTest": "npm run testCompile && ts-node ./scripts/test/integrationTest.ts && npm run report", - "lint": "eslint -c .eslintrc.js --ext .ts .", - "lintfix": "eslint -c .eslintrc.js --fix --ext .ts .", - "package": "ts-node ./scripts/build/package.ts", - "install-plugin": "vsce package -o aws-toolkit-vscode-test.vsix && code --install-extension aws-toolkit-vscode-test.vsix", - "generateClients": "ts-node ./scripts/build/generateServiceClient.ts ", - "generatePackage": "ts-node ./scripts/build/generateIcons.ts", - "generateTelemetry": "node node_modules/@aws-toolkits/telemetry/lib/generateTelemetry.js --extraInput=src/shared/telemetry/vscodeTelemetry.json --output=src/shared/telemetry/telemetry.gen.ts", - "generateNonCodeFiles": "ts-node ./scripts/build/generateNonCodeFiles.ts", - "generateConfigurationAttributes": "ts-node ./scripts/build/generateConfigurationAttributes.ts", - "newChange": "ts-node ./scripts/newChange.ts", - "createRelease": "ts-node ./scripts/build/createRelease.ts", - "serve": "webpack serve --config-name vue-hmr --mode development", - "instrument": "nyc instrument --in-place ./dist/src", - "report": "nyc report --reporter=html --reporter=json" - }, - "devDependencies": { - "@aws-toolkits/telemetry": "^1.0.87", - "@cspotcode/source-map-support": "^0.8.1", - "@sinonjs/fake-timers": "^8.1.0", - "@types/adm-zip": "^0.4.34", - "@types/async-lock": "^1.1.3", - "@types/bytes": "^3.1.0", - "@types/cross-spawn": "^6.0.0", - "@types/fs-extra": "^9.0.11", - "@types/glob": "^7.1.1", - "@types/js-yaml": "^4.0.5", - "@types/lodash": "^4.14.180", - "@types/marked": "^4.0.2", - "@types/mime-types": "^2.1.1", - "@types/mocha": "^10.0.0", - "@types/node": "^14.18.5", - "@types/readline-sync": "^1.4.3", - "@types/semver": "^7.3.6", - "@types/sinon": "^10.0.5", - "@types/sinonjs__fake-timers": "^8.1.1", - "@types/tcp-port-used": "^1.0.1", - "@types/uuid": "^8.3.3", - "@types/vscode": "1.50.0", - "@types/vscode-webview": "^1.57.0", - "@types/xml2js": "^0.4.8", - "@typescript-eslint/eslint-plugin": "^5.38.0", - "@typescript-eslint/parser": "^5.38.0", - "@vscode/codicons": "^0.0.32", - "@vscode/test-electron": "^2.2.3", - "@vue/compiler-sfc": "^3.2.40", - "circular-dependency-plugin": "^5.2.2", - "css-loader": "^6.5.1", - "esbuild-loader": "2.20.0", - "eslint": "^8.26.0", - "eslint-config-prettier": "8.3", - "eslint-plugin-header": "^3.1.1", - "eslint-plugin-no-null": "^1.0.2", - "glob": "^7.1.7", - "husky": "^7.0.2", - "json-schema-to-typescript": "^11.0.2", - "marked": "^4.0.10", - "mocha": "^10.1.0", - "mocha-junit-reporter": "^2.0.0", - "mocha-multi-reporters": "^1.5.1", - "nyc": "^15.1.0", - "prettier": "^2.7.1", - "prettier-plugin-sh": "^0.8.1", - "pretty-quick": "^3.1.0", - "readline-sync": "^1.4.9", - "sinon": "^14.0.0", - "ts-mockito": "^2.5.0", - "ts-node": "^10.7.0", - "umd-compat-loader": "^2.1.2", - "vsce": "^2.6.3", - "vscode-nls-dev": "^3.3.1", - "vue-loader": "^16.8.1", - "vue-style-loader": "^4.1.3", - "webfont": "^11.2.26", - "webpack": "^5.65.0", - "webpack-cli": "^4.9.1", - "webpack-dev-server": "^4.9.2" - }, - "dependencies": { - "@aws-sdk/client-sso": "^3.181.0", - "@aws-sdk/client-sso-oidc": "^3.181.0", - "@aws-sdk/credential-provider-ini": "^3.46.0", - "@aws-sdk/credential-provider-process": "^3.15.0", - "@aws-sdk/credential-provider-sso": "^3.38.0", - "@aws-sdk/util-arn-parser": "^3.46.0", - "@iarna/toml": "^2.2.5", - "adm-zip": "^0.5.9", - "amazon-states-language-service": "^1.8.0", - "async-lock": "^1.3.0", - "aws-sdk": "^2.1267.0", - "aws-ssm-document-language-service": "^1.0.0", - "bytes": "^3.1.2", - "cross-spawn": "^7.0.3", - "fast-json-patch": "^3.1.1", - "fs-extra": "^10.0.1", - "got": "^11.8.5", - "immutable": "^4.0.0", - "js-yaml": "^4.1.0", - "jsonc-parser": "^3.0.0", - "lodash": "^4.17.21", - "mime-types": "^2.1.32", - "moment": "^2.29.4", - "portfinder": "^1.0.25", - "semver": "^7.3.5", - "strip-ansi": "^5.2.0", - "tcp-port-used": "^1.0.1", - "typescript": "^4.8.4", - "uuid": "^8.3.2", - "vscode-languageclient": "^6.1.4", - "vscode-languageserver": "^6.1.1", - "vscode-languageserver-textdocument": "^1.0.3", - "vscode-nls": "^5.0.0", - "vue": "^3.2.31", - "winston": "^3.7.1", - "winston-transport": "^4.5.0", - "xml2js": "^0.4.19", - "yaml": "^1.9.2", - "yaml-cfn": "^0.3.1" - }, "prettier": { "printWidth": 120, "trailingComma": "es5", @@ -3626,7 +15,70 @@ "singleQuote": true, "semi": false, "bracketSpacing": true, - "arrowParens": "avoid", + "arrowParens": "always", "endOfLine": "lf" + }, + "scripts": { + "prepare": "ts-node ./scripts/prepare.ts", + "preinstall": "cd src.gen/@amzn/codewhisperer-streaming && npm i && cd ../amazon-q-developer-streaming-client && npm i", + "postinstall": "npm run buildCustomLintPlugin && npm run postinstall -ws --if-present", + "buildCustomLintPlugin": "npm run build -w plugins/eslint-plugin-aws-toolkits", + "compile": "npm run compile -w packages/", + "testCompile": "npm run testCompile -w packages/ --if-present", + "test": "npm run test -w packages/ --if-present", + "testWeb": "npm run testWeb -w packages/ --if-present", + "testE2E": "npm run testE2E -w packages/ --if-present", + "testInteg": "npm run testInteg -w packages/ --if-present", + "package": "npm run package -w packages/toolkit -w packages/amazonq", + "newChange": "echo 'Must specify subproject/workspace with -w packages/' && false", + "createRelease": "echo 'Must specify subproject/workspace with -w packages/' && false", + "lint": "npm run lint -w packages/ --if-present", + "lintfix": "eslint -c .eslintrc.js --ignore-path .gitignore --ignore-pattern '**/*.json' --ignore-pattern '**/*.gen.ts' --ignore-pattern '**/types/*.d.ts' --ignore-pattern '**/src/testFixtures/**' --fix --ext .ts packages plugins", + "clean": "npm run clean -w packages/ -w plugins/", + "reset": "npm run clean && ts-node ./scripts/clean.ts node_modules && npm install", + "generateNonCodeFiles": "npm run generateNonCodeFiles -w packages/ --if-present", + "mergeReports": "ts-node ./scripts/mergeReports.ts", + "skippedTestReport": "ts-node ./scripts/skippedTestReport.ts ./packages/amazonq/test/e2e/", + "scan-licenses": "ts-node ./scripts/scan-licenses.ts" + }, + "devDependencies": { + "@aws-toolkits/telemetry": "^1.0.329", + "@playwright/browser-chromium": "^1.43.1", + "@stylistic/eslint-plugin": "^2.11.0", + "@types/he": "^1.2.3", + "@types/jaro-winkler": "^0.2.4", + "@types/vscode": "^1.68.0", + "@types/vscode-webview": "^1.57.1", + "@types/webpack-env": "^1.18.5", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.14.1", + "@vscode/codicons": "^0.0.33", + "@vscode/test-electron": "^2.3.8", + "@vscode/test-web": "^0.0.65", + "@vscode/vsce": "^2.19.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-aws-toolkits": "file:plugins/eslint-plugin-aws-toolkits", + "eslint-plugin-header": "^3.1.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-security-node": "^1.1.4", + "eslint-plugin-unicorn": "^54.0.0", + "husky": "^9.0.7", + "prettier": "^3.3.3", + "prettier-plugin-sh": "^0.14.0", + "pretty-quick": "^4.0.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4", + "webpack": "^5.95.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.15.2", + "webpack-merge": "^5.10.0" + }, + "dependencies": { + "@aws/language-server-runtimes": "^0.2.128", + "@types/node": "^22.7.5", + "jaro-winkler": "^0.2.8", + "vscode-nls": "^5.2.0", + "vscode-nls-dev": "^4.0.4" } } diff --git a/package.nls.json b/package.nls.json deleted file mode 100644 index 3b0c0e7cc47..00000000000 --- a/package.nls.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "AWS.title": "AWS", - "AWS.title.cn": "Amazon", - "AWS.productName": "AWS Toolkit", - "AWS.productName.cn": "Amazon Toolkit", - "AWS.configuration.profileDescription": "The name of the credential profile to obtain credentials from.", - "AWS.configuration.description.lambda.recentlyUploaded": "Recently selected Lambda upload targets.", - "AWS.configuration.description.ecs.openTerminalCommand": "The command to run when starting a new interactive terminal session.", - "AWS.configuration.description.logLevel": "The AWS Toolkit's log level (changes reflected on restart)", - "AWS.configuration.description.logLevel.cn": "The Amazon Toolkit's log level (changes reflected on restart)", - "AWS.configuration.description.iot.maxItemsPerPage": "Controls how many IoT Things, Certificates, or Policies are listed before showing a node to `Load More...`.", - "AWS.configuration.description.s3.maxItemsPerPage": "Controls how many S3 items are listed before showing a node to `Load More...`.\nThis corresponds to the `MaxKeys` requested in a single call to S3. [Learn More](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html#AmazonS3-ListObjectsV2-response-MaxKeys)", - "AWS.configuration.description.samcli.lambdaTimeout": "Maximum time (in milliseconds) to wait for SAM output while starting a Local Lambda session", - "AWS.configuration.description.samcli.location": "Location of SAM CLI. SAM CLI is used to create, build, package, and deploy Serverless Applications. [Learn More](https://aws.amazon.com/serverless/sam/)", - "AWS.configuration.description.samcli.legacyDeploy": "Use the legacy SAM deploy experience, disabling all SAM sync functionality.", - "AWS.configuration.description.telemetry": "Enable AWS Toolkit to send usage data to AWS.", - "AWS.configuration.description.telemetry.cn": "Enable Amazon Toolkit to send usage data to Amazon.", - "AWS.configuration.description.suppressPrompts": "Prompts which ask for confirmation. Checking an item suppresses the prompt.", - "AWS.configuration.enableCodeLenses": "Enable SAM hints in source files", - "AWS.configuration.description.resources.enabledResources": "AWS resources to display in the 'Resources' portion of the explorer.", - "AWS.configuration.description.experiments": "Try experimental features and give feedback. Note that experimental features may be removed at any time.\n * `jsonResourceModification` - Enables basic create, update, and delete support for cloud resources via the JSON Resources explorer component.\n * `samSyncCode` - Adds an additional code-only option when synchronizing SAM applications. Code-only synchronizations are faster but can cause drift in the CloudFormation stack. Does nothing when using the legacy SAM deploy feature.", - "AWS.stepFunctions.asl.format.enable.desc": "Enables the default formatter used with Amazon States Language files", - "AWS.stepFunctions.asl.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).", - "AWS.configuration.description.awssam.debug.api": "API Gateway configuration", - "AWS.configuration.description.awssam.debug.api.clientCertId": "The API Gateway client certificate ID", - "AWS.configuration.description.awssam.debug.api.headers": "Additional HTTP headers", - "AWS.configuration.description.awssam.debug.api.httpMethod": "The HTTP message method that will be used", - "AWS.configuration.description.awssam.debug.api.path": "The path to the api (must start with /)", - "AWS.configuration.description.awssam.debug.api.queryString": "URL query string (e.g. key=foo&value=bar)", - "AWS.configuration.description.awssam.debug.api.stageVariables": "key-value map of API Gateway stage variables", - "AWS.configuration.description.awssam.debug.buildDir": "Base directory to build and run the application.\n A temporary directory will be created if not specified.", - "AWS.configuration.description.awssam.debug.label": "AWS SAM: Debug Lambda Function Locally", - "AWS.configuration.description.awssam.debug.invokeTarget": "Configures the application to launch", - "AWS.configuration.description.awssam.debug.target": "The type of invocation to launch. Possible values:\n* `template` uses a CFN/SAM Template as an entrypoint\n* `code` invokes Lambda code directly.\n* `api` uses the CFN/SAM Template to emulate API Gateway", - "AWS.configuration.description.awssam.debug.architecture": "Architecture used for local SAM Lambda emulation", - "AWS.configuration.description.awssam.debug.lambdaHandler": "Lambda Function handler to invoke.", - "AWS.configuration.description.awssam.debug.projectRoot": "The root of the project, used to determine where in the file-system to locate the lambdaHandler.", - "AWS.configuration.description.awssam.debug.templatePath": "Path to the CFN/SAM template.", - "AWS.configuration.description.awssam.debug.logicalId": "Resource name of an AWS::Lambda::Function or AWS::Serverless::Function to invoke.", - "AWS.configuration.description.awssam.debug.envvars": "Environment variables to pass to the function invocation (replaces template variables).", - "AWS.configuration.description.awssam.debug.lambda": "Lambda specific details of the invocation", - "AWS.configuration.description.awssam.debug.memoryMb": "The amount of memory (in Mb) the Lambda function has access to.", - "AWS.configuration.description.awssam.debug.runtime": "The Lambda Function's runtime", - "AWS.configuration.description.awssam.debug.timeout": "The amount of time (in seconds) that Lambda allows a function to run before stopping it.", - "AWS.configuration.description.awssam.debug.aws": "AWS connection details", - "AWS.configuration.description.awssam.debug.credentials": "The AWS credentials provider and name to use during the invocation. Example: credential profile \"default\" would be entered as `profile:default`.", - "AWS.configuration.description.awssam.debug.credentials.cn": "The Amazon credentials provider and name to use during the invocation. Example: credential profile \"default\" would be entered as `profile:default`.", - "AWS.configuration.description.awssam.debug.region": "AWS region to use during the invocation.", - "AWS.configuration.description.awssam.debug.event": "Event payload to pass to the Lambda invocation.\n Must specify one of 'json' or 'path'.", - "AWS.configuration.description.awssam.debug.event.json": "JSON definition to use as the event payload", - "AWS.configuration.description.awssam.debug.event.path": "Path to a file to use as the event payload", - "AWS.configuration.description.awssam.debug.sam": "SAM CLI specific configurations", - "AWS.configuration.description.awssam.debug.buildArguments": "Additional arguments to pass to the `sam build` command.", - "AWS.configuration.description.awssam.debug.containerBuild": "Whether to build inside a container (default: false).", - "AWS.configuration.description.awssam.debug.dockerNetwork": "Specifies the name or id of an existing Docker network that Lambda Docker containers should connect to.", - "AWS.configuration.description.awssam.debug.localArguments": "Additional arguments to pass to the `sam local` command.", - "AWS.configuration.description.awssam.debug.skipNewImageCheck": "Specifies whether the command should skip pulling down the latest Docker image for Lambda runtime (default: false).", - "AWS.configuration.description.awssam.debug.template": "Values to override in the template", - "AWS.configuration.description.awssam.debug.templateParameters": "Key:value mappings for SAM template parameter overrides. More information can be found here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stackinstances-override.html#stackinstances-override-cli", - "AWS.configuration.description.awssam.debug.snippets.lambdaCode.label": "AWS SAM: Direct Lambda handler invoke", - "AWS.configuration.description.awssam.debug.snippets.lambdaCode.description": "A new configuration for invoking an AWS Lambda directly via the SAM CLI.", - "AWS.configuration.description.awssam.debug.snippets.lambdaCode.label.cn": "Amazon SAM: Direct Lambda handler invoke", - "AWS.configuration.description.awssam.debug.snippets.lambdaCode.description.cn": "A new configuration for invoking an Amazon Lambda directly via the SAM CLI.", - "AWS.configuration.description.awssam.debug.snippets.lambdaTemplate.label": "AWS SAM: Template-based Lambda invoke", - "AWS.configuration.description.awssam.debug.snippets.lambdaTemplate.description": "A new configuration for invoking an AWS Lambda in a CloudFormation template via the SAM CLI.", - "AWS.configuration.description.awssam.debug.snippets.lambdaTemplate.label.cn": "Amazon SAM: Template-based Lambda invoke", - "AWS.configuration.description.awssam.debug.snippets.lambdaTemplate.description.cn": "A new configuration for invoking an Amazon Lambda in a CloudFormation template via the SAM CLI.", - "AWS.configuration.description.awssam.debug.snippets.api.label": "AWS SAM: API Gateway lambda invoke", - "AWS.configuration.description.awssam.debug.snippets.api.description": "A new configuration for invoking an AWS Lambda in a CloudFormation template, simulating API Gateway", - "AWS.configuration.description.awssam.debug.snippets.api.label.cn": "Amazon SAM: API Gateway lambda invoke", - "AWS.configuration.description.awssam.debug.snippets.api.description.cn": "A new configuration for invoking an Amazon Lambda in a CloudFormation template, simulating API Gateway", - "AWS.configuration.description.codewhisperer": "When checked, CodeWhisperer will include suggestions with code references", - "AWS.configuration.description.codewhisperer.shareCodeWhispererContentWithAWS": "When checked, your content processed by CodeWhisperer may be used for service improvement. Unchecking this box will cause AWS to delete any of your content used for that purpose. The information used to provide the CodeWhisperer service to you will not be affected. See the Preview terms and [Service Terms](https://aws.amazon.com/service-terms/) for more detail.", - "AWS.command.apig.copyUrl": "Copy URL", - "AWS.command.apig.invokeRemoteRestApi": "Invoke on AWS", - "AWS.command.apig.invokeRemoteRestApi.cn": "Invoke on Amazon", - "AWS.command.auth.addConnection": "Add New Connection", - "AWS.command.auth.switchConnections": "Switch Connections", - "AWS.command.auth.signout": "Sign out", - "AWS.command.github": "View Source on GitHub", - "AWS.command.help": "View Toolkit Documentation", - "AWS.command.login": "Connect to AWS", - "AWS.command.login.cn": "Connect to Amazon", - "AWS.command.logout": "Sign out", - "AWS.command.createIssueOnGitHub": "Report Toolkit Issue", - "AWS.command.createNewSamApp": "Create Lambda SAM Application", - "AWS.command.credentials.profile.create": "Create Credentials Profile", - "AWS.command.credentials.edit": "Edit Credentials", - "AWS.command.showRegion": "Show or Hide Regions", - "AWS.command.codecatalyst.openOrg": "Open CodeCatalyst Space", - "AWS.command.codecatalyst.openProject": "Open CodeCatalyst Project", - "AWS.command.codecatalyst.openRepo": "Open CodeCatalyst Repository", - "AWS.command.codecatalyst.openDevEnv": "Open CodeCatalyst Dev Environment", - "AWS.command.codecatalyst.cloneRepo": "Clone CodeCatalyst Repository", - "AWS.command.codecatalyst.createDevEnv": "Create CodeCatalyst Dev Environment", - "AWS.command.codecatalyst.listCommands": "List CodeCatalyst Commands", - "AWS.command.codecatalyst.login": "Connect to CodeCatalyst", - "AWS.command.codecatalyst.logout": "Sign out of CodeCatalyst", - "AWS.command.codecatalyst.removeConnection": "Remove Connection from Tool", - "AWS.command.deploySamApplication": "Deploy SAM Application", - "AWS.command.aboutToolkit": "About Toolkit", - "AWS.command.downloadLambda": "Download...", - "AWS.command.uploadLambda": "Upload Lambda...", - "AWS.command.invokeLambda": "Invoke on AWS", - "AWS.command.invokeLambda.cn": "Invoke on Amazon", - "AWS.command.refreshAwsExplorer": "Refresh Explorer", - "AWS.command.refreshCdkExplorer": "Refresh CDK Explorer", - "AWS.command.cdk.help": "View CDK Documentation", - "AWS.command.ecr.copyTagUri": "Copy Tag URI", - "AWS.command.ecr.copyRepositoryUri": "Copy Repository URI", - "AWS.command.ecr.createRepository": "Create Repository...", - "AWS.command.ecr.deleteRepository": "Delete Repository...", - "AWS.command.ecr.deleteTag": "Delete Tag...", - "AWS.ecs.enableEcsExec": "Enable Command Execution", - "AWS.ecs.disableEcsExec": "Disable Command Execution", - "AWS.ecs.runCommandInContainer": "Run Command in Container", - "AWS.ecs.openTaskInTerminal": "Open Terminal...", - "AWS.command.samcli.detect": "Detect SAM CLI", - "AWS.command.deleteCloudFormation": "Delete CloudFormation Stack", - "AWS.command.viewSchemaItem": "View Schema", - "AWS.command.searchSchema": "Search Schemas", - "AWS.command.executeStateMachine": "Start Execution...", - "AWS.command.renderStateMachineGraph": "Render graph", - "AWS.command.copyArn": "Copy ARN", - "AWS.command.copyName": "Copy Name", - "AWS.command.listCommands": "Show AWS Commands...", - "AWS.command.listCommands.cn": "Show Amazon Commands...", - "AWS.command.downloadStateMachineDefinition": "Download Definition...", - "AWS.command.searchSchemaPerRegistry": "Search Schemas in Registry", - "AWS.command.submitFeedback": "Send Feedback...", - "AWS.command.downloadSchemaItemCode": "Download Code Bindings", - "AWS.command.viewLogs": "View Toolkit Logs", - "AWS.command.sam.newTemplate": "Create new SAM Template", - "AWS.command.cloudFormation.newTemplate": "Create new CloudFormation Template", - "AWS.command.quickStart": "View Quick Start", - "AWS.command.iot.createThing": "Create Thing...", - "AWS.command.iot.createCert": "Create Certificate...", - "AWS.command.iot.createPolicy": "Create Policy from Document...", - "AWS.command.iot.createPolicyVersion": "Create new version from Document...", - "AWS.command.iot.attachCert": "Attach Certificate...", - "AWS.command.iot.attachPolicy": "Attach Policy...", - "AWS.command.iot.activateCert": "Activate...", - "AWS.command.iot.deactivateCert": "Deactivate...", - "AWS.command.iot.revokeCert": "Revoke...", - "AWS.command.iot.detachCert": "Detach...", - "AWS.command.iot.setDefaultPolicy": "Set as Default", - "AWS.command.iot.viewPolicyVersion": "View...", - "AWS.command.iot.copyEndpoint": "Copy Endpoint...", - "AWS.command.samcli.sync": "Sync SAM Application", - "AWS.command.samcli.syncCode": "Sync SAM Application (code only)", - "AWS.command.s3.downloadFileAs": "Download As...", - "AWS.command.s3.editFile": "Edit File", - "AWS.command.s3.openFile": "Open File", - "AWS.command.s3.copyPath": "Copy Path", - "AWS.command.s3.presignedURL": "Generate Presigned URL...", - "AWS.command.s3.createBucket": "Create Bucket...", - "AWS.command.s3.createFolder": "Create Folder...", - "AWS.command.s3.uploadFile": "Upload Files...", - "AWS.command.s3.uploadFileToParent": "Upload to Parent...", - "AWS.command.stepFunctions.createStateMachineFromTemplate": "Create a new Step Functions state machine", - "AWS.command.stepFunctions.publishStateMachine": "Publish state machine to Step Functions", - "AWS.command.stepFunctions.previewStateMachine": "Render state machine graph", - "AWS.command.cdk.previewStateMachine": "Render state machine graph from CDK application", - "AWS.command.copyLogStreamName": "Copy Log Stream Name", - "AWS.command.saveCurrentLogStreamContent": "Save Current Log Content to File", - "AWS.command.viewLogStream": "View Log Stream...", - "AWS.command.ssmDocument.createLocalDocument": "Create a new Systems Manager Document locally", - "AWS.command.ssmDocument.deleteDocument": "Delete Document", - "AWS.command.ssmDocument.updateDocumentVersion": "Set Default Version", - "AWS.command.ssmDocument.openLocalDocument": "Download", - "AWS.command.ssmDocument.openLocalDocumentJson": "Download as JSON", - "AWS.command.ssmDocument.openLocalDocumentYaml": "Download as YAML", - "AWS.command.ssmDocument.publishDocument": "Publish a Systems Manager Document", - "AWS.command.launchConfigForm.title": "Edit SAM Debug Configuration", - "AWS.command.addSamDebugConfig": "Add SAM Debug Configuration", - "AWS.command.toggleSamCodeLenses": "Toggle SAM hints in source files", - "AWS.command.apprunner.createService": "Create Service", - "AWS.command.apprunner.createServiceFromEcr": "Create App Runner Service", - "AWS.command.apprunner.pauseService": "Pause Service", - "AWS.command.apprunner.resumeService": "Resume Service", - "AWS.command.apprunner.copyServiceUrl": "Copy Service URL", - "AWS.command.apprunner.open": "Open in Browser", - "AWS.command.apprunner.startDeployment": "Start Deployment", - "AWS.command.resources.copyIdentifier": "Copy Identifier", - "AWS.command.resources.configure": "Show Resources...", - "AWS.command.codewhisperer.introduction": "What is CodeWhisperer?", - "AWS.command.codewhisperer.configure": "CodeWhisperer Settings", - "AWS.command.codewhisperer.removeConnection": "Remove Connection from Tool", - "AWS.lambda.explorerTitle": "Explorer", - "AWS.developerTools.explorerTitle": "Developer Tools", - "AWS.cloudWatchLogs.limit.desc": "Maximum amount of log entries pulled per request from CloudWatch Logs (max 10000)", - "AWS.samcli.deploy.bucket.recentlyUsed": "Buckets recently used for SAM deployments", - "AWS.submenu.auth.title": "Authentication", - "AWS.generic.create": "Create...", - "AWS.generic.save": "Save", - "AWS.generic.close": "Close", - "AWS.generic.copyUrl": "Copy URL", - "AWS.generic.promptDelete": "Delete...", - "AWS.generic.promptUpdate": "Update...", - "AWS.generic.preview": "Preview", - "AWS.generic.viewDocs": "View Documentation", - "AWS.ssmDocument.ssm.maxItemsComputed.desc": "Controls the maximum number of problems produced by the SSM Document language server.", - "AWS.walkthrough.gettingStarted.title": "Get started with AWS", - "AWS.walkthrough.gettingStarted.description": "These walkthroughs help you set up the AWS Toolkit.", - "AWS.walkthrough.gettingStarted.description.cn": "These walkthroughs help you set up the Amazon Toolkit.", - "AWS.walkthrough.gettingStarted.connect": "Connect to AWS", - "AWS.walkthrough.gettingStarted.changeRegions": "Change AWS Regions", - "AWS.walkthrough.gettingStarted.setupToolchain": "Configure your toolchain", - "AWS.command.codewhisperer.title": "CodeWhisperer Invoke Service" -} diff --git a/packages/.vscodeignore.packages b/packages/.vscodeignore.packages new file mode 100644 index 00000000000..239c2bb0fe2 --- /dev/null +++ b/packages/.vscodeignore.packages @@ -0,0 +1,28 @@ +# Ignore everything by default. #1899 +* +*/** + +# Ignore outside dirs up to the root package.json, otherwise those will be collected as well +# (until vsce officially supports workspaces: https://github.com/microsoft/vscode-vsce/issues/580) +../** +../../** + +# Allowlist +# --------- +!dist/* +!dist/libs +!dist/*/!(testFixtures|test)/**/!(*.*.map) +!dist/*/!(*.*.map) +!resources/** +!syntaxes/** +!templates/** +!types/** +!package.json +!package.nls.json +!package.nls.*.json + +!quickStart** +!README.** +!CHANGELOG.md +!LICENSE +!NOTICE diff --git a/packages/amazonq/.c8rc.json b/packages/amazonq/.c8rc.json new file mode 100644 index 00000000000..95d5af7c32b --- /dev/null +++ b/packages/amazonq/.c8rc.json @@ -0,0 +1,6 @@ +{ + "report-dir": "../../coverage/amazonq", + "reporter": ["lcov"], + "all": true, + "exclude": ["**/test*/**", "**/node_modules/**"] +} diff --git a/packages/amazonq/.changes/1.0.0.json b/packages/amazonq/.changes/1.0.0.json new file mode 100644 index 00000000000..9499804cd6d --- /dev/null +++ b/packages/amazonq/.changes/1.0.0.json @@ -0,0 +1,86 @@ +{ + "date": "2024-04-29", + "version": "1.0.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Code Transformation: Address various issues in TransformationHub UX." + }, + { + "type": "Bug Fix", + "description": "Code Transformation: Transform may fail if JAVA_HOME has leading or trailing whitespace" + }, + { + "type": "Bug Fix", + "description": "Chat: Q panel doesn't fit to its parent" + }, + { + "type": "Bug Fix", + "description": "Feature Development: update welcome message and menu item description for /dev command" + }, + { + "type": "Bug Fix", + "description": "Code Transformation: show error messages in chat" + }, + { + "type": "Bug Fix", + "description": "Code Transformation: Proposed changes not updated when multiple transformation jobs run in sequence." + }, + { + "type": "Bug Fix", + "description": "Feature Development: Update error message for monthly conversation limit reach" + }, + { + "type": "Bug Fix", + "description": "Code Transformation: Omit Maven metadata files when uploading dependencies to fix certain build failures in backend." + }, + { + "type": "Feature", + "description": "Code Transformation: Refreshed UI during CodeTransformation" + }, + { + "type": "Feature", + "description": "Chat: cmd + i to open chat" + }, + { + "type": "Feature", + "description": "Right Click + no code selected shows Q context menu" + }, + { + "type": "Feature", + "description": "Security Scan: Scans can now run on all files in the project" + }, + { + "type": "Feature", + "description": "Chat: Updates quick action commands style and groupings" + }, + { + "type": "Feature", + "description": "Code Transformation: add details about expected changes in transformation plan" + }, + { + "type": "Feature", + "description": "Enable Amazon Q feature development and Amazon Q transform capabilities (/dev and /transform) for AWS Builder ID users." + }, + { + "type": "Feature", + "description": "Initial release" + }, + { + "type": "Feature", + "description": "Chat: Added metric parameters to recordAddMessage telemetry event." + }, + { + "type": "Feature", + "description": "Security Scan: Scans can now run automatically when file changes are made" + }, + { + "type": "Feature", + "description": "Chat: brief CodeLens to advertise chat" + }, + { + "type": "Feature", + "description": "Security Scan: Send security issue to chat for explanation and fix" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.1.0.json b/packages/amazonq/.changes/1.1.0.json new file mode 100644 index 00000000000..4e3083f31a5 --- /dev/null +++ b/packages/amazonq/.changes/1.1.0.json @@ -0,0 +1,18 @@ +{ + "date": "2024-04-30", + "version": "1.1.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixed markdown is not getting parsed inside list items." + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Copy to clipboard on code blocks doesn't work" + }, + { + "type": "Bug Fix", + "description": "Fixed a crash when trying to use Q /dev on large projects or projects containing files with unsupported encoding." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.10.0.json b/packages/amazonq/.changes/1.10.0.json new file mode 100644 index 00000000000..6d6521e6fa6 --- /dev/null +++ b/packages/amazonq/.changes/1.10.0.json @@ -0,0 +1,30 @@ +{ + "date": "2024-06-21", + "version": "1.10.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Security Scan: Fixes an issue where project-scans time out for larger projects." + }, + { + "type": "Bug Fix", + "description": "Amazon Q /dev command: Fix file rejections for files outside of src/" + }, + { + "type": "Bug Fix", + "description": "Feature Development: update /dev welcome message" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixed broken code blocks with typewriter text in list items." + }, + { + "type": "Feature", + "description": "UX: New style for the login window" + }, + { + "type": "Removal", + "description": "Auth: No longer share SSO sessions with AWS Toolkit." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.100.0.json b/packages/amazonq/.changes/1.100.0.json new file mode 100644 index 00000000000..e1deb61908b --- /dev/null +++ b/packages/amazonq/.changes/1.100.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-10-16", + "version": "1.100.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.101.0.json b/packages/amazonq/.changes/1.101.0.json new file mode 100644 index 00000000000..7a72dabfc9e --- /dev/null +++ b/packages/amazonq/.changes/1.101.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-10-22", + "version": "1.101.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.102.0.json b/packages/amazonq/.changes/1.102.0.json new file mode 100644 index 00000000000..df8ee166397 --- /dev/null +++ b/packages/amazonq/.changes/1.102.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-10-30", + "version": "1.102.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.11.0.json b/packages/amazonq/.changes/1.11.0.json new file mode 100644 index 00000000000..202625b2560 --- /dev/null +++ b/packages/amazonq/.changes/1.11.0.json @@ -0,0 +1,26 @@ +{ + "date": "2024-06-27", + "version": "1.11.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fix for inline buttons don't have borders" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fix for some edge cases when followups appear on top without styles" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fix for prompt input removes whole word if it starts with @ character but there is no context selected" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fix for prompt input doesn't show multi line content properly after it reaches 10-15 lines" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /dev command: Fix in progress experience for ongoing backend calls" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.12.0.json b/packages/amazonq/.changes/1.12.0.json new file mode 100644 index 00000000000..a2cf430a220 --- /dev/null +++ b/packages/amazonq/.changes/1.12.0.json @@ -0,0 +1,46 @@ +{ + "date": "2024-07-08", + "version": "1.12.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Security Scans: Fixed unnecessary yellow lines appearing in both auto scans and project scans." + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixed prompt input becomes invisible if an html special character is inserted" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixed button font sizes are too big" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixed buttons don't show borders inside a message" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Code Transform: Link UI messages to troubleshooting docs" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /dev command: improve user error messages" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixed button texts are cropped too short" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixed prompt input and selected command horizontal alignment" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixed prompt input becomes invisible when multine text inserted with paste" + }, + { + "type": "Feature", + "description": "Q feature dev: Only use relevant code and related files" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.13.0.json b/packages/amazonq/.changes/1.13.0.json new file mode 100644 index 00000000000..7bf940dd04a --- /dev/null +++ b/packages/amazonq/.changes/1.13.0.json @@ -0,0 +1,14 @@ +{ + "date": "2024-07-11", + "version": "1.13.0", + "entries": [ + { + "type": "Bug Fix", + "description": "AD/LDAP users may see \"uv_os_get_passwd ENOENT\" error on startup #5277" + }, + { + "type": "Feature", + "description": "Add support for [Amazon Q Chat Workspace Context](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/workspace-context.html). Customers can use `@workspace` to ask questions regarding local workspace." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.14.0.json b/packages/amazonq/.changes/1.14.0.json new file mode 100644 index 00000000000..95f9248b933 --- /dev/null +++ b/packages/amazonq/.changes/1.14.0.json @@ -0,0 +1,10 @@ +{ + "date": "2024-07-11", + "version": "1.14.0", + "entries": [ + { + "type": "Feature", + "description": "Amazon Q/dev proactively show code generation iterations" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.15.0.json b/packages/amazonq/.changes/1.15.0.json new file mode 100644 index 00000000000..cda200cb196 --- /dev/null +++ b/packages/amazonq/.changes/1.15.0.json @@ -0,0 +1,18 @@ +{ + "date": "2024-07-15", + "version": "1.15.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixes a bug when the prompt input exceeds the width of the chat box it's not always wrapped correctly." + }, + { + "type": "Bug Fix", + "description": "Amazon Q: Corrected a miswording in the Amazon Q: Share Content With AWS setting." + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixes a bug when user input contains 4 or more spaces at the beginning of the line for multiline inputs, that line appears like a code block instead of a paragraph" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.16.0.json b/packages/amazonq/.changes/1.16.0.json new file mode 100644 index 00000000000..1d24082abd6 --- /dev/null +++ b/packages/amazonq/.changes/1.16.0.json @@ -0,0 +1,18 @@ +{ + "date": "2024-07-18", + "version": "1.16.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon q /dev: include granular error handling for code generation failed state" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Code Transformation: always show build logs from last job run" + }, + { + "type": "Bug Fix", + "description": "Unexpected SSO expiration on Windows due to EPERM" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.17.0.json b/packages/amazonq/.changes/1.17.0.json new file mode 100644 index 00000000000..d65bee6fdcc --- /dev/null +++ b/packages/amazonq/.changes/1.17.0.json @@ -0,0 +1,18 @@ +{ + "date": "2024-07-25", + "version": "1.17.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Dev and Transform introduction text formatted incorrectly" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /dev: update error message for code gen timeout and include backfill for error name" + }, + { + "type": "Bug Fix", + "description": "Sign-in page may fail to render in rare circumstances." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.18.0.json b/packages/amazonq/.changes/1.18.0.json new file mode 100644 index 00000000000..8ca73a4cc3f --- /dev/null +++ b/packages/amazonq/.changes/1.18.0.json @@ -0,0 +1,14 @@ +{ + "date": "2024-07-29", + "version": "1.18.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Security Scan: Fixed an issue scans were not able to succeed on Java projects with .class files" + }, + { + "type": "Bug Fix", + "description": "FileNotFound error causing early SSO expiration" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.19.0.json b/packages/amazonq/.changes/1.19.0.json new file mode 100644 index 00000000000..90fe678b7fd --- /dev/null +++ b/packages/amazonq/.changes/1.19.0.json @@ -0,0 +1,26 @@ +{ + "date": "2024-08-01", + "version": "1.19.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixing issue with an incorrect input cursor position in the prompt text box" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixing issue with the max tabs notification not being dismissible." + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Showing/hiding the scrollbars is now controlled by the OS settings" + }, + { + "type": "Bug Fix", + "description": "Q chat may stop responding after processing Python/Java code" + }, + { + "type": "Feature", + "description": "Amazon q /dev: i18n support for messaging" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.2.0.json b/packages/amazonq/.changes/1.2.0.json new file mode 100644 index 00000000000..df142bc0933 --- /dev/null +++ b/packages/amazonq/.changes/1.2.0.json @@ -0,0 +1,30 @@ +{ + "date": "2024-05-07", + "version": "1.2.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Fix bug when Amazon Q chat sends code selection while user has no selection" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Code Transformation: make jobId visible in job history tab at start of job and allow summary.md + icons to be saved when accepting changes" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Typewriter animator parts showing up in code fields inside listitems" + }, + { + "type": "Bug Fix", + "description": "Security Scan: Addresses a bug where security issues sometimes appear multiple times" + }, + { + "type": "Feature", + "description": "Update cross file context config for Q inline suggestion" + }, + { + "type": "Feature", + "description": "Amazon Q: Security scans now support C, C++, and PHP files" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.20.0.json b/packages/amazonq/.changes/1.20.0.json new file mode 100644 index 00000000000..fbaee8ce3f8 --- /dev/null +++ b/packages/amazonq/.changes/1.20.0.json @@ -0,0 +1,22 @@ +{ + "date": "2024-08-08", + "version": "1.20.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q /dev: include a retry option for the same prompt after folder reselection" + }, + { + "type": "Bug Fix", + "description": "Ignore virtual environment when indexing workspace" + }, + { + "type": "Feature", + "description": "Amazon Q Code Transformation: show pro tier users estimated cost of /transform on projects over 100K lines" + }, + { + "type": "Feature", + "description": "Amazon Q Code Transformation: warn user if absolute file paths are found in the pom.xml" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.21.0.json b/packages/amazonq/.changes/1.21.0.json new file mode 100644 index 00000000000..d6c630a689e --- /dev/null +++ b/packages/amazonq/.changes/1.21.0.json @@ -0,0 +1,10 @@ +{ + "date": "2024-08-15", + "version": "1.21.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Q feature dev: update file extension list and minor UI fixes" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.22.0.json b/packages/amazonq/.changes/1.22.0.json new file mode 100644 index 00000000000..2053fb1f123 --- /dev/null +++ b/packages/amazonq/.changes/1.22.0.json @@ -0,0 +1,26 @@ +{ + "date": "2024-08-22", + "version": "1.22.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Avoid refreshing code suggestion for paginated response" + }, + { + "type": "Bug Fix", + "description": "Update login logo styling" + }, + { + "type": "Bug Fix", + "description": "Correct indentation when insert Q chat code at cursor position" + }, + { + "type": "Feature", + "description": "Add notification for extended session to IdC users" + }, + { + "type": "Feature", + "description": "Support more programming languages for workspace index" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.23.0.json b/packages/amazonq/.changes/1.23.0.json new file mode 100644 index 00000000000..1aff236995a --- /dev/null +++ b/packages/amazonq/.changes/1.23.0.json @@ -0,0 +1,38 @@ +{ + "date": "2024-08-29", + "version": "1.23.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Fix bug when undo inline suggestion causes command not found" + }, + { + "type": "Bug Fix", + "description": "Auth: `SyntaxError` causing unexpected SSO logout" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Code Transformation: allow symlinks for JDK path" + }, + { + "type": "Bug Fix", + "description": "Fix bug where text with inline code copied from Amazon Q Chat had new line breaks around the inline code text" + }, + { + "type": "Bug Fix", + "description": "Fix bug with code indentation and nested list formatting in chat response prompt" + }, + { + "type": "Bug Fix", + "description": "Fix bug when disabled commands does not get filtered in quick actions" + }, + { + "type": "Bug Fix", + "description": "Auth: Users may be silently logged out due to network issues when starting the extension." + }, + { + "type": "Feature", + "description": "Support AB testing" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.24.0.json b/packages/amazonq/.changes/1.24.0.json new file mode 100644 index 00000000000..a5a87d9b747 --- /dev/null +++ b/packages/amazonq/.changes/1.24.0.json @@ -0,0 +1,34 @@ +{ + "date": "2024-09-05", + "version": "1.24.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Network errors causing premature SSO logout" + }, + { + "type": "Bug Fix", + "description": "Fix SyntaxError causing premature expiration (edge case)" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Code Transformation: show instructions for finding JDK path on Linux" + }, + { + "type": "Bug Fix", + "description": "UI: 'Start using Amazon Q' may display even if the user is signed in." + }, + { + "type": "Bug Fix", + "description": "Add getFeature and isEnabled utility methods to FeatureConfigProvider" + }, + { + "type": "Feature", + "description": "Amazon Q /dev: include in progress state agent in code generation" + }, + { + "type": "Feature", + "description": "Reduce workspace CPU indexing time by 50%" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.25.0.json b/packages/amazonq/.changes/1.25.0.json new file mode 100644 index 00000000000..b3cdaff40c1 --- /dev/null +++ b/packages/amazonq/.changes/1.25.0.json @@ -0,0 +1,22 @@ +{ + "date": "2024-09-12", + "version": "1.25.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fixed inline code blocks are not vertically aligned with texts" + }, + { + "type": "Feature", + "description": "Record telemetry event when AWS Toolkits extension is uninstalled." + }, + { + "type": "Feature", + "description": "Improve workspace indexing by only index files that are changed since last indexing" + }, + { + "type": "Removal", + "description": "Amazon Q Feature dev: Remove approach generation flow" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.26.0.json b/packages/amazonq/.changes/1.26.0.json new file mode 100644 index 00000000000..5f7e5361811 --- /dev/null +++ b/packages/amazonq/.changes/1.26.0.json @@ -0,0 +1,34 @@ +{ + "date": "2024-09-19", + "version": "1.26.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Security Scan: Fixed an issue where the wrong icon was used in the status bar menu." + }, + { + "type": "Bug Fix", + "description": "Disable Amazon Q LSP in AL2" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fix shifted chat contents when closing and opening chat panel back" + }, + { + "type": "Bug Fix", + "description": "Security Scan: Minor styling improvements in the security issue webview panel" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Fix tooltip remaining on screen when closing and opening chat panel back" + }, + { + "type": "Bug Fix", + "description": "Auth: Login state not updating across multiple VS Code windows." + }, + { + "type": "Feature", + "description": "Support @workspace queries for specific files like `@workspace what does test.ts do? `. " + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.27.0.json b/packages/amazonq/.changes/1.27.0.json new file mode 100644 index 00000000000..005008c342f --- /dev/null +++ b/packages/amazonq/.changes/1.27.0.json @@ -0,0 +1,10 @@ +{ + "date": "2024-09-27", + "version": "1.27.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Security Scan: Fixes an issue that incorrectly removes hardcoded credentials detections from auto scans." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.28.0.json b/packages/amazonq/.changes/1.28.0.json new file mode 100644 index 00000000000..481a4d8e7d2 --- /dev/null +++ b/packages/amazonq/.changes/1.28.0.json @@ -0,0 +1,18 @@ +{ + "date": "2024-10-03", + "version": "1.28.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q /dev: define first folder as a root path for LLM-created files when using workspaces" + }, + { + "type": "Feature", + "description": "Amazon Q Code Transformation: allow users to skip running tests" + }, + { + "type": "Feature", + "description": "Amazon Q Developer: Updated legal disclaimer text" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.29.0.json b/packages/amazonq/.changes/1.29.0.json new file mode 100644 index 00000000000..35cc08b1b25 --- /dev/null +++ b/packages/amazonq/.changes/1.29.0.json @@ -0,0 +1,54 @@ +{ + "date": "2024-10-10", + "version": "1.29.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q /dev: include telemetry for workspace usage when generating new files" + }, + { + "type": "Bug Fix", + "description": "Amazon Q extension may fail to start if AWS Toolkit extension fails to start" + }, + { + "type": "Bug Fix", + "description": "Start language server by default" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Feature Dev: Add error messages when the upload URL expires" + }, + { + "type": "Bug Fix", + "description": "Amazon Q (/dev): view diffs of previous /dev iterations" + }, + { + "type": "Bug Fix", + "description": "Q dev handle no change required" + }, + { + "type": "Deprecation", + "description": "The next release of this extension will require VS Code 1.83.0 or newer." + }, + { + "type": "Feature", + "description": "Automatically pause and resume @workspace indexing when OS CPU load is high" + }, + { + "type": "Feature", + "description": "Add buttons to code blocks to view and accept diffs." + }, + { + "type": "Feature", + "description": "Inline completion for more json files, and all yaml files" + }, + { + "type": "Feature", + "description": "Show a one-time warning if new VS Code is required" + }, + { + "type": "Removal", + "description": "Minimum required VSCode version is now 1.83" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.3.0.json b/packages/amazonq/.changes/1.3.0.json new file mode 100644 index 00000000000..9645f416074 --- /dev/null +++ b/packages/amazonq/.changes/1.3.0.json @@ -0,0 +1,30 @@ +{ + "date": "2024-05-08", + "version": "1.3.0", + "entries": [ + { + "type": "Bug Fix", + "description": "modifying the root folder for /dev now modifies it" + }, + { + "type": "Bug Fix", + "description": "Q chat may stop responding after processing Javascript/Typescript code" + }, + { + "type": "Bug Fix", + "description": "Completion may fail unexpected if user opened many tabs" + }, + { + "type": "Feature", + "description": "Inline Suggestions: Only display the 'Open Chat' CodeLens if the user is signed into Amazon Q." + }, + { + "type": "Feature", + "description": "Security Scan: Scans can now be run without an open editor" + }, + { + "type": "Feature", + "description": "Security Scan: Multi-root workspace support" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.30.0.json b/packages/amazonq/.changes/1.30.0.json new file mode 100644 index 00000000000..070f0f7be5f --- /dev/null +++ b/packages/amazonq/.changes/1.30.0.json @@ -0,0 +1,10 @@ +{ + "date": "2024-10-17", + "version": "1.30.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Various fixes and changes" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.31.0.json b/packages/amazonq/.changes/1.31.0.json new file mode 100644 index 00000000000..882fbad0f49 --- /dev/null +++ b/packages/amazonq/.changes/1.31.0.json @@ -0,0 +1,34 @@ +{ + "date": "2024-10-29", + "version": "1.31.0", + "entries": [ + { + "type": "Breaking Change", + "description": "Change keybind for focusing chat to ctrl+win+i on Windows, ctrl+cmd+i on macOS and ctrl+meta+i on Linux" + }, + { + "type": "Bug Fix", + "description": "Inline Suggestions: Occasional `ValidationException` if user context has too many characters." + }, + { + "type": "Bug Fix", + "description": "Update `@workspace` index when adding or deleting a file" + }, + { + "type": "Bug Fix", + "description": "fixed device code detection when running auth through tunneled vscode" + }, + { + "type": "Feature", + "description": "Use inline chat to select code and transform it with natural language instructions" + }, + { + "type": "Feature", + "description": "Amazon Q /dev: Add stop generation action" + }, + { + "type": "Feature", + "description": "Provide more frequent updates about code changes made by agent" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.32.0.json b/packages/amazonq/.changes/1.32.0.json new file mode 100644 index 00000000000..cfa6f4796bf --- /dev/null +++ b/packages/amazonq/.changes/1.32.0.json @@ -0,0 +1,26 @@ +{ + "date": "2024-10-29", + "version": "1.32.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Remove warning when no input is provided to inline chat input box" + }, + { + "type": "Bug Fix", + "description": "Use Sagemaker environment IAM Credentials for Code Completion when they're available" + }, + { + "type": "Bug Fix", + "description": "Inline: Code completion not working for Sagemaker Pro Tier users." + }, + { + "type": "Bug Fix", + "description": "Disable /transform and /dev commands for sagemaker users as they're not supported" + }, + { + "type": "Feature", + "description": "Amazon SageMaker Studio: Enable Free Tier Chat for IAM users" + } + ] +} diff --git a/packages/amazonq/.changes/1.33.0.json b/packages/amazonq/.changes/1.33.0.json new file mode 100644 index 00000000000..ac740352518 --- /dev/null +++ b/packages/amazonq/.changes/1.33.0.json @@ -0,0 +1,10 @@ +{ + "date": "2024-10-30", + "version": "1.33.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q /dev: fix for stop button showing for Code Transformation" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.34.0.json b/packages/amazonq/.changes/1.34.0.json new file mode 100644 index 00000000000..35d1a3b1e1d --- /dev/null +++ b/packages/amazonq/.changes/1.34.0.json @@ -0,0 +1,22 @@ +{ + "date": "2024-11-07", + "version": "1.34.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Align example help text with prompt message in chat" + }, + { + "type": "Bug Fix", + "description": "Improve `@workspace` index auto pause start strategy. " + }, + { + "type": "Feature", + "description": "Allow users to View and Apply diff when they explictily send code to Amazon Q using - Fix, Refactor, Optimize and Send To Prompt." + }, + { + "type": "Feature", + "description": "Security Scan: Auto-scan now supports JSX, TSX, Kotlin, Scala, and Shell files." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.35.0.json b/packages/amazonq/.changes/1.35.0.json new file mode 100644 index 00000000000..45b791ac4c4 --- /dev/null +++ b/packages/amazonq/.changes/1.35.0.json @@ -0,0 +1,18 @@ +{ + "date": "2024-11-11", + "version": "1.35.0", + "entries": [ + { + "type": "Breaking Change", + "description": "Change focus chat keybind to win+alt+i on Windows, cmd+alt+i on macOS, and meta+alt+i on Linux" + }, + { + "type": "Bug Fix", + "description": "Fix suboptimal inline suggestions from Amazon Q caused by improperly formatted supplemental context" + }, + { + "type": "Bug Fix", + "description": "Fix empty chunks being sent to service and get validationException" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.36.0.json b/packages/amazonq/.changes/1.36.0.json new file mode 100644 index 00000000000..0b10e6d8720 --- /dev/null +++ b/packages/amazonq/.changes/1.36.0.json @@ -0,0 +1,22 @@ +{ + "date": "2024-11-14", + "version": "1.36.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Fix broken inline suggestion auto-trigger on Systemverfilog files if users dont have systemverilog extension installed and enabled" + }, + { + "type": "Bug Fix", + "description": "tutorial always showing on start" + }, + { + "type": "Feature", + "description": "Enable default `@workspace` context of Amazon Q chat for certain users" + }, + { + "type": "Feature", + "description": "Amazon Q /dev: Add an action to accept individual files" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.37.0.json b/packages/amazonq/.changes/1.37.0.json new file mode 100644 index 00000000000..3076836aa3c --- /dev/null +++ b/packages/amazonq/.changes/1.37.0.json @@ -0,0 +1,50 @@ +{ + "date": "2024-11-22", + "version": "1.37.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Feature Dev: display limit reached error message" + }, + { + "type": "Bug Fix", + "description": "Amazon Q chat: `@workspace` command shown in all tab types" + }, + { + "type": "Bug Fix", + "description": "Chat container exceeds width of container" + }, + { + "type": "Bug Fix", + "description": "amazon q inline: skip indexing when no workspace folders are found" + }, + { + "type": "Bug Fix", + "description": "file details and name unneccessary cropping" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /dev: update diff window behavior after a change is accepted" + }, + { + "type": "Feature", + "description": "Amazon Q /dev: support `.gradle` files" + }, + { + "type": "Feature", + "description": "Code Transform: Enable support for Java 17 projects." + }, + { + "type": "Feature", + "description": "Notifications: Support for delivering critical alerts and product updates" + }, + { + "type": "Feature", + "description": "Retrieve and display a customization name when a customization is overridden in an AB test" + }, + { + "type": "Feature", + "description": "Feature(Amazon Q Code Transformation): support conversions of embedded SQL from Oracle to PostgreSQL" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.38.0.json b/packages/amazonq/.changes/1.38.0.json new file mode 100644 index 00000000000..cc04393dfa7 --- /dev/null +++ b/packages/amazonq/.changes/1.38.0.json @@ -0,0 +1,18 @@ +{ + "date": "2024-11-27", + "version": "1.38.0", + "entries": [ + { + "type": "Feature", + "description": "Amazon Q /dev: support `Dockerfile` files" + }, + { + "type": "Feature", + "description": "Introduce @workspace command to enhance context fetching for Chat" + }, + { + "type": "Feature", + "description": "Feature(Amazon Q Code Transformation): allow users to view results in 5 smaller diffs" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.39.0.json b/packages/amazonq/.changes/1.39.0.json new file mode 100644 index 00000000000..750b30ba03a --- /dev/null +++ b/packages/amazonq/.changes/1.39.0.json @@ -0,0 +1,34 @@ +{ + "date": "2024-12-03", + "version": "1.39.0", + "entries": [ + { + "type": "Feature", + "description": "Added a getting started page for exploring amazon q agents" + }, + { + "type": "Feature", + "description": "`/test` in Q chat to generate unit tests for java and python" + }, + { + "type": "Feature", + "description": "`/doc` in Q chat to generate and update documentation for your project" + }, + { + "type": "Feature", + "description": "Amazon Q Code Scan is now Amazon Q Code Review" + }, + { + "type": "Feature", + "description": "`/review` in Q chat to scan your code for vulnerabilities and quality issues, and generate fixes" + }, + { + "type": "Feature", + "description": "Security Scan: New TreeView to display security scan issues and vulnerabilities detected in your project. The TreeView provides an organized and hierarchical view of the scan results, making it easier to navigate and prioritize the issues that need to be addressed." + }, + { + "type": "Feature", + "description": "Security Scan: Added ability to suppress or ignore security issues" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.4.0.json b/packages/amazonq/.changes/1.4.0.json new file mode 100644 index 00000000000..aab7e1f2b70 --- /dev/null +++ b/packages/amazonq/.changes/1.4.0.json @@ -0,0 +1,30 @@ +{ + "date": "2024-05-13", + "version": "1.4.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Auth: No longer request AWS account scopes during login." + }, + { + "type": "Bug Fix", + "description": "Security Scan: Fixes an issue where scans fail for projects with Terraform files" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Code Transform: Show additional status messages to align with experience when JAVA_HOME set incorrectly." + }, + { + "type": "Feature", + "description": "UX: Added keyboard navigation to login screen." + }, + { + "type": "Feature", + "description": "New SSO Authorization Code flow for faster logins" + }, + { + "type": "Feature", + "description": "Transform: Add human intervention to help update dependencies during transformation." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.40.0.json b/packages/amazonq/.changes/1.40.0.json new file mode 100644 index 00000000000..ea251bdd8fe --- /dev/null +++ b/packages/amazonq/.changes/1.40.0.json @@ -0,0 +1,66 @@ +{ + "date": "2024-12-10", + "version": "1.40.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Improved LLM code review for file review." + }, + { + "type": "Bug Fix", + "description": "@workspace is missing from the welcome to q chat tab" + }, + { + "type": "Bug Fix", + "description": "Fix chat syntax highlighting when using several different themes" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /doc: progress bar persists after cancelling README creation" + }, + { + "type": "Bug Fix", + "description": "Code Review: Fixed a bug where some issues are missing from the code issues view for workspaces with custom names" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /doc: Prompt user to choose a folder in chat" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /dev not adding Dockerfiles in nested folders" + }, + { + "type": "Bug Fix", + "description": "Improved Code Fix generation for code review issues" + }, + { + "type": "Bug Fix", + "description": "Fix the quick start buttons on the explore page to show amazon q colours on hover" + }, + { + "type": "Feature", + "description": "Q feature dev: recognize .bms, .pli code files" + }, + { + "type": "Feature", + "description": "Amazon Q Code Transformation: show job ID in Transformation Hub" + }, + { + "type": "Feature", + "description": "UI improvements to Amazon Q Chat: New splash loader animation, initial streaming card animation, improved button colours" + }, + { + "type": "Feature", + "description": "Add acknowledgement button for amazon q chat disclaimer" + }, + { + "type": "Feature", + "description": "Navigate through prompt history by using the up/down arrows" + }, + { + "type": "Feature", + "description": "Amazon Q: Simplify log channel" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.41.0.json b/packages/amazonq/.changes/1.41.0.json new file mode 100644 index 00000000000..d0ca09cb476 --- /dev/null +++ b/packages/amazonq/.changes/1.41.0.json @@ -0,0 +1,62 @@ +{ + "date": "2024-12-17", + "version": "1.41.0", + "entries": [ + { + "type": "Bug Fix", + "description": "/review: Apply fix removes other issues in the same file." + }, + { + "type": "Bug Fix", + "description": "Fix(Amazon Q Code Transformation): show correct diff when running consecutive transformations" + }, + { + "type": "Bug Fix", + "description": "Improve when the welcome page is shown in amazon q chat" + }, + { + "type": "Bug Fix", + "description": "Code Review: Cleaned up output logs when running /review" + }, + { + "type": "Bug Fix", + "description": "Code Review: Fixed a bug where applying a fix did not update the positions of other issues in the same file." + }, + { + "type": "Bug Fix", + "description": "Chat: When navigating to previous prompts, code attachments are sometimes displayed incorrectly" + }, + { + "type": "Bug Fix", + "description": "/review: Diagnostics in the problems panel are mapped to the wrong code" + }, + { + "type": "Bug Fix", + "description": "Fix opentabs context possibly timeout due to race condition of misuse of different timeout functionalities" + }, + { + "type": "Bug Fix", + "description": "Auth: SSO session was bad, but no reauth prompt given" + }, + { + "type": "Bug Fix", + "description": "Reduce frequency of system status poll" + }, + { + "type": "Bug Fix", + "description": "Chat: When writing a prompt without sending it, navigating via up/down arrows sometimes deletes the unsent prompt." + }, + { + "type": "Bug Fix", + "description": "Code Review: Fixed a bug where projects with repeated path names did not scan properly." + }, + { + "type": "Feature", + "description": "/review: Code fix automatically scrolls into view after generation." + }, + { + "type": "Feature", + "description": "Chat: improve font size and line-height in footer (below prompt input field)" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.42.0.json b/packages/amazonq/.changes/1.42.0.json new file mode 100644 index 00000000000..7327ed0c9c4 --- /dev/null +++ b/packages/amazonq/.changes/1.42.0.json @@ -0,0 +1,58 @@ +{ + "date": "2025-01-09", + "version": "1.42.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q /doc: Improve button text phrasing" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /dev: Fix issue when files are deleted while preparing context" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Code Transformation: allow POSTGRESQL as target DB for SQL conversions" + }, + { + "type": "Bug Fix", + "description": "Fix context menu displaying when typing @, even though input is disallowed" + }, + { + "type": "Bug Fix", + "description": "Amazon Q can update mvn and gradle build files" + }, + { + "type": "Bug Fix", + "description": "/transform: use correct documentation link in SQL conversion help message" + }, + { + "type": "Bug Fix", + "description": "Up/down history navigation only triggering on first/last line of prompt input" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /test: Fix to redirect /test to generate tests in chat for external files out of workspace scope." + }, + { + "type": "Bug Fix", + "description": "/review: Code block extends beyond page margins in code issue detail view" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Code Transformation: retry project upload up to 3 times" + }, + { + "type": "Feature", + "description": "Amazon Q Code Transformation: add view summary button in chat" + }, + { + "type": "Feature", + "description": "Amazon Q: new code syntax highlighter for improved accuracy" + }, + { + "type": "Removal", + "description": "Settings: No longer migrate old CodeWhisperer settings or initialize telemetry setting from AWS Toolkit." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.43.0.json b/packages/amazonq/.changes/1.43.0.json new file mode 100644 index 00000000000..a4f2376f2e6 --- /dev/null +++ b/packages/amazonq/.changes/1.43.0.json @@ -0,0 +1,42 @@ +{ + "date": "2025-01-15", + "version": "1.43.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Auth: Valid StartURL not accepted at login" + }, + { + "type": "Bug Fix", + "description": "Fix inline completion supplementalContext length exceeding maximum in certain cases" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /test: Unit test generation completed message shows after accept/reject action" + }, + { + "type": "Bug Fix", + "description": "/test: for unsupported languages was sometimes unreliable" + }, + { + "type": "Bug Fix", + "description": "User-selected customizations are sometimes not being persisted." + }, + { + "type": "Bug Fix", + "description": "Amazon q /dev: Remove hard-coded limits and instead rely server-side data to communicate number of code generations remaining" + }, + { + "type": "Feature", + "description": "Adds capability to send new context commands to AB groups" + }, + { + "type": "Feature", + "description": "feat(amazonq): Add error message for updated README too large" + }, + { + "type": "Feature", + "description": "Enhance Q inline completion context fetching for better suggestion quality" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.44.0.json b/packages/amazonq/.changes/1.44.0.json new file mode 100644 index 00000000000..7593e9a7af3 --- /dev/null +++ b/packages/amazonq/.changes/1.44.0.json @@ -0,0 +1,50 @@ +{ + "date": "2025-01-23", + "version": "1.44.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q: word duplication when pressing tab on context selector fixed" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /doc: Prevent users from requesting changes if no iterations remain" + }, + { + "type": "Bug Fix", + "description": "`/test`: view diffs by clicking files in the file tree, aligning the behavior with the 'View Diff' button." + }, + { + "type": "Bug Fix", + "description": "/review: Improved error handling for code fix operations" + }, + { + "type": "Bug Fix", + "description": "Amazon Q: cursor no longer jumps after navigating prompt history" + }, + { + "type": "Bug Fix", + "description": "Improve the text description of workspace index settings" + }, + { + "type": "Bug Fix", + "description": "Notifications: 'Dismiss' command visible in command palette." + }, + { + "type": "Bug Fix", + "description": "/transform: replace icons in Transformation Hub with text" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /doc: Ask for user prompt if error occurs while updating documentation" + }, + { + "type": "Feature", + "description": "Amazon Q: increase chat current active file context char limit to 40k" + }, + { + "type": "Feature", + "description": "/review: Code issues can be grouped by file location or severity" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.45.0-SNAPSHOT.json b/packages/amazonq/.changes/1.45.0-SNAPSHOT.json new file mode 100644 index 00000000000..881d3e94e5a --- /dev/null +++ b/packages/amazonq/.changes/1.45.0-SNAPSHOT.json @@ -0,0 +1,18 @@ +{ + "date": "2025-01-30", + "version": "1.45.0-SNAPSHOT", + "entries": [ + { + "type": "Bug Fix", + "description": "Allow AB users with an overridden customization to go back to the default customization" + }, + { + "type": "Bug Fix", + "description": "For security reasons, disabled auto linkify for link texts coming in markdown other than [TEXT](URL) format" + }, + { + "type": "Feature", + "description": "Add setting to allow Q /dev to run code and test commands" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.45.0.json b/packages/amazonq/.changes/1.45.0.json new file mode 100644 index 00000000000..7ef04691624 --- /dev/null +++ b/packages/amazonq/.changes/1.45.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-01-30", + "version": "1.45.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Allow AB users with an overridden customization to go back to the default customization" + }, + { + "type": "Bug Fix", + "description": "For security reasons, disabled auto linkify for link texts coming in markdown other than [TEXT](URL) format" + }, + { + "type": "Feature", + "description": "Add setting to allow Q /dev to run code and test commands" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.46.0.json b/packages/amazonq/.changes/1.46.0.json new file mode 100644 index 00000000000..1253cbe3d01 --- /dev/null +++ b/packages/amazonq/.changes/1.46.0.json @@ -0,0 +1,38 @@ +{ + "date": "2025-02-05", + "version": "1.46.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Citation links are not clickable as numbers, but appear as non-clickable texts" + }, + { + "type": "Bug Fix", + "description": "Fix language server start failure in AL2023 ARM64" + }, + { + "type": "Bug Fix", + "description": "/review: Auto-review issues did not populate code issues list" + }, + { + "type": "Bug Fix", + "description": "Amazon Q: Fix code upload error when using /dev or /doc on Remote SSH" + }, + { + "type": "Bug Fix", + "description": "/test placeholder text aligned across IDEs" + }, + { + "type": "Bug Fix", + "description": "Inline: Typos in the first example suggestion" + }, + { + "type": "Feature", + "description": "Inline suggestions: Pre-fetch recommendations to reduce suggestion latency." + }, + { + "type": "Feature", + "description": "Added github issue link and description to the chat answer feedback form" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.47.0.json b/packages/amazonq/.changes/1.47.0.json new file mode 100644 index 00000000000..5e168b79fe2 --- /dev/null +++ b/packages/amazonq/.changes/1.47.0.json @@ -0,0 +1,38 @@ +{ + "date": "2025-02-13", + "version": "1.47.0", + "entries": [ + { + "type": "Bug Fix", + "description": "`Send to prompt` and other context menu options not sent if chat was closed" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /test: Truncating user input to 4096 characters for unit test generation." + }, + { + "type": "Bug Fix", + "description": "Amazon Q /test: Q identify active test file and infer source file for test generation." + }, + { + "type": "Bug Fix", + "description": "/review: Code review starts automatically when invoked from menu" + }, + { + "type": "Feature", + "description": "Amazon Q /dev: support `.hbs`, `.gjs`, `.gts`, `.astro`, `.mdx`, `.svelte`, `.erb`, `.rake` files" + }, + { + "type": "Feature", + "description": "/transform: automatically download results when ready" + }, + { + "type": "Feature", + "description": "/transform: support Java 21 transformations" + }, + { + "type": "Removal", + "description": "Reverted prefetch logic to enable more stable inline completion" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.48.0.json b/packages/amazonq/.changes/1.48.0.json new file mode 100644 index 00000000000..409649ab817 --- /dev/null +++ b/packages/amazonq/.changes/1.48.0.json @@ -0,0 +1,30 @@ +{ + "date": "2025-02-20", + "version": "1.48.0", + "entries": [ + { + "type": "Bug Fix", + "description": "/dev and /doc: Multi-root workspace with duplicate files causes infinite 'Uploading code...' loop" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /doc: update workspace too large error message " + }, + { + "type": "Bug Fix", + "description": "/review: Auto-review should not remove issues from manual reviews" + }, + { + "type": "Bug Fix", + "description": "/transform: allow View Summary button to work even after accepting diff" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /test: Fixing the issue of target file does not exist." + }, + { + "type": "Feature", + "description": "Amazon Q /doc: Add support for infrastructure diagrams" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.49.0.json b/packages/amazonq/.changes/1.49.0.json new file mode 100644 index 00000000000..b08aff46ac7 --- /dev/null +++ b/packages/amazonq/.changes/1.49.0.json @@ -0,0 +1,26 @@ +{ + "date": "2025-02-27", + "version": "1.49.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q /test: Unit test generation displays an inaccurate diff view for non-primary packages in the workspace." + }, + { + "type": "Bug Fix", + "description": "Amazon Q /doc: Fix uploading file method throwing incorrect workspace too large error message" + }, + { + "type": "Bug Fix", + "description": "/transform: skip running tests locally when user chooses to do so" + }, + { + "type": "Bug Fix", + "description": "/review: ignored lines should not show up in scan issues" + }, + { + "type": "Bug Fix", + "description": "/test: update capability card text" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.5.0.json b/packages/amazonq/.changes/1.5.0.json new file mode 100644 index 00000000000..1e59c67e102 --- /dev/null +++ b/packages/amazonq/.changes/1.5.0.json @@ -0,0 +1,26 @@ +{ + "date": "2024-05-17", + "version": "1.5.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Security Scan: Fixes an issue when scanning projects with binary files" + }, + { + "type": "Bug Fix", + "description": "Fixes an issue where the /dev chat wouldn't let customers modify the source folder when exceeding the size limit" + }, + { + "type": "Bug Fix", + "description": "Security Scan: Improved error notifications" + }, + { + "type": "Feature", + "description": "Security Scan: Added custom command to run the security scan." + }, + { + "type": "Feature", + "description": "Security Scan: \"View details\" and \"Explain\" options can now be accessed from the problems panel" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.50.0.json b/packages/amazonq/.changes/1.50.0.json new file mode 100644 index 00000000000..bd832127681 --- /dev/null +++ b/packages/amazonq/.changes/1.50.0.json @@ -0,0 +1,34 @@ +{ + "date": "2025-03-06", + "version": "1.50.0", + "entries": [ + { + "type": "Bug Fix", + "description": "/doc: Usage in multiple chat tabs may cause unexpected behavior." + }, + { + "type": "Bug Fix", + "description": "/review: subsequent reviews weren't possible" + }, + { + "type": "Feature", + "description": "Amazon Q chat: Use `@` to add folders, files, and saved prompts as context" + }, + { + "type": "Feature", + "description": "Amazon Q chat: increase chat input height to 3 lines" + }, + { + "type": "Feature", + "description": "Amazon Q chat: Show list of files sent as context in chat response" + }, + { + "type": "Feature", + "description": "Amazon Q chat: Add support for `.md` file rules in workspace-level `.amazonq/rules` directory" + }, + { + "type": "Test", + "description": "add Q Chat /review command test coverage" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.51.0.json b/packages/amazonq/.changes/1.51.0.json new file mode 100644 index 00000000000..97870fda325 --- /dev/null +++ b/packages/amazonq/.changes/1.51.0.json @@ -0,0 +1,34 @@ +{ + "date": "2025-03-12", + "version": "1.51.0", + "entries": [ + { + "type": "Bug Fix", + "description": "increase scan timeout to reduce front-end timeout errors" + }, + { + "type": "Bug Fix", + "description": "Amazon Q chat: Create a new prompt form does not autofocus or submit with Enter press" + }, + { + "type": "Bug Fix", + "description": "/review: Zip files are created with the wrong file path for file scans in multifolder workspaces." + }, + { + "type": "Bug Fix", + "description": "/review: Invalid file path characters caused some detections to be skipped on Windows" + }, + { + "type": "Feature", + "description": "Amazon Q Chat: You can now keep a \"library\" of prompt files in your home directory under `~/.aws/amazonq/prompts` and then quickly add them to the context using `@` on any project you’re working on. Prompt files are in markdown (`.md`) format." + }, + { + "type": "Feature", + "description": "/review: show code diff for fix preview" + }, + { + "type": "Feature", + "description": "/test: display test plan summary in chat after generating tests" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.52.0.json b/packages/amazonq/.changes/1.52.0.json new file mode 100644 index 00000000000..a4f357edc87 --- /dev/null +++ b/packages/amazonq/.changes/1.52.0.json @@ -0,0 +1,50 @@ +{ + "date": "2025-03-20", + "version": "1.52.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q chat: @Folders and @Files are missing `@` prefix in chat history" + }, + { + "type": "Bug Fix", + "description": "/review: Code Issues ellipses menu displays AWS Toolkit options, if installed." + }, + { + "type": "Bug Fix", + "description": "Amazon Q chat: Progress indicator height is stretched" + }, + { + "type": "Bug Fix", + "description": "Amazon Q chat: Long descriptions in context list are cut off" + }, + { + "type": "Bug Fix", + "description": "Amazon Q chat: Improve responses for saved prompts and workspace rules" + }, + { + "type": "Bug Fix", + "description": "/test: show descriptive error message" + }, + { + "type": "Bug Fix", + "description": "Code Review: Fixed a bug where issues are double counted in the Q chat" + }, + { + "type": "Bug Fix", + "description": "Amazon Q chat: Animation timings are too long" + }, + { + "type": "Bug Fix", + "description": "Fix inline completion failure due to context length exceeding the threshold" + }, + { + "type": "Feature", + "description": "/review: passing referenceTrackerConfiguration to StartCodeFixJob" + }, + { + "type": "Feature", + "description": "/review: rename setting `showInlineCodeSuggestionsWithCodeReferences` to `showCodeWithReferences`" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.53.0.json b/packages/amazonq/.changes/1.53.0.json new file mode 100644 index 00000000000..cb548513e35 --- /dev/null +++ b/packages/amazonq/.changes/1.53.0.json @@ -0,0 +1,22 @@ +{ + "date": "2025-03-28", + "version": "1.53.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Choosing a nested subfolder for `/doc` on Windows results in `The folder you chose did not contain any source files` error" + }, + { + "type": "Feature", + "description": "Add support for Code search in Q chat" + }, + { + "type": "Feature", + "description": "(Experimental) Amazon Q inline code suggestions via Amazon Q Language Server. (enable with `aws.experiments.amazonqLSP: true`)" + }, + { + "type": "Feature", + "description": "Command Palette: Add `Amazon Q: Open Chat` command." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.54.0.json b/packages/amazonq/.changes/1.54.0.json new file mode 100644 index 00000000000..32e6ccabf4a --- /dev/null +++ b/packages/amazonq/.changes/1.54.0.json @@ -0,0 +1,38 @@ +{ + "date": "2025-04-03", + "version": "1.54.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q chat: `@prompts` not added to context" + }, + { + "type": "Feature", + "description": "Amazon Q chat: View and search chat history" + }, + { + "type": "Feature", + "description": "SageMaker Unified Studio: Disable Sign out" + }, + { + "type": "Feature", + "description": "SageMaker Unified Studio: Update Q Chat Introduction message" + }, + { + "type": "Feature", + "description": "/review: automatically generate fix without clicking Generate Fix button" + }, + { + "type": "Feature", + "description": "Amazon Q chat: Automatically persist chats between IDE sessions" + }, + { + "type": "Feature", + "description": "Save user command execution logs to plugin output." + }, + { + "type": "Feature", + "description": "Amazon Q chat: Code blocks in chat messages have a max-height of 21 lines and can be scrolled inside" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.55.0.json b/packages/amazonq/.changes/1.55.0.json new file mode 100644 index 00000000000..194814fc3be --- /dev/null +++ b/packages/amazonq/.changes/1.55.0.json @@ -0,0 +1,38 @@ +{ + "date": "2025-04-09", + "version": "1.55.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Update chat history icon" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: chat occasionally freezes and displays gray screen" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Set owner-only permissions for chat history and saved prompt files" + }, + { + "type": "Feature", + "description": "`/test` generates tests in all languages, not only Java/Python" + }, + { + "type": "Feature", + "description": "Amazon Q chat: Click export icon to save chat transcript in Markdown or HTML" + }, + { + "type": "Feature", + "description": "SageMaker: Disable the unsupported agentic commands and welcome prompt" + }, + { + "type": "Feature", + "description": "Amazon Q Chat: Add `@code` context for PHP, Ruby, Scala, Shell, and Swift projects" + }, + { + "type": "Feature", + "description": "Enterprise users can choose their preferred Amazon Q profile to improve personalization and workflow across different business regions" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.56.0.json b/packages/amazonq/.changes/1.56.0.json new file mode 100644 index 00000000000..8de6eea2db5 --- /dev/null +++ b/packages/amazonq/.changes/1.56.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-04-09", + "version": "1.56.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Improve status message while loading Amazon Q Profiles during login" + }, + { + "type": "Bug Fix", + "description": "\"failed to run command\" error" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.57.0.json b/packages/amazonq/.changes/1.57.0.json new file mode 100644 index 00000000000..3a7a8d2ab95 --- /dev/null +++ b/packages/amazonq/.changes/1.57.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-04-10", + "version": "1.57.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Fix bug where generate fix does not work" + }, + { + "type": "Bug Fix", + "description": "Fix bug where review shows 0 findings" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.58.0.json b/packages/amazonq/.changes/1.58.0.json new file mode 100644 index 00000000000..02395a6dfd8 --- /dev/null +++ b/packages/amazonq/.changes/1.58.0.json @@ -0,0 +1,22 @@ +{ + "date": "2025-04-11", + "version": "1.58.0", + "entries": [ + { + "type": "Bug Fix", + "description": "inline chat activates properly when using 'aws.experiments.amazonqChatLSP' feature flag" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: code blocks in responses flicker, switching tabs during answer streaming makes expand button disappear" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: tab bar buttons disappear when closing non-active tab" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: chat history list does not truncate markdown" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.59.0.json b/packages/amazonq/.changes/1.59.0.json new file mode 100644 index 00000000000..840d65f5294 --- /dev/null +++ b/packages/amazonq/.changes/1.59.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-04-11", + "version": "1.59.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Code fix line number or file is sometimes not accurate" + }, + { + "type": "Bug Fix", + "description": "Fix Q agents will fail for /transform /dev /test features if IdC kms key is configured with 400 error" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.6.0.json b/packages/amazonq/.changes/1.6.0.json new file mode 100644 index 00000000000..9974f06a414 --- /dev/null +++ b/packages/amazonq/.changes/1.6.0.json @@ -0,0 +1,18 @@ +{ + "date": "2024-05-21", + "version": "1.6.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Inside chat body, if there is a code block inside a list item it shows
tags" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Prompt input field allows additional input beyond the character limit" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Chat: Prompt input field not getting focus when chat window opens" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.60.0.json b/packages/amazonq/.changes/1.60.0.json new file mode 100644 index 00000000000..64a9b9ea137 --- /dev/null +++ b/packages/amazonq/.changes/1.60.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-04-18", + "version": "1.60.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Users might be bound to a customization which they dont have access with the selected profile and it causes service throwing 403 when using inline suggestion and chat features" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.61.0.json b/packages/amazonq/.changes/1.61.0.json new file mode 100644 index 00000000000..64b0f4da610 --- /dev/null +++ b/packages/amazonq/.changes/1.61.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-04-22", + "version": "1.61.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Some users not signaled they needed to select a Region Profile to get features working" + }, + { + "type": "bugfix", + "description": "/review: disable auto-review by default" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.62.0.json b/packages/amazonq/.changes/1.62.0.json new file mode 100644 index 00000000000..530f26ccb29 --- /dev/null +++ b/packages/amazonq/.changes/1.62.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-04-25", + "version": "1.62.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Toast message to warn users if Developer Profile is not selected" + }, + { + "type": "Bug Fix", + "description": "Fix users can not log in successfully with 2+ IDE instnaces open due to throttle error throw by the service" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.63.0.json b/packages/amazonq/.changes/1.63.0.json new file mode 100644 index 00000000000..10020659c5a --- /dev/null +++ b/packages/amazonq/.changes/1.63.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-05-01", + "version": "1.63.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Q profile selection hangs when a region is blocked" + }, + { + "type": "Feature", + "description": "Agentic coding experience: Amazon Q can now write code and run shell commands on your behalf" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.64.0.json b/packages/amazonq/.changes/1.64.0.json new file mode 100644 index 00000000000..461ad140c01 --- /dev/null +++ b/packages/amazonq/.changes/1.64.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-05-02", + "version": "1.64.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Enable Amazon Q LSP in AL2 instances" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.65.0.json b/packages/amazonq/.changes/1.65.0.json new file mode 100644 index 00000000000..ec01584f56b --- /dev/null +++ b/packages/amazonq/.changes/1.65.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-05-05", + "version": "1.65.0", + "entries": [ + { + "type": "Feature", + "description": "Support selecting customizations across all Q profiles with automatic profile switching for enterprise users" + }, + { + "type": "Feature", + "description": "Memorize and autofill users' last Sso login profile" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.66.0.json b/packages/amazonq/.changes/1.66.0.json new file mode 100644 index 00000000000..ab4a819b85a --- /dev/null +++ b/packages/amazonq/.changes/1.66.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-05-09", + "version": "1.66.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Avoid inline completion 'Improperly formed request' errors when file is too large" + }, + { + "type": "Bug Fix", + "description": "Named agent tabs sometimes open with unnecessary input options" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.67.0.json b/packages/amazonq/.changes/1.67.0.json new file mode 100644 index 00000000000..59ff03eacdd --- /dev/null +++ b/packages/amazonq/.changes/1.67.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-05-14", + "version": "1.67.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Previous and subsequent cells are used as context for completion in a Jupyter notebook" + }, + { + "type": "Bug Fix", + "description": "Support chat in AL2 aarch64" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.68.0.json b/packages/amazonq/.changes/1.68.0.json new file mode 100644 index 00000000000..2c21170aa0b --- /dev/null +++ b/packages/amazonq/.changes/1.68.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-05-15", + "version": "1.68.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Fix Error: 'Amazon Q service is not signed in'" + }, + { + "type": "Bug Fix", + "description": "Fix Error: 'Amazon Q Profile is not selected for IDC connection type'" + }, + { + "type": "Feature", + "description": "Add inline completion support for abap language" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.69.0.json b/packages/amazonq/.changes/1.69.0.json new file mode 100644 index 00000000000..caa8ed28676 --- /dev/null +++ b/packages/amazonq/.changes/1.69.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-05-22", + "version": "1.69.0", + "entries": [ + { + "type": "Bug Fix", + "description": "/transform: avoid prompting user for target JDK path unnecessarily" + }, + { + "type": "Removal", + "description": "/transform: remove option to select multiple diffs" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.7.0.json b/packages/amazonq/.changes/1.7.0.json new file mode 100644 index 00000000000..cfc03fc40e6 --- /dev/null +++ b/packages/amazonq/.changes/1.7.0.json @@ -0,0 +1,26 @@ +{ + "date": "2024-05-30", + "version": "1.7.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Feature Development: File rejection is not rejecting a file when code is generated" + }, + { + "type": "Bug Fix", + "description": "Security Scan: Improved accuracy when applying security fixes" + }, + { + "type": "Bug Fix", + "description": "Amazon Q Code Transformation: show more specific error messages on failure cases" + }, + { + "type": "Feature", + "description": "Security Scan: Support for scanning files outside of workspaces." + }, + { + "type": "Feature", + "description": "Amazon Q now publishes to Open VSX: https://open-vsx.org/namespace/amazonwebservices" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.70.0.json b/packages/amazonq/.changes/1.70.0.json new file mode 100644 index 00000000000..841e8107430 --- /dev/null +++ b/packages/amazonq/.changes/1.70.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-05-28", + "version": "1.70.0", + "entries": [ + { + "type": "Removal", + "description": "Disable local workspace LSP" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.71.0.json b/packages/amazonq/.changes/1.71.0.json new file mode 100644 index 00000000000..be5cc5a2013 --- /dev/null +++ b/packages/amazonq/.changes/1.71.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-06-04", + "version": "1.71.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.72.0.json b/packages/amazonq/.changes/1.72.0.json new file mode 100644 index 00000000000..10b0b374c3a --- /dev/null +++ b/packages/amazonq/.changes/1.72.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-06-11", + "version": "1.72.0", + "entries": [ + { + "type": "Feature", + "description": "Launch LSP with bundled artifacts as fallback" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.73.0.json b/packages/amazonq/.changes/1.73.0.json new file mode 100644 index 00000000000..25cda6dcf03 --- /dev/null +++ b/packages/amazonq/.changes/1.73.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-06-11", + "version": "1.73.0", + "entries": [ + { + "type": "Feature", + "description": "Add MCP Server Support" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.74.0.json b/packages/amazonq/.changes/1.74.0.json new file mode 100644 index 00000000000..e584f7a9d01 --- /dev/null +++ b/packages/amazonq/.changes/1.74.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-06-12", + "version": "1.74.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.75.0.json b/packages/amazonq/.changes/1.75.0.json new file mode 100644 index 00000000000..384d07654a4 --- /dev/null +++ b/packages/amazonq/.changes/1.75.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-06-13", + "version": "1.75.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.76.0.json b/packages/amazonq/.changes/1.76.0.json new file mode 100644 index 00000000000..eaa2ce8af56 --- /dev/null +++ b/packages/amazonq/.changes/1.76.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-06-18", + "version": "1.76.0", + "entries": [ + { + "type": "Bug Fix", + "description": "/transform: only show lines of code statistic in plan" + }, + { + "type": "Feature", + "description": "Add model selection feature" + }, + { + "type": "Feature", + "description": "Pin context items in chat and manage workspace rules" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.77.0.json b/packages/amazonq/.changes/1.77.0.json new file mode 100644 index 00000000000..37436c259f9 --- /dev/null +++ b/packages/amazonq/.changes/1.77.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-06-18", + "version": "1.77.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.78.0.json b/packages/amazonq/.changes/1.78.0.json new file mode 100644 index 00000000000..9a6f35cf36f --- /dev/null +++ b/packages/amazonq/.changes/1.78.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-06-20", + "version": "1.78.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Resolve missing chat options in Amazon Q chat interface." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.79.0.json b/packages/amazonq/.changes/1.79.0.json new file mode 100644 index 00000000000..51d910cca2b --- /dev/null +++ b/packages/amazonq/.changes/1.79.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-06-25", + "version": "1.79.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Added automatic system certificate detection and VSCode proxy settings support" + }, + { + "type": "Bug Fix", + "description": "Improved Amazon Linux 2 support with better SageMaker environment detection" + }, + { + "type": "Feature", + "description": "/transform: run all builds client-side" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.8.0.json b/packages/amazonq/.changes/1.8.0.json new file mode 100644 index 00000000000..9f64639c310 --- /dev/null +++ b/packages/amazonq/.changes/1.8.0.json @@ -0,0 +1,26 @@ +{ + "date": "2024-06-07", + "version": "1.8.0", + "entries": [ + { + "type": "Bug Fix", + "description": "fix(featureDev): fix file rejection for multi-workspaces" + }, + { + "type": "Feature", + "description": "The `Send to Amazon Q` [context menu](https://github.com/aws/aws-toolkit-vscode/assets/371007/ce4c61a4-1b58-48ee-8500-56667d45dd7d) was renamed to `Amazon Q`" + }, + { + "type": "Feature", + "description": "Amazon Q Transform: Increase project upload size limit to 2GB" + }, + { + "type": "Feature", + "description": "feat(featureDev): generated plan being shown from top" + }, + { + "type": "Feature", + "description": "Add additional commands for Amazon Q." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.80.0.json b/packages/amazonq/.changes/1.80.0.json new file mode 100644 index 00000000000..20e948b69f2 --- /dev/null +++ b/packages/amazonq/.changes/1.80.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-07-01", + "version": "1.80.0", + "entries": [] +} \ No newline at end of file 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/1.82.0.json b/packages/amazonq/.changes/1.82.0.json new file mode 100644 index 00000000000..816da045f4a --- /dev/null +++ b/packages/amazonq/.changes/1.82.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-07-07", + "version": "1.82.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Prompt re-authenticate if auto trigger failed with expired token" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.83.0.json b/packages/amazonq/.changes/1.83.0.json new file mode 100644 index 00000000000..5997b2b1b95 --- /dev/null +++ b/packages/amazonq/.changes/1.83.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-07-09", + "version": "1.83.0", + "entries": [ + { + "type": "Feature", + "description": "Amazon Q /test, /doc, and /dev capabilities integrated into Agentic coding." + }, + { + "type": "Feature", + "description": "Added image support to Amazon Q chat, users can now upload images from their local file system" + }, + { + "type": "Removal", + "description": "Deprecate \"amazon q is generating...\" UI for inline suggestion" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.84.0.json b/packages/amazonq/.changes/1.84.0.json new file mode 100644 index 00000000000..e73a685e054 --- /dev/null +++ b/packages/amazonq/.changes/1.84.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-07-17", + "version": "1.84.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Slightly delay rendering inline completion when user is typing" + }, + { + "type": "Bug Fix", + "description": "Render first response before receiving all paginated inline completion results" + }, + { + "type": "Feature", + "description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.85.0.json b/packages/amazonq/.changes/1.85.0.json new file mode 100644 index 00000000000..b0aba38025b --- /dev/null +++ b/packages/amazonq/.changes/1.85.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-07-19", + "version": "1.85.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.86.0.json b/packages/amazonq/.changes/1.86.0.json new file mode 100644 index 00000000000..abe84ce5b5f --- /dev/null +++ b/packages/amazonq/.changes/1.86.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-07-30", + "version": "1.86.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Let Enter invoke auto completion more consistently" + }, + { + "type": "Bug Fix", + "description": "Faster and more responsive inline completion UX" + }, + { + "type": "Bug Fix", + "description": "Use documentChangeEvent as auto trigger condition" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.87.0.json b/packages/amazonq/.changes/1.87.0.json new file mode 100644 index 00000000000..d80e11a2bfa --- /dev/null +++ b/packages/amazonq/.changes/1.87.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-07-31", + "version": "1.87.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.88.0.json b/packages/amazonq/.changes/1.88.0.json new file mode 100644 index 00000000000..05e006954d8 --- /dev/null +++ b/packages/amazonq/.changes/1.88.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-08-06", + "version": "1.88.0", + "entries": [ + { + "type": "Feature", + "description": "Amazon Q Chat provides error explanations and fixes when hovering or right-clicking on error indicators and messages" + }, + { + "type": "Feature", + "description": "/transform: Show transformation history in Transformation Hub and allow users to resume jobs" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.89.0.json b/packages/amazonq/.changes/1.89.0.json new file mode 100644 index 00000000000..95ef52909d5 --- /dev/null +++ b/packages/amazonq/.changes/1.89.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-08-13", + "version": "1.89.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.9.0.json b/packages/amazonq/.changes/1.9.0.json new file mode 100644 index 00000000000..546b595dc56 --- /dev/null +++ b/packages/amazonq/.changes/1.9.0.json @@ -0,0 +1,26 @@ +{ + "date": "2024-06-14", + "version": "1.9.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q inline suggestions: remember `Pause Auto-Suggestions` after IDE restart" + }, + { + "type": "Bug Fix", + "description": "Amazon Q /dev command: stop showing spinner when there is an error." + }, + { + "type": "Bug Fix", + "description": "Security Scan: Fixes an issue where auto-scans cause the editor to become unresponsive for larger projects." + }, + { + "type": "Bug Fix", + "description": "Fix(Amazon Q Code Transformation): show more detailed error messages for proxy issues" + }, + { + "type": "Feature", + "description": "Amazon Q Code Transform: Allow user to view transformation build log" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.90.0.json b/packages/amazonq/.changes/1.90.0.json new file mode 100644 index 00000000000..547528bce40 --- /dev/null +++ b/packages/amazonq/.changes/1.90.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-08-15", + "version": "1.90.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.91.0.json b/packages/amazonq/.changes/1.91.0.json new file mode 100644 index 00000000000..b555f97447c --- /dev/null +++ b/packages/amazonq/.changes/1.91.0.json @@ -0,0 +1,14 @@ +{ + "date": "2025-08-22", + "version": "1.91.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Enable inline completion in Jupyter Notebook" + }, + { + "type": "Feature", + "description": "Amazon Q supports admin control for MCP servers to restrict MCP server usage" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.92.0.json b/packages/amazonq/.changes/1.92.0.json new file mode 100644 index 00000000000..46f2518fb37 --- /dev/null +++ b/packages/amazonq/.changes/1.92.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-08-28", + "version": "1.92.0", + "entries": [ + { + "type": "Feature", + "description": "Amazon Q supports admin control for MCP servers to restrict MCP server usage" + }, + { + "type": "Feature", + "description": "Enabling dynamic model fetching capabilities in Amazon Q chat" + }, + { + "type": "Feature", + "description": "Amazon Q: Support for configuring and utilizing remote MCP servers." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.93.0.json b/packages/amazonq/.changes/1.93.0.json new file mode 100644 index 00000000000..c8f34a95645 --- /dev/null +++ b/packages/amazonq/.changes/1.93.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-09-05", + "version": "1.93.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.94.0.json b/packages/amazonq/.changes/1.94.0.json new file mode 100644 index 00000000000..d0adc1ee037 --- /dev/null +++ b/packages/amazonq/.changes/1.94.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-09-11", + "version": "1.94.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.95.0.json b/packages/amazonq/.changes/1.95.0.json new file mode 100644 index 00000000000..8014b9e23b2 --- /dev/null +++ b/packages/amazonq/.changes/1.95.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-09-19", + "version": "1.95.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q automatically refreshes expired IAM Credentials in Sagemaker instances" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.96.0.json b/packages/amazonq/.changes/1.96.0.json new file mode 100644 index 00000000000..17919dd6374 --- /dev/null +++ b/packages/amazonq/.changes/1.96.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-09-25", + "version": "1.96.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Amazon Q support web/container environments running Ubuntu/Linux, even when the host machine is Amazon Linux 2." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.97.0.json b/packages/amazonq/.changes/1.97.0.json new file mode 100644 index 00000000000..94952817128 --- /dev/null +++ b/packages/amazonq/.changes/1.97.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-09-29", + "version": "1.97.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.98.0.json b/packages/amazonq/.changes/1.98.0.json new file mode 100644 index 00000000000..a71130bc08a --- /dev/null +++ b/packages/amazonq/.changes/1.98.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-10-02", + "version": "1.98.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/1.99.0.json b/packages/amazonq/.changes/1.99.0.json new file mode 100644 index 00000000000..9d1089ee8fa --- /dev/null +++ b/packages/amazonq/.changes/1.99.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-10-10", + "version": "1.99.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/next-release/Feature-ab31cbb6-3fe4-4ee3-a0a3-290430277856.json b/packages/amazonq/.changes/next-release/Feature-ab31cbb6-3fe4-4ee3-a0a3-290430277856.json new file mode 100644 index 00000000000..71c1583e77b --- /dev/null +++ b/packages/amazonq/.changes/next-release/Feature-ab31cbb6-3fe4-4ee3-a0a3-290430277856.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Q CodeTransformation: add more job metadata to history table" +} diff --git a/packages/amazonq/.vscode/launch.json b/packages/amazonq/.vscode/launch.json new file mode 100644 index 00000000000..b00c5071ce5 --- /dev/null +++ b/packages/amazonq/.vscode/launch.json @@ -0,0 +1,168 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "env": { + "SSMDOCUMENT_LANGUAGESERVER_PORT": "6010", + "WEBPACK_DEVELOPER_SERVER": "http://localhost:8080" + // Below allows for overrides used during development + // "__AMAZONQLSP_PATH": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/agent-standalone.js", + // "__AMAZONQLSP_UI": "${workspaceFolder}/../../../language-servers/chat-client/build/amazonq-ui.js" + }, + "envFile": "${workspaceFolder}/.local.env", + "outFiles": ["${workspaceFolder}/dist/**/*.js", "${workspaceFolder}/../core/dist/**/*.js"], + "preLaunchTask": "watch", + "postDebugTask": "terminate", + "presentation": { + "group": "1_Extension", + "order": 2 + } + }, + { + /** Handles the entire process of building and running the toolkit extension in the browser. **/ + "name": "Extension (Web)", + "type": "chrome", + "request": "attach", + "port": 9222, + /** + To get an understanding why we need the following: + - comment out the following + - set a breakpoint in VS Code that gets triggerd on extension startup + Now in the Chrome Developer Tools menu, the extension will load slower and open up random files. + I think this is due to source maps for irrelevant code being attempted to be resolved and slowing execution. + + What this is doing is ignoring certain modules that match the following paths, it matches the path of + a file in `Developer Tools` > `Sources`. + I was inspired by this: https://github.com/microsoft/vscode-test-web/blob/897bca4907a87a6bc564efc242ce6794e5da3232/.vscode/launch.json#L28 + **/ + "resolveSourceMapLocations": ["!**/node_modules/**", "!**/vs/**", "!**/extensions/**"], + "preLaunchTask": "webRun", + "postDebugTask": "webRunTerminate", + "presentation": { + "group": "1_Extension", + "order": 4 + } + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/dist/test/unit/index", + "${workspaceFolder}/../core/dist/src/testFixtures/workspaceFolder" + ], + "env": { + "DEVELOPMENT_PATH": "${workspaceFolder}", + "AWS_TOOLKIT_AUTOMATION": "local" + }, + "outFiles": ["${workspaceFolder}/dist/**/*.js", "${workspaceFolder}/../core/dist/**/*.js"], + "preLaunchTask": "watch", + "presentation": { + "group": "3_ExtensionTests", + "order": 2 + } + }, + { + "name": "Extension Tests (current file)", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/dist/test/unit/index", + "${workspaceFolder}/../core/dist/src/testFixtures/workspaceFolder" + ], + "env": { + "TEST_FILE": "${relativeFile}", + "DEVELOPMENT_PATH": "${workspaceFolder}", + "AWS_TOOLKIT_AUTOMATION": "local" + }, + "outFiles": ["${workspaceFolder}/dist/**/*.js", "${workspaceFolder}/../core/dist/**/*.js"], + "preLaunchTask": "watch", + "presentation": { + "group": "2_ExtensionTestsCurrentFile", + "order": 2 + } + }, + { + "name": "Extension Tests (web)", + "type": "extensionHost", + "debugWebWorkerHost": true, + "request": "launch", + "args": [ + "--disable-extension=amazonwebservices.aws-toolkit-vscode", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionDevelopmentKind=web", + "--extensionTestsPath=${workspaceFolder}/dist/test/web/testRunnerWebCore", + "${workspaceFolder}/../core/dist/src/testFixtures/workspaceFolder" + ], + "outFiles": ["${workspaceFolder}/dist/**/*.js", "${workspaceFolder}/../core/dist/**/*.js"], + "preLaunchTask": "webRun", + "presentation": { + "group": "3_ExtensionTests", + "order": 3 + } + }, + { + "name": "E2E Test (current file)", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--disable-extension=amazonwebservices.aws-toolkit-vscode", + "${workspaceFolder}/../core/dist/src/testFixtures/workspaceFolder", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/dist/test/e2e/index.js" + ], + "env": { + "TEST_FILE": "${relativeFile}", + "DEVELOPMENT_PATH": "${workspaceFolder}", + "AWS_TOOLKIT_AUTOMATION": "local" + }, + "outFiles": ["${workspaceFolder}/dist/**/*.js", "${workspaceFolder}/../core/dist/**/*.js"], + "preLaunchTask": "watch", + "presentation": { + "group": "4_E2ETestCurrentFile", + "order": 2 + } + }, + { + "name": "Attach to Language Server", + "type": "node", + "request": "attach", + "port": 6080, // Hard defined in core/src/shared/lsp/platform.ts + "outFiles": ["${workspaceFolder}/../../../language-servers/**/out/**/*.js"], + "skipFiles": [ + "/**", + "${workspaceFolder}/../../../language-servers/**/node_modules/**/*.js" + ], + "restart": { + "maxAttempts": 10, + "delay": 1000 + } + } + ], + "compounds": [ + { + "name": "Launch LSP with Debugging", + "configurations": ["Extension", "Attach to Language Server"], + "presentation": { + "group": "1_Extension", + "order": 5 + } + } + ] +} diff --git a/packages/amazonq/.vscode/settings.json b/packages/amazonq/.vscode/settings.json new file mode 100644 index 00000000000..4715e0254bf --- /dev/null +++ b/packages/amazonq/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "files.exclude": {}, + "search.exclude": { + "dist": true, + "node_modules": true + }, + "files.watcherExclude": {}, + "typescript.preferences.importModuleSpecifier": "relative", + "javascript.preferences.importModuleSpecifier": "relative", + "typescript.tsdk": "../../node_modules/typescript/lib", + "npm.packageManager": "npm" +} diff --git a/packages/amazonq/.vscode/tasks.json b/packages/amazonq/.vscode/tasks.json new file mode 100644 index 00000000000..86652ec71df --- /dev/null +++ b/packages/amazonq/.vscode/tasks.json @@ -0,0 +1,174 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "label": "watch", + "type": "shell", + "command": "npm", + "args": ["run", "watch"], + "problemMatcher": "$tsc-watch", + "isBackground": true, + "group": { + "kind": "build", + "isDefault": true + }, + "dependsOn": ["watchCore", "webpackCore", "serveVueCore"] + }, + { + "label": "watchCore", + "command": "npm run compileOnly -- --watch", + "type": "shell", + "isBackground": true, + "problemMatcher": "$tsc-watch", + "options": { + "cwd": "${workspaceFolder}/../../packages/core" + } + }, + { + "label": "webpackCore", + "command": "npm run webpackDev -- --watch", + "type": "shell", + "isBackground": true, + "problemMatcher": "$ts-webpack-watch", + "options": { + "cwd": "${workspaceFolder}/../../packages/core" + } + }, + { + "label": "serveVueCore", + "command": "npm run serveVue", + "type": "shell", + "detail": "Webpack + local server for Vue webview files from `core`.", + "isBackground": true, + "problemMatcher": "$ts-webpack-watch", + "options": { + "cwd": "${workspaceFolder}/../../packages/core" + }, + "presentation": { "panel": "dedicated" } + }, + { + "label": "terminate", + "command": "echo run terminate", + "type": "shell", + "presentation": { + "close": true + } + }, + // ---------- Start: Web Mode Tasks ---------- + { + "label": "webRun", + "type": "npm", + "script": "webRun", + "detail": "Runs VS Code in the Chrome browser with our toolkit installed", + "isBackground": true, + "dependsOn": ["webWatch"], + /** + We only need this problem matcher to signal when this task is completed. + Since this task starts a web server it does not terminate and we need to use + problemMatcher.background.endsPattern to read the CLI and know when this task + can signal it is done. + + The rest of the data in problemMatcher is required by VS Code to be "valid", + but not important for what we need. + + Doc: https://code.visualstudio.com/Docs/editor/tasks#_defining-a-problem-matcher + **/ + "problemMatcher": { + "pattern": [ + { + "regexp": "this section irrelevant but it must exist to work", + "file": 1, + "location": 2, + "message": 3 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": "^@vscode/test-web", + "endsPattern": "^Listening on" + } + } + }, + { + "label": "webWatch", + "type": "npm", + "script": "webWatch", + "detail": "Webpacks our toolkit code (with --watch) in preparation to be run in the browser", + "isBackground": true, + // Since `webpack --watch` never terminates (but finishes packaging at some point), + // VS Code uses this to parse the CLI output to pattern match something that indicates it is done + "problemMatcher": "$ts-webpack-watch", + "dependsOn": ["webCoreModuleWatch"] + }, + { + "label": "webCoreModuleWatch", + "type": "shell", + "command": "npm run compileDev -- --watch", + "detail": "Webpacks our toolkit code as a module in preparation to be webpacked by the toolkit package", + "isBackground": true, + // Since `webpack --watch` never terminates (but finishes packaging at some point), + // VS Code uses this to parse the CLI output to pattern match something that indicates it is done + "problemMatcher": "$ts-webpack-watch", + "options": { + "cwd": "${workspaceFolder}/../../packages/core" + } + }, + /** + After we stop debugging our browser, we also want to stop the web server. + When this task is ran it will stop the web server. + + From: https://stackoverflow.com/a/60330174 + **/ + { + "label": "webRunTerminate", + "command": "echo ${input:webRunTerminate}", + "type": "shell", + "dependsOn": [] + }, + // ---------- End: Web Mode Tasks ---------- + { + "type": "npm", + "script": "lint", + "problemMatcher": "$eslint-stylish" + }, + { + "type": "npm", + "script": "lintfix", + "problemMatcher": "$eslint-stylish" + }, + { + "type": "npm", + "script": "compile", + "dependsOn": ["Kill Tasks"], + "problemMatcher": "$tsc" + }, + { + "type": "npm", + "script": "clean", + "dependsOn": ["Kill Tasks"], + "problemMatcher": [] + }, + { + "label": "Kill Tasks", + "type": "process", + "command": "${input:killTasks}", + "problemMatcher": [] + } + ], + "inputs": [ + { + "id": "killTasks", + "type": "command", + "command": "workbench.action.tasks.terminate", + "args": "terminateAll" + }, + { + "id": "webRunTerminate", + "type": "command", + "command": "workbench.action.tasks.terminate", + "args": "webRun" + } + ] +} diff --git a/packages/amazonq/CHANGELOG.md b/packages/amazonq/CHANGELOG.md new file mode 100644 index 00000000000..361633276b5 --- /dev/null +++ b/packages/amazonq/CHANGELOG.md @@ -0,0 +1,724 @@ +## 1.102.0 2025-10-30 + +- Miscellaneous non-user-facing changes + +## 1.101.0 2025-10-22 + +- Miscellaneous non-user-facing changes + +## 1.100.0 2025-10-16 + +- Miscellaneous non-user-facing changes + +## 1.99.0 2025-10-10 + +- Miscellaneous non-user-facing changes + +## 1.98.0 2025-10-02 + +- Miscellaneous non-user-facing changes + +## 1.97.0 2025-09-29 + +- Miscellaneous non-user-facing changes + +## 1.96.0 2025-09-25 + +- **Bug Fix** Amazon Q support web/container environments running Ubuntu/Linux, even when the host machine is Amazon Linux 2. + +## 1.95.0 2025-09-19 + +- **Bug Fix** Amazon Q automatically refreshes expired IAM Credentials in Sagemaker instances + +## 1.94.0 2025-09-11 + +- Miscellaneous non-user-facing changes + +## 1.93.0 2025-09-05 + +- Miscellaneous non-user-facing changes + +## 1.92.0 2025-08-28 + +- **Feature** Amazon Q supports admin control for MCP servers to restrict MCP server usage +- **Feature** Enabling dynamic model fetching capabilities in Amazon Q chat +- **Feature** Amazon Q: Support for configuring and utilizing remote MCP servers. + +## 1.91.0 2025-08-22 + +- **Bug Fix** Enable inline completion in Jupyter Notebook +- **Feature** Amazon Q supports admin control for MCP servers to restrict MCP server usage + +## 1.90.0 2025-08-15 + +- Miscellaneous non-user-facing changes + +## 1.89.0 2025-08-13 + +- Miscellaneous non-user-facing changes + +## 1.88.0 2025-08-06 + +- **Feature** Amazon Q Chat provides error explanations and fixes when hovering or right-clicking on error indicators and messages +- **Feature** /transform: Show transformation history in Transformation Hub and allow users to resume jobs + +## 1.87.0 2025-07-31 + +- Miscellaneous non-user-facing changes + +## 1.86.0 2025-07-30 + +- **Bug Fix** Let Enter invoke auto completion more consistently +- **Bug Fix** Faster and more responsive inline completion UX +- **Bug Fix** Use documentChangeEvent as auto trigger condition + +## 1.85.0 2025-07-19 + +- Miscellaneous non-user-facing changes + +## 1.84.0 2025-07-17 + +- **Bug Fix** Slightly delay rendering inline completion when user is typing +- **Bug Fix** Render first response before receiving all paginated inline completion results +- **Feature** Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab. + +## 1.83.0 2025-07-09 + +- **Feature** Amazon Q /test, /doc, and /dev capabilities integrated into Agentic coding. +- **Feature** Added image support to Amazon Q chat, users can now upload images from their local file system +- **Removal** Deprecate "amazon q is generating..." UI for inline suggestion + +## 1.82.0 2025-07-07 + +- **Bug Fix** Prompt re-authenticate if auto trigger failed with expired token + +## 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 + +## 1.79.0 2025-06-25 + +- **Bug Fix** Added automatic system certificate detection and VSCode proxy settings support +- **Bug Fix** Improved Amazon Linux 2 support with better SageMaker environment detection +- **Feature** /transform: run all builds client-side + +## 1.78.0 2025-06-20 + +- **Bug Fix** Resolve missing chat options in Amazon Q chat interface. + +## 1.77.0 2025-06-18 + +- Miscellaneous non-user-facing changes + +## 1.76.0 2025-06-18 + +- **Bug Fix** /transform: only show lines of code statistic in plan +- **Feature** Add model selection feature +- **Feature** Pin context items in chat and manage workspace rules + +## 1.75.0 2025-06-13 + +- Miscellaneous non-user-facing changes + +## 1.74.0 2025-06-12 + +- Miscellaneous non-user-facing changes + +## 1.73.0 2025-06-11 + +- **Feature** Add MCP Server Support + +## 1.72.0 2025-06-11 + +- **Feature** Launch LSP with bundled artifacts as fallback + +## 1.71.0 2025-06-04 + +- Miscellaneous non-user-facing changes + +## 1.70.0 2025-05-28 + +- **Removal** Disable local workspace LSP + +## 1.69.0 2025-05-22 + +- **Bug Fix** /transform: avoid prompting user for target JDK path unnecessarily +- **Removal** /transform: remove option to select multiple diffs + +## 1.68.0 2025-05-15 + +- **Bug Fix** Fix Error: 'Amazon Q service is not signed in' +- **Bug Fix** Fix Error: 'Amazon Q Profile is not selected for IDC connection type' +- **Feature** Add inline completion support for abap language + +## 1.67.0 2025-05-14 + +- **Bug Fix** Previous and subsequent cells are used as context for completion in a Jupyter notebook +- **Bug Fix** Support chat in AL2 aarch64 + +## 1.66.0 2025-05-09 + +- **Bug Fix** Avoid inline completion 'Improperly formed request' errors when file is too large +- **Bug Fix** Named agent tabs sometimes open with unnecessary input options + +## 1.65.0 2025-05-05 + +- **Feature** Support selecting customizations across all Q profiles with automatic profile switching for enterprise users +- **Feature** Memorize and autofill users' last Sso login profile + +## 1.64.0 2025-05-02 + +- **Bug Fix** Enable Amazon Q LSP in AL2 instances + +## 1.63.0 2025-05-01 + +- **Bug Fix** Q profile selection hangs when a region is blocked +- **Feature** Agentic coding experience: Amazon Q can now write code and run shell commands on your behalf + +## 1.62.0 2025-04-25 + +- **Bug Fix** Toast message to warn users if Developer Profile is not selected +- **Bug Fix** Fix users can not log in successfully with 2+ IDE instnaces open due to throttle error throw by the service + +## 1.61.0 2025-04-22 + +- **Bug Fix** Some users not signaled they needed to select a Region Profile to get features working +- **bugfix** /review: disable auto-review by default + +## 1.60.0 2025-04-18 + +- **Bug Fix** Users might be bound to a customization which they dont have access with the selected profile and it causes service throwing 403 when using inline suggestion and chat features + +## 1.59.0 2025-04-11 + +- **Bug Fix** Code fix line number or file is sometimes not accurate +- **Bug Fix** Fix Q agents will fail for /transform /dev /test features if IdC kms key is configured with 400 error + +## 1.58.0 2025-04-11 + +- **Bug Fix** inline chat activates properly when using 'aws.experiments.amazonqChatLSP' feature flag +- **Bug Fix** Amazon Q Chat: code blocks in responses flicker, switching tabs during answer streaming makes expand button disappear +- **Bug Fix** Amazon Q Chat: tab bar buttons disappear when closing non-active tab +- **Bug Fix** Amazon Q Chat: chat history list does not truncate markdown + +## 1.57.0 2025-04-10 + +- **Bug Fix** Fix bug where generate fix does not work +- **Bug Fix** Fix bug where review shows 0 findings + +## 1.56.0 2025-04-09 + +- **Bug Fix** Improve status message while loading Amazon Q Profiles during login +- **Bug Fix** "failed to run command" error + +## 1.55.0 2025-04-09 + +- **Bug Fix** Amazon Q Chat: Update chat history icon +- **Bug Fix** Amazon Q Chat: chat occasionally freezes and displays gray screen +- **Bug Fix** Amazon Q Chat: Set owner-only permissions for chat history and saved prompt files +- **Feature** `/test` generates tests in all languages, not only Java/Python +- **Feature** Amazon Q chat: Click export icon to save chat transcript in Markdown or HTML +- **Feature** SageMaker: Disable the unsupported agentic commands and welcome prompt +- **Feature** Amazon Q Chat: Add `@code` context for PHP, Ruby, Scala, Shell, and Swift projects +- **Feature** Enterprise users can choose their preferred Amazon Q profile to improve personalization and workflow across different business regions + +## 1.54.0 2025-04-03 + +- **Bug Fix** Amazon Q chat: `@prompts` not added to context +- **Feature** Amazon Q chat: View and search chat history +- **Feature** SageMaker Unified Studio: Disable Sign out +- **Feature** SageMaker Unified Studio: Update Q Chat Introduction message +- **Feature** /review: automatically generate fix without clicking Generate Fix button +- **Feature** Amazon Q chat: Automatically persist chats between IDE sessions +- **Feature** Save user command execution logs to plugin output. +- **Feature** Amazon Q chat: Code blocks in chat messages have a max-height of 21 lines and can be scrolled inside + +## 1.53.0 2025-03-28 + +- **Bug Fix** Amazon Q Chat: Choosing a nested subfolder for `/doc` on Windows results in `The folder you chose did not contain any source files` error +- **Feature** Add support for Code search in Q chat +- **Feature** (Experimental) Amazon Q inline code suggestions via Amazon Q Language Server. (enable with `aws.experiments.amazonqLSP: true`) +- **Feature** Command Palette: Add `Amazon Q: Open Chat` command. + +## 1.52.0 2025-03-20 + +- **Bug Fix** Amazon Q chat: @Folders and @Files are missing `@` prefix in chat history +- **Bug Fix** /review: Code Issues ellipses menu displays AWS Toolkit options, if installed. +- **Bug Fix** Amazon Q chat: Progress indicator height is stretched +- **Bug Fix** Amazon Q chat: Long descriptions in context list are cut off +- **Bug Fix** Amazon Q chat: Improve responses for saved prompts and workspace rules +- **Bug Fix** /test: show descriptive error message +- **Bug Fix** Code Review: Fixed a bug where issues are double counted in the Q chat +- **Bug Fix** Amazon Q chat: Animation timings are too long +- **Bug Fix** Fix inline completion failure due to context length exceeding the threshold +- **Feature** /review: passing referenceTrackerConfiguration to StartCodeFixJob +- **Feature** /review: rename setting `showInlineCodeSuggestionsWithCodeReferences` to `showCodeWithReferences` + +## 1.51.0 2025-03-12 + +- **Bug Fix** increase scan timeout to reduce front-end timeout errors +- **Bug Fix** Amazon Q chat: Create a new prompt form does not autofocus or submit with Enter press +- **Bug Fix** /review: Zip files are created with the wrong file path for file scans in multifolder workspaces. +- **Bug Fix** /review: Invalid file path characters caused some detections to be skipped on Windows +- **Feature** Amazon Q Chat: You can now keep a "library" of prompt files in your home directory under `~/.aws/amazonq/prompts` and then quickly add them to the context using `@` on any project you’re working on. Prompt files are in markdown (`.md`) format. +- **Feature** /review: show code diff for fix preview +- **Feature** /test: display test plan summary in chat after generating tests + +## 1.50.0 2025-03-06 + +- **Bug Fix** /doc: Usage in multiple chat tabs may cause unexpected behavior. +- **Bug Fix** /review: subsequent reviews weren't possible +- **Feature** Amazon Q chat: Use `@` to add folders, files, and saved prompts as context +- **Feature** Amazon Q chat: increase chat input height to 3 lines +- **Feature** Amazon Q chat: Show list of files sent as context in chat response +- **Feature** Amazon Q chat: Add support for `.md` file rules in workspace-level `.amazonq/rules` directory +- **Test** add Q Chat /review command test coverage + +## 1.49.0 2025-02-27 + +- **Bug Fix** Amazon Q /test: Unit test generation displays an inaccurate diff view for non-primary packages in the workspace. +- **Bug Fix** Amazon Q /doc: Fix uploading file method throwing incorrect workspace too large error message +- **Bug Fix** /transform: skip running tests locally when user chooses to do so +- **Bug Fix** /review: ignored lines should not show up in scan issues +- **Bug Fix** /test: update capability card text + +## 1.48.0 2025-02-20 + +- **Bug Fix** /dev and /doc: Multi-root workspace with duplicate files causes infinite 'Uploading code...' loop +- **Bug Fix** Amazon Q /doc: update workspace too large error message +- **Bug Fix** /review: Auto-review should not remove issues from manual reviews +- **Bug Fix** /transform: allow View Summary button to work even after accepting diff +- **Bug Fix** Amazon Q /test: Fixing the issue of target file does not exist. +- **Feature** Amazon Q /doc: Add support for infrastructure diagrams + +## 1.47.0 2025-02-13 + +- **Bug Fix** `Send to prompt` and other context menu options not sent if chat was closed +- **Bug Fix** Amazon Q /test: Truncating user input to 4096 characters for unit test generation. +- **Bug Fix** Amazon Q /test: Q identify active test file and infer source file for test generation. +- **Bug Fix** /review: Code review starts automatically when invoked from menu +- **Feature** Amazon Q /dev: support `.hbs`, `.gjs`, `.gts`, `.astro`, `.mdx`, `.svelte`, `.erb`, `.rake` files +- **Feature** /transform: automatically download results when ready +- **Feature** /transform: support Java 21 transformations +- **Removal** Reverted prefetch logic to enable more stable inline completion + +## 1.46.0 2025-02-05 + +- **Bug Fix** Citation links are not clickable as numbers, but appear as non-clickable texts +- **Bug Fix** Fix language server start failure in AL2023 ARM64 +- **Bug Fix** /review: Auto-review issues did not populate code issues list +- **Bug Fix** Amazon Q: Fix code upload error when using /dev or /doc on Remote SSH +- **Bug Fix** /test placeholder text aligned across IDEs +- **Bug Fix** Inline: Typos in the first example suggestion +- **Feature** Inline suggestions: Pre-fetch recommendations to reduce suggestion latency. +- **Feature** Added github issue link and description to the chat answer feedback form + +## 1.45.0 2025-01-30 + +- **Bug Fix** Allow AB users with an overridden customization to go back to the default customization +- **Bug Fix** For security reasons, disabled auto linkify for link texts coming in markdown other than [TEXT](URL) format +- **Feature** Add setting to allow Q /dev to run code and test commands + +## 1.44.0 2025-01-23 + +- **Bug Fix** Amazon Q: word duplication when pressing tab on context selector fixed +- **Bug Fix** Amazon Q /doc: Prevent users from requesting changes if no iterations remain +- **Bug Fix** `/test`: view diffs by clicking files in the file tree, aligning the behavior with the 'View Diff' button. +- **Bug Fix** /review: Improved error handling for code fix operations +- **Bug Fix** Amazon Q: cursor no longer jumps after navigating prompt history +- **Bug Fix** Improve the text description of workspace index settings +- **Bug Fix** Notifications: 'Dismiss' command visible in command palette. +- **Bug Fix** /transform: replace icons in Transformation Hub with text +- **Bug Fix** Amazon Q /doc: Ask for user prompt if error occurs while updating documentation +- **Feature** Amazon Q: increase chat current active file context char limit to 40k +- **Feature** /review: Code issues can be grouped by file location or severity + +## 1.43.0 2025-01-15 + +- **Bug Fix** Auth: Valid StartURL not accepted at login +- **Bug Fix** Fix inline completion supplementalContext length exceeding maximum in certain cases +- **Bug Fix** Amazon Q /test: Unit test generation completed message shows after accept/reject action +- **Bug Fix** /test: for unsupported languages was sometimes unreliable +- **Bug Fix** User-selected customizations are sometimes not being persisted. +- **Bug Fix** Amazon q /dev: Remove hard-coded limits and instead rely server-side data to communicate number of code generations remaining +- **Feature** Adds capability to send new context commands to AB groups +- **Feature** feat(amazonq): Add error message for updated README too large +- **Feature** Enhance Q inline completion context fetching for better suggestion quality + +## 1.42.0 2025-01-09 + +- **Bug Fix** Amazon Q /doc: Improve button text phrasing +- **Bug Fix** Amazon Q /dev: Fix issue when files are deleted while preparing context +- **Bug Fix** Amazon Q Code Transformation: allow POSTGRESQL as target DB for SQL conversions +- **Bug Fix** Fix context menu displaying when typing @, even though input is disallowed +- **Bug Fix** Amazon Q can update mvn and gradle build files +- **Bug Fix** /transform: use correct documentation link in SQL conversion help message +- **Bug Fix** Up/down history navigation only triggering on first/last line of prompt input +- **Bug Fix** Amazon Q /test: Fix to redirect /test to generate tests in chat for external files out of workspace scope. +- **Bug Fix** /review: Code block extends beyond page margins in code issue detail view +- **Bug Fix** Amazon Q Code Transformation: retry project upload up to 3 times +- **Feature** Amazon Q Code Transformation: add view summary button in chat +- **Feature** Amazon Q: new code syntax highlighter for improved accuracy +- **Removal** Settings: No longer migrate old CodeWhisperer settings or initialize telemetry setting from AWS Toolkit. + +## 1.41.0 2024-12-17 + +- **Bug Fix** /review: Apply fix removes other issues in the same file. +- **Bug Fix** Fix(Amazon Q Code Transformation): show correct diff when running consecutive transformations +- **Bug Fix** Improve when the welcome page is shown in amazon q chat +- **Bug Fix** Code Review: Cleaned up output logs when running /review +- **Bug Fix** Code Review: Fixed a bug where applying a fix did not update the positions of other issues in the same file. +- **Bug Fix** Chat: When navigating to previous prompts, code attachments are sometimes displayed incorrectly +- **Bug Fix** /review: Diagnostics in the problems panel are mapped to the wrong code +- **Bug Fix** Fix opentabs context possibly timeout due to race condition of misuse of different timeout functionalities +- **Bug Fix** Auth: SSO session was bad, but no reauth prompt given +- **Bug Fix** Reduce frequency of system status poll +- **Bug Fix** Chat: When writing a prompt without sending it, navigating via up/down arrows sometimes deletes the unsent prompt. +- **Bug Fix** Code Review: Fixed a bug where projects with repeated path names did not scan properly. +- **Feature** /review: Code fix automatically scrolls into view after generation. +- **Feature** Chat: improve font size and line-height in footer (below prompt input field) + +## 1.40.0 2024-12-10 + +- **Bug Fix** Improved LLM code review for file review. +- **Bug Fix** @workspace is missing from the welcome to q chat tab +- **Bug Fix** Fix chat syntax highlighting when using several different themes +- **Bug Fix** Amazon Q /doc: progress bar persists after cancelling README creation +- **Bug Fix** Code Review: Fixed a bug where some issues are missing from the code issues view for workspaces with custom names +- **Bug Fix** Amazon Q /doc: Prompt user to choose a folder in chat +- **Bug Fix** Amazon Q /dev not adding Dockerfiles in nested folders +- **Bug Fix** Improved Code Fix generation for code review issues +- **Bug Fix** Fix the quick start buttons on the explore page to show amazon q colours on hover +- **Feature** Q feature dev: recognize .bms, .pli code files +- **Feature** Amazon Q Code Transformation: show job ID in Transformation Hub +- **Feature** UI improvements to Amazon Q Chat: New splash loader animation, initial streaming card animation, improved button colours +- **Feature** Add acknowledgement button for amazon q chat disclaimer +- **Feature** Navigate through prompt history by using the up/down arrows +- **Feature** Amazon Q: Simplify log channel + +## 1.39.0 2024-12-03 + +- **Feature** Added a getting started page for exploring amazon q agents +- **Feature** `/test` in Q chat to generate unit tests for java and python +- **Feature** `/doc` in Q chat to generate and update documentation for your project +- **Feature** Amazon Q Code Scan is now Amazon Q Code Review +- **Feature** `/review` in Q chat to scan your code for vulnerabilities and quality issues, and generate fixes +- **Feature** Security Scan: New TreeView to display security scan issues and vulnerabilities detected in your project. The TreeView provides an organized and hierarchical view of the scan results, making it easier to navigate and prioritize the issues that need to be addressed. +- **Feature** Security Scan: Added ability to suppress or ignore security issues + +## 1.38.0 2024-11-27 + +- **Feature** Amazon Q /dev: support `Dockerfile` files +- **Feature** Introduce @workspace command to enhance context fetching for Chat +- **Feature** Feature(Amazon Q Code Transformation): allow users to view results in 5 smaller diffs + +## 1.37.0 2024-11-22 + +- **Bug Fix** Amazon Q Feature Dev: display limit reached error message +- **Bug Fix** Amazon Q chat: `@workspace` command shown in all tab types +- **Bug Fix** Chat container exceeds width of container +- **Bug Fix** amazon q inline: skip indexing when no workspace folders are found +- **Bug Fix** file details and name unneccessary cropping +- **Bug Fix** Amazon Q /dev: update diff window behavior after a change is accepted +- **Feature** Amazon Q /dev: support `.gradle` files +- **Feature** Code Transform: Enable support for Java 17 projects. +- **Feature** Notifications: Support for delivering critical alerts and product updates +- **Feature** Retrieve and display a customization name when a customization is overridden in an AB test +- **Feature** Feature(Amazon Q Code Transformation): support conversions of embedded SQL from Oracle to PostgreSQL + +## 1.36.0 2024-11-14 + +- **Bug Fix** Fix broken inline suggestion auto-trigger on Systemverfilog files if users dont have systemverilog extension installed and enabled +- **Bug Fix** tutorial always showing on start +- **Feature** Enable default `@workspace` context of Amazon Q chat for certain users +- **Feature** Amazon Q /dev: Add an action to accept individual files + +## 1.35.0 2024-11-11 + +- **Breaking Change** Change focus chat keybind to win+alt+i on Windows, cmd+alt+i on macOS, and meta+alt+i on Linux +- **Bug Fix** Fix suboptimal inline suggestions from Amazon Q caused by improperly formatted supplemental context +- **Bug Fix** Fix empty chunks being sent to service and get validationException + +## 1.34.0 2024-11-07 + +- **Bug Fix** Align example help text with prompt message in chat +- **Bug Fix** Improve `@workspace` index auto pause start strategy. +- **Feature** Allow users to View and Apply diff when they explictily send code to Amazon Q using - Fix, Refactor, Optimize and Send To Prompt. +- **Feature** Security Scan: Auto-scan now supports JSX, TSX, Kotlin, Scala, and Shell files. + +## 1.33.0 2024-10-30 + +- **Bug Fix** Amazon Q /dev: fix for stop button showing for Code Transformation + +## 1.32.0 2024-10-29 + +- **Bug Fix** Remove warning when no input is provided to inline chat input box +- **Bug Fix** Use Sagemaker environment IAM Credentials for Code Completion when they're available +- **Bug Fix** Inline: Code completion not working for Sagemaker Pro Tier users. +- **Bug Fix** Disable /transform and /dev commands for sagemaker users as they're not supported +- **Feature** Amazon SageMaker Studio: Enable Free Tier Chat for IAM users + +## 1.31.0 2024-10-29 + +- **Breaking Change** Change keybind for focusing chat to ctrl+win+i on Windows, ctrl+cmd+i on macOS and ctrl+meta+i on Linux +- **Bug Fix** Inline Suggestions: Occasional `ValidationException` if user context has too many characters. +- **Bug Fix** Update `@workspace` index when adding or deleting a file +- **Bug Fix** fixed device code detection when running auth through tunneled vscode +- **Feature** Use inline chat to select code and transform it with natural language instructions +- **Feature** Amazon Q /dev: Add stop generation action +- **Feature** Provide more frequent updates about code changes made by agent + +## 1.30.0 2024-10-17 + +- **Bug Fix** Various fixes and changes + +## 1.29.0 2024-10-10 + +- **Bug Fix** Amazon Q /dev: include telemetry for workspace usage when generating new files +- **Bug Fix** Amazon Q extension may fail to start if AWS Toolkit extension fails to start +- **Bug Fix** Start language server by default +- **Bug Fix** Amazon Q Feature Dev: Add error messages when the upload URL expires +- **Bug Fix** Amazon Q (/dev): view diffs of previous /dev iterations +- **Bug Fix** Q dev handle no change required +- **Deprecation** The next release of this extension will require VS Code 1.83.0 or newer. +- **Feature** Automatically pause and resume @workspace indexing when OS CPU load is high +- **Feature** Add buttons to code blocks to view and accept diffs. +- **Feature** Inline completion for more json files, and all yaml files +- **Feature** Show a one-time warning if new VS Code is required +- **Removal** Minimum required VSCode version is now 1.83 + +## 1.28.0 2024-10-03 + +- **Bug Fix** Amazon Q /dev: define first folder as a root path for LLM-created files when using workspaces +- **Feature** Amazon Q Code Transformation: allow users to skip running tests +- **Feature** Amazon Q Developer: Updated legal disclaimer text + +## 1.27.0 2024-09-27 + +- **Bug Fix** Security Scan: Fixes an issue that incorrectly removes hardcoded credentials detections from auto scans. + +## 1.26.0 2024-09-19 + +- **Bug Fix** Security Scan: Fixed an issue where the wrong icon was used in the status bar menu. +- **Bug Fix** Disable Amazon Q LSP in AL2 +- **Bug Fix** Amazon Q Chat: Fix shifted chat contents when closing and opening chat panel back +- **Bug Fix** Security Scan: Minor styling improvements in the security issue webview panel +- **Bug Fix** Amazon Q Chat: Fix tooltip remaining on screen when closing and opening chat panel back +- **Bug Fix** Auth: Login state not updating across multiple VS Code windows. +- **Feature** Support @workspace queries for specific files like `@workspace what does test.ts do? `. + +## 1.25.0 2024-09-12 + +- **Bug Fix** Amazon Q Chat: Fixed inline code blocks are not vertically aligned with texts +- **Feature** Record telemetry event when Amazon Q extension is uninstalled. +- **Feature** Improve workspace indexing by only index files that are changed since last indexing +- **Removal** Amazon Q Feature dev: Remove approach generation flow + +## 1.24.0 2024-09-05 + +- **Bug Fix** Network errors causing premature SSO logout +- **Bug Fix** Fix SyntaxError causing premature expiration (edge case) +- **Bug Fix** Amazon Q Code Transformation: show instructions for finding JDK path on Linux +- **Bug Fix** UI: 'Start using Amazon Q' may display even if the user is signed in. +- **Bug Fix** Add getFeature and isEnabled utility methods to FeatureConfigProvider +- **Feature** Amazon Q /dev: include in progress state agent in code generation +- **Feature** Reduce workspace CPU indexing time by 50% + +## 1.23.0 2024-08-29 + +- **Bug Fix** Fix bug when undo inline suggestion causes command not found +- **Bug Fix** Auth: `SyntaxError` causing unexpected SSO logout +- **Bug Fix** Amazon Q Code Transformation: allow symlinks for JDK path +- **Bug Fix** Fix bug where text with inline code copied from Amazon Q Chat had new line breaks around the inline code text +- **Bug Fix** Fix bug with code indentation and nested list formatting in chat response prompt +- **Bug Fix** Fix bug when disabled commands does not get filtered in quick actions +- **Bug Fix** Auth: Users may be silently logged out due to network issues when starting the extension. +- **Feature** Support AB testing + +## 1.22.0 2024-08-22 + +- **Bug Fix** Avoid refreshing code suggestion for paginated response +- **Bug Fix** Update login logo styling +- **Bug Fix** Correct indentation when insert Q chat code at cursor position +- **Feature** Add notification for extended session to IdC users +- **Feature** Support more programming languages for workspace index + +## 1.21.0 2024-08-15 + +- **Bug Fix** Q feature dev: update file extension list and minor UI fixes + +## 1.20.0 2024-08-08 + +- **Bug Fix** Amazon Q /dev: include a retry option for the same prompt after folder reselection +- **Bug Fix** Ignore virtual environment when indexing workspace +- **Feature** Amazon Q Code Transformation: show pro tier users estimated cost of /transform on projects over 100K lines +- **Feature** Amazon Q Code Transformation: warn user if absolute file paths are found in the pom.xml + +## 1.19.0 2024-08-01 + +- **Bug Fix** Amazon Q Chat: Fixing issue with an incorrect input cursor position in the prompt text box +- **Bug Fix** Amazon Q Chat: Fixing issue with the max tabs notification not being dismissible. +- **Bug Fix** Amazon Q Chat: Showing/hiding the scrollbars is now controlled by the OS settings +- **Bug Fix** Q chat may stop responding after processing Python/Java code +- **Feature** Amazon q /dev: i18n support for messaging + +## 1.18.0 2024-07-29 + +- **Bug Fix** Security Scan: Fixed an issue scans were not able to succeed on Java projects with .class files +- **Bug Fix** FileNotFound error causing early SSO expiration + +## 1.17.0 2024-07-25 + +- **Bug Fix** Amazon Q Dev and Transform introduction text formatted incorrectly +- **Bug Fix** Amazon Q /dev: update error message for code gen timeout and include backfill for error name +- **Bug Fix** Sign-in page may fail to render in rare circumstances. + +## 1.16.0 2024-07-18 + +- **Bug Fix** Amazon q /dev: include granular error handling for code generation failed state +- **Bug Fix** Amazon Q Code Transformation: always show build logs from last job run +- **Bug Fix** Unexpected SSO expiration on Windows due to EPERM + +## 1.15.0 2024-07-15 + +- **Bug Fix** Amazon Q Chat: Fixes a bug when the prompt input exceeds the width of the chat box it's not always wrapped correctly. +- **Bug Fix** Amazon Q: Corrected a miswording in the Amazon Q: Share Content With AWS setting. +- **Bug Fix** Amazon Q Chat: Fixes a bug when user input contains 4 or more spaces at the beginning of the line for multiline inputs, that line appears like a code block instead of a paragraph + +## 1.14.0 2024-07-11 + +- **Feature** Amazon Q/dev proactively show code generation iterations + +## 1.13.0 2024-07-11 + +- **Bug Fix** AD/LDAP users may see "uv_os_get_passwd ENOENT" error on startup #5277 +- **Feature** Add support for [Amazon Q Chat Workspace Context](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/workspace-context.html). Customers can use `@workspace` to ask questions regarding local workspace. + +## 1.12.0 2024-07-08 + +- **Bug Fix** Amazon Q Security Scans: Fixed unnecessary yellow lines appearing in both auto scans and project scans. +- **Bug Fix** Amazon Q Chat: Fixed prompt input becomes invisible if an html special character is inserted +- **Bug Fix** Amazon Q Chat: Fixed button font sizes are too big +- **Bug Fix** Amazon Q Chat: Fixed buttons don't show borders inside a message +- **Bug Fix** Amazon Q Code Transform: Link UI messages to troubleshooting docs +- **Bug Fix** Amazon Q /dev command: improve user error messages +- **Bug Fix** Amazon Q Chat: Fixed button texts are cropped too short +- **Bug Fix** Amazon Q Chat: Fixed prompt input and selected command horizontal alignment +- **Bug Fix** Amazon Q Chat: Fixed prompt input becomes invisible when multine text inserted with paste +- **Feature** Q feature dev: Only use relevant code and related files + +## 1.11.0 2024-06-27 + +- **Bug Fix** Amazon Q Chat: Fix for inline buttons don't have borders +- **Bug Fix** Amazon Q Chat: Fix for some edge cases when followups appear on top without styles +- **Bug Fix** Amazon Q Chat: Fix for prompt input removes whole word if it starts with @ character but there is no context selected +- **Bug Fix** Amazon Q Chat: Fix for prompt input doesn't show multi line content properly after it reaches 10-15 lines +- **Bug Fix** Amazon Q /dev command: Fix in progress experience for ongoing backend calls + +## 1.10.0 2024-06-21 + +- **Bug Fix** Security Scan: Fixes an issue where project-scans time out for larger projects. +- **Bug Fix** Amazon Q /dev command: Fix file rejections for files outside of src/ +- **Bug Fix** Feature Development: update /dev welcome message +- **Bug Fix** Amazon Q Chat: Fixed broken code blocks with typewriter text in list items. +- **Feature** UX: New style for the login window +- **Removal** Auth: No longer share SSO sessions with AWS Toolkit. + +## 1.9.0 2024-06-14 + +- **Bug Fix** Amazon Q inline suggestions: remember `Pause Auto-Suggestions` after IDE restart +- **Bug Fix** Amazon Q /dev command: stop showing spinner when there is an error. +- **Bug Fix** Security Scan: Fixes an issue where auto-scans cause the editor to become unresponsive for larger projects. +- **Bug Fix** Fix(Amazon Q Code Transformation): show more detailed error messages for proxy issues +- **Feature** Amazon Q Code Transform: Allow user to view transformation build log + +## 1.8.0 2024-06-07 + +- **Bug Fix** fix(featureDev): fix file rejection for multi-workspaces +- **Feature** The `Send to Amazon Q` [context menu](https://github.com/aws/aws-toolkit-vscode/assets/371007/ce4c61a4-1b58-48ee-8500-56667d45dd7d) was renamed to `Amazon Q` +- **Feature** Amazon Q Transform: Increase project upload size limit to 2GB +- **Feature** feat(featureDev): generated plan being shown from top +- **Feature** Add additional commands for Amazon Q. + +## 1.7.0 2024-05-30 + +- **Bug Fix** Feature Development: File rejection is not rejecting a file when code is generated +- **Bug Fix** Security Scan: Improved accuracy when applying security fixes +- **Bug Fix** Amazon Q Code Transformation: show more specific error messages on failure cases +- **Feature** Security Scan: Support for scanning files outside of workspaces. +- **Feature** Amazon Q now publishes to Open VSX: https://open-vsx.org/namespace/amazonwebservices + +## 1.6.0 2024-05-21 + +- **Bug Fix** Amazon Q Chat: Inside chat body, if there is a code block inside a list item it shows
tags +- **Bug Fix** Amazon Q Chat: Prompt input field allows additional input beyond the character limit +- **Bug Fix** Amazon Q Chat: Prompt input field not getting focus when chat window opens + +## 1.5.0 2024-05-17 + +- **Bug Fix** Security Scan: Fixes an issue when scanning projects with binary files +- **Bug Fix** Fixes an issue where the /dev chat wouldn't let customers modify the source folder when exceeding the size limit +- **Bug Fix** Security Scan: Improved error notifications +- **Feature** Security Scan: Added custom command to run the security scan. +- **Feature** Security Scan: "View details" and "Explain" options can now be accessed from the problems panel + +## 1.4.0 2024-05-13 + +- **Bug Fix** Auth: No longer request AWS account scopes during login. +- **Bug Fix** Security Scan: Fixes an issue where scans fail for projects with Terraform files +- **Bug Fix** Amazon Q Code Transform: Show additional status messages to align with experience when JAVA_HOME set incorrectly. +- **Feature** UX: Added keyboard navigation to login screen. +- **Feature** New SSO Authorization Code flow for faster logins +- **Feature** Transform: Add human intervention to help update dependencies during transformation. + +## 1.3.0 2024-05-08 + +- **Bug Fix** modifying the root folder for /dev now modifies it +- **Bug Fix** Q chat may stop responding after processing Javascript/Typescript code +- **Bug Fix** Completion may fail unexpected if user opened many tabs +- **Feature** Inline Suggestions: Only display the 'Open Chat' CodeLens if the user is signed into Amazon Q. +- **Feature** Security Scan: Scans can now be run without an open editor +- **Feature** Security Scan: Multi-root workspace support + +## 1.2.0 2024-05-07 + +- **Bug Fix** Fix bug when Amazon Q chat sends code selection while user has no selection +- **Bug Fix** Amazon Q Code Transformation: make jobId visible in job history tab at start of job and allow summary.md + icons to be saved when accepting changes +- **Bug Fix** Amazon Q Chat: Typewriter animator parts showing up in code fields inside listitems +- **Bug Fix** Security Scan: Addresses a bug where security issues sometimes appear multiple times +- **Feature** Update cross file context config for Q inline suggestion +- **Feature** Amazon Q: Security scans now support C, C++, and PHP files + +## 1.1.0 2024-04-30 + +- **Bug Fix** Amazon Q Chat: Fixed markdown is not getting parsed inside list items. +- **Bug Fix** Amazon Q Chat: Copy to clipboard on code blocks doesn't work +- **Bug Fix** Fixed a crash when trying to use Q /dev on large projects or projects containing files with unsupported encoding. + +## 1.0.0 2024-04-29 + +- **Bug Fix** Code Transformation: Address various issues in TransformationHub UX. +- **Bug Fix** Code Transformation: Transform may fail if JAVA_HOME has leading or trailing whitespace +- **Bug Fix** Chat: Q panel doesn't fit to its parent +- **Bug Fix** Feature Development: update welcome message and menu item description for /dev command +- **Bug Fix** Code Transformation: show error messages in chat +- **Bug Fix** Code Transformation: Proposed changes not updated when multiple transformation jobs run in sequence. +- **Bug Fix** Feature Development: Update error message for monthly conversation limit reach +- **Bug Fix** Code Transformation: Omit Maven metadata files when uploading dependencies to fix certain build failures in backend. +- **Feature** Code Transformation: Refreshed UI during CodeTransformation +- **Feature** Chat: cmd + i to open chat +- **Feature** Right Click + no code selected shows Q context menu +- **Feature** Security Scan: Scans can now run on all files in the project +- **Feature** Chat: Updates quick action commands style and groupings +- **Feature** Code Transformation: add details about expected changes in transformation plan +- **Feature** Enable Amazon Q feature development and Amazon Q transform capabilities (/dev and /transform) for AWS Builder ID users. +- **Feature** Initial release +- **Feature** Chat: Added metric parameters to recordAddMessage telemetry event. +- **Feature** Security Scan: Scans can now run automatically when file changes are made +- **Feature** Chat: brief CodeLens to advertise chat +- **Feature** Security Scan: Send security issue to chat for explanation and fix + diff --git a/packages/amazonq/README.md b/packages/amazonq/README.md new file mode 100644 index 00000000000..e3ec16bb2ac --- /dev/null +++ b/packages/amazonq/README.md @@ -0,0 +1,58 @@ +![Try Amazon Q Free Tier](https://img.shields.io/badge/Try%20Amazon%20Q-Free%20Tier-success?style=flat-square) +[![Twitter Follow](https://img.shields.io/badge/follow-@aws-1DA1F2?style=flat-square&logo=aws&logoColor=white&label=Follow)](https://x.com/awscloud) +[![Youtube Channel Views](https://img.shields.io/youtube/channel/views/UCd6MoB9NC6uYN2grvUNT-Zg?style=flat-square&logo=youtube&label=Youtube)](https://www.youtube.com/@amazonwebservices) +![Marketplace Installs](https://img.shields.io/vscode-marketplace/i/AmazonWebServices.amazon-q-vscode.svg?label=Installs&style=flat-square) + +# Agentic coding experience + +Amazon Q Developer uses information across native and MCP server-based tools to intelligently perform actions beyond code suggestions, such as reading files, generating code diffs, and running commands based on your natural language instruction. Simply type your prompt in your preferred language and Q Developer will provide continuous status updates and iteratively apply changes based on your feedback, helping you accomplish tasks faster. + +### Implement new features + +Generate new code across your entire project and implement features. + +### Generate documentation + +Write API, technical design, and onboarding documentation. + +### Automate code reviews + +Perform code reviews, flagging suspicious code patterns and assessing deployment risk. + +### Generate unit tests + +Generate unit tests and add them to your project, helping you improve code quality, fast. + +
+ +# Core features + +### MCP support + +Add Model Context Protocol (MCP) servers to give Amazon Q Developer access to important context. + +### Inline suggestions + +Receive real-time code suggestions ranging from snippets to full functions based on your comments and existing code. + +[_15+ languages supported including Python, TypeScript, Rust, Terraform, AWS Cloudformation, and more_](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/q-language-ide-support.html) + +### Inline chat + +Seamlessly chat within the inline coding experience. Select a section of code that you need assistance with and initiate chat within the editor to request actions such as "Optimize this code", "Add comments", or "Write tests". + +### Chat + +Generate code, explain code, and get answers about software development. + +
+ +# Getting Started + +**Free Tier** - create or log in with an AWS Builder ID (a personal profile from AWS). + +**Pro Tier** - if your organization is on the Amazon Q Developer Pro tier, log in with single sign-on. + +# Troubleshooting & feedback + +[File a bug](https://github.com/aws/aws-toolkit-vscode/issues/new?assignees=&labels=bug&projects=&template=bug_report.md) or [submit a feature request](https://github.com/aws/aws-toolkit-vscode/issues/new?assignees=&labels=feature-request&projects=&template=feature_request.md) on our Github repository. diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json new file mode 100644 index 00000000000..3e239d0cd0e --- /dev/null +++ b/packages/amazonq/package.json @@ -0,0 +1,1492 @@ +{ + "name": "amazon-q-vscode", + "displayName": "Amazon Q", + "description": "The most capable generative AI–powered assistant for software development.", + "version": "1.103.0-SNAPSHOT", + "extensionKind": [ + "workspace" + ], + "publisher": "amazonwebservices", + "icon": "resources/marketplace/amazonq-icon-256x256.png", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-toolkit-vscode" + }, + "bugs": { + "url": "https://github.com/aws/aws-toolkit-vscode/issues" + }, + "galleryBanner": { + "color": "#232F3E", + "theme": "dark" + }, + "categories": [ + "AI", + "Chat", + "Programming Languages", + "Education", + "Machine Learning" + ], + "keywords": [ + "AWS", + "Codewhisperer", + "AI", + "Assistant", + "Chatbot", + "Q Developer" + ], + "preview": false, + "qna": "https://github.com/aws/aws-toolkit-vscode/issues", + "activationEvents": [ + "onStartupFinished", + "onUri", + "onCommand:aws.amazonq.accept", + "onView:aws.codeWhisperer.securityPanel" + ], + "main": "./dist/src/extensionNode", + "browser": "./dist/src/extensionWeb", + "scripts": { + "vscode:prepublish": "npm run clean && npm run buildScripts && webpack --mode production", + "buildScripts": "npm run generateNonCodeFiles && npm run copyFiles && npm run generateIcons && npm run generateSettings && tsc -p ./ --noEmit", + "generateNonCodeFiles": "ts-node ../../scripts/generateNonCodeFiles.ts", + "copyFiles": "ts-node ./scripts/build/copyFiles.ts", + "clean": "ts-node ../../scripts/clean.ts dist/ LICENSE NOTICE", + "compile": "npm run clean && npm run buildScripts && webpack", + "compileDev": "npm run testCompile && webpack --mode development", + "compileOnly": "tsc -p ./", + "package": "ts-node ../../scripts/package.ts", + "lint": "true", + "watch": "npm run clean && npm run buildScripts && tsc -watch -p ./", + "testCompile": "npm run clean && npm run buildScripts && npm run compileOnly", + "test": "npm run testCompile && c8 --allowExternal ts-node ../core/scripts/test/launchTest.ts unit dist/test/unit/index.js ../core/dist/src/testFixtures/workspaceFolder", + "testE2E": "npm run testCompile && c8 --allowExternal ts-node ../core/scripts/test/launchTest.ts e2e dist/test/e2e/index.js ../core/dist/src/testFixtures/workspaceFolder", + "testWeb": "npm run compileDev && c8 --allowExternal ts-node ../core/scripts/test/launchTest.ts web dist/test/web/testRunnerWebCore.js", + "webRun": "npx @vscode/test-web --open-devtools --browserOption=--disable-web-security --waitForDebugger=9222 --extensionDevelopmentPath=. .", + "webWatch": "npm run clean && npm run buildScripts && webpack --mode development --watch", + "serve": "webpack serve --config-name mainServe --mode development", + "newChange": "ts-node ../../scripts/newChange.ts", + "createRelease": "ts-node ../../scripts/createRelease.ts", + "generateIcons": "ts-node ../../scripts/generateIcons.ts", + "generateSettings": "ts-node ../../scripts/generateSettings.ts" + }, + "dependencies": { + "aws-core-vscode": "file:../core/" + }, + "contributesComments": { + "configuration": { + "properties": "Any settings also defined in packages/core/package.json will override same-named settings in this file." + } + }, + "contributes": { + "configuration": { + "type": "object", + "title": "%AWS.amazonq.productName%", + "properties": { + "amazonQ.telemetry": { + "type": "boolean", + "default": true, + "markdownDescription": "%AWS.configuration.description.amazonq.telemetry%" + }, + "amazonQ.suppressPrompts": { + "type": "object", + "description": "%AWS.configuration.description.suppressPrompts%", + "default": {}, + "properties": { + "createCredentialsProfile": { + "type": "boolean", + "default": false + }, + "codeWhispererNewWelcomeMessage": { + "type": "boolean", + "default": false + }, + "codeWhispererConnectionExpired": { + "type": "boolean", + "default": false + }, + "amazonQWelcomePage": { + "type": "boolean", + "default": false + }, + "amazonQSessionConfigurationMessage": { + "type": "boolean", + "default": false + }, + "minIdeVersion": { + "type": "boolean", + "default": false + }, + "ssoCacheError": { + "type": "boolean", + "default": false + }, + "amazonQLspManifestMessage": { + "type": "boolean", + "default": false + }, + "amazonQWorkspaceLspManifestMessage": { + "type": "boolean", + "default": false + }, + "amazonQChatDisclaimer": { + "type": "boolean", + "default": false + }, + "amazonQChatPairProgramming": { + "type": "boolean", + "default": false + }, + "amazonQSelectDeveloperProfile": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "amazonQ.showCodeWithReferences": { + "type": "boolean", + "markdownDescription": "%AWS.configuration.description.amazonq%", + "default": true + }, + "amazonQ.allowFeatureDevelopmentToRunCodeAndTests": { + "markdownDescription": "%AWS.configuration.description.featureDevelopment.allowRunningCodeAndTests%", + "type": "object", + "default": {} + }, + "amazonQ.importRecommendationForInlineCodeSuggestions": { + "type": "boolean", + "description": "%AWS.configuration.description.amazonq.importRecommendation%", + "default": true + }, + "amazonQ.shareContentWithAWS": { + "type": "boolean", + "markdownDescription": "%AWS.configuration.description.amazonq.shareContentWithAWS%", + "default": true, + "scope": "application" + }, + "amazonQ.workspaceIndex": { + "type": "boolean", + "markdownDescription": "%AWS.configuration.description.amazonq.workspaceIndex%", + "default": false, + "scope": "application" + }, + "amazonQ.workspaceIndexWorkerThreads": { + "type": "number", + "markdownDescription": "%AWS.configuration.description.amazonq.workspaceIndexWorkerThreads%", + "default": 0, + "scope": "application" + }, + "amazonQ.workspaceIndexUseGPU": { + "type": "boolean", + "markdownDescription": "%AWS.configuration.description.amazonq.workspaceIndexUseGPU%", + "default": false, + "scope": "application" + }, + "amazonQ.workspaceIndexMaxSize": { + "type": "number", + "markdownDescription": "%AWS.configuration.description.amazonq.workspaceIndexMaxSize%", + "default": 2048, + "scope": "application" + }, + "amazonQ.workspaceIndexMaxFileSize": { + "type": "number", + "markdownDescription": "%AWS.configuration.description.amazonq.workspaceIndexMaxFileSize%", + "default": 10, + "scope": "application" + }, + "amazonQ.workspaceIndexCacheDirPath": { + "type": "string", + "markdownDescription": "%AWS.configuration.description.amazonq.workspaceIndexCacheDirPath%", + "default": null, + "scope": "application" + }, + "amazonQ.workspaceIndexIgnoreFilePatterns": { + "type": "array", + "markdownDescription": "%AWS.configuration.description.amazonq.workspaceIndexIgnoreFilePatterns%", + "default": [], + "scope": "application" + }, + "amazonQ.ignoredSecurityIssues": { + "type": "array", + "markdownDescription": "%AWS.configuration.description.amazonq.ignoredSecurityIssues%", + "scope": "window", + "items": { + "type": "string" + } + }, + "amazonQ.proxy.certificateAuthority": { + "type": "string", + "markdownDescription": "%AWS.configuration.description.amazonq.proxy.certificateAuthority%", + "default": null, + "scope": "application" + }, + "amazonQ.proxy.enableProxyAndCertificateAutoDiscovery": { + "type": "boolean", + "markdownDescription": "%AWS.configuration.description.amazonq.proxy.enableProxyAndCertificateAutoDiscovery%", + "default": true + } + } + }, + "viewsContainers": { + "activitybar": [ + { + "id": "amazonq", + "title": "%AWS.amazonq.title%", + "icon": "resources/amazonq-logo.svg" + } + ], + "panel": [ + { + "id": "aws-codewhisperer-reference-log", + "title": "Code Reference Log", + "icon": "media/aws-logo.svg" + }, + { + "id": "aws-codewhisperer-transformation-hub", + "title": "Transformation Hub", + "icon": "media/aws-logo.svg" + } + ] + }, + "views": { + "amazonq": [ + { + "id": "aws.amazonq.notifications", + "name": "%AWS.notifications.title%", + "when": "!(isCloud9 || aws.isSageMaker) && aws.amazonq.notifications.show" + }, + { + "type": "webview", + "id": "aws.amazonq.AmazonCommonAuth", + "name": "%AWS.amazonq.login%", + "when": "!aws.isWebExtHost && aws.amazonq.showLoginView" + }, + { + "type": "tree", + "id": "aws.amazonq.SecurityIssuesTree", + "name": "%AWS.amazonq.security%", + "when": "!aws.isSageMaker && !aws.isWebExtHost && !aws.amazonq.showLoginView", + "visibility": "collapsed" + }, + { + "type": "webview", + "id": "aws.amazonq.AmazonQChatView", + "name": "%AWS.amazonq.chat%", + "when": "!aws.isWebExtHost && !aws.amazonq.showLoginView" + } + ], + "aws-codewhisperer-reference-log": [ + { + "type": "webview", + "id": "aws.codeWhisperer.referenceLog", + "name": "" + } + ], + "aws-codewhisperer-transformation-hub": [ + { + "type": "webview", + "id": "aws.amazonq.transformationHub", + "name": "Status", + "when": "gumby.wasQCodeTransformationUsed" + }, + { + "id": "aws.amazonq.transformationProposedChangesTree", + "name": "Proposed Changes", + "when": "gumby.transformationProposalReviewInProgress" + } + ] + }, + "viewsWelcome": [ + { + "view": "aws.amazonq.transformationProposedChangesTree", + "contents": "Project transformation is complete.\n[Download Proposed Changes](command:aws.amazonq.transformationHub.reviewChanges.startReview)", + "when": "gumby.reviewState == NotStarted" + }, + { + "view": "aws.amazonq.transformationProposedChangesTree", + "contents": "Project transformation is complete.\n Downloading the proposed changes...", + "when": "gumby.reviewState == PreparingReview" + }, + { + "view": "aws.amazonq.SecurityIssuesTree", + "contents": "No code issues have been detected in the workspace.", + "when": "!aws.amazonq.security.noMatches" + }, + { + "view": "aws.amazonq.SecurityIssuesTree", + "contents": "No matches.\n[Clear Filters](command:aws.amazonq.securityIssuesTreeFilter.clearFilters)", + "when": "aws.amazonq.security.noMatches" + } + ], + "submenus": [ + { + "label": "%AWS.submenu.amazonqEditorContextSubmenu.title%", + "id": "amazonqEditorContextSubmenu" + }, + { + "label": "%AWS.generic.feedback%", + "id": "aws.amazonq.submenu.feedback" + }, + { + "label": "%AWS.generic.help%", + "id": "aws.amazonq.submenu.help" + }, + { + "label": "%AWS.generic.moreActions%", + "id": "aws.amazonq.submenu.securityIssueMoreActions", + "icon": "$(ellipsis)" + } + ], + "menus": { + "commandPalette": [ + { + "command": "aws.amazonq.reconnect", + "when": "false" + }, + { + "command": "amazonq.dev.openMenu", + "when": "aws.isDevMode" + }, + { + "command": "aws.amazonq.learnMore", + "when": "false" + } + ], + "view/title": [ + { + "command": "aws.amazonq.stopTransformationInHub", + "when": "view == aws.amazonq.transformationHub", + "group": "navigation@1" + }, + { + "command": "aws.amazonq.showPlanProgressInHub", + "when": "view == aws.amazonq.transformationHub", + "group": "navigation@2" + }, + { + "command": "aws.amazonq.showHistoryInHub", + "when": "view == aws.amazonq.transformationHub", + "group": "navigation@3" + }, + { + "command": "aws.amazonq.showTransformationPlanInHub", + "when": "view == aws.amazonq.transformationHub", + "group": "navigation@4" + }, + { + "command": "aws.amazonq.transformationHub.summary.reveal", + "when": "view == aws.amazonq.transformationHub", + "group": "navigation@5" + }, + { + "command": "aws.amazonq.transformationHub.reviewChanges.acceptChanges", + "when": "view == aws.amazonq.transformationProposedChangesTree && gumby.reviewState == InReview", + "group": "navigation@1" + }, + { + "command": "aws.amazonq.transformationHub.reviewChanges.rejectChanges", + "when": "view == aws.amazonq.transformationProposedChangesTree && gumby.reviewState == InReview", + "group": "navigation@2" + }, + { + "command": "aws.amazonq.openReferencePanel", + "when": "view == aws.amazonq.AmazonQChatView", + "group": "0_topAmazonQ@1" + }, + { + "command": "aws.amazonq.selectRegionProfile", + "when": "view == aws.amazonq.AmazonQChatView && aws.amazonq.connectedSsoIdc == true", + "group": "1_amazonQ@1" + }, + { + "command": "aws.amazonq.manageSubscription", + "when": "(view == aws.amazonq.AmazonQChatView) && aws.codewhisperer.connected", + "group": "1_amazonQ@2" + }, + { + "command": "aws.amazonq.learnMore", + "when": "view =~ /^aws\\.amazonq/", + "group": "1_amazonQ@3" + }, + { + "command": "aws.amazonq.signout", + "when": "(view == aws.amazonq.AmazonQChatView) && aws.codewhisperer.connected && !aws.isSageMakerUnifiedStudio", + "group": "2_amazonQ@4" + }, + { + "command": "aws.amazonq.showLogs", + "when": "!aws.isSageMakerUnifiedStudio", + "group": "1_amazonQ@5" + }, + { + "command": "aws.amazonq.reconnect", + "when": "(view == aws.amazonq.AmazonQChatView) && aws.codewhisperer.connectionExpired", + "group": "2_amazonQ@3" + }, + { + "submenu": "aws.amazonq.submenu.feedback", + "when": "view =~ /^aws\\.amazonq/", + "group": "y_toolkitMeta@1" + }, + { + "submenu": "aws.amazonq.submenu.help", + "when": "view =~ /^aws\\.amazonq/", + "group": "y_toolkitMeta@2" + }, + { + "command": "aws.amazonq.codescan.showGroupingStrategy", + "when": "view == aws.amazonq.SecurityIssuesTree", + "group": "navigation@1" + }, + { + "command": "aws.amazonq.security.showFilters", + "when": "view == aws.amazonq.SecurityIssuesTree", + "group": "navigation@2" + } + ], + "view/item/context": [ + { + "command": "_aws.amazonq.notifications.dismiss", + "when": "viewItem == amazonqNotificationStartUp", + "group": "inline@1" + }, + { + "command": "aws.amazonq.openSecurityIssuePanel", + "when": "false && view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)", + "group": "inline@4" + }, + { + "command": "aws.amazonq.security.explain", + "when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)", + "group": "inline@4" + }, + { + "command": "aws.amazonq.security.generateFix", + "when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)", + "group": "inline@5" + }, + { + "command": "aws.amazonq.security.ignore", + "when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)", + "group": "inline@6" + }, + { + "submenu": "aws.amazonq.submenu.securityIssueMoreActions", + "when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)", + "group": "inline@7" + } + ], + "amazonqEditorContextSubmenu": [ + { + "command": "aws.amazonq.explainCode", + "group": "cw_chat@1" + }, + { + "command": "aws.amazonq.refactorCode", + "group": "cw_chat@2" + }, + { + "command": "aws.amazonq.fixCode", + "group": "cw_chat@3" + }, + { + "command": "aws.amazonq.optimizeCode", + "group": "cw_chat@4" + }, + { + "command": "aws.amazonq.generateUnitTests", + "when": "!aws.isSageMaker", + "group": "cw_chat@5" + }, + { + "command": "aws.amazonq.sendToPrompt", + "group": "cw_chat@6" + }, + { + "command": "aws.amazonq.inline.invokeChat", + "group": "cw_chat@7" + } + ], + "editor/context": [ + { + "submenu": "amazonqEditorContextSubmenu", + "group": "cw_chat" + } + ], + "aws.amazonq.submenu.feedback": [ + { + "command": "aws.amazonq.submitFeedback", + "when": "!aws.isWebExtHost", + "group": "1_feedback@1" + }, + { + "command": "aws.amazonq.createIssueOnGitHub", + "group": "1_feedback@2" + } + ], + "aws.amazonq.submenu.help": [ + { + "command": "aws.amazonq.walkthrough.show", + "group": "1_help@1" + }, + { + "command": "aws.amazonq.github", + "group": "1_help@2" + }, + { + "command": "aws.amazonq.aboutExtension", + "group": "1_help@3" + }, + { + "command": "aws.amazonq.viewLogs", + "group": "1_help@4" + } + ], + "aws.amazonq.submenu.securityIssueMoreActions": [ + { + "command": "aws.amazonq.security.explain", + "when": "false", + "group": "1_more@1" + }, + { + "command": "aws.amazonq.applySecurityFix", + "when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix", + "group": "1_more@3" + }, + { + "command": "aws.amazonq.security.regenerateFix", + "when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix", + "group": "1_more@4" + }, + { + "command": "aws.amazonq.security.ignoreAll", + "group": "1_more@5" + } + ] + }, + "commands": [ + { + "command": "aws.amazonq.stopCmdExecution", + "title": "Stop Amazon Q", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.runCmdExecution", + "title": "Run Amazon Q Tool", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.rejectCmdExecution", + "title": "Reject Amazon Q Tool", + "category": "%AWS.amazonq.title%" + }, + { + "command": "_aws.amazonq.notifications.dismiss", + "title": "%AWS.generic.dismiss%", + "category": "%AWS.amazonq.title%", + "enablement": "view == aws.amazonq.notifications", + "icon": "$(remove-close)" + }, + { + "command": "aws.amazonq.explainCode", + "title": "%AWS.command.amazonq.explainCode%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.refactorCode", + "title": "%AWS.command.amazonq.refactorCode%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.fixCode", + "title": "%AWS.command.amazonq.fixCode%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.optimizeCode", + "title": "%AWS.command.amazonq.optimizeCode%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.sendToPrompt", + "title": "%AWS.command.amazonq.sendToPrompt%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.generateUnitTests", + "title": "%AWS.command.amazonq.generateUnitTests%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected && !aws.isSageMaker" + }, + { + "command": "aws.amazonq.reconnect", + "title": "%AWS.command.codewhisperer.reconnect%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.openReferencePanel", + "title": "%AWS.command.codewhisperer.openReferencePanel%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.showLogs", + "title": "%AWS.command.codewhisperer.showLogs%", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.selectRegionProfile", + "title": "Change Profile", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.transformationHub.reviewChanges.acceptChanges", + "title": "%AWS.command.q.transform.acceptChanges%" + }, + { + "command": "aws.amazonq.transformationHub.reviewChanges.rejectChanges", + "title": "%AWS.command.q.transform.rejectChanges%" + }, + { + "command": "aws.amazonq.transformationHub.summary.reveal", + "title": "%AWS.command.q.transform.showChangeSummary%", + "enablement": "gumby.isSummaryAvailable" + }, + { + "command": "aws.amazonq.showTransformationPlanInHub", + "title": "%AWS.command.q.transform.showTransformationPlan%", + "enablement": "gumby.isPlanAvailable" + }, + { + "command": "aws.amazonq.createIssueOnGitHub", + "title": "%AWS.command.createIssueOnGitHub%", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.submitFeedback", + "title": "%AWS.command.submitFeedback%", + "enablement": "!aws.isWebExtHost", + "category": "%AWS.amazonq.title%", + "icon": "$(comment)" + }, + { + "command": "aws.amazonq.viewLogs", + "title": "%AWS.command.viewLogs%", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.github", + "title": "%AWS.command.github%", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.aboutExtension", + "title": "%AWS.command.aboutToolkit%", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.invokeInlineCompletion", + "title": "%AWS.command.codewhisperer.title%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.configure", + "title": "%AWS.command.codewhisperer.configure%", + "category": "%AWS.amazonq.title%", + "icon": "$(gear)" + }, + { + "command": "aws.amazonq.introduction", + "title": "%AWS.command.codewhisperer.introduction%", + "category": "%AWS.amazonq.title%", + "icon": "$(question)" + }, + { + "command": "aws.amazonq.manageSubscription", + "title": "%AWS.command.manageSubscription%", + "category": "%AWS.amazonq.title%", + "icon": "$(gear)", + "enablement": "aws.codewhisperer.connected && !aws.amazonq.connectedSsoIdc" + }, + { + "command": "aws.amazonq.signout", + "title": "%AWS.command.codewhisperer.signout%", + "category": "%AWS.amazonq.title%", + "icon": "$(debug-disconnect)", + "enablement": "aws.codewhisperer.connected && !aws.isSageMakerUnifiedStudio" + }, + { + "command": "aws.amazonq.learnMore", + "title": "%AWS.amazonq.learnMore%", + "category": "%AWS.amazonq.title%" + }, + { + "command": "amazonq.dev.openMenu", + "title": "Open Developer Menu", + "category": "Amazon Q (Developer)", + "enablement": "aws.isDevMode" + }, + { + "command": "aws.amazonq.stopTransformationInHub", + "title": "%AWS.command.q.transform.stopJobInHub%", + "enablement": "gumby.isStopButtonAvailable" + }, + { + "command": "aws.amazonq.showPlanProgressInHub", + "title": "%AWS.command.q.transform.viewJobProgress%" + }, + { + "command": "aws.amazonq.showHistoryInHub", + "title": "%AWS.command.q.transform.viewJobHistory%" + }, + { + "command": "aws.amazonq.selectCustomization", + "title": "%AWS.codewhisperer.customization.notification.new_customizations.select%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.gettingStarted", + "title": "Try inline suggestion examples", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.toggleCodeSuggestion", + "title": "%AWS.amazonq.toggleCodeSuggestion%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.codeWhisperer.toggleCodeScan", + "title": "%AWS.amazonq.toggleCodeScan%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.securityIssuesTreeFilter.clearFilters", + "title": "Clear Filters", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.security.generateFix", + "title": "%AWS.command.amazonq.generateFix%", + "icon": "$(wrench)", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.applySecurityFix", + "title": "%AWS.command.amazonq.acceptFix%", + "icon": "$(check)", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.security.regenerateFix", + "title": "%AWS.command.amazonq.regenerateFix%", + "icon": "$(lightbulb-autofix)", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.openSecurityIssuePanel", + "title": "%AWS.command.amazonq.viewDetails%", + "icon": "$(search)", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.security.explain", + "title": "%AWS.command.amazonq.explainIssue%", + "icon": "$(search)", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.security.ignore", + "title": "%AWS.command.amazonq.ignoreIssue%", + "icon": "$(circle-slash)", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.security.ignoreAll", + "title": "%AWS.command.amazonq.ignoreAllIssues%", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.security.showFilters", + "title": "%AWS.command.amazonq.filterIssues%", + "icon": "$(filter)", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.codescan.showGroupingStrategy", + "title": "%AWS.command.amazonq.groupIssues%", + "icon": "$(list-filter)", + "enablement": "view == aws.amazonq.SecurityIssuesTree" + }, + { + "command": "aws.amazonq.inline.invokeChat", + "title": "%AWS.amazonq.inline.invokeChat%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.focusChat", + "title": "%AWS.amazonq.openChat%", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.walkthrough.show", + "title": "%AWS.amazonq.welcomeWalkthrough%" + }, + { + "command": "aws.amazonq.clearCache", + "title": "%AWS.amazonq.clearCache%", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.inline.acceptEdit", + "title": "%AWS.amazonq.inline.acceptEdit%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.inline.rejectEdit", + "title": "%AWS.amazonq.inline.rejectEdit%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.toggleNextEditPredictionPanel", + "title": "%AWS.amazonq.toggleNextEditPredictionPanel%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + } + ], + "keybindings": [ + { + "command": "aws.amazonq.stopCmdExecution", + "key": "ctrl+shift+backspace", + "mac": "cmd+shift+backspace", + "when": "aws.amazonq.amazonqChatLSP.isFocus" + }, + { + "command": "aws.amazonq.runCmdExecution", + "key": "ctrl+shift+enter", + "mac": "cmd+shift+enter", + "when": "aws.amazonq.amazonqChatLSP.isFocus" + }, + { + "command": "aws.amazonq.rejectCmdExecution", + "key": "ctrl+shift+r", + "mac": "cmd+shift+r", + "when": "aws.amazonq.amazonqChatLSP.isFocus" + }, + { + "command": "_aws.amazonq.focusChat.keybinding", + "win": "win+alt+i", + "mac": "cmd+alt+i", + "linux": "meta+alt+i" + }, + { + "command": "aws.amazonq.inline.debugAcceptEdit", + "key": "ctrl+alt+a", + "mac": "cmd+alt+a", + "when": "editorTextFocus" + }, + { + "command": "aws.amazonq.inline.debugRejectEdit", + "key": "ctrl+alt+r", + "mac": "cmd+alt+r", + "when": "editorTextFocus" + }, + { + "command": "aws.amazonq.explainCode", + "win": "win+alt+e", + "mac": "cmd+alt+e", + "linux": "meta+alt+e" + }, + { + "command": "aws.amazonq.refactorCode", + "win": "win+alt+u", + "mac": "cmd+alt+u", + "linux": "meta+alt+u" + }, + { + "command": "aws.amazonq.fixCode", + "win": "win+alt+h", + "mac": "cmd+alt+y", + "linux": "meta+alt+y" + }, + { + "command": "aws.amazonq.optimizeCode", + "win": "win+alt+a", + "mac": "cmd+alt+a", + "linux": "meta+alt+a" + }, + { + "command": "aws.amazonq.sendToPrompt", + "key": "win+alt+q", + "mac": "cmd+alt+q", + "linux": "meta+alt+q" + }, + { + "command": "aws.amazonq.generateUnitTests", + "key": "win+alt+n", + "mac": "cmd+alt+t", + "linux": "meta+alt+t" + }, + { + "command": "aws.amazonq.invokeInlineCompletion", + "key": "alt+c", + "mac": "alt+c", + "when": "editorTextFocus && aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.rejectCodeSuggestion", + "key": "escape", + "mac": "escape", + "when": "inlineSuggestionVisible && !editorReadonly && aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.dismissTutorial", + "key": "escape", + "mac": "escape", + "when": "aws.codewhisperer.tutorial.workInProgress && !inlineSuggestionVisible && !suggestWidgetVisible" + }, + { + "key": "right", + "command": "aws.amazonq.showNext", + "when": "inlineSuggestionVisible && !editorReadonly && aws.codewhisperer.connected" + }, + { + "key": "left", + "command": "aws.amazonq.showPrev", + "when": "inlineSuggestionVisible && !editorReadonly && aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.checkInlineSuggestionVisibility", + "when": "inlineSuggestionVisible && !editorReadonly && aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.inline.invokeChat", + "win": "ctrl+i", + "mac": "cmd+i", + "linux": "ctrl+i", + "when": "editorTextFocus && aws.codewhisperer.connected" + }, + { + "command": "aws.amazonq.inline.waitForUserDecisionAcceptAll", + "key": "enter", + "when": "editorTextFocus && aws.codewhisperer.connected && amazonq.inline.codelensShortcutEnabled" + }, + { + "command": "aws.amazonq.inline.waitForUserDecisionRejectAll", + "key": "escape", + "when": "editorTextFocus && aws.codewhisperer.connected && amazonq.inline.codelensShortcutEnabled" + }, + { + "command": "aws.amazonq.inline.acceptEdit", + "key": "tab", + "when": "editorTextFocus && aws.amazonq.editSuggestionActive" + }, + { + "command": "aws.amazonq.inline.rejectEdit", + "key": "escape", + "when": "editorTextFocus && aws.amazonq.editSuggestionActive" + } + ], + "icons": { + "aws-amazonq-q-gradient": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1aa" + } + }, + "aws-amazonq-q-squid-ink": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1ab" + } + }, + "aws-amazonq-q-white": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1ac" + } + }, + "aws-amazonq-severity-critical": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1ad" + } + }, + "aws-amazonq-severity-high": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1ae" + } + }, + "aws-amazonq-severity-info": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1af" + } + }, + "aws-amazonq-severity-low": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b0" + } + }, + "aws-amazonq-severity-medium": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b1" + } + }, + "aws-amazonq-transform-arrow-dark": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b2" + } + }, + "aws-amazonq-transform-arrow-light": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b3" + } + }, + "aws-amazonq-transform-default-dark": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b4" + } + }, + "aws-amazonq-transform-default-light": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b5" + } + }, + "aws-amazonq-transform-dependencies-dark": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b6" + } + }, + "aws-amazonq-transform-dependencies-light": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b7" + } + }, + "aws-amazonq-transform-file-dark": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b8" + } + }, + "aws-amazonq-transform-file-light": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1b9" + } + }, + "aws-amazonq-transform-logo": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1ba" + } + }, + "aws-amazonq-transform-step-into-dark": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1bb" + } + }, + "aws-amazonq-transform-step-into-light": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1bc" + } + }, + "aws-amazonq-transform-variables-dark": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1bd" + } + }, + "aws-amazonq-transform-variables-light": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1be" + } + }, + "aws-applicationcomposer-icon": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1bf" + } + }, + "aws-applicationcomposer-icon-dark": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c0" + } + }, + "aws-apprunner-service": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c1" + } + }, + "aws-cdk-logo": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c2" + } + }, + "aws-cloudformation-stack": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c3" + } + }, + "aws-cloudwatch-log-group": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c4" + } + }, + "aws-codecatalyst-logo": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c5" + } + }, + "aws-codewhisperer-icon-black": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c6" + } + }, + "aws-codewhisperer-icon-white": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c7" + } + }, + "aws-codewhisperer-learn": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c8" + } + }, + "aws-ecr-registry": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1c9" + } + }, + "aws-ecs-cluster": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1ca" + } + }, + "aws-ecs-container": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1cb" + } + }, + "aws-ecs-service": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1cc" + } + }, + "aws-generic-attach-file": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1cd" + } + }, + "aws-iot-certificate": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1ce" + } + }, + "aws-iot-policy": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1cf" + } + }, + "aws-iot-thing": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d0" + } + }, + "aws-lambda-create-stack": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d1" + } + }, + "aws-lambda-create-stack-light": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d2" + } + }, + "aws-lambda-function": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d3" + } + }, + "aws-mynah-MynahIconBlack": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d4" + } + }, + "aws-mynah-MynahIconWhite": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d5" + } + }, + "aws-mynah-logo": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d6" + } + }, + "aws-redshift-cluster": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d7" + } + }, + "aws-redshift-cluster-connected": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d8" + } + }, + "aws-redshift-database": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1d9" + } + }, + "aws-redshift-redshift-cluster-connected": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1da" + } + }, + "aws-redshift-schema": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1db" + } + }, + "aws-redshift-table": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1dc" + } + }, + "aws-s3-bucket": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1dd" + } + }, + "aws-s3-create-bucket": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1de" + } + }, + "aws-sagemaker-code-editor": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1df" + } + }, + "aws-sagemaker-jupyter-lab": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e0" + } + }, + "aws-sagemakerunifiedstudio-catalog": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e1" + } + }, + "aws-sagemakerunifiedstudio-spaces": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e2" + } + }, + "aws-sagemakerunifiedstudio-spaces-dark": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e3" + } + }, + "aws-sagemakerunifiedstudio-symbol-int": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e4" + } + }, + "aws-sagemakerunifiedstudio-table": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e5" + } + }, + "aws-schemas-registry": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e6" + } + }, + "aws-schemas-schema": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e7" + } + }, + "aws-stepfunctions-preview": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e8" + } + } + }, + "walkthroughs": [ + { + "id": "aws.amazonq.walkthrough", + "title": "Meet Amazon Q", + "description": "Your generative AI-powered assistant across the software development lifecycle.", + "steps": [ + { + "id": "aws.amazonq.walkthrough.inlineSuggestions", + "title": "Get inline code suggestions", + "description": "Amazon Q suggests code as you type based on your open files. Accepted suggestions from licensed code will go into the Code Reference Log.\n\n[Try Example](command:_aws.amazonq.walkthrough.inlineSuggestionsExample)\n\n**TIP: Invoke manually with opt/alt + c**", + "media": { + "markdown": "./resources/walkthrough/amazonq/inline.md" + }, + "completionEvents": [ + "onCommand:_aws.amazonq.walkthrough.inlineSuggestionsExample" + ] + }, + { + "id": "aws.amazonq.walkthrough.chat", + "title": "Ask using chat", + "description": "Amazon Q answers software development questions, writes code based on your current file, and cites sources.\n\nUse the right-click menu for quick commands.\n\n**TIP: Drag and drop the Q panel to dock on the right**", + "media": { + "markdown": "./resources/walkthrough/amazonq/chat.md" + }, + "completionEvents": [] + }, + { + "id": "aws.amazonq.walkthrough.settings", + "title": "Access actions and options", + "description": "Pause inline suggestions, open the Code Reference Log, access Settings, and more from the Amazon Q menu.\n\n[Open the status bar menu](command:_aws.amazonq.walkthrough.listCommands)", + "media": { + "markdown": "./resources/walkthrough/amazonq/menu.md" + }, + "completionEvents": [ + "onCommand:_aws.amazonq.walkthrough.listCommands" + ] + } + ] + } + ] + }, + "engines": { + "npm": "^10.1.0", + "vscode": "^1.83.0" + } +} diff --git a/packages/amazonq/scripts/build/copyFiles.ts b/packages/amazonq/scripts/build/copyFiles.ts new file mode 100644 index 00000000000..45b1d263f0b --- /dev/null +++ b/packages/amazonq/scripts/build/copyFiles.ts @@ -0,0 +1,96 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* eslint-disable no-restricted-imports */ +import fs from 'fs' +import * as path from 'path' + +// Moves all dependencies into `dist` + +const projectRoot = process.cwd() +const outRoot = path.join(projectRoot, 'dist') +let vueHr = false + +// The target file or directory must exist, otherwise we should fail the whole build. +interface CopyTask { + /** + * Target file or directory to copy. + */ + readonly target: string + + /** + * Providing no destination means the target will be copied relative to the root directory. + */ + readonly destination?: string +} + +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'), + destination: path.join('..', 'package.nls.json'), + }, + { target: 'test/unit/amazonqGumby/resources' }, + + // Vue + { + target: '../core/src/auth/sso/vue', + destination: 'src/auth/sso/vue', + }, + { + target: path.join('../core', 'resources', 'js', 'vscode.js'), + destination: path.join('libs', 'vscode.js'), + }, + { + target: path.join('../../node_modules', 'vue', 'dist', 'vue.global.prod.js'), + destination: path.join('libs', 'vue.min.js'), + }, + { + target: path.join('../../node_modules', 'aws-core-vscode', 'dist', vueHr ? 'vuehr' : 'vue'), + destination: 'vue/', + }, + + { + target: path.join('../../node_modules', 'web-tree-sitter', 'tree-sitter.wasm'), + destination: path.join('src', 'tree-sitter.wasm'), + }, +] + +function copy(task: CopyTask): void { + const src = path.resolve(projectRoot, task.target) + const dst = path.resolve(outRoot, task.destination ?? task.target) + + try { + fs.cpSync(src, dst, { + recursive: true, + force: true, + errorOnExist: false, + }) + } catch (error) { + throw new Error(`Copy "${src}" to "${dst}" failed: ${error instanceof Error ? error.message : error}`) + } +} + +const args = process.argv.slice(2) +if (args.includes('--vueHr')) { + vueHr = true + console.log('Using Vue Hot Reload webpacks from core/') +} + +function main() { + try { + tasks.map(copy) + } catch (error) { + console.error('`copyFiles.ts` failed') + console.error(error) + process.exit(1) + } +} + +void main() diff --git a/packages/amazonq/src/api.ts b/packages/amazonq/src/api.ts new file mode 100644 index 00000000000..03b2a32ea55 --- /dev/null +++ b/packages/amazonq/src/api.ts @@ -0,0 +1,33 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SendMessageCommandOutput, SendMessageRequest } from '@amzn/amazon-q-developer-streaming-client' +import { GenerateAssistantResponseCommandOutput, GenerateAssistantResponseRequest } from '@amzn/codewhisperer-streaming' +import { AuthUtil } from 'aws-core-vscode/codewhisperer' +import { ChatSession } from 'aws-core-vscode/codewhispererChat' +import { api } from 'aws-core-vscode/amazonq' + +export default { + chatApi: { + async chat(request: GenerateAssistantResponseRequest): Promise { + const chatSession = new ChatSession() + return chatSession.chatSso(request) + }, + async chatIam(request: SendMessageRequest): Promise { + const chatSession = new ChatSession() + return chatSession.chatIam(request) + }, + }, + authApi: { + async reauthIfNeeded() { + if (AuthUtil.instance.isConnectionExpired()) { + await AuthUtil.instance.showReauthenticatePrompt() + } + }, + async getChatAuthState() { + return AuthUtil.instance.getChatAuthState() + }, + }, +} satisfies api diff --git a/packages/amazonq/src/app/amazonqScan/app.ts b/packages/amazonq/src/app/amazonqScan/app.ts new file mode 100644 index 00000000000..bd12e3acd01 --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/app.ts @@ -0,0 +1,70 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { AmazonQAppInitContext, MessageListener } from 'aws-core-vscode/amazonq' +import { AuthUtil, codeScanState, onDemandFileScanState } from 'aws-core-vscode/codewhisperer' +import { ScanChatControllerEventEmitters, ChatSessionManager } from 'aws-core-vscode/amazonqScan' +import { ScanController } from './chat/controller/controller' +import { AppToWebViewMessageDispatcher } from './chat/views/connector/connector' +import { Messenger } from './chat/controller/messenger/messenger' +import { UIMessageListener } from './chat/views/actions/uiMessageListener' +import { debounce } from 'lodash' + +export function init(appContext: AmazonQAppInitContext) { + const scanChatControllerEventEmitters: ScanChatControllerEventEmitters = { + authClicked: new vscode.EventEmitter(), + tabOpened: new vscode.EventEmitter(), + tabClosed: new vscode.EventEmitter(), + runScan: new vscode.EventEmitter(), + formActionClicked: new vscode.EventEmitter(), + errorThrown: new vscode.EventEmitter(), + showSecurityScan: new vscode.EventEmitter(), + scanStopped: new vscode.EventEmitter(), + followUpClicked: new vscode.EventEmitter(), + scanProgress: new vscode.EventEmitter(), + processResponseBodyLinkClick: new vscode.EventEmitter(), + fileClicked: new vscode.EventEmitter(), + scanCancelled: new vscode.EventEmitter(), + processChatItemVotedMessage: new vscode.EventEmitter(), + } + const dispatcher = new AppToWebViewMessageDispatcher(appContext.getAppsToWebViewMessagePublisher()) + const messenger = new Messenger(dispatcher) + + new ScanController(scanChatControllerEventEmitters, messenger, appContext.onDidChangeAmazonQVisibility.event) + + const scanChatUIInputEventEmitter = new vscode.EventEmitter() + + new UIMessageListener({ + chatControllerEventEmitters: scanChatControllerEventEmitters, + webViewMessageListener: new MessageListener(scanChatUIInputEventEmitter), + }) + + const debouncedEvent = debounce(async () => { + const authenticated = (await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected' + let authenticatingSessionID = '' + + if (authenticated) { + const session = ChatSessionManager.Instance.getSession() + + if (session.isTabOpen() && session.isAuthenticating) { + authenticatingSessionID = session.tabID! + session.isAuthenticating = false + } + } + + messenger.sendAuthenticationUpdate(authenticated, [authenticatingSessionID]) + }, 500) + + AuthUtil.instance.secondaryAuth.onDidChangeActiveConnection(() => { + return debouncedEvent() + }) + AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { + return debouncedEvent() + }) + + codeScanState.setChatControllers(scanChatControllerEventEmitters) + onDemandFileScanState.setChatControllers(scanChatControllerEventEmitters) +} diff --git a/packages/amazonq/src/app/amazonqScan/chat/controller/controller.ts b/packages/amazonq/src/app/amazonqScan/chat/controller/controller.ts new file mode 100644 index 00000000000..72af0a200c5 --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/chat/controller/controller.ts @@ -0,0 +1,372 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * This class is responsible for responding to UI events by calling + * the Scan extension. + */ +import * as vscode from 'vscode' +import { AuthController } from 'aws-core-vscode/amazonq' +import { getLogger, placeholder, i18n, openUrl, fs, TabTypeDataMap, randomUUID } from 'aws-core-vscode/shared' +import { ScanChatControllerEventEmitters, Session, ChatSessionManager } from 'aws-core-vscode/amazonqScan' +import { + AggregatedCodeScanIssue, + AuthUtil, + CodeAnalysisScope, + codeScanState, + isGitRepo, + onDemandFileScanState, + SecurityScanError, + SecurityScanStep, + showFileScan, + showSecurityScan, +} from 'aws-core-vscode/codewhisperer' +import { Messenger, ScanNamedMessages } from './messenger/messenger' +import MessengerUtils from './messenger/messengerUtils' +import { + cancellingProgressField, + fileScanProgressField, + projectScanProgressField, + ScanAction, + scanProgressMessage, + scanSummaryMessage, +} from '../../models/constants' +import path from 'path' +import { telemetry } from 'aws-core-vscode/telemetry' + +export class ScanController { + private readonly messenger: Messenger + private readonly sessionStorage: ChatSessionManager + private authController: AuthController + + public constructor( + private readonly chatControllerMessageListeners: ScanChatControllerEventEmitters, + messenger: Messenger, + onDidChangeAmazonQVisibility: vscode.Event + ) { + this.messenger = messenger + this.sessionStorage = ChatSessionManager.Instance + this.authController = new AuthController() + + this.chatControllerMessageListeners.tabOpened.event((data) => { + return this.tabOpened(data) + }) + + this.chatControllerMessageListeners.tabClosed.event((data) => { + return this.tabClosed(data) + }) + + this.chatControllerMessageListeners.authClicked.event((data) => { + this.authClicked(data) + }) + + this.chatControllerMessageListeners.runScan.event((data) => { + return this.scanInitiated(data) + }) + + this.chatControllerMessageListeners.formActionClicked.event((data) => { + return this.formActionClicked(data) + }) + + this.chatControllerMessageListeners.errorThrown.event((data) => { + return this.handleError(data) + }) + + this.chatControllerMessageListeners.showSecurityScan.event((data) => { + return this.handleScanResults(data) + }) + + this.chatControllerMessageListeners.scanStopped.event((data) => { + return this.handleScanStopped(data) + }) + + this.chatControllerMessageListeners.followUpClicked.event((data) => { + return this.handleFollowUpClicked(data) + }) + + this.chatControllerMessageListeners.scanProgress.event((data) => { + return this.handleScanProgress(data) + }) + + this.chatControllerMessageListeners.processResponseBodyLinkClick.event((data) => { + return this.processLink(data) + }) + + this.chatControllerMessageListeners.fileClicked.event((data) => { + return this.processFileClick(data) + }) + + this.chatControllerMessageListeners.scanCancelled.event((data) => { + return this.handleScanCancelled(data) + }) + + this.chatControllerMessageListeners.processChatItemVotedMessage.event((data) => { + telemetry.amazonq_feedback.emit({ + featureId: 'amazonQReview', + amazonqConversationId: this.sessionStorage.getSession().scanUuid, + credentialStartUrl: AuthUtil.instance.startUrl, + interactionType: data.vote, + }) + }) + + AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { + this.sessionStorage.removeActiveTab() + }) + } + + private async tabOpened(message: any) { + const session: Session = this.sessionStorage.getSession() + const tabID = this.sessionStorage.setActiveTab(message.tabID) + + // check if authentication has expired + try { + getLogger().debug(`Q - Review: Session created with id: ${session.tabID}`) + + const authState = await AuthUtil.instance.getChatAuthState() + if (authState.amazonQ !== 'connected') { + void this.messenger.sendAuthNeededExceptionMessage(authState, tabID) + session.isAuthenticating = true + return + } + } catch (err: any) { + this.messenger.sendErrorMessage(err.message, message.tabID) + } + } + + private async tabClosed(data: any) { + this.sessionStorage.removeActiveTab() + } + + private authClicked(message: any) { + this.authController.handleAuth(message.authType) + + this.messenger.sendAnswer({ + type: 'answer', + tabID: message.tabID, + message: 'Follow instructions to re-authenticate ...', + }) + + // Explicitly ensure the user goes through the re-authenticate flow + this.messenger.sendChatInputEnabled(message.tabID, false) + } + + private async scanInitiated(message: any) { + const session: Session = this.sessionStorage.getSession() + try { + // check that a project is open + const workspaceFolders = vscode.workspace.workspaceFolders + if (workspaceFolders === undefined || workspaceFolders.length === 0) { + this.messenger.sendChatInputEnabled(message.tabID, false) + this.messenger.sendErrorResponse('no-project-found', message.tabID) + return + } + // check that the session is authenticated + const authState = await AuthUtil.instance.getChatAuthState() + if (authState.amazonQ !== 'connected') { + void this.messenger.sendAuthNeededExceptionMessage(authState, message.tabID) + session.isAuthenticating = true + return + } + this.messenger.sendPromptMessage({ + tabID: message.tabID, + message: i18n('AWS.amazonq.scans.runCodeScan'), + }) + this.messenger.sendCapabilityCard({ tabID: message.tabID }) + // Displaying types of scans and wait for user input + this.messenger.sendUpdatePlaceholder(message.tabID, i18n('AWS.amazonq.scans.waitingForInput')) + + this.messenger.sendScans(message.tabID, i18n('AWS.amazonq.scans.chooseScan.description')) + } catch (e: any) { + this.messenger.sendErrorMessage(e.message, message.tabID) + } + } + + private async formActionClicked(message: any) { + const typedAction = MessengerUtils.stringToEnumValue(ScanAction, message.action as any) + switch (typedAction) { + case ScanAction.STOP_PROJECT_SCAN: + codeScanState.setToCancelling() + this.messenger.sendUpdatePromptProgress(message.tabID, cancellingProgressField) + break + case ScanAction.STOP_FILE_SCAN: + onDemandFileScanState.setToCancelling() + this.messenger.sendUpdatePromptProgress(message.tabID, cancellingProgressField) + break + } + } + + private async handleError(message: { + error: SecurityScanError + tabID: string + scope: CodeAnalysisScope + fileName: string | undefined + scanUuid?: string + }) { + if (this.isNotMatchingId(message)) { + return + } + if (message.error.code === 'NoSourceFilesError') { + this.messenger.sendScanResults(message.tabID, message.scope, message.fileName, true) + this.messenger.sendAnswer({ + tabID: message.tabID, + type: 'answer', + canBeVoted: true, + message: scanSummaryMessage(message.scope, []), + }) + } else { + this.messenger.sendErrorResponse(message.error, message.tabID) + } + } + + private async handleScanResults(message: { + error: Error + totalIssues: number + tabID: string + securityRecommendationCollection: AggregatedCodeScanIssue[] + scope: CodeAnalysisScope + fileName: string + scanUuid?: string + }) { + if (this.isNotMatchingId(message)) { + return + } + this.messenger.sendScanResults(message.tabID, message.scope, message.fileName, true) + this.messenger.sendAnswer({ + tabID: message.tabID, + type: 'answer', + canBeVoted: true, + message: scanSummaryMessage(message.scope, message.securityRecommendationCollection), + }) + } + + private async handleScanStopped(message: { tabID: string }) { + this.messenger.sendUpdatePlaceholder(message.tabID, TabTypeDataMap.review.placeholder) + // eslint-disable-next-line unicorn/no-null + this.messenger.sendUpdatePromptProgress(message.tabID, null) + this.messenger.sendChatInputEnabled(message.tabID, true) + } + + private async handleFollowUpClicked(message: any) { + switch (message.followUp.type) { + case ScanAction.RUN_PROJECT_SCAN: { + this.messenger.sendPromptMessage({ + tabID: message.tabID, + message: i18n('AWS.amazonq.scans.projectScan'), + }) + + const workspaceFolders = vscode.workspace.workspaceFolders ?? [] + for (const folder of workspaceFolders) { + if (!(await isGitRepo(folder.uri))) { + this.messenger.sendAnswer({ + tabID: message.tabID, + type: 'answer', + message: i18n('AWS.amazonq.scans.noGitRepo'), + }) + break + } + } + + this.messenger.sendScanInProgress({ + type: 'answer-stream', + tabID: message.tabID, + canBeVoted: true, + message: scanProgressMessage(0, CodeAnalysisScope.PROJECT), + }) + this.messenger.sendUpdatePromptProgress(message.tabID, projectScanProgressField) + const scanUuid = randomUUID() + this.sessionStorage.getSession().scanUuid = scanUuid + void showSecurityScan.execute(placeholder, 'amazonQChat', true, scanUuid) + break + } + case ScanAction.RUN_FILE_SCAN: { + // check if IDE has active file open. + const activeEditor = vscode.window.activeTextEditor + // also check all open editors and allow this to proceed if only one is open (even if not main focus) + const allVisibleEditors = vscode.window.visibleTextEditors + const openFileEditors = allVisibleEditors.filter((editor) => editor.document.uri.scheme === 'file') + const hasOnlyOneOpenFileSplitView = openFileEditors.length === 1 + getLogger().debug(`hasOnlyOneOpenSplitView: ${hasOnlyOneOpenFileSplitView}`) + // is not a file if the currently highlighted window is not a file, and there is either more than one or no file windows open + const isNotFile = activeEditor?.document.uri.scheme !== 'file' && !hasOnlyOneOpenFileSplitView + getLogger().debug(`activeEditor: ${activeEditor}, isNotFile: ${isNotFile}`) + if (!activeEditor || isNotFile) { + this.messenger.sendErrorResponse( + isNotFile ? 'invalid-file-type' : 'no-open-file-found', + message.tabID + ) + this.messenger.sendUpdatePlaceholder( + message.tabID, + 'Please open and highlight a source code file in order run a code scan.' + ) + this.messenger.sendChatInputEnabled(message.tabID, true) + return + } + const fileEditorToTest = hasOnlyOneOpenFileSplitView ? openFileEditors[0] : activeEditor + const fileName = fileEditorToTest.document.uri.fsPath + + this.messenger.sendPromptMessage({ + tabID: message.tabID, + message: i18n('AWS.amazonq.scans.fileScan'), + }) + this.messenger.sendScanInProgress({ + type: 'answer-stream', + tabID: message.tabID, + canBeVoted: true, + message: scanProgressMessage( + SecurityScanStep.GENERATE_ZIP - 1, + CodeAnalysisScope.FILE_ON_DEMAND, + fileName ? path.basename(fileName) : undefined + ), + }) + this.messenger.sendUpdatePromptProgress(message.tabID, fileScanProgressField) + const scanUuid = randomUUID() + this.sessionStorage.getSession().scanUuid = scanUuid + void showFileScan.execute(placeholder, 'amazonQChat', scanUuid) + break + } + } + } + + private async handleScanProgress(message: any) { + if (this.isNotMatchingId(message)) { + return + } + this.messenger.sendAnswer({ + type: 'answer-part', + tabID: message.tabID, + messageID: ScanNamedMessages.SCAN_SUBMISSION_STATUS_MESSAGE, + message: scanProgressMessage( + message.step, + message.scope, + message.fileName ? path.basename(message.fileName) : undefined + ), + }) + } + + private processLink(message: any) { + void openUrl(vscode.Uri.parse(message.link)) + } + + private async processFileClick(message: any) { + const workspaceFolders = vscode.workspace.workspaceFolders ?? [] + for (const workspaceFolder of workspaceFolders) { + const projectPath = workspaceFolder.uri.fsPath + const filePathWithoutProjectName = message.filePath.split('/').slice(1).join('/') + const absolutePath = path.join(projectPath, filePathWithoutProjectName) + if (await fs.existsFile(absolutePath)) { + const document = await vscode.workspace.openTextDocument(absolutePath) + await vscode.window.showTextDocument(document) + } + } + } + + private async handleScanCancelled(message: any) { + this.messenger.sendAnswer({ type: 'answer', tabID: message.tabID, message: 'Cancelled' }) + } + + private isNotMatchingId(data: { scanUuid?: string }): boolean { + const messagescanUuid = data.scanUuid + const currentscanUuid = this.sessionStorage.getSession().scanUuid + return Boolean(messagescanUuid) && Boolean(currentscanUuid) && messagescanUuid !== currentscanUuid + } +} diff --git a/packages/amazonq/src/app/amazonqScan/chat/controller/messenger/messenger.ts b/packages/amazonq/src/app/amazonqScan/chat/controller/messenger/messenger.ts new file mode 100644 index 00000000000..18b05e8bb84 --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/chat/controller/messenger/messenger.ts @@ -0,0 +1,230 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * This class controls the presentation of the various chat bubbles presented by the + * Q Security Scans. + * + * As much as possible, all strings used in the experience should originate here. + */ + +import { AuthFollowUpType, AuthMessageDataMap } from 'aws-core-vscode/amazonq' +import { + FeatureAuthState, + SecurityScanError, + CodeWhispererConstants, + SecurityScanStep, + DefaultCodeScanErrorMessage, +} from 'aws-core-vscode/codewhisperer' +import { ChatItemButton, ProgressField } from '@aws/mynah-ui/dist/static' +import { MynahIcons, ChatItemAction } from '@aws/mynah-ui' +import { ChatItemType } from 'aws-core-vscode/amazonq' +import { + AppToWebViewMessageDispatcher, + AuthNeededException, + AuthenticationUpdateMessage, + CapabilityCardMessage, + ChatInputEnabledMessage, + ChatMessage, + ChatPrompt, + ErrorMessage, + UpdatePlaceholderMessage, + UpdatePromptProgressMessage, +} from '../../views/connector/connector' +import { i18n } from 'aws-core-vscode/shared' +import { ScanAction, scanProgressMessage } from '../../../models/constants' +import path from 'path' + +export type UnrecoverableErrorType = 'no-project-found' | 'no-open-file-found' | 'invalid-file-type' + +export enum ScanNamedMessages { + SCAN_SUBMISSION_STATUS_MESSAGE = 'scanSubmissionMessage', +} + +export class Messenger { + public constructor(private readonly dispatcher: AppToWebViewMessageDispatcher) {} + + public sendAnswer(params: { + message?: string + type: ChatItemType + tabID: string + messageID?: string + followUps?: ChatItemAction[] + canBeVoted?: boolean + }) { + this.dispatcher.sendChatMessage( + new ChatMessage( + { + message: params.message, + messageType: params.type, + messageId: params.messageID, + followUps: params.followUps, + canBeVoted: true, + }, + params.tabID + ) + ) + } + + public sendChatInputEnabled(tabID: string, enabled: boolean) { + this.dispatcher.sendChatInputEnabled(new ChatInputEnabledMessage(tabID, enabled)) + } + + public sendUpdatePlaceholder(tabID: string, newPlaceholder: string) { + this.dispatcher.sendUpdatePlaceholder(new UpdatePlaceholderMessage(tabID, newPlaceholder)) + } + + public sendUpdatePromptProgress(tabID: string, progressField: ProgressField | null) { + this.dispatcher.sendUpdatePromptProgress(new UpdatePromptProgressMessage(tabID, progressField)) + } + + public async sendAuthNeededExceptionMessage(credentialState: FeatureAuthState, tabID: string) { + let authType: AuthFollowUpType = 'full-auth' + let message = AuthMessageDataMap[authType].message + + switch (credentialState.amazonQ) { + case 'disconnected': + authType = 'full-auth' + message = AuthMessageDataMap[authType].message + break + case 'unsupported': + authType = 'use-supported-auth' + message = AuthMessageDataMap[authType].message + break + case 'expired': + authType = 're-auth' + message = AuthMessageDataMap[authType].message + break + } + + this.dispatcher.sendAuthNeededExceptionMessage(new AuthNeededException(message, authType, tabID)) + } + + public sendAuthenticationUpdate(scanEnabled: boolean, authenticatingTabIDs: string[]) { + this.dispatcher.sendAuthenticationUpdate(new AuthenticationUpdateMessage(scanEnabled, authenticatingTabIDs)) + } + + public sendScanInProgress(params: { + message?: string + type: ChatItemType + tabID: string + messageID?: string + canBeVoted?: boolean + }) { + this.dispatcher.sendChatMessage( + new ChatMessage( + { + message: params.message, + messageType: params.type, + messageId: ScanNamedMessages.SCAN_SUBMISSION_STATUS_MESSAGE, + canBeVoted: params.canBeVoted, + }, + params.tabID + ) + ) + } + + public sendErrorMessage(errorMessage: string, tabID: string) { + this.dispatcher.sendErrorMessage( + new ErrorMessage(CodeWhispererConstants.genericErrorMessage, errorMessage, tabID) + ) + } + + public sendScanResults( + tabID: string, + scope: CodeWhispererConstants.CodeAnalysisScope, + fileName?: string, + canBeVoted?: boolean + ) { + this.dispatcher.sendChatMessage( + new ChatMessage( + { + message: scanProgressMessage( + SecurityScanStep.PROCESS_SCAN_RESULTS + 1, + scope, + fileName ? path.basename(fileName) : undefined + ), + messageType: 'answer-part', + messageId: ScanNamedMessages.SCAN_SUBMISSION_STATUS_MESSAGE, + canBeVoted: canBeVoted, + }, + tabID + ) + ) + } + + public sendErrorResponse(error: UnrecoverableErrorType | SecurityScanError, tabID: string) { + let message = DefaultCodeScanErrorMessage + const buttons: ChatItemButton[] = [] + if (typeof error === 'string') { + switch (error) { + case 'no-project-found': { + // TODO: If required we can add "Open the Projects" button in the chat panel. + message = CodeWhispererConstants.noOpenProjectsFound + break + } + case 'no-open-file-found': { + message = CodeWhispererConstants.noOpenFileFound + break + } + case 'invalid-file-type': { + message = CodeWhispererConstants.invalidFileTypeChatMessage + break + } + } + } else if (error.code === 'NoActiveFileError') { + message = CodeWhispererConstants.noOpenFileFound + } else if (error.code === 'ContentLengthError') { + message = CodeWhispererConstants.ProjectSizeExceededErrorMessage + } else if (error.code === 'NoSourceFilesError') { + message = CodeWhispererConstants.noSourceFilesErrorMessage + } else { + message = error.customerFacingMessage + } + this.dispatcher.sendChatMessage( + new ChatMessage( + { + message, + messageType: 'answer', + buttons, + }, + tabID + ) + ) + } + + public sendScans(tabID: string, message: string) { + const followUps: ChatItemAction[] = [] + followUps.push({ + pillText: i18n('AWS.amazonq.scans.projectScan'), + status: 'info', + icon: 'folder' as MynahIcons, + type: ScanAction.RUN_PROJECT_SCAN, + }) + followUps.push({ + pillText: i18n('AWS.amazonq.scans.fileScan'), + status: 'info', + icon: 'file' as MynahIcons, + type: ScanAction.RUN_FILE_SCAN, + }) + this.dispatcher.sendChatMessage( + new ChatMessage( + { + message, + messageType: 'ai-prompt', + followUps, + }, + tabID + ) + ) + } + + // This function shows selected scan type in the chat panel as a user input + public sendPromptMessage(params: { tabID: string; message: string }) { + this.dispatcher.sendPromptMessage(new ChatPrompt(params.message, params.tabID)) + } + + public sendCapabilityCard(params: { tabID: string }) { + this.dispatcher.sendChatMessage(new CapabilityCardMessage(params.tabID)) + } +} diff --git a/packages/amazonq/src/app/amazonqScan/chat/controller/messenger/messengerUtils.ts b/packages/amazonq/src/app/amazonqScan/chat/controller/messenger/messengerUtils.ts new file mode 100644 index 00000000000..455a4ebf4af --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/chat/controller/messenger/messengerUtils.ts @@ -0,0 +1,19 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + */ +// TODO: Refactor the common functionality between Transform, FeatureDev, CWSPRChat, Scan and UTG to a new Folder. + +export default class MessengerUtils { + static stringToEnumValue = ( + enumObject: T, + value: `${T[K]}` + ): T[K] => { + if (Object.values(enumObject).includes(value)) { + return value as unknown as T[K] + } else { + throw new Error('Value provided was not found in Enum') + } + } +} diff --git a/packages/amazonq/src/app/amazonqScan/chat/session/session.ts b/packages/amazonq/src/app/amazonqScan/chat/session/session.ts new file mode 100644 index 00000000000..1ca7e8d7362 --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/chat/session/session.ts @@ -0,0 +1,25 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export enum ConversationState { + IDLE, + JOB_SUBMITTED, +} + +export class Session { + // Used to keep track of whether or not the current session is currently authenticating/needs authenticating + public isAuthenticating: boolean = false + + // A tab may or may not be currently open + public tabID: string | undefined + + public conversationState: ConversationState = ConversationState.IDLE + + constructor() {} + + public isTabOpen(): boolean { + return this.tabID !== undefined + } +} diff --git a/packages/amazonq/src/app/amazonqScan/chat/storages/chatSession.ts b/packages/amazonq/src/app/amazonqScan/chat/storages/chatSession.ts new file mode 100644 index 00000000000..b7df6eb0cc6 --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/chat/storages/chatSession.ts @@ -0,0 +1,54 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + */ + +import { Session } from '../session/session' + +export class SessionNotFoundError extends Error {} + +export class ChatSessionManager { + private static _instance: ChatSessionManager + private activeSession: Session | undefined + + constructor() {} + + public static get Instance() { + return this._instance || (this._instance = new this()) + } + + private createSession(): Session { + this.activeSession = new Session() + return this.activeSession + } + + public getSession(): Session { + if (this.activeSession === undefined) { + return this.createSession() + } + + return this.activeSession + } + + public setActiveTab(tabID: string): string { + if (this.activeSession !== undefined) { + if (!this.activeSession.isTabOpen()) { + this.activeSession.tabID = tabID + return tabID + } + return this.activeSession.tabID! + } + + throw new SessionNotFoundError() + } + + public removeActiveTab(): void { + if (this.activeSession !== undefined) { + if (this.activeSession.isTabOpen()) { + this.activeSession.tabID = undefined + return + } + } + } +} diff --git a/packages/amazonq/src/app/amazonqScan/chat/views/actions/uiMessageListener.ts b/packages/amazonq/src/app/amazonqScan/chat/views/actions/uiMessageListener.ts new file mode 100644 index 00000000000..efa577313ab --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/chat/views/actions/uiMessageListener.ts @@ -0,0 +1,126 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { MessageListener, ExtensionMessage } from 'aws-core-vscode/amazonq' +import { ScanChatControllerEventEmitters } from 'aws-core-vscode/amazonqScan' + +type UIMessage = ExtensionMessage & { + tabID?: string +} + +export interface UIMessageListenerProps { + readonly chatControllerEventEmitters: ScanChatControllerEventEmitters + readonly webViewMessageListener: MessageListener +} + +export class UIMessageListener { + private scanControllerEventsEmitters: ScanChatControllerEventEmitters | undefined + private webViewMessageListener: MessageListener + + constructor(props: UIMessageListenerProps) { + this.scanControllerEventsEmitters = props.chatControllerEventEmitters + this.webViewMessageListener = props.webViewMessageListener + + // Now we are listening to events that get sent from amazonq/webview/actions/actionListener (e.g. the tab) + this.webViewMessageListener.onMessage((msg) => { + this.handleMessage(msg) + }) + } + + private handleMessage(msg: ExtensionMessage) { + switch (msg.command) { + case 'new-tab-was-created': + this.tabOpened(msg) + break + case 'tab-was-removed': + this.tabClosed(msg) + break + case 'auth-follow-up-was-clicked': + this.authClicked(msg) + break + case 'review': + this.scan(msg) + break + case 'form-action-click': + this.formActionClicked(msg) + break + case 'follow-up-was-clicked': + this.followUpClicked(msg) + break + case 'response-body-link-click': + this.processResponseBodyLinkClick(msg) + break + case 'file-click': + this.processFileClick(msg) + break + case 'chat-item-voted': + this.chatItemVoted(msg) + break + } + } + + private scan(msg: UIMessage) { + this.scanControllerEventsEmitters?.runScan.fire({ + tabID: msg.tabID, + }) + } + + private formActionClicked(msg: UIMessage) { + this.scanControllerEventsEmitters?.formActionClicked.fire({ + ...msg, + }) + } + + private tabOpened(msg: UIMessage) { + this.scanControllerEventsEmitters?.tabOpened.fire({ + tabID: msg.tabID, + }) + } + + private tabClosed(msg: UIMessage) { + this.scanControllerEventsEmitters?.tabClosed.fire({ + tabID: msg.tabID, + }) + } + + private authClicked(msg: UIMessage) { + this.scanControllerEventsEmitters?.authClicked.fire({ + tabID: msg.tabID, + authType: msg.authType, + }) + } + + private followUpClicked(msg: UIMessage) { + this.scanControllerEventsEmitters?.followUpClicked.fire({ + followUp: msg.followUp, + tabID: msg.tabID, + }) + } + + private processResponseBodyLinkClick(msg: UIMessage) { + this.scanControllerEventsEmitters?.processResponseBodyLinkClick.fire({ + command: msg.command, + messageId: msg.messageId, + tabID: msg.tabID, + link: msg.link, + }) + } + + private processFileClick(msg: UIMessage) { + this.scanControllerEventsEmitters?.fileClicked.fire({ + tabID: msg.tabID, + messageId: msg.messageId, + filePath: msg.filePath, + }) + } + + private chatItemVoted(msg: any) { + this.scanControllerEventsEmitters?.processChatItemVotedMessage.fire({ + tabID: msg.tabID, + command: msg.command, + vote: msg.vote, + }) + } +} diff --git a/packages/amazonq/src/app/amazonqScan/chat/views/connector/connector.ts b/packages/amazonq/src/app/amazonqScan/chat/views/connector/connector.ts new file mode 100644 index 00000000000..c906a401f91 --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/chat/views/connector/connector.ts @@ -0,0 +1,192 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AuthFollowUpType, MessagePublisher, ChatItemType } from 'aws-core-vscode/amazonq' +import { ScanMessageType } from 'aws-core-vscode/amazonqScan' +import { ChatItemButton, ProgressField, ChatItemAction, ChatItemContent } from '@aws/mynah-ui/dist/static' +import { scanChat } from '../../../models/constants' +import { MynahIcons } from '@aws/mynah-ui' + +class UiMessage { + readonly time: number = Date.now() + readonly sender: string = scanChat + readonly type: ScanMessageType = 'chatMessage' + readonly status: string = 'info' + + public constructor(protected tabID: string) {} +} + +export class AuthenticationUpdateMessage { + readonly time: number = Date.now() + readonly sender: string = scanChat + readonly type: ScanMessageType = 'authenticationUpdateMessage' + + constructor( + readonly scanEnabled: boolean, + readonly authenticatingTabIDs: string[] + ) {} +} + +export class AuthNeededException extends UiMessage { + override type: ScanMessageType = 'authNeededException' + + constructor( + readonly message: string, + readonly authType: AuthFollowUpType, + tabID: string + ) { + super(tabID) + } +} + +export interface ChatMessageProps { + readonly message: string | undefined + readonly messageId?: string | undefined + readonly messageType: ChatItemType + readonly canBeVoted?: boolean + readonly buttons?: ChatItemButton[] + readonly followUps?: ChatItemAction[] | undefined + readonly informationCard?: ChatItemContent['informationCard'] + readonly fileList?: ChatItemContent['fileList'] +} + +export class ChatMessage extends UiMessage { + readonly message: string | undefined + readonly messageId?: string | undefined + readonly messageType: ChatItemType + readonly canBeVoted?: boolean + readonly buttons: ChatItemButton[] + readonly followUps: ChatItemAction[] | undefined + readonly informationCard: ChatItemContent['informationCard'] + readonly fileList: ChatItemContent['fileList'] + override type: ScanMessageType = 'chatMessage' + + constructor(props: ChatMessageProps, tabID: string) { + super(tabID) + this.message = props.message + this.messageType = props.messageType + this.buttons = props.buttons || [] + this.messageId = props.messageId || undefined + this.followUps = props.followUps + this.informationCard = props.informationCard || undefined + this.fileList = props.fileList + this.canBeVoted = props.canBeVoted || undefined + } +} + +export class CapabilityCardMessage extends ChatMessage { + constructor(tabID: string) { + super( + { + message: '', + messageType: 'answer', + informationCard: { + title: '/review', + description: 'Included in your Q Developer subscription', + content: { + body: `I can review your workspace for vulnerabilities and issues. + +After you begin a review, I will: +1. Review all relevant code in your workspace or your current file +2. Provide a list of issues for your review + +You can then investigate, fix, or ignore issues. + +To learn more, check out our [User Guide](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/security-scans.html).`, + }, + icon: 'bug' as MynahIcons, + }, + }, + tabID + ) + } +} + +export class ChatInputEnabledMessage extends UiMessage { + override type: ScanMessageType = 'chatInputEnabledMessage' + + constructor( + tabID: string, + readonly enabled: boolean + ) { + super(tabID) + } +} + +export class UpdatePlaceholderMessage extends UiMessage { + readonly newPlaceholder: string + override type: ScanMessageType = 'updatePlaceholderMessage' + + constructor(tabID: string, newPlaceholder: string) { + super(tabID) + this.newPlaceholder = newPlaceholder + } +} + +export class UpdatePromptProgressMessage extends UiMessage { + readonly progressField: ProgressField | null + override type: ScanMessageType = 'updatePromptProgress' + constructor(tabID: string, progressField: ProgressField | null) { + super(tabID) + this.progressField = progressField + } +} + +export class ErrorMessage extends UiMessage { + override type: ScanMessageType = 'errorMessage' + constructor( + readonly title: string, + readonly message: string, + tabID: string + ) { + super(tabID) + } +} + +export class ChatPrompt extends UiMessage { + readonly message: string | undefined + readonly messageType = 'system-prompt' + override type: ScanMessageType = 'chatPrompt' + constructor(message: string | undefined, tabID: string) { + super(tabID) + this.message = message + } +} + +export class AppToWebViewMessageDispatcher { + constructor(private readonly appsToWebViewMessagePublisher: MessagePublisher) {} + + public sendChatMessage(message: ChatMessage) { + this.appsToWebViewMessagePublisher.publish(message) + } + + public sendUpdatePlaceholder(message: UpdatePlaceholderMessage) { + this.appsToWebViewMessagePublisher.publish(message) + } + + public sendUpdatePromptProgress(message: UpdatePromptProgressMessage) { + this.appsToWebViewMessagePublisher.publish(message) + } + + public sendAuthenticationUpdate(message: AuthenticationUpdateMessage) { + this.appsToWebViewMessagePublisher.publish(message) + } + + public sendAuthNeededExceptionMessage(message: AuthNeededException) { + this.appsToWebViewMessagePublisher.publish(message) + } + + public sendChatInputEnabled(message: ChatInputEnabledMessage) { + this.appsToWebViewMessagePublisher.publish(message) + } + + public sendErrorMessage(message: ErrorMessage) { + this.appsToWebViewMessagePublisher.publish(message) + } + + public sendPromptMessage(message: ChatPrompt) { + this.appsToWebViewMessagePublisher.publish(message) + } +} diff --git a/packages/amazonq/src/app/amazonqScan/index.ts b/packages/amazonq/src/app/amazonqScan/index.ts new file mode 100644 index 00000000000..c195193740b --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/index.ts @@ -0,0 +1,7 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export { default as MessengerUtils } from './chat/controller/messenger/messengerUtils' +export { init as scanChatAppInit } from './app' diff --git a/packages/amazonq/src/app/amazonqScan/models/constants.ts b/packages/amazonq/src/app/amazonqScan/models/constants.ts new file mode 100644 index 00000000000..4180b130b78 --- /dev/null +++ b/packages/amazonq/src/app/amazonqScan/models/constants.ts @@ -0,0 +1,101 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { ProgressField, MynahIcons, ChatItemButton } from '@aws/mynah-ui' +import { AggregatedCodeScanIssue, CodeAnalysisScope, SecurityScanStep, severities } from 'aws-core-vscode/codewhisperer' +import { i18n } from 'aws-core-vscode/shared' + +// For uniquely identifiying which chat messages should be routed to Scan +export const scanChat = 'scanChat' + +export enum ScanAction { + RUN_PROJECT_SCAN = 'runProjectScan', + RUN_FILE_SCAN = 'runFileScan', + STOP_PROJECT_SCAN = 'stopProjectScan', + STOP_FILE_SCAN = 'stopFileScan', +} + +export const cancelFileScanButton: ChatItemButton = { + id: ScanAction.STOP_FILE_SCAN, + text: i18n('AWS.generic.cancel'), + icon: 'cancel' as MynahIcons, +} + +export const cancelProjectScanButton: ChatItemButton = { + ...cancelFileScanButton, + id: ScanAction.STOP_PROJECT_SCAN, +} + +export const fileScanProgressField: ProgressField = { + status: 'default', + text: i18n('AWS.amazonq.scans.fileScanInProgress'), + value: -1, + actions: [cancelFileScanButton], +} + +export const projectScanProgressField: ProgressField = { + ...fileScanProgressField, + text: i18n('AWS.amazonq.scans.projectScanInProgress'), + actions: [cancelProjectScanButton], +} + +export const cancellingProgressField: ProgressField = { + status: 'warning', + text: i18n('AWS.generic.cancelling'), + value: -1, + actions: [], +} + +const checkIcons = { + wait: '☐', + current: '☐', + done: '☑', +} +export const scanProgressMessage = ( + currentStep: SecurityScanStep, + scope: CodeAnalysisScope, + fileName?: string +) => `Okay, I'm reviewing ${scope === CodeAnalysisScope.PROJECT ? 'your project' : fileName ? `\`${fileName}\`` : 'your file'} for code issues. + +This may take a few minutes. I'll share my progress here. + +${getIconForStep(SecurityScanStep.CREATE_SCAN_JOB, currentStep)} Initiating code review + +${getIconForStep(SecurityScanStep.POLL_SCAN_STATUS, currentStep)} Reviewing your code + +${getIconForStep(SecurityScanStep.PROCESS_SCAN_RESULTS, currentStep)} Processing review results +` + +export const scanSummaryMessage = ( + scope: CodeAnalysisScope, + securityRecommendationCollection: AggregatedCodeScanIssue[] +) => { + const severityCounts = securityRecommendationCollection.reduce( + (accumulator, current) => ({ + ...Object.fromEntries( + severities.map((severity) => [ + severity, + accumulator[severity] + + current.issues.filter((issue) => issue.severity === severity && issue.visible).length, + ]) + ), + }), + Object.fromEntries(severities.map((severity) => [severity, 0])) + ) + return `I completed the code review. I found the following issues in your ${scope === CodeAnalysisScope.PROJECT ? 'workspace' : 'file'}: +${Object.entries(severityCounts) + .map(([severity, count]) => `- ${severity}: \`${count} ${count === 1 ? 'issue' : 'issues'}\``) + .join('\n')} +` +} + +const getIconForStep = (targetStep: number, currentStep: number) => { + return currentStep === targetStep + ? checkIcons.current + : currentStep > targetStep + ? checkIcons.done + : checkIcons.wait +} + +export const codeReviewInChat = true diff --git a/packages/amazonq/src/app/chat/activation.ts b/packages/amazonq/src/app/chat/activation.ts new file mode 100644 index 00000000000..7517d668497 --- /dev/null +++ b/packages/amazonq/src/app/chat/activation.ts @@ -0,0 +1,74 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { ExtensionContext } from 'vscode' +import { telemetry } from 'aws-core-vscode/telemetry' +import { AuthUtil } from 'aws-core-vscode/codewhisperer' +import { Commands, getLogger, placeholder } from 'aws-core-vscode/shared' +import * as amazonq from 'aws-core-vscode/amazonq' + +export async function activate(context: ExtensionContext) { + const appInitContext = amazonq.DefaultAmazonQAppInitContext.instance + await amazonq.TryChatCodeLensProvider.register(appInitContext.onDidChangeAmazonQVisibility.event) + + context.subscriptions.push( + amazonq.focusAmazonQChatWalkthrough.register(), + amazonq.walkthroughInlineSuggestionsExample.register(), + amazonq.openAmazonQWalkthrough.register(), + amazonq.listCodeWhispererCommandsWalkthrough.register(), + amazonq.focusAmazonQPanel.register(), + amazonq.focusAmazonQPanelKeybinding.register(), + amazonq.tryChatCodeLensCommand.register() + ) + + Commands.register('aws.amazonq.learnMore', () => { + void vscode.env.openExternal(vscode.Uri.parse(amazonq.amazonQHelpUrl)) + }) + + void setupAuthNotification() +} + +/** + * Display a notification to user for Log In. + * + * Authentication Notification is displayed when: + * - User is not authenticated + * - Once every session + * + */ +async function setupAuthNotification() { + let notificationDisplayed = false // Auth Notification should be displayed only once. + await tryShowNotification() + + async function tryShowNotification() { + // Do not show the notification if the IDE starts and user is already authenticated. + if (AuthUtil.instance.isConnected()) { + notificationDisplayed = true + } + + if (notificationDisplayed) { + return + } + + const source = 'authNotification' + const buttonAction = 'Sign In' + notificationDisplayed = true + + telemetry.toolkit_showNotification.emit({ + component: 'editor', + id: source, + reason: 'notLoggedIn', + result: 'Succeeded', + }) + const selection = await vscode.window.showWarningMessage('Start using Amazon Q', buttonAction) + + if (selection === buttonAction) { + amazonq.focusAmazonQPanel.execute(placeholder, source).catch((e) => { + getLogger().error('focusAmazonQPanel failed: %s', e) + }) + } + } +} diff --git a/packages/amazonq/src/app/chat/node/activateAgents.ts b/packages/amazonq/src/app/chat/node/activateAgents.ts new file mode 100644 index 00000000000..cd0309d7f2d --- /dev/null +++ b/packages/amazonq/src/app/chat/node/activateAgents.ts @@ -0,0 +1,16 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as amazonqNode from 'aws-core-vscode/amazonq/node' +import { scanChatAppInit } from '../../amazonqScan' +import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq' + +export function activateAgents() { + const appInitContext = DefaultAmazonQAppInitContext.instance + + amazonqNode.cwChatAppInit(appInitContext) + amazonqNode.gumbyChatAppInit(appInitContext) + scanChatAppInit(appInitContext) +} diff --git a/packages/amazonq/src/app/inline/EditRendering/diffUtils.ts b/packages/amazonq/src/app/inline/EditRendering/diffUtils.ts new file mode 100644 index 00000000000..7f9ca54b6aa --- /dev/null +++ b/packages/amazonq/src/app/inline/EditRendering/diffUtils.ts @@ -0,0 +1,93 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +// TODO: deprecate this file in favor of core/shared/utils/diffUtils +import { applyPatch } from 'diff' + +export type LineDiff = + | { type: 'added'; content: string } + | { type: 'removed'; content: string } + | { type: 'modified'; before: string; after: string } + +/** + * Apply a unified diff to original code to generate modified code + * @param originalCode The original code as a string + * @param unifiedDiff The unified diff content + * @returns The modified code after applying the diff + */ +export function applyUnifiedDiff(docText: string, unifiedDiff: string): string { + try { + // First try the standard diff package + try { + const result = applyPatch(docText, unifiedDiff) + if (result !== false) { + return result + } + } catch (error) {} + + // Parse the unified diff to extract the changes + const diffLines = unifiedDiff.split('\n') + let result = docText + + // Find all hunks in the diff + const hunkStarts = diffLines + .map((line, index) => (line.startsWith('@@ ') ? index : -1)) + .filter((index) => index !== -1) + + // Process each hunk + for (const hunkStart of hunkStarts) { + // Parse the hunk header + const hunkHeader = diffLines[hunkStart] + const match = hunkHeader.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/) + + if (!match) { + continue + } + + const oldStart = parseInt(match[1]) + const oldLines = parseInt(match[2]) + + // Extract the content lines for this hunk + let i = hunkStart + 1 + const contentLines = [] + while (i < diffLines.length && !diffLines[i].startsWith('@@')) { + contentLines.push(diffLines[i]) + i++ + } + + // Build the old and new text + let oldText = '' + let newText = '' + + for (const line of contentLines) { + if (line.startsWith('-')) { + oldText += line.substring(1) + '\n' + } else if (line.startsWith('+')) { + newText += line.substring(1) + '\n' + } else if (line.startsWith(' ')) { + oldText += line.substring(1) + '\n' + newText += line.substring(1) + '\n' + } + } + + // Remove trailing newline if it was added + oldText = oldText.replace(/\n$/, '') + newText = newText.replace(/\n$/, '') + + // Find the text to replace in the document + const docLines = docText.split('\n') + const startLine = oldStart - 1 // Convert to 0-based + const endLine = startLine + oldLines + + // Extract the text that should be replaced + const textToReplace = docLines.slice(startLine, endLine).join('\n') + + // Replace the text + result = result.replace(textToReplace, newText) + } + return result + } catch (error) { + return docText // Return original text if all methods fail + } +} diff --git a/packages/amazonq/src/app/inline/EditRendering/displayImage.ts b/packages/amazonq/src/app/inline/EditRendering/displayImage.ts new file mode 100644 index 00000000000..035621f0ba4 --- /dev/null +++ b/packages/amazonq/src/app/inline/EditRendering/displayImage.ts @@ -0,0 +1,507 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getContext, getLogger, setContext } from 'aws-core-vscode/shared' +import * as vscode from 'vscode' +import { applyPatch, diffLines } from 'diff' +import { BaseLanguageClient } from 'vscode-languageclient' +import { CodeWhispererSession } from '../sessionManager' +import { LogInlineCompletionSessionResultsParams } from '@aws/language-server-runtimes/protocol' +import { InlineCompletionItemWithReferences } from '@aws/language-server-runtimes/protocol' +import path from 'path' +import { imageVerticalOffset } from './svgGenerator' +import { EditSuggestionState } from '../editSuggestionState' +import type { AmazonQInlineCompletionItemProvider } from '../completion' +import { vsCodeState } from 'aws-core-vscode/codewhisperer' + +const autoRejectEditCursorDistance = 25 +const autoDiscardEditCursorDistance = 10 + +export class EditDecorationManager { + private imageDecorationType: vscode.TextEditorDecorationType + private removedCodeDecorationType: vscode.TextEditorDecorationType + private currentImageDecoration: vscode.DecorationOptions | undefined + private currentRemovedCodeDecorations: vscode.DecorationOptions[] = [] + private acceptHandler: (() => void) | undefined + private rejectHandler: ((isDiscard: boolean) => void) | undefined + + constructor() { + this.registerCommandHandlers() + this.imageDecorationType = vscode.window.createTextEditorDecorationType({ + isWholeLine: true, + }) + + this.removedCodeDecorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: 'rgba(255, 0, 0, 0.2)', + }) + } + + private imageToDecoration(image: vscode.Uri, range: vscode.Range) { + return { + range, + renderOptions: { + after: { + contentIconPath: image, + verticalAlign: 'text-top', + width: '100%', + height: 'auto', + margin: '1px 0', + }, + }, + hoverMessage: new vscode.MarkdownString('Edit suggestion. Press [Tab] to accept or [Esc] to reject.'), + } + } + + /** + * Highlights code that will be removed using the provided highlight ranges + * @param editor The active text editor + * @param startLine The line where the edit starts + * @param highlightRanges Array of ranges specifying which parts to highlight + * @returns Array of decoration options + */ + private highlightRemovedLines( + editor: vscode.TextEditor, + startLine: number, + highlightRanges: Array<{ line: number; start: number; end: number }> + ): vscode.DecorationOptions[] { + const decorations: vscode.DecorationOptions[] = [] + + // Group ranges by line for more efficient processing + const rangesByLine = new Map>() + + // Process each range and adjust line numbers relative to document + for (const range of highlightRanges) { + const documentLine = startLine + range.line + + // Skip if line is out of bounds + if (documentLine >= editor.document.lineCount) { + continue + } + + // Add to ranges map, grouped by line + if (!rangesByLine.has(documentLine)) { + rangesByLine.set(documentLine, []) + } + rangesByLine.get(documentLine)!.push({ + start: range.start, + end: range.end, + }) + } + + // Process each line with ranges + for (const [lineNumber, ranges] of rangesByLine.entries()) { + const lineLength = editor.document.lineAt(lineNumber).text.length + + if (ranges.length === 0) { + continue + } + + // Check if we should highlight the entire line + if (ranges.length === 1 && ranges[0].start === 0 && ranges[0].end >= lineLength) { + // Highlight entire line + const range = new vscode.Range( + new vscode.Position(lineNumber, 0), + new vscode.Position(lineNumber, lineLength) + ) + decorations.push({ range }) + } else { + // Create individual decorations for each range on this line + for (const range of ranges) { + const end = Math.min(range.end, lineLength) + if (range.start < end) { + const vsRange = new vscode.Range( + new vscode.Position(lineNumber, range.start), + new vscode.Position(lineNumber, end) + ) + decorations.push({ range: vsRange }) + } + } + } + } + + return decorations + } + + /** + * Displays an edit suggestion as an SVG image in the editor and highlights removed code + */ + public async displayEditSuggestion( + editor: vscode.TextEditor, + svgImage: vscode.Uri, + startLine: number, + onAccept: () => Promise, + onReject: (isDiscard: boolean) => Promise, + originalCode: string, + newCode: string, + originalCodeHighlightRanges: Array<{ line: number; start: number; end: number }> + ): Promise { + // Clear old decorations but don't reset state (state is already set in displaySvgDecoration) + editor.setDecorations(this.imageDecorationType, []) + editor.setDecorations(this.removedCodeDecorationType, []) + this.currentImageDecoration = undefined + this.currentRemovedCodeDecorations = [] + + this.acceptHandler = onAccept + this.rejectHandler = onReject + + // Get the line text to determine the end position + const lineText = editor.document.lineAt(Math.max(0, startLine - imageVerticalOffset)).text + const endPosition = new vscode.Position(Math.max(0, startLine - imageVerticalOffset), lineText.length) + const range = new vscode.Range(endPosition, endPosition) + + this.currentImageDecoration = this.imageToDecoration(svgImage, range) + + // Apply image decoration + editor.setDecorations(this.imageDecorationType, [this.currentImageDecoration]) + + // Highlight removed code with red background + this.currentRemovedCodeDecorations = this.highlightRemovedLines(editor, startLine, originalCodeHighlightRanges) + editor.setDecorations(this.removedCodeDecorationType, this.currentRemovedCodeDecorations) + } + + /** + * Clears all edit suggestion decorations + */ + public async clearDecorations(editor: vscode.TextEditor): Promise { + editor.setDecorations(this.imageDecorationType, []) + editor.setDecorations(this.removedCodeDecorationType, []) + this.currentImageDecoration = undefined + this.currentRemovedCodeDecorations = [] + this.acceptHandler = undefined + this.rejectHandler = undefined + await setContext('aws.amazonq.editSuggestionActive' as any, false) + EditSuggestionState.setEditSuggestionActive(false) + } + + /** + * Registers command handlers for accepting/rejecting suggestions + */ + public registerCommandHandlers(): void { + // Register Tab key handler for accepting suggestion + vscode.commands.registerCommand('aws.amazonq.inline.acceptEdit', () => { + if (this.acceptHandler) { + this.acceptHandler() + } + }) + + // Register Esc key handler for rejecting suggestion + vscode.commands.registerCommand('aws.amazonq.inline.rejectEdit', (isDiscard: boolean = false) => { + if (this.rejectHandler) { + this.rejectHandler(isDiscard) + } + }) + } + + /** + * Disposes resources + */ + public dispose(): void { + this.imageDecorationType.dispose() + this.removedCodeDecorationType.dispose() + } + + // Use process-wide singleton to prevent multiple instances on Windows + static readonly decorationManagerKey = Symbol.for('aws.amazonq.decorationManager') + + static getDecorationManager(): EditDecorationManager { + const globalObj = global as any + if (!globalObj[this.decorationManagerKey]) { + globalObj[this.decorationManagerKey] = new EditDecorationManager() + } + return globalObj[this.decorationManagerKey] + } +} + +export const decorationManager = EditDecorationManager.getDecorationManager() + +/** + * Function to replace editor's content with new code + */ +async function replaceEditorContent(editor: vscode.TextEditor, newCode: string): Promise { + const document = editor.document + const fullRange = new vscode.Range( + 0, + 0, + document.lineCount - 1, + document.lineAt(document.lineCount - 1).text.length + ) + + await editor.edit((editBuilder) => { + editBuilder.replace(fullRange, newCode) + }) +} + +/** + * Calculates the end position of the actual edited content by finding the last changed part + */ +function getEndOfEditPosition(originalCode: string, newCode: string): vscode.Position { + const changes = diffLines(originalCode, newCode) + let lineOffset = 0 + + // Track the end position of the last added chunk + let lastChangeEndLine = 0 + let lastChangeEndColumn = 0 + let foundAddedContent = false + + for (const part of changes) { + if (part.added) { + foundAddedContent = true + + // Calculate lines in this added part + const lines = part.value.split('\n') + const linesCount = lines.length + + // Update position to the end of this added chunk + lastChangeEndLine = lineOffset + linesCount - 1 + + // Get the length of the last line in this added chunk + lastChangeEndColumn = lines[linesCount - 1].length + } + + // Update line offset (skip removed parts) + if (!part.removed) { + const partLineCount = part.value.split('\n').length + lineOffset += partLineCount - 1 + } + } + + // If we found added content, return position at the end of the last addition + if (foundAddedContent) { + return new vscode.Position(lastChangeEndLine, lastChangeEndColumn) + } + + // Fallback to current cursor position if no changes were found + const editor = vscode.window.activeTextEditor + return editor ? editor.selection.active : new vscode.Position(0, 0) +} + +/** + * Helper function to create discard telemetry params + */ +function createDiscardTelemetryParams( + session: CodeWhispererSession, + item: InlineCompletionItemWithReferences +): LogInlineCompletionSessionResultsParams { + return { + sessionId: session.sessionId, + completionSessionResult: { + [item.itemId]: { + seen: false, + accepted: false, + discarded: true, + }, + }, + totalSessionDisplayTime: Date.now() - session.requestStartTime, + firstCompletionDisplayLatency: session.firstCompletionDisplayLatency, + isInlineEdit: true, + } +} + +/** + * Helper function to display SVG decorations + */ +export async function displaySvgDecoration( + editor: vscode.TextEditor, + svgImage: vscode.Uri, + startLine: number, + newCode: string, + originalCodeHighlightRanges: Array<{ line: number; start: number; end: number }>, + session: CodeWhispererSession, + languageClient: BaseLanguageClient, + item: InlineCompletionItemWithReferences, + inlineCompletionProvider?: AmazonQInlineCompletionItemProvider +) { + function logSuggestionFailure(type: 'DISCARD' | 'REJECT', reason: string, suggestionContent: string) { + getLogger('nextEditPrediction').debug( + `Auto ${type} edit suggestion with reason=${reason}, suggetion: ${suggestionContent}` + ) + } + // Check if edit is too far from current cursor position + const currentCursorLine = editor.selection.active.line + if (Math.abs(startLine - currentCursorLine) >= autoDiscardEditCursorDistance) { + // Emit DISCARD telemetry for edit suggestion that can't be shown because the suggestion is too far away + const params = createDiscardTelemetryParams(session, item) + void languageClient.sendNotification('aws/logInlineCompletionSessionResults', params) + logSuggestionFailure('DISCARD', 'cursor is too far away', item.insertText as string) + return + } + + const originalCode = editor.document.getText() + + // Set edit state immediately to prevent race condition with completion requests + await setContext('aws.amazonq.editSuggestionActive' as any, true) + EditSuggestionState.setEditSuggestionActive(true) + + // Check if a completion suggestion is currently active - if so, discard edit suggestion + if (inlineCompletionProvider && (await inlineCompletionProvider.isCompletionActive())) { + // Clean up state since we're not showing the edit + await setContext('aws.amazonq.editSuggestionActive' as any, false) + EditSuggestionState.setEditSuggestionActive(false) + + // Emit DISCARD telemetry for edit suggestion that can't be shown due to active completion + const params = createDiscardTelemetryParams(session, item) + void languageClient.sendNotification('aws/logInlineCompletionSessionResults', params) + logSuggestionFailure('DISCARD', 'Conflicting active inline completion', item.insertText as string) + return + } + + const isPatchValid = applyPatch(editor.document.getText(), item.insertText as string) + if (!isPatchValid) { + // Clean up state since we're not showing the edit + await setContext('aws.amazonq.editSuggestionActive' as any, false) + EditSuggestionState.setEditSuggestionActive(false) + + const params = createDiscardTelemetryParams(session, item) + // TODO: this session is closed on flare side hence discarded is not emitted in flare + void languageClient.sendNotification('aws/logInlineCompletionSessionResults', params) + logSuggestionFailure('DISCARD', 'Invalid patch', item.insertText as string) + return + } + const documentChangeListener = vscode.workspace.onDidChangeTextDocument((e) => { + if (e.contentChanges.length <= 0) { + return + } + if (e.document !== editor.document) { + return + } + if (vsCodeState.isCodeWhispererEditing) { + return + } + if (getContext('aws.amazonq.editSuggestionActive') === false) { + return + } + + const isPatchValid = applyPatch(e.document.getText(), item.insertText as string) + if (!isPatchValid) { + logSuggestionFailure('REJECT', 'Invalid patch due to document change', item.insertText as string) + void vscode.commands.executeCommand('aws.amazonq.inline.rejectEdit') + } + }) + const cursorChangeListener = vscode.window.onDidChangeTextEditorSelection((e) => { + if (!EditSuggestionState.isEditSuggestionActive()) { + return + } + if (e.textEditor !== editor) { + return + } + const currentPosition = e.selections[0].active + const distance = Math.abs(currentPosition.line - startLine) + if (distance > autoRejectEditCursorDistance) { + logSuggestionFailure( + 'REJECT', + `cursor position move too far away off ${autoRejectEditCursorDistance} lines`, + item.insertText as string + ) + void vscode.commands.executeCommand('aws.amazonq.inline.rejectEdit') + } + }) + await decorationManager.displayEditSuggestion( + editor, + svgImage, + startLine, + async () => { + // Handle accept + getLogger().info('Edit suggestion accepted') + + // Replace content + try { + vsCodeState.isCodeWhispererEditing = true + await replaceEditorContent(editor, newCode) + } finally { + vsCodeState.isCodeWhispererEditing = false + } + + // Move cursor to end of the actual changed content + const endPosition = getEndOfEditPosition(originalCode, newCode) + editor.selection = new vscode.Selection(endPosition, endPosition) + + await decorationManager.clearDecorations(editor) + documentChangeListener.dispose() + cursorChangeListener.dispose() + const params: LogInlineCompletionSessionResultsParams = { + sessionId: session.sessionId, + completionSessionResult: { + [item.itemId]: { + seen: true, + accepted: true, + discarded: false, + }, + }, + totalSessionDisplayTime: Date.now() - session.requestStartTime, + firstCompletionDisplayLatency: session.firstCompletionDisplayLatency, + isInlineEdit: true, + } + void languageClient.sendNotification('aws/logInlineCompletionSessionResults', params) + session.triggerOnAcceptance = true + }, + async (isDiscard: boolean) => { + // Handle reject + if (isDiscard) { + getLogger().info('Edit suggestion discarded') + } else { + getLogger().info('Edit suggestion rejected') + } + await decorationManager.clearDecorations(editor) + documentChangeListener.dispose() + cursorChangeListener.dispose() + const suggestionState = isDiscard + ? { + seen: false, + accepted: false, + discarded: true, + } + : { + seen: true, + accepted: false, + discarded: false, + } + const params: LogInlineCompletionSessionResultsParams = { + sessionId: session.sessionId, + completionSessionResult: { + [item.itemId]: suggestionState, + }, + totalSessionDisplayTime: Date.now() - session.requestStartTime, + firstCompletionDisplayLatency: session.firstCompletionDisplayLatency, + isInlineEdit: true, + } + void languageClient.sendNotification('aws/logInlineCompletionSessionResults', params) + }, + originalCode, + newCode, + originalCodeHighlightRanges + ) +} + +export function deactivate() { + decorationManager.dispose() +} + +let decorationType: vscode.TextEditorDecorationType | undefined + +export function decorateLinesWithGutterIcon(lineNumbers: number[]) { + const editor = vscode.window.activeTextEditor + if (!editor) { + return + } + + // Dispose previous decoration if it exists + if (decorationType) { + decorationType.dispose() + } + + // Create a new gutter decoration with a small green dot + decorationType = vscode.window.createTextEditorDecorationType({ + gutterIconPath: vscode.Uri.file( + path.join(__dirname, 'media', 'green-dot.svg') // put your svg file in a `media` folder + ), + gutterIconSize: 'contain', + }) + + const decorations: vscode.DecorationOptions[] = lineNumbers.map((line) => ({ + range: new vscode.Range(new vscode.Position(line, 0), new vscode.Position(line, 0)), + })) + + editor.setDecorations(decorationType, decorations) +} diff --git a/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts new file mode 100644 index 00000000000..497239a6c96 --- /dev/null +++ b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts @@ -0,0 +1,59 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { displaySvgDecoration } from './displayImage' +import { SvgGenerationService } from './svgGenerator' +import { getLogger } from 'aws-core-vscode/shared' +import { BaseLanguageClient } from 'vscode-languageclient' +import { InlineCompletionItemWithReferences } from '@aws/language-server-runtimes/protocol' +import { CodeWhispererSession } from '../sessionManager' +import type { AmazonQInlineCompletionItemProvider } from '../completion' + +export async function showEdits( + item: InlineCompletionItemWithReferences, + editor: vscode.TextEditor | undefined, + session: CodeWhispererSession, + languageClient: BaseLanguageClient, + inlineCompletionProvider?: AmazonQInlineCompletionItemProvider +) { + if (!editor) { + return + } + try { + const svgGenerationService = new SvgGenerationService() + // Generate your SVG image with the file contents + const currentFile = editor.document.uri.fsPath + const { svgImage, startLine, newCode, originalCodeHighlightRange } = await svgGenerationService.generateDiffSvg( + currentFile, + item.insertText as string + ) + + // TODO: To investigate why it fails and patch [generateDiffSvg] + if (newCode.length === 0) { + getLogger('nextEditPrediction').warn('not able to apply provided edit suggestion, skip rendering') + return + } + + if (svgImage) { + // display the SVG image + await displaySvgDecoration( + editor, + svgImage, + startLine, + newCode, + originalCodeHighlightRange, + session, + languageClient, + item, + inlineCompletionProvider + ) + } else { + getLogger('nextEditPrediction').error('SVG image generation returned an empty result.') + } + } catch (error) { + getLogger('nextEditPrediction').error(`Error generating SVG image: ${error}`) + } +} diff --git a/packages/amazonq/src/app/inline/EditRendering/stringUtils.ts b/packages/amazonq/src/app/inline/EditRendering/stringUtils.ts new file mode 100644 index 00000000000..b8c9a52d052 --- /dev/null +++ b/packages/amazonq/src/app/inline/EditRendering/stringUtils.ts @@ -0,0 +1,28 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Strips common indentation from each line of code that may contain HTML tags + * @param lines Array of code lines (may contain HTML tags) + * @returns Array of code lines with common indentation removed + */ +export function stripCommonIndentation(lines: string[]): string[] { + if (lines.length === 0) { + return lines + } + const removeFirstTag = (line: string) => line.replace(/^<[^>]*>/, '') + const getLeadingWhitespace = (text: string) => text.match(/^\s*/)?.[0] || '' + + // Find minimum indentation across all lines + const minIndentLength = Math.min(...lines.map((line) => getLeadingWhitespace(removeFirstTag(line)).length)) + + // Remove common indentation from each line + return lines.map((line) => { + const firstTagRemovedLine = removeFirstTag(line) + const leadingWhitespace = getLeadingWhitespace(firstTagRemovedLine) + const reducedWhitespace = leadingWhitespace.substring(minIndentLength) + return line.replace(leadingWhitespace, reducedWhitespace) + }) +} diff --git a/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts b/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts new file mode 100644 index 00000000000..59752a7b08a --- /dev/null +++ b/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts @@ -0,0 +1,519 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { diffWordsWithSpace, diffLines } from 'diff' +import * as vscode from 'vscode' +import { ToolkitError, getLogger } from 'aws-core-vscode/shared' +import { diffUtilities } from 'aws-core-vscode/shared' +import { stripCommonIndentation } from './stringUtils' +type Range = { line: number; start: number; end: number } + +const logger = getLogger('nextEditPrediction') +export const imageVerticalOffset = 1 +export const emptyDiffSvg = { + svgImage: vscode.Uri.parse(''), + startLine: 0, + newCode: '', + originalCodeHighlightRange: [], +} + +const defaultLineHighlightLength = 4 + +export class SvgGenerationService { + /** + * Generates an SVG image representing a code diff + * @param originalCode The original code + * @param newCode The new code with editsss + * @param theme The editor theme information + * @param offSet The margin to add to the left of the image + */ + public async generateDiffSvg( + filePath: string, + udiff: string + ): Promise<{ + svgImage: vscode.Uri + startLine: number + newCode: string + originalCodeHighlightRange: Range[] + }> { + const textDoc = await vscode.workspace.openTextDocument(filePath) + const originalCode = textDoc.getText().replaceAll('\r\n', '\n') + if (originalCode === '') { + logger.error(`udiff format error`) + throw new ToolkitError('udiff format error') + } + const newCode = await diffUtilities.getPatchedCode(filePath, udiff) + + const { createSVGWindow } = await import('svgdom') + + const svgjs = await import('@svgdotjs/svg.js') + const SVG = svgjs.SVG + const registerWindow = svgjs.registerWindow + + // Get editor theme info + const currentTheme = this.getEditorTheme() + + // Get edit diffs with highlight + const { addedLines, removedLines } = this.getEditedLinesFromCode(originalCode, newCode) + + const modifiedLines = diffUtilities.getModifiedLinesFromCode(addedLines, removedLines) + // TODO remove + // eslint-disable-next-line aws-toolkits/no-json-stringify-in-log + logger.info(`Line mapping: ${JSON.stringify(modifiedLines)}`) + + // Calculate dimensions based on code content + const { offset, editStartLine, isPositionValid } = this.calculatePosition( + originalCode.split('\n'), + newCode.split('\n'), + addedLines, + currentTheme + ) + + // if the position for the EDITS suggestion is not valid (there is no difference between new + // and current code content), return EMPTY_DIFF_SVG and skip the suggestion. + if (!isPositionValid) { + return emptyDiffSvg + } + + const highlightRanges = this.generateHighlightRanges(removedLines, addedLines, modifiedLines) + const diffAddedWithHighlight = this.getHighlightEdit(addedLines, highlightRanges.addedRanges) + const normalizedDiffLines = stripCommonIndentation(diffAddedWithHighlight) + + // Create SVG window, document, and container + const window = createSVGWindow() + const document = window.document + registerWindow(window, document) + const draw = SVG(document.documentElement) as any + + const { width, height } = this.calculateDimensions(addedLines, currentTheme) + draw.size(width + offset, height) + + // Generate CSS for syntax highlighting HTML content based on theme + const styles = this.generateStyles(currentTheme) + const htmlContent = this.generateHtmlContent(normalizedDiffLines, styles, offset) + + // Create foreignObject to embed HTML + const foreignObject = draw.foreignObject(width + offset, height) + foreignObject.node.innerHTML = htmlContent.trim() + + const svgData = draw.svg() + const svgResult = `data:image/svg+xml;base64,${Buffer.from(svgData).toString('base64')}` + + return { + svgImage: vscode.Uri.parse(svgResult), + startLine: editStartLine, + newCode: newCode, + originalCodeHighlightRange: highlightRanges.removedRanges, + } + } + + private calculateDimensions(newLines: string[], currentTheme: editorThemeInfo): { width: number; height: number } { + // Calculate appropriate width and height based on diff content + const maxLineLength = Math.max(...newLines.map((line) => line.length)) + + const headerFrontSize = Math.ceil(currentTheme.fontSize * 0.66) + + // Estimate width based on character count and font size + const width = Math.max(41 * headerFrontSize * 0.7, maxLineLength * currentTheme.fontSize * 0.7) + + // Calculate height based on diff line count and line height + const totalLines = newLines.length + 1 // +1 for header + const height = totalLines * currentTheme.lingHeight + 25 // +25 for padding + + return { width, height } + } + + private generateStyles(theme: editorThemeInfo): string { + // Generate CSS styles based on editor theme + const fontSize = theme.fontSize + const headerFrontSize = Math.ceil(fontSize * 0.66) + const lineHeight = theme.lingHeight + const foreground = theme.foreground + const bordeColor = 'rgba(212, 212, 212, 0.5)' + const background = theme.background || '#1e1e1e' + const diffRemoved = theme.diffRemoved || 'rgba(255, 0, 0, 0.2)' + const diffAdded = 'rgba(72, 128, 72, 0.52)' + return ` + .code-container { + font-family: ${'monospace'}; + color: ${foreground}; + font-size: ${fontSize}px; + line-height: ${lineHeight}px; + background-color: ${background}; + border: 1px solid ${bordeColor}; + border-radius: 0px; + padding-top: 3px; + padding-bottom: 5px; + padding-left: 10px; + } + .diff-header { + color: ${theme.foreground || '#d4d4d4'}; + margin: 0; + font-size: ${headerFrontSize}px; + padding: 0px; + } + .diff-removed { + background-color: ${diffRemoved}; + white-space: pre-wrap; /* Preserve whitespace */ + text-decoration: line-through; + opacity: 0.7; + } + .diff-changed { + white-space: pre-wrap; /* Preserve whitespace */ + background-color: ${diffAdded}; + } + .diff-unchanged { + white-space: pre-wrap; /* Preserve indentation for unchanged lines */ + } + ` + } + + private generateHtmlContent(diffLines: string[], styles: string, offSet: number): string { + return ` +
+ +
+
Q: Press [Tab] to accept or [Esc] to reject:
+ ${diffLines.map((line) => `
${line}
`).join('')} +
+
+ ` + } + + /** + * Extract added and removed lines by comparing original and new code + * @param originalCode The original code string + * @param newCode The new code string + * @returns Object containing arrays of added and removed lines + */ + private getEditedLinesFromCode( + originalCode: string, + newCode: string + ): { addedLines: string[]; removedLines: string[] } { + const addedLines: string[] = [] + const removedLines: string[] = [] + + const changes = diffLines(originalCode, newCode) + + for (const change of changes) { + if (change.added) { + addedLines.push(...change.value.split('\n').filter((line) => line.length > 0)) + } else if (change.removed) { + removedLines.push(...change.value.split('\n').filter((line) => line.length > 0)) + } + } + + return { addedLines, removedLines } + } + + /** + * Applies highlighting to code lines based on the specified ranges + * @param newLines Array of code lines to highlight + * @param highlightRanges Array of ranges specifying which parts of the lines to highlight + * @returns Array of HTML strings with appropriate spans for highlighting + */ + private getHighlightEdit(newLines: string[], highlightRanges: Range[]): string[] { + const result: string[] = [] + + // Group ranges by line for easier lookup + const rangesByLine = new Map() + for (const range of highlightRanges) { + if (!rangesByLine.has(range.line)) { + rangesByLine.set(range.line, []) + } + rangesByLine.get(range.line)!.push(range) + } + + // Process each line of code + for (let lineIndex = 0; lineIndex < newLines.length; lineIndex++) { + const line = newLines[lineIndex] + // Get ranges for this line + const lineRanges = rangesByLine.get(lineIndex) || [] + + // If no ranges for this line, leave it as-is with HTML escaping + if (lineRanges.length === 0) { + result.push(`${this.escapeHtml(line)}`) + continue + } + + // Sort ranges by start position to ensure correct ordering + lineRanges.sort((a, b) => a.start - b.start) + + // Build the highlighted line + let highlightedLine = '' + let currentPos = 0 + + for (const range of lineRanges) { + // Add text before the current range (with HTML escaping) + if (range.start > currentPos) { + const beforeText = line.substring(currentPos, range.start) + highlightedLine += `${this.escapeHtml(beforeText)}` + } + + // Add the highlighted part (with HTML escaping) + const highlightedText = line.substring(range.start, range.end) + highlightedLine += `${this.escapeHtml(highlightedText)}` + + // Update current position + currentPos = range.end + } + + // Add any remaining text after the last range (with HTML escaping) + if (currentPos < line.length) { + const afterText = line.substring(currentPos) + highlightedLine += `${this.escapeHtml(afterText)}` + } + + result.push(highlightedLine) + } + + return result + } + + private getEditorTheme(): editorThemeInfo { + const editorConfig = vscode.workspace.getConfiguration('editor') + const fontSize = editorConfig.get('fontSize', 12) // Default to 12 if not set + const lineHeightSetting = editorConfig.get('lineHeight', 0) // Default to 0 if not set + + /** + * Calculate effective line height, documented as such: + * Use 0 to automatically compute the line height from the font size. + * Values between 0 and 8 will be used as a multiplier with the font size. + * Values greater than or equal to 8 will be used as effective values. + */ + let effectiveLineHeight: number + if (lineHeightSetting > 0 && lineHeightSetting < 8) { + effectiveLineHeight = lineHeightSetting * fontSize + } else if (lineHeightSetting >= 8) { + effectiveLineHeight = lineHeightSetting + } else { + effectiveLineHeight = Math.round(1.5 * fontSize) + } + + const themeName = vscode.workspace.getConfiguration('workbench').get('colorTheme', 'Default') + const themeColors = this.getThemeColors(themeName) + + return { + fontSize: fontSize, + lingHeight: effectiveLineHeight, + ...themeColors, + } + } + + private getThemeColors(themeName: string): { + foreground: string + background: string + diffAdded: string + diffRemoved: string + } { + // Define default dark theme colors + const darkThemeColors = { + foreground: 'rgba(212, 212, 212, 1)', + background: 'rgba(30, 30, 30, 1)', + diffAdded: 'rgba(231, 245, 231, 0.2)', + diffRemoved: 'rgba(255, 0, 0, 0.2)', + } + + // Define default light theme colors + const lightThemeColors = { + foreground: 'rgba(0, 0, 0, 1)', + background: 'rgba(255, 255, 255, 1)', + diffAdded: 'rgba(198, 239, 206, 0.2)', + diffRemoved: 'rgba(255, 199, 206, 0.5)', + } + + // For dark and light modes + const themeNameLower = themeName.toLowerCase() + + if (themeNameLower.includes('dark')) { + return darkThemeColors + } else if (themeNameLower.includes('light')) { + return lightThemeColors + } + + // Define colors for specific themes, add more if needed. + const themeColorMap: { + [key: string]: { foreground: string; background: string; diffAdded: string; diffRemoved: string } + } = { + Abyss: { + foreground: 'rgba(255, 255, 255, 1)', + background: 'rgba(0, 12, 24, 1)', + diffAdded: 'rgba(0, 255, 0, 0.2)', + diffRemoved: 'rgba(255, 0, 0, 0.3)', + }, + Red: { + foreground: 'rgba(255, 0, 0, 1)', + background: 'rgba(51, 0, 0, 1)', + diffAdded: 'rgba(255, 100, 100, 0.2)', + diffRemoved: 'rgba(255, 0, 0, 0.5)', + }, + } + + // Return colors for the specific theme or default to light theme + return themeColorMap[themeName] || lightThemeColors + } + + private calculatePosition( + originalLines: string[], + newLines: string[], + diffLines: string[], + theme: editorThemeInfo + ): { offset: number; editStartLine: number; isPositionValid: boolean } { + // Determine the starting line of the edit in the original file + let editStartLineInOldFile = 0 + const maxLength = Math.min(originalLines.length, newLines.length) + + for (let i = 0; i <= maxLength; i++) { + // if there is no difference between the original lines and the new lines, skip calculating for the start position. + if (i === maxLength && originalLines[i] === newLines[i] && originalLines.length === newLines.length) { + logger.info( + 'There is no difference between current and new code suggestion. Skip calculating for start position.' + ) + return { + offset: 0, + editStartLine: 0, + isPositionValid: false, + } + } + if (originalLines[i] !== newLines[i] || i === maxLength) { + editStartLineInOldFile = i + break + } + } + const shiftedStartLine = Math.max(0, editStartLineInOldFile - imageVerticalOffset) + + // Determine the range to consider + const startLine = shiftedStartLine + const endLine = Math.min(editStartLineInOldFile + diffLines.length, originalLines.length) + + // Find the longest line within the specified range + let maxLineLength = 0 + for (let i = startLine; i <= endLine; i++) { + const lineLength = originalLines[i]?.length || 0 + if (lineLength > maxLineLength) { + maxLineLength = lineLength + } + } + + // Calculate the offset based on the longest line and the starting line length + const startLineLength = originalLines[startLine]?.length || 0 + const offset = (maxLineLength - startLineLength) * theme.fontSize * 0.7 + 10 // padding + + return { offset, editStartLine: editStartLineInOldFile, isPositionValid: true } + } + + private escapeHtml(text: string): string { + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') + } + + /** + * Generates character-level highlight ranges for both original and modified code. + * @param originalCode Array of original code lines + * @param afterCode Array of code lines after modification + * @param modifiedLines Map of original lines to modified lines + * @returns Object containing ranges for original and after code character level highlighting + */ + private generateHighlightRanges( + originalCode: string[], + afterCode: string[], + modifiedLines: Map + ): { removedRanges: Range[]; addedRanges: Range[] } { + const originalRanges: Range[] = [] + const afterRanges: Range[] = [] + + // Create reverse mapping for quicker lookups + const reverseMap = new Map() + for (const [original, modified] of modifiedLines.entries()) { + reverseMap.set(modified, original) + } + + // Process original code lines - produces highlight ranges in current editor text + for (let lineIndex = 0; lineIndex < originalCode.length; lineIndex++) { + const line = originalCode[lineIndex] + + /** + * If [line] is an empty line or only contains whitespace char, [diffWordsWithSpace] will say it's not an "remove", i.e. [part.removed] will be undefined, + * therefore the deletion will not be highlighted. Thus fallback this scenario to highlight the entire line + */ + // If line exists in modifiedLines as a key, process character diffs + if (Array.from(modifiedLines.keys()).includes(line) && line.trim().length > 0) { + const modifiedLine = modifiedLines.get(line)! + const changes = diffWordsWithSpace(line, modifiedLine) + + let charPos = 0 + for (const part of changes) { + if (part.removed) { + originalRanges.push({ + line: lineIndex, + start: charPos, + end: charPos + part.value.length, + }) + } + + if (!part.added) { + charPos += part.value.length + } + } + } else { + // Line doesn't exist in modifiedLines values, highlight entire line + originalRanges.push({ + line: lineIndex, + start: 0, + end: line.length ?? defaultLineHighlightLength, + }) + } + } + + // Process after code lines - used for highlight in SVG image + for (let lineIndex = 0; lineIndex < afterCode.length; lineIndex++) { + const line = afterCode[lineIndex] + + if (reverseMap.has(line)) { + const originalLine = reverseMap.get(line)! + const changes = diffWordsWithSpace(originalLine, line) + + let charPos = 0 + for (const part of changes) { + if (part.added) { + afterRanges.push({ + line: lineIndex, + start: charPos, + end: charPos + part.value.length, + }) + } + + if (!part.removed) { + charPos += part.value.length + } + } + } else { + afterRanges.push({ + line: lineIndex, + start: 0, + end: line.length, + }) + } + } + + return { + removedRanges: originalRanges, + addedRanges: afterRanges, + } + } +} + +interface editorThemeInfo { + fontSize: number + lingHeight: number + foreground?: string + background?: string + diffAdded?: string + diffRemoved?: string +} diff --git a/packages/amazonq/src/app/inline/activation.ts b/packages/amazonq/src/app/inline/activation.ts new file mode 100644 index 00000000000..5a86d340c00 --- /dev/null +++ b/packages/amazonq/src/app/inline/activation.ts @@ -0,0 +1,128 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import vscode from 'vscode' +import { + acceptSuggestion, + AuthUtil, + CodeSuggestionsState, + CodeWhispererCodeCoverageTracker, + CodeWhispererConstants, + CodeWhispererSettings, + ConfigurationEntry, + DefaultCodeWhispererClient, + invokeRecommendation, + isInlineCompletionEnabled, + KeyStrokeHandler, + RecommendationHandler, + runtimeLanguageContext, + TelemetryHelper, + UserWrittenCodeTracker, + vsCodeState, +} from 'aws-core-vscode/codewhisperer' +import { Commands, getLogger, globals, sleep } from 'aws-core-vscode/shared' +import { BaseLanguageClient } from 'vscode-languageclient' + +export async function activate(languageClient: BaseLanguageClient) { + const codewhispererSettings = CodeWhispererSettings.instance + const client = new DefaultCodeWhispererClient() + + if (isInlineCompletionEnabled()) { + await setSubscriptionsforInlineCompletion() + await AuthUtil.instance.setVscodeContextProps() + RecommendationHandler.instance.setLanguageClient(languageClient) + } + + function getAutoTriggerStatus(): boolean { + return CodeSuggestionsState.instance.isSuggestionsEnabled() + } + + async function getConfigEntry(): Promise { + const isShowMethodsEnabled: boolean = + vscode.workspace.getConfiguration('editor').get('suggest.showMethods') || false + const isAutomatedTriggerEnabled: boolean = getAutoTriggerStatus() + const isManualTriggerEnabled: boolean = true + const isSuggestionsWithCodeReferencesEnabled = codewhispererSettings.isSuggestionsWithCodeReferencesEnabled() + + // TODO:remove isManualTriggerEnabled + return { + isShowMethodsEnabled, + isManualTriggerEnabled, + isAutomatedTriggerEnabled, + isSuggestionsWithCodeReferencesEnabled, + } + } + + async function setSubscriptionsforInlineCompletion() { + RecommendationHandler.instance.subscribeSuggestionCommands() + + /** + * Automated trigger + */ + globals.context.subscriptions.push( + acceptSuggestion.register(globals.context), + vscode.window.onDidChangeActiveTextEditor(async (editor) => { + await RecommendationHandler.instance.onEditorChange() + }), + vscode.window.onDidChangeWindowState(async (e) => { + await RecommendationHandler.instance.onFocusChange() + }), + vscode.window.onDidChangeTextEditorSelection(async (e) => { + await RecommendationHandler.instance.onCursorChange(e) + }), + vscode.workspace.onDidChangeTextDocument(async (e) => { + const editor = vscode.window.activeTextEditor + if (!editor) { + return + } + if (e.document !== editor.document) { + return + } + if (!runtimeLanguageContext.isLanguageSupported(e.document)) { + return + } + + CodeWhispererCodeCoverageTracker.getTracker(e.document.languageId)?.countTotalTokens(e) + UserWrittenCodeTracker.instance.onTextDocumentChange(e) + /** + * Handle this keystroke event only when + * 1. It is not a backspace + * 2. It is not caused by CodeWhisperer editing + * 3. It is not from undo/redo. + */ + if (e.contentChanges.length === 0 || vsCodeState.isCodeWhispererEditing) { + return + } + + if (vsCodeState.lastUserModificationTime) { + TelemetryHelper.instance.setTimeSinceLastModification( + Date.now() - vsCodeState.lastUserModificationTime + ) + } + vsCodeState.lastUserModificationTime = Date.now() + /** + * Important: Doing this sleep(10) is to make sure + * 1. this event is processed by vs code first + * 2. editor.selection.active has been successfully updated by VS Code + * Then this event can be processed by our code. + */ + await sleep(CodeWhispererConstants.vsCodeCursorUpdateDelay) + if (!RecommendationHandler.instance.isSuggestionVisible()) { + await KeyStrokeHandler.instance.processKeyStroke(e, editor, client, await getConfigEntry()) + } + }), + // manual trigger + Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => { + invokeRecommendation( + vscode.window.activeTextEditor as vscode.TextEditor, + client, + await getConfigEntry() + ).catch((e: Error) => { + getLogger().error('invokeRecommendation failed: %s', (e as Error).message) + }) + }) + ) + } +} diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts new file mode 100644 index 00000000000..2e5d25be165 --- /dev/null +++ b/packages/amazonq/src/app/inline/completion.ts @@ -0,0 +1,589 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { + CancellationToken, + InlineCompletionContext, + InlineCompletionItem, + InlineCompletionItemProvider, + Position, + TextDocument, + commands, + languages, + Disposable, + window, + TextEditor, + InlineCompletionTriggerKind, + Range, +} from 'vscode' +import { BaseLanguageClient } from 'vscode-languageclient' +import { + InlineCompletionItemWithReferences, + LogInlineCompletionSessionResultsParams, +} from '@aws/language-server-runtimes/protocol' +import { SessionManager } from './sessionManager' +import { GetAllRecommendationsOptions, RecommendationService } from './recommendationService' +import { + CodeWhispererConstants, + ReferenceHoverProvider, + ReferenceLogViewProvider, + ImportAdderProvider, + CodeSuggestionsState, + vsCodeState, + noInlineSuggestionsMsg, + getDiagnosticsDifferences, + getDiagnosticsOfCurrentFile, + toIdeDiagnostics, + handleExtraBrackets, +} from 'aws-core-vscode/codewhisperer' +import { LineTracker } from './stateTracker/lineTracker' +import { InlineTutorialAnnotation } from './tutorials/inlineTutorialAnnotation' +import { TelemetryHelper } from './telemetryHelper' +import { Experiments, getLogger, sleep } from 'aws-core-vscode/shared' +import { messageUtils } from 'aws-core-vscode/utils' +import { showEdits } from './EditRendering/imageRenderer' +import { ICursorUpdateRecorder } from './cursorUpdateManager' +import { DocumentEventListener } from './documentEventListener' + +export class InlineCompletionManager implements Disposable { + private disposable: Disposable + private inlineCompletionProvider: AmazonQInlineCompletionItemProvider + private languageClient: BaseLanguageClient + private sessionManager: SessionManager + private recommendationService: RecommendationService + private lineTracker: LineTracker + + private inlineTutorialAnnotation: InlineTutorialAnnotation + private readonly logSessionResultMessageName = 'aws/logInlineCompletionSessionResults' + private documentEventListener: DocumentEventListener + + constructor( + languageClient: BaseLanguageClient, + sessionManager: SessionManager, + lineTracker: LineTracker, + inlineTutorialAnnotation: InlineTutorialAnnotation, + cursorUpdateRecorder?: ICursorUpdateRecorder + ) { + this.languageClient = languageClient + this.sessionManager = sessionManager + this.lineTracker = lineTracker + this.recommendationService = new RecommendationService(this.sessionManager, cursorUpdateRecorder) + this.inlineTutorialAnnotation = inlineTutorialAnnotation + this.documentEventListener = new DocumentEventListener() + this.inlineCompletionProvider = new AmazonQInlineCompletionItemProvider( + languageClient, + this.recommendationService, + this.sessionManager, + this.inlineTutorialAnnotation, + this.documentEventListener + ) + + this.disposable = languages.registerInlineCompletionItemProvider( + CodeWhispererConstants.platformLanguageIds, + this.inlineCompletionProvider + ) + this.lineTracker.ready() + } + + public getInlineCompletionProvider(): AmazonQInlineCompletionItemProvider { + return this.inlineCompletionProvider + } + + public dispose(): void { + if (this.disposable) { + this.disposable.dispose() + this.lineTracker.dispose() + } + if (this.documentEventListener) { + this.documentEventListener.dispose() + } + } + + public registerInlineCompletion() { + const onInlineAcceptance = async ( + sessionId: string, + item: InlineCompletionItemWithReferences, + editor: TextEditor, + requestStartTime: number, + position: vscode.Position, + firstCompletionDisplayLatency?: number + ) => { + try { + vsCodeState.isCodeWhispererEditing = true + const startLine = position.line + // TODO: also log the seen state for other suggestions in session + // Calculate timing metrics before diagnostic delay + const totalSessionDisplayTime = Date.now() - requestStartTime + await sleep(500) + const diagnosticDiff = getDiagnosticsDifferences( + this.sessionManager.getActiveSession()?.diagnosticsBeforeAccept, + getDiagnosticsOfCurrentFile() + ) + // try remove the extra } ) ' " if there is a new reported problem + // the extra } will cause syntax error + if (diagnosticDiff.added.length > 0) { + await handleExtraBrackets(editor, editor.selection.active, position) + } + const params: LogInlineCompletionSessionResultsParams = { + sessionId: sessionId, + completionSessionResult: { + [item.itemId]: { + seen: true, + accepted: true, + discarded: false, + }, + }, + totalSessionDisplayTime: totalSessionDisplayTime, + firstCompletionDisplayLatency: firstCompletionDisplayLatency, + addedDiagnostics: diagnosticDiff.added.map((it) => toIdeDiagnostics(it)), + removedDiagnostics: diagnosticDiff.removed.map((it) => toIdeDiagnostics(it)), + } + void this.languageClient.sendNotification(this.logSessionResultMessageName, params) + this.disposable.dispose() + this.disposable = languages.registerInlineCompletionItemProvider( + CodeWhispererConstants.platformLanguageIds, + this.inlineCompletionProvider + ) + if (item.references && item.references.length) { + const referenceLog = ReferenceLogViewProvider.getReferenceLog( + item.insertText as string, + item.references, + editor + ) + ReferenceLogViewProvider.instance.addReferenceLog(referenceLog) + ReferenceHoverProvider.instance.addCodeReferences(item.insertText as string, item.references) + } + if (item.mostRelevantMissingImports?.length) { + await ImportAdderProvider.instance.onAcceptRecommendation(editor, item, startLine) + } + this.sessionManager.incrementSuggestionCount() + // clear session manager states once accepted + this.sessionManager.clear() + } finally { + vsCodeState.isCodeWhispererEditing = false + } + } + commands.registerCommand('aws.amazonq.acceptInline', onInlineAcceptance) + + const onInlineRejection = async () => { + try { + vsCodeState.isCodeWhispererEditing = true + const session = this.sessionManager.getActiveSession() + if (session === undefined) { + return + } + const requestStartTime = session.requestStartTime + const totalSessionDisplayTime = Date.now() - requestStartTime + await commands.executeCommand('editor.action.inlineSuggest.hide') + // TODO: also log the seen state for other suggestions in session + this.disposable.dispose() + this.disposable = languages.registerInlineCompletionItemProvider( + CodeWhispererConstants.platformLanguageIds, + this.inlineCompletionProvider + ) + const sessionId = session.sessionId + const itemId = this.sessionManager.getActiveRecommendation()[0]?.itemId + if (!itemId) { + return + } + const params: LogInlineCompletionSessionResultsParams = { + sessionId: sessionId, + completionSessionResult: { + [itemId]: { + seen: true, + accepted: false, + discarded: false, + }, + }, + firstCompletionDisplayLatency: session.firstCompletionDisplayLatency, + totalSessionDisplayTime: totalSessionDisplayTime, + } + void this.languageClient.sendNotification(this.logSessionResultMessageName, params) + // clear session manager states once rejected + this.sessionManager.clear() + } finally { + vsCodeState.isCodeWhispererEditing = false + } + } + commands.registerCommand('aws.amazonq.rejectCodeSuggestion', onInlineRejection) + } +} + +export class AmazonQInlineCompletionItemProvider implements InlineCompletionItemProvider { + private logger = getLogger() + private pendingRequest: Promise | undefined + + constructor( + private readonly languageClient: BaseLanguageClient, + private readonly recommendationService: RecommendationService, + private readonly sessionManager: SessionManager, + private readonly inlineTutorialAnnotation: InlineTutorialAnnotation, + private readonly documentEventListener: DocumentEventListener + ) {} + + private readonly logSessionResultMessageName = 'aws/logInlineCompletionSessionResults' + + // Ideally use this API handleDidShowCompletionItem + // https://github.com/microsoft/vscode/blob/main/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts#L83 + // we need this because the returned items of provideInlineCompletionItems may not be actually rendered on screen + // if VS Code believes the user is actively typing then it will not show such item + async checkWhetherInlineCompletionWasShown() { + // this line is to force VS Code to re-render the inline completion + // if it decides the inline completion can be shown + await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') + // yield event loop to let backend state transition finish plus wait for vsc to render + await sleep(10) + // run the command to detect if inline suggestion is really shown or not + await vscode.commands.executeCommand(`aws.amazonq.checkInlineSuggestionVisibility`) + } + + /** + * Check if a completion suggestion is currently active/displayed + */ + public async isCompletionActive(): Promise { + const session = this.sessionManager.getActiveSession() + if (session === undefined || !session.displayed || session.suggestions.some((item) => item.isInlineEdit)) { + return false + } + + // Use VS Code command to check if inline suggestion is actually visible on screen + // This command only executes when inlineSuggestionVisible context is true + await vscode.commands.executeCommand('aws.amazonq.checkInlineSuggestionVisibility') + const isInlineSuggestionVisible = Date.now() - session.lastVisibleTime < 50 + return isInlineSuggestionVisible + } + + /** + * Batch discard telemetry for completion suggestions when edit suggestion is active + */ + public batchDiscardTelemetryForEditSuggestion(items: any[], session: any): void { + // Emit DISCARD telemetry for completion suggestions that can't be shown due to active edit + const completionSessionResult: { + [key: string]: { seen: boolean; accepted: boolean; discarded: boolean } + } = {} + + for (const item of items) { + if (!item.isInlineEdit && item.itemId) { + completionSessionResult[item.itemId] = { + seen: false, + accepted: false, + discarded: true, + } + } + } + + // Send single telemetry event for all discarded items + if (Object.keys(completionSessionResult).length > 0) { + const params: LogInlineCompletionSessionResultsParams = { + sessionId: session.sessionId, + completionSessionResult, + firstCompletionDisplayLatency: session.firstCompletionDisplayLatency, + totalSessionDisplayTime: Date.now() - session.requestStartTime, + } + void this.languageClient.sendNotification(this.logSessionResultMessageName, params) + } + } + + // this method is automatically invoked by VS Code as user types + async provideInlineCompletionItems( + document: TextDocument, + position: Position, + context: InlineCompletionContext, + token: CancellationToken, + getAllRecommendationsOptions?: GetAllRecommendationsOptions + ): Promise { + getLogger().info('_provideInlineCompletionItems called with: %O', { + documentUri: document.uri.toString(), + position, + context, + triggerKind: context.triggerKind === InlineCompletionTriggerKind.Automatic ? 'Automatic' : 'Invoke', + options: JSON.stringify(getAllRecommendationsOptions), + }) + + // If there's already a pending request, wait for it to complete instead of starting a new one + // This prevents race conditions where multiple concurrent calls cause the later (empty) response + // to override the earlier (valid) response + if (this.pendingRequest) { + getLogger().info('Reusing pending inline completion request to avoid race condition') + try { + const result = await this.pendingRequest + // Check if THIS call's token was cancelled (not the original call's token) + if (token.isCancellationRequested) { + getLogger().info('Reused request completed but this call was cancelled') + return [] + } + return result + } catch (e) { + // If the pending request failed, continue with a new request + getLogger().info('Pending request failed, starting new request: %O', e) + } + } + + // Start a new request and track it + this.pendingRequest = this._provideInlineCompletionItemsImpl( + document, + position, + context, + token, + getAllRecommendationsOptions + ) + + try { + return await this.pendingRequest + } finally { + this.pendingRequest = undefined + } + } + + private async _provideInlineCompletionItemsImpl( + document: TextDocument, + position: Position, + context: InlineCompletionContext, + token: CancellationToken, + getAllRecommendationsOptions?: GetAllRecommendationsOptions + ): Promise { + if (vsCodeState.isCodeWhispererEditing) { + getLogger().info('Q is editing, returning empty') + return [] + } + + // there is a bug in VS Code, when hitting Enter, the context.triggerKind is Invoke (0) + // when hitting other keystrokes, the context.triggerKind is Automatic (1) + // we only mark option + C as manual trigger + // this is a workaround since the inlineSuggest.trigger command take no params + const isAutoTrigger = Date.now() - vsCodeState.lastManualTriggerTime > 50 + if (isAutoTrigger && !CodeSuggestionsState.instance.isSuggestionsEnabled()) { + // return early when suggestions are disabled with auto trigger + return [] + } + + // yield event loop to let the document listen catch updates + await sleep(1) + + let logstr = `GenerateCompletion activity:\\n` + try { + const t0 = Date.now() + vsCodeState.isRecommendationsActive = true + // handling previous session + const prevSession = this.sessionManager.getActiveSession() + const prevSessionId = prevSession?.sessionId + const prevItemId = this.sessionManager.getActiveRecommendation()?.[0]?.itemId + const prevStartPosition = prevSession?.startPosition + const editsTriggerOnAcceptance = prevSession?.triggerOnAcceptance + if (editsTriggerOnAcceptance) { + getAllRecommendationsOptions = { + ...getAllRecommendationsOptions, + editsStreakToken: prevSession?.editsStreakPartialResultToken, + } + } + const editor = window.activeTextEditor + // Skip prefix matching for Edits suggestions that trigger on acceptance. + if (prevSession && prevSessionId && prevItemId && prevStartPosition && !editsTriggerOnAcceptance) { + const prefix = document.getText(new Range(prevStartPosition, position)) + const prevItemMatchingPrefix = [] + for (const item of this.sessionManager.getActiveRecommendation()) { + // if item is an Edit suggestion, insertText is a diff instead of new code contents, skip the logic to check for prefix. + if (item.isInlineEdit) { + continue + } + const text = typeof item.insertText === 'string' ? item.insertText : item.insertText.value + if (text.startsWith(prefix) && position.isAfterOrEqual(prevStartPosition)) { + item.command = { + command: 'aws.amazonq.acceptInline', + title: 'On acceptance', + arguments: [ + prevSessionId, + item, + editor, + prevSession?.requestStartTime, + position, + prevSession?.firstCompletionDisplayLatency, + ], + } + item.range = new Range(prevStartPosition, position) + prevItemMatchingPrefix.push(item as InlineCompletionItem) + } + } + // re-use previous suggestions as long as new typed prefix matches + if (prevItemMatchingPrefix.length > 0) { + logstr += `- not call LSP and reuse previous suggestions that match user typed characters + - duration between trigger to completion suggestion is displayed ${Date.now() - t0}` + void this.checkWhetherInlineCompletionWasShown() + return prevItemMatchingPrefix + } + + // if no such suggestions, report the previous suggestion as Reject or Discarded + const params: LogInlineCompletionSessionResultsParams = { + sessionId: prevSessionId, + completionSessionResult: { + [prevItemId]: { + seen: prevSession.displayed, + accepted: false, + discarded: !prevSession.displayed, + }, + }, + firstCompletionDisplayLatency: prevSession.firstCompletionDisplayLatency, + totalSessionDisplayTime: Date.now() - prevSession.requestStartTime, + } + void this.languageClient.sendNotification(this.logSessionResultMessageName, params) + this.sessionManager.clear() + // Do not make auto trigger if user rejects a suggestion + // by typing characters that does not match + return [] + } + + // tell the tutorial that completions has been triggered + await this.inlineTutorialAnnotation.triggered(context.triggerKind) + + TelemetryHelper.instance.setInvokeSuggestionStartTime() + TelemetryHelper.instance.setTriggerType(context.triggerKind) + + const t1 = Date.now() + + await this.recommendationService.getAllRecommendations( + this.languageClient, + document, + position, + { + triggerKind: isAutoTrigger ? 1 : 0, + selectedCompletionInfo: context.selectedCompletionInfo, + }, + token, + isAutoTrigger, + this.documentEventListener, + getAllRecommendationsOptions + ) + // get active item from session for displaying + const items = this.sessionManager.getActiveRecommendation() + const itemId = this.sessionManager.getActiveRecommendation()?.[0]?.itemId + + // eslint-disable-next-line @typescript-eslint/no-base-to-string + const itemLog = items[0] ? `${items[0].insertText.toString()}` : `no suggestion` + + const t2 = Date.now() + + logstr += `- number of suggestions: ${items.length} +- sessionId: ${this.sessionManager.getActiveSession()?.sessionId} +- first suggestion content (next line): +${itemLog} +- duration between trigger to before sending LSP call: ${t1 - t0}ms +- duration between trigger to after receiving LSP response: ${t2 - t0}ms +- duration between before sending LSP call to after receving LSP response: ${t2 - t1}ms +` + const session = this.sessionManager.getActiveSession() + + // Show message to user when manual invoke fails to produce results. + if (items.length === 0 && context.triggerKind === InlineCompletionTriggerKind.Invoke) { + void messageUtils.showTimedMessage(noInlineSuggestionsMsg, 2000) + } + + if (!session || !items.length || !editor) { + logstr += `Failed to produce inline suggestion results. Received ${items.length} items from service` + return [] + } + + const cursorPosition = document.validatePosition(position) + + // Completion will not be rendered if users cursor moves to a position which is before the position when the service is invoked + if (items.length > 0 && !items[0].isInlineEdit) { + if (position.isAfter(editor.selection.active)) { + const params: LogInlineCompletionSessionResultsParams = { + sessionId: session.sessionId, + completionSessionResult: { + [itemId]: { + seen: false, + accepted: false, + discarded: true, + }, + }, + } + void this.languageClient.sendNotification(this.logSessionResultMessageName, params) + this.sessionManager.clear() + logstr += `- cursor moved behind trigger position. Discarding completion suggestion...` + return [] + } + } + + // delay the suggestion rendeing if user is actively typing + // see https://github.com/aws/aws-toolkit-vscode/commit/a537602a96f498f372ed61ec9d82cf8577a9d854 + for (let i = 0; i < 30; i++) { + const lastDocumentChange = this.documentEventListener.getLastDocumentChangeEvent(document.uri.fsPath) + if ( + lastDocumentChange && + Date.now() - lastDocumentChange.timestamp < CodeWhispererConstants.inlineSuggestionShowDelay + ) { + await sleep(CodeWhispererConstants.showRecommendationTimerPollPeriod) + } else { + break + } + } + + // the user typed characters from invoking suggestion cursor position to receiving suggestion position + const typeahead = document.getText(new Range(position, editor.selection.active)) + + const itemsMatchingTypeahead = [] + + for (const item of items) { + if (item.isInlineEdit) { + // Check if Next Edit Prediction feature flag is enabled + if (Experiments.instance.get('amazonqLSPNEP', true)) { + await showEdits(item, editor, session, this.languageClient, this) + logstr += `- duration between trigger to edits suggestion is displayed: ${Date.now() - t0}ms` + } + return [] + } + + item.insertText = typeof item.insertText === 'string' ? item.insertText : item.insertText.value + if (item.insertText.startsWith(typeahead)) { + item.command = { + command: 'aws.amazonq.acceptInline', + title: 'On acceptance', + arguments: [ + session.sessionId, + item, + editor, + session.requestStartTime, + cursorPosition, + session.firstCompletionDisplayLatency, + ], + } + item.range = new Range(cursorPosition, cursorPosition) + itemsMatchingTypeahead.push(item) + } + } + + // report discard if none of suggestions match typeahead + if (itemsMatchingTypeahead.length === 0) { + const params: LogInlineCompletionSessionResultsParams = { + sessionId: session.sessionId, + completionSessionResult: { + [itemId]: { + seen: false, + accepted: false, + discarded: true, + }, + }, + } + void this.languageClient.sendNotification(this.logSessionResultMessageName, params) + this.sessionManager.clear() + logstr += `- suggestion does not match user typeahead from insertion position. Discarding suggestion...` + return [] + } + + this.sessionManager.updateCodeReferenceAndImports() + // suggestions returned here will be displayed on screen + logstr += `- duration between trigger to completion suggestion is displayed: ${Date.now() - t0}ms` + void this.checkWhetherInlineCompletionWasShown() + return itemsMatchingTypeahead as InlineCompletionItem[] + } catch (e) { + getLogger('amazonqLsp').error('Failed to provide completion items: %O', e) + logstr += `- failed to provide completion items ${(e as Error).message}` + return [] + } finally { + vsCodeState.isRecommendationsActive = false + this.logger.info(logstr) + } + } +} diff --git a/packages/amazonq/src/app/inline/cursorUpdateManager.ts b/packages/amazonq/src/app/inline/cursorUpdateManager.ts new file mode 100644 index 00000000000..e06285d1f32 --- /dev/null +++ b/packages/amazonq/src/app/inline/cursorUpdateManager.ts @@ -0,0 +1,211 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { BaseLanguageClient } from 'vscode-languageclient' +import { getLogger } from 'aws-core-vscode/shared' +import { globals } from 'aws-core-vscode/shared' +import { AmazonQInlineCompletionItemProvider } from './completion' +import { CodeSuggestionsState } from 'aws-core-vscode/codewhisperer' + +// Configuration section for cursor updates +export const cursorUpdateConfigurationSection = 'aws.q.cursorUpdate' + +/** + * Interface for recording completion requests + */ +export interface ICursorUpdateRecorder { + recordCompletionRequest(): void +} + +/** + * Manages periodic cursor position updates for Next Edit Prediction + */ +export class CursorUpdateManager implements vscode.Disposable, ICursorUpdateRecorder { + private readonly logger = getLogger('amazonqLsp') + private updateIntervalMs = 250 + private updateTimer?: NodeJS.Timeout + private lastPosition?: vscode.Position + private lastDocumentUri?: string + private lastSentPosition?: vscode.Position + private lastSentDocumentUri?: string + private isActive = false + private lastRequestTime = 0 + private autotriggerStateDisposable?: vscode.Disposable + + constructor( + private readonly languageClient: BaseLanguageClient, + private readonly inlineCompletionProvider?: AmazonQInlineCompletionItemProvider + ) { + // Listen for autotrigger state changes to enable/disable the timer + this.autotriggerStateDisposable = CodeSuggestionsState.instance.onDidChangeState((isEnabled: boolean) => { + if (isEnabled && this.isActive) { + // If autotrigger is enabled and we're active, ensure timer is running + this.setupUpdateTimer() + } else { + // If autotrigger is disabled, clear the timer but keep isActive state + this.clearUpdateTimer() + } + }) + } + + /** + * Start tracking cursor positions and sending periodic updates + */ + public async start(): Promise { + if (this.isActive) { + return + } + + // Request configuration from server + try { + const config = await this.languageClient.sendRequest('aws/getConfigurationFromServer', { + section: cursorUpdateConfigurationSection, + }) + + if ( + config && + typeof config === 'object' && + 'intervalMs' in config && + typeof config.intervalMs === 'number' && + config.intervalMs > 0 + ) { + this.updateIntervalMs = config.intervalMs + } + } catch (error) { + this.logger.warn(`Failed to get cursor update configuration from server: ${error}`) + } + + this.isActive = true + if (CodeSuggestionsState.instance.isSuggestionsEnabled()) { + this.setupUpdateTimer() + } + } + + /** + * Stop tracking cursor positions and sending updates + */ + public stop(): void { + this.isActive = false + this.clearUpdateTimer() + } + + /** + * Update the current cursor position + */ + public updatePosition(position: vscode.Position, documentUri: string): void { + // If the document changed, set the last sent position to the current position + // This prevents triggering an immediate recommendation when switching tabs + if (this.lastDocumentUri !== documentUri) { + this.lastSentPosition = position.with() // Create a copy + this.lastSentDocumentUri = documentUri + } + + this.lastPosition = position.with() // Create a copy + this.lastDocumentUri = documentUri + } + + /** + * Record that a regular InlineCompletionWithReferences request was made + * This will prevent cursor updates from being sent for the update interval + */ + public recordCompletionRequest(): void { + this.lastRequestTime = globals.clock.Date.now() + } + + /** + * Set up the timer for periodic cursor position updates + */ + private setupUpdateTimer(): void { + this.clearUpdateTimer() + + this.updateTimer = globals.clock.setInterval(async () => { + await this.sendCursorUpdate() + }, this.updateIntervalMs) + } + + /** + * Clear the update timer + */ + private clearUpdateTimer(): void { + if (this.updateTimer) { + globals.clock.clearInterval(this.updateTimer) + this.updateTimer = undefined + } + } + + /** + * Creates a cancellation token source + * This method exists to make testing easier by allowing it to be stubbed + */ + private createCancellationTokenSource(): vscode.CancellationTokenSource { + return new vscode.CancellationTokenSource() + } + + /** + * Request LSP generate a completion for the current cursor position. + */ + private async sendCursorUpdate(): Promise { + // Don't send an update if a regular request was made recently + const now = globals.clock.Date.now() + if (now - this.lastRequestTime < this.updateIntervalMs) { + return + } + + const editor = vscode.window.activeTextEditor + if (!editor || editor.document.uri.toString() !== this.lastDocumentUri) { + return + } + + // Don't send an update if the position hasn't changed since the last update + if ( + this.lastSentPosition && + this.lastPosition && + this.lastSentDocumentUri === this.lastDocumentUri && + this.lastSentPosition.line === this.lastPosition.line && + this.lastSentPosition.character === this.lastPosition.character + ) { + return + } + + // Only proceed if we have a valid position and provider + if (this.lastPosition && this.inlineCompletionProvider) { + const position = this.lastPosition.with() // Create a copy + + // Call the inline completion provider instead of directly calling getAllRecommendations + try { + await this.inlineCompletionProvider.provideInlineCompletionItems( + editor.document, + position, + { + triggerKind: vscode.InlineCompletionTriggerKind.Automatic, + selectedCompletionInfo: undefined, + }, + this.createCancellationTokenSource().token, + { emitTelemetry: false, showUi: false } + ) + + // Only update the last sent position after successfully sending the request + this.lastSentPosition = position + this.lastSentDocumentUri = this.lastDocumentUri + } catch (error) { + this.logger.error(`Error sending cursor update: ${error}`) + } + } + } + + /** + * Dispose of resources + */ + public dispose(): void { + // Dispose of the autotrigger state change listener + if (this.autotriggerStateDisposable) { + this.autotriggerStateDisposable.dispose() + this.autotriggerStateDisposable = undefined + } + + this.stop() + } +} diff --git a/packages/amazonq/src/app/inline/documentEventListener.ts b/packages/amazonq/src/app/inline/documentEventListener.ts new file mode 100644 index 00000000000..7af22a3015a --- /dev/null +++ b/packages/amazonq/src/app/inline/documentEventListener.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' + +export interface DocumentChangeEvent { + event: vscode.TextDocumentChangeEvent + timestamp: number +} + +export class DocumentEventListener { + private lastDocumentChangeEventMap: Map = new Map() + private documentChangeListener: vscode.Disposable + private _maxDocument = 1000 + + constructor() { + this.documentChangeListener = vscode.workspace.onDidChangeTextDocument((e) => { + if (e.contentChanges.length > 0) { + if (this.lastDocumentChangeEventMap.size > this._maxDocument) { + this.lastDocumentChangeEventMap.clear() + } + this.lastDocumentChangeEventMap.set(e.document.uri.fsPath, { event: e, timestamp: Date.now() }) + // The VS Code provideInlineCompletionCallback may not trigger when Enter is pressed, especially in Python files + // manually make this trigger. In case of duplicate, the provideInlineCompletionCallback is already debounced + if (this.isEnter(e) && vscode.window.activeTextEditor) { + void vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') + } + } + }) + } + + public isLastEventDeletion(filepath: string): boolean { + const result = this.lastDocumentChangeEventMap.get(filepath) + if (result) { + const event = result.event + const eventTime = result.timestamp + const isDelete = + (event && event.contentChanges.length === 1 && event.contentChanges[0].text === '') || false + const timeDiff = Math.abs(Date.now() - eventTime) + return timeDiff < 500 && isDelete + } + return false + } + + public getLastDocumentChangeEvent(filepath: string): DocumentChangeEvent | undefined { + return this.lastDocumentChangeEventMap.get(filepath) + } + + public dispose(): void { + if (this.documentChangeListener) { + this.documentChangeListener.dispose() + } + } + + private isEnter(e: vscode.TextDocumentChangeEvent): boolean { + if (e.contentChanges.length !== 1) { + return false + } + const str = e.contentChanges[0].text + if (str.length === 0) { + return false + } + return ( + (str.startsWith('\r\n') && str.substring(2).trim() === '') || + (str[0] === '\n' && str.substring(1).trim() === '') + ) + } +} diff --git a/packages/amazonq/src/app/inline/editSuggestionState.ts b/packages/amazonq/src/app/inline/editSuggestionState.ts new file mode 100644 index 00000000000..61e4aebd142 --- /dev/null +++ b/packages/amazonq/src/app/inline/editSuggestionState.ts @@ -0,0 +1,27 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Manages the state of edit suggestions to avoid circular dependencies + */ +export class EditSuggestionState { + private static isEditSuggestionCurrentlyActive = false + private static displayStartTime = Date.now() + + static setEditSuggestionActive(active: boolean): void { + this.isEditSuggestionCurrentlyActive = active + if (active) { + this.displayStartTime = Date.now() + } + } + + static isEditSuggestionActive(): boolean { + return this.isEditSuggestionCurrentlyActive + } + + static isEditSuggestionDisplayingOverOneSecond(): boolean { + return this.isEditSuggestionActive() && Date.now() - this.displayStartTime > 1000 + } +} diff --git a/packages/amazonq/src/app/inline/notebookUtil.ts b/packages/amazonq/src/app/inline/notebookUtil.ts new file mode 100644 index 00000000000..928de1aad33 --- /dev/null +++ b/packages/amazonq/src/app/inline/notebookUtil.ts @@ -0,0 +1,98 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' + +import { CodeWhispererConstants, runtimeLanguageContext } from 'aws-core-vscode/codewhisperer' +import { InlineCompletionWithReferencesParams } from '@aws/language-server-runtimes/server-interface' + +function getEnclosingNotebook(document: vscode.TextDocument): vscode.NotebookDocument | undefined { + // For notebook cells, find the existing notebook with a cell that matches the current document. + return vscode.workspace.notebookDocuments.find( + (nb) => nb.notebookType === 'jupyter-notebook' && nb.getCells().some((cell) => cell.document === document) + ) +} + +export function getNotebookContext( + notebook: vscode.NotebookDocument, + document: vscode.TextDocument, + position: vscode.Position +) { + // Expand the context for a cell inside of a noteboo with whatever text fits from the preceding and subsequent cells + const allCells = notebook.getCells() + const cellIndex = allCells.findIndex((cell) => cell.document === document) + let caretLeftFileContext = '' + let caretRightFileContext = '' + + if (cellIndex >= 0 && cellIndex < allCells.length) { + // Add content from previous cells + for (let i = 0; i < cellIndex; i++) { + caretLeftFileContext += convertCellContent(allCells[i]) + '\n' + } + + // Add content from current cell up to cursor + caretLeftFileContext += allCells[cellIndex].document.getText( + new vscode.Range(new vscode.Position(0, 0), position) + ) + + // Add content from cursor to end of current cell + caretRightFileContext = allCells[cellIndex].document.getText( + new vscode.Range( + position, + allCells[cellIndex].document.positionAt(allCells[cellIndex].document.getText().length) + ) + ) + + // Add content from following cells + for (let i = cellIndex + 1; i < allCells.length; i++) { + caretRightFileContext += '\n' + convertCellContent(allCells[i]) + } + } + caretLeftFileContext = caretLeftFileContext.slice(-CodeWhispererConstants.charactersLimit) + caretRightFileContext = caretRightFileContext.slice(0, CodeWhispererConstants.charactersLimit) + return { caretLeftFileContext, caretRightFileContext } +} + +// Convert the markup cells into code with comments +export function convertCellContent(cell: vscode.NotebookCell) { + const cellText = cell.document.getText() + if (cell.kind === vscode.NotebookCellKind.Markup) { + const commentPrefix = runtimeLanguageContext.getSingleLineCommentPrefix( + runtimeLanguageContext.normalizeLanguage(cell.document.languageId) ?? cell.document.languageId + ) + if (commentPrefix === '') { + return cellText + } + return cell.document + .getText() + .split('\n') + .map((line) => `${commentPrefix}${line}`) + .join('\n') + } + return cellText +} + +export function extractFileContextInNotebooks( + document: vscode.TextDocument, + position: vscode.Position +): InlineCompletionWithReferencesParams['fileContextOverride'] | undefined { + let caretLeftFileContext = '' + let caretRightFileContext = '' + const languageName = runtimeLanguageContext.normalizeLanguage(document.languageId) ?? document.languageId + if (document.uri.scheme === 'vscode-notebook-cell') { + const notebook = getEnclosingNotebook(document) + if (notebook) { + ;({ caretLeftFileContext, caretRightFileContext } = getNotebookContext(notebook, document, position)) + return { + leftFileContent: caretLeftFileContext, + rightFileContent: caretRightFileContext, + filename: document.fileName, + fileUri: document.uri.toString(), + programmingLanguage: languageName, + } + } + } + return undefined +} diff --git a/packages/amazonq/src/app/inline/recommendationService.ts b/packages/amazonq/src/app/inline/recommendationService.ts new file mode 100644 index 00000000000..b601b2d90da --- /dev/null +++ b/packages/amazonq/src/app/inline/recommendationService.ts @@ -0,0 +1,318 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { + InlineCompletionListWithReferences, + InlineCompletionWithReferencesParams, + inlineCompletionWithReferencesRequestType, + TextDocumentContentChangeEvent, + editCompletionRequestType, + LogInlineCompletionSessionResultsParams, +} from '@aws/language-server-runtimes/protocol' +import { CancellationToken, InlineCompletionContext, Position, TextDocument, commands } from 'vscode' +import { BaseLanguageClient } from 'vscode-languageclient' +import { SessionManager } from './sessionManager' +import { + AuthUtil, + CodeWhispererConstants, + CodeWhispererStatusBarManager, + vsCodeState, +} from 'aws-core-vscode/codewhisperer' +import { TelemetryHelper } from './telemetryHelper' +import { ICursorUpdateRecorder } from './cursorUpdateManager' +import { getLogger } from 'aws-core-vscode/shared' +import { DocumentEventListener } from './documentEventListener' +import { getOpenFilesInWindow } from 'aws-core-vscode/utils' +import { asyncCallWithTimeout } from '../../util/timeoutUtil' +import { extractFileContextInNotebooks } from './notebookUtil' +import { EditSuggestionState } from './editSuggestionState' + +export interface GetAllRecommendationsOptions { + emitTelemetry?: boolean + showUi?: boolean + editsStreakToken?: number | string +} + +export class RecommendationService { + private logger = getLogger() + + constructor( + private readonly sessionManager: SessionManager, + private cursorUpdateRecorder?: ICursorUpdateRecorder + ) {} + /** + * Set the recommendation service + */ + public setCursorUpdateRecorder(recorder: ICursorUpdateRecorder): void { + this.cursorUpdateRecorder = recorder + } + + async getRecommendationsWithTimeout( + languageClient: BaseLanguageClient, + request: InlineCompletionWithReferencesParams, + token: CancellationToken + ) { + const resultPromise: Promise = languageClient.sendRequest( + inlineCompletionWithReferencesRequestType.method, + request, + token + ) + return await asyncCallWithTimeout( + resultPromise, + `${inlineCompletionWithReferencesRequestType.method} time out`, + CodeWhispererConstants.promiseTimeoutLimit * 1000 + ) + } + + async getAllRecommendations( + languageClient: BaseLanguageClient, + document: TextDocument, + position: Position, + context: InlineCompletionContext, + token: CancellationToken, + isAutoTrigger: boolean, + documentEventListener: DocumentEventListener, + options: GetAllRecommendationsOptions = { emitTelemetry: true, showUi: true } + ) { + const documentChangeEvent = documentEventListener?.getLastDocumentChangeEvent(document.uri.fsPath)?.event + + // Record that a regular request is being made + this.cursorUpdateRecorder?.recordCompletionRequest() + const documentChangeParams = documentChangeEvent + ? { + textDocument: { + uri: document.uri.toString(), + version: document.version, + }, + contentChanges: documentChangeEvent.contentChanges.map((x) => x as TextDocumentContentChangeEvent), + } + : undefined + const openTabs = await getOpenFilesInWindow() + let request: InlineCompletionWithReferencesParams = { + textDocument: { + uri: document.uri.toString(), + }, + position, + context, + documentChangeParams: documentChangeParams, + openTabFilepaths: openTabs, + } + if (options.editsStreakToken) { + request = { ...request, partialResultToken: options.editsStreakToken } + } + if (document.uri.scheme === 'vscode-notebook-cell') { + request.fileContextOverride = extractFileContextInNotebooks(document, position) + } + const requestStartTime = Date.now() + const statusBar = CodeWhispererStatusBarManager.instance + + // Only track telemetry if enabled + TelemetryHelper.instance.setInvokeSuggestionStartTime() + TelemetryHelper.instance.setPreprocessEndTime() + TelemetryHelper.instance.setSdkApiCallStartTime() + + try { + // Show UI indicators only if UI is enabled + if (options.showUi) { + await statusBar.setLoading() + } + + // Handle first request + this.logger.info('Sending inline completion request: %O', { + method: inlineCompletionWithReferencesRequestType.method, + request: { + textDocument: request.textDocument, + position: request.position, + context: request.context, + nextToken: request.partialResultToken, + }, + }) + const t0 = Date.now() + + // Best effort estimate of deletion + const isTriggerByDeletion = documentEventListener.isLastEventDeletion(document.uri.fsPath) + + const ps: Promise[] = [] + /** + * IsTriggerByDeletion is to prevent user deletion invoking Completions. + * PartialResultToken is not a hack for now since only Edits suggestion use partialResultToken across different calls of [getAllRecommendations], + * Completions use PartialResultToken with single 1 call of [getAllRecommendations]. + * Edits leverage partialResultToken to achieve EditStreak such that clients can pull all continuous suggestions generated by the model within 1 EOS block. + */ + if (!isTriggerByDeletion && !request.partialResultToken && !EditSuggestionState.isEditSuggestionActive()) { + const completionPromise: Promise = languageClient.sendRequest( + inlineCompletionWithReferencesRequestType.method, + request, + token + ) + ps.push(completionPromise) + } + + /** + * Though Edit request is sent on keystrokes everytime, the language server will execute the request in a debounced manner so that it won't be immediately executed. + */ + const editPromise: Promise = languageClient.sendRequest( + editCompletionRequestType.method, + request, + token + ) + ps.push(editPromise) + + /** + * First come first serve, ideally we should simply return the first response returned. However there are some caviar here because either + * (1) promise might be returned early without going through service + * (2) some users are not enabled with edits suggestion, therefore service will return empty result without passing through the model + * With the scenarios listed above or others, it's possible that 1 promise will ALWAYS win the race and users will NOT get any suggestion back. + * This is the hack to return first "NON-EMPTY" response + */ + let result = await Promise.race(ps) + if (ps.length > 1 && result.items.length === 0) { + for (const p of ps) { + const r = await p + if (r.items.length > 0) { + result = r + } + } + } + + this.logger.info('Received inline completion response from LSP: %O', { + sessionId: result.sessionId, + latency: Date.now() - t0, + itemCount: result.items?.length || 0, + items: result.items?.map((item) => ({ + itemId: item.itemId, + insertText: + (typeof item.insertText === 'string' ? item.insertText : String(item.insertText))?.substring( + 0, + 50 + ) + '...', + })), + }) + + if (result.items.length > 0 && result.items[0].isInlineEdit === false) { + if (isTriggerByDeletion) { + this.logger.info(`Suggestions were discarded; reason: triggerByDeletion`) + return [] + } + // Completion will not be rendered if an edit suggestion has been active for longer than 1 second + if (EditSuggestionState.isEditSuggestionDisplayingOverOneSecond()) { + const session = this.sessionManager.getActiveSession() + if (!session) { + this.logger.error(`Suggestions were discarded; reason: undefined conflicting session`) + return [] + } + const params: LogInlineCompletionSessionResultsParams = { + sessionId: session.sessionId, + completionSessionResult: Object.fromEntries( + result.items.map((item) => [ + item.itemId, + { + seen: false, + accepted: false, + discarded: true, + }, + ]) + ), + } + void languageClient.sendNotification('aws/logInlineCompletionSessionResults', params) + this.sessionManager.clear() + this.logger.info( + 'Suggetions were discarded; reason: active edit suggestion displayed longer than 1 second' + ) + return [] + } else if (EditSuggestionState.isEditSuggestionActive()) { + // discard the current edit suggestion if its display time is less than 1 sec + await commands.executeCommand('aws.amazonq.inline.rejectEdit', true) + this.logger.info('Discarding active edit suggestion displaying less than 1 second') + } + } + + TelemetryHelper.instance.setSdkApiCallEndTime() + TelemetryHelper.instance.setSessionId(result.sessionId) + if (result.items.length > 0 && result.items[0].itemId !== undefined) { + TelemetryHelper.instance.setFirstResponseRequestId(result.items[0].itemId as string) + } + TelemetryHelper.instance.setFirstSuggestionShowTime() + + const firstCompletionDisplayLatency = Date.now() - requestStartTime + this.sessionManager.startSession( + result.sessionId, + result.items, + requestStartTime, + position, + firstCompletionDisplayLatency + ) + + const isInlineEdit = result.items.some((item) => item.isInlineEdit) + + // TODO: question, is it possible that the first request returns empty suggestion but has non-empty next token? + if (result.partialResultToken) { + let logstr = `found non null next token; ` + if (!isInlineEdit) { + // If the suggestion is COMPLETIONS and there are more results to fetch, handle them in the background + logstr += 'Suggestion type is COMPLETIONS. Start pulling more items' + this.processRemainingRequests(languageClient, request, result, token).catch((error) => { + languageClient.warn(`Error when getting suggestions: ${error}`) + }) + } else { + // Skip fetching for more items if the suggesion is EDITS. If it is EDITS suggestion, only fetching for more + // suggestions when the user start to accept a suggesion. + // Save editsStreakPartialResultToken for the next EDITS suggestion trigger if user accepts. + logstr += 'Suggestion type is EDITS. Skip pulling more items' + this.sessionManager.updateActiveEditsStreakToken(result.partialResultToken) + } + + this.logger.info(logstr) + } + } catch (error: any) { + this.logger.error('Error getting recommendations: %O', error) + // bearer token expired + if (error.data && error.data.awsErrorCode === 'E_AMAZON_Q_CONNECTION_EXPIRED') { + // ref: https://github.com/aws/aws-toolkit-vscode/blob/amazonq/v1.74.0/packages/core/src/codewhisperer/service/inlineCompletionService.ts#L104 + // show re-auth once if connection expired + if (AuthUtil.instance.isConnectionExpired()) { + await AuthUtil.instance.notifyReauthenticate(isAutoTrigger) + } else { + // get a new bearer token, if this failed, the connection will be marked as expired. + // new tokens will be synced per 10 seconds in auth.startTokenRefreshInterval + await AuthUtil.instance.getBearerToken() + } + } + return [] + } finally { + // Remove all UI indicators if UI is enabled + if (options.showUi) { + void statusBar.refreshStatusBar() // effectively "stop loading" + } + } + } + + private async processRemainingRequests( + languageClient: BaseLanguageClient, + initialRequest: InlineCompletionWithReferencesParams, + firstResult: InlineCompletionListWithReferences, + token: CancellationToken + ): Promise { + let nextToken = firstResult.partialResultToken + while (nextToken) { + const request = { ...initialRequest, partialResultToken: nextToken } + + const result = await this.getRecommendationsWithTimeout(languageClient, request, token) + // when pagination is in progress, but user has already accepted or rejected an inline completion + // then stop pagination + if (this.sessionManager.getActiveSession() === undefined || vsCodeState.isCodeWhispererEditing) { + break + } + this.sessionManager.updateSessionSuggestions(result.items) + nextToken = result.partialResultToken + } + + this.sessionManager.closeSession() + + // refresh inline completion items to render paginated responses + // All pagination requests completed + TelemetryHelper.instance.setAllPaginationEndTime() + TelemetryHelper.instance.tryRecordClientComponentLatency() + } +} diff --git a/packages/amazonq/src/app/inline/sessionManager.ts b/packages/amazonq/src/app/inline/sessionManager.ts new file mode 100644 index 00000000000..ef2ee2a84d0 --- /dev/null +++ b/packages/amazonq/src/app/inline/sessionManager.ts @@ -0,0 +1,183 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { InlineCompletionItemWithReferences } from '@aws/language-server-runtimes-types' +import { + FileDiagnostic, + getDiagnosticsOfCurrentFile, + ImportAdderProvider, + ReferenceInlineProvider, +} from 'aws-core-vscode/codewhisperer' + +// TODO: add more needed data to the session interface +export interface CodeWhispererSession { + sessionId: string + suggestions: InlineCompletionItemWithReferences[] + // TODO: might need to convert to enum states + isRequestInProgress: boolean + requestStartTime: number + firstCompletionDisplayLatency?: number + startPosition: vscode.Position + diagnosticsBeforeAccept: FileDiagnostic | undefined + // partialResultToken for the next trigger if user accepts an EDITS suggestion + editsStreakPartialResultToken?: number | string + triggerOnAcceptance?: boolean + // whether any suggestion in this session was displayed on screen + displayed: boolean + // timestamp when the suggestion was last visible + lastVisibleTime: number +} + +export class SessionManager { + private activeSession?: CodeWhispererSession + private _acceptedSuggestionCount: number = 0 + private _refreshedSessions = new Set() + private _currentSuggestionIndex = 0 + constructor() {} + + public startSession( + sessionId: string, + suggestions: InlineCompletionItemWithReferences[], + requestStartTime: number, + startPosition: vscode.Position, + firstCompletionDisplayLatency?: number + ) { + const diagnosticsBeforeAccept = getDiagnosticsOfCurrentFile() + this.activeSession = { + sessionId, + suggestions, + isRequestInProgress: true, + requestStartTime, + startPosition, + firstCompletionDisplayLatency, + diagnosticsBeforeAccept, + displayed: false, + lastVisibleTime: 0, + } + this._currentSuggestionIndex = 0 + } + + public closeSession() { + if (!this.activeSession) { + return + } + this.activeSession.isRequestInProgress = false + } + + public getActiveSession() { + return this.activeSession + } + + public updateSessionSuggestions(suggestions: InlineCompletionItemWithReferences[]) { + if (!this.activeSession) { + return + } + this.activeSession.suggestions = [...this.activeSession.suggestions, ...suggestions] + } + + public getActiveRecommendation(): InlineCompletionItemWithReferences[] { + return this.activeSession?.suggestions ?? [] + } + + public get acceptedSuggestionCount(): number { + return this._acceptedSuggestionCount + } + + public incrementSuggestionCount() { + this._acceptedSuggestionCount += 1 + } + + public updateActiveEditsStreakToken(partialResultToken: number | string) { + if (!this.activeSession) { + return + } + this.activeSession.editsStreakPartialResultToken = partialResultToken + } + + public clear() { + this.activeSession = undefined + this._currentSuggestionIndex = 0 + this.clearReferenceInlineHintsAndImportHints() + } + + // re-render the session ghost text to display paginated responses once per completed session + public async maybeRefreshSessionUx() { + if ( + this.activeSession && + !this.activeSession.isRequestInProgress && + !this._refreshedSessions.has(this.activeSession.sessionId) + ) { + await vscode.commands.executeCommand('editor.action.inlineSuggest.hide') + await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') + if (this._refreshedSessions.size > 1000) { + this._refreshedSessions.clear() + } + this._refreshedSessions.add(this.activeSession.sessionId) + } + } + + public onNextSuggestion() { + if (this.activeSession?.suggestions && this.activeSession?.suggestions.length > 0) { + this._currentSuggestionIndex = (this._currentSuggestionIndex + 1) % this.activeSession.suggestions.length + this.updateCodeReferenceAndImports() + } + } + + public onPrevSuggestion() { + if (this.activeSession?.suggestions && this.activeSession.suggestions.length > 0) { + this._currentSuggestionIndex = + (this._currentSuggestionIndex - 1 + this.activeSession.suggestions.length) % + this.activeSession.suggestions.length + this.updateCodeReferenceAndImports() + } + } + + public checkInlineSuggestionVisibility() { + if (this.activeSession) { + this.activeSession.displayed = true + this.activeSession.lastVisibleTime = Date.now() + } + } + + private clearReferenceInlineHintsAndImportHints() { + ReferenceInlineProvider.instance.removeInlineReference() + ImportAdderProvider.instance.clear() + } + + // Ideally use this API handleDidShowCompletionItem + // https://github.com/microsoft/vscode/blob/main/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts#L83 + updateCodeReferenceAndImports() { + try { + this.clearReferenceInlineHintsAndImportHints() + if ( + this.activeSession?.suggestions && + this.activeSession.suggestions[this._currentSuggestionIndex] && + this.activeSession.suggestions.length > 0 + ) { + const reference = this.activeSession.suggestions[this._currentSuggestionIndex].references + const insertText = this.activeSession.suggestions[this._currentSuggestionIndex].insertText + if (reference && reference.length > 0) { + const insertTextStr = + typeof insertText === 'string' ? insertText : (insertText.value ?? String(insertText)) + + ReferenceInlineProvider.instance.setInlineReference( + this.activeSession.startPosition.line, + insertTextStr, + reference + ) + } + if (vscode.window.activeTextEditor) { + ImportAdderProvider.instance.onShowRecommendation( + vscode.window.activeTextEditor.document, + this.activeSession.startPosition.line, + this.activeSession.suggestions[this._currentSuggestionIndex] + ) + } + } + } catch { + // do nothing as this is not critical path + } + } +} diff --git a/packages/amazonq/src/app/inline/stateTracker/lineTracker.ts b/packages/amazonq/src/app/inline/stateTracker/lineTracker.ts new file mode 100644 index 00000000000..58bee329a40 --- /dev/null +++ b/packages/amazonq/src/app/inline/stateTracker/lineTracker.ts @@ -0,0 +1,178 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { editorUtilities, setContext } from 'aws-core-vscode/shared' + +export interface LineSelection { + anchor: number + active: number +} + +export interface LinesChangeEvent { + readonly editor: vscode.TextEditor | undefined + readonly selections: LineSelection[] | undefined + + readonly reason: 'editor' | 'selection' | 'content' +} + +/** + * This class providees a single interface to manage and access users' "line" selections + * Callers could use it by subscribing onDidChangeActiveLines to do UI updates or logic needed to be executed when line selections get changed + */ +export class LineTracker implements vscode.Disposable { + private _onDidChangeActiveLines = new vscode.EventEmitter() + get onDidChangeActiveLines(): vscode.Event { + return this._onDidChangeActiveLines.event + } + + private _editor: vscode.TextEditor | undefined + private _disposable: vscode.Disposable | undefined + + private _selections: LineSelection[] | undefined + get selections(): LineSelection[] | undefined { + return this._selections + } + + private _onReady: vscode.EventEmitter = new vscode.EventEmitter() + get onReady(): vscode.Event { + return this._onReady.event + } + + private _ready: boolean = false + get isReady() { + return this._ready + } + + constructor() { + this._disposable = vscode.Disposable.from( + vscode.window.onDidChangeActiveTextEditor(async (e) => { + await this.onActiveTextEditorChanged(e) + }), + vscode.window.onDidChangeTextEditorSelection(async (e) => { + await this.onTextEditorSelectionChanged(e) + }), + vscode.workspace.onDidChangeTextDocument((e) => { + this.onContentChanged(e) + }) + ) + + queueMicrotask(async () => await this.onActiveTextEditorChanged(vscode.window.activeTextEditor)) + } + + dispose() { + this._disposable?.dispose() + } + + ready() { + if (this._ready) { + throw new Error('Linetracker is already activated') + } + + this._ready = true + queueMicrotask(() => this._onReady.fire()) + } + + // @VisibleForTesting + async onActiveTextEditorChanged(editor: vscode.TextEditor | undefined) { + if (editor === this._editor) { + return + } + + this._editor = editor + this._selections = toLineSelections(editor?.selections) + if (this._selections && this._selections[0]) { + const s = this._selections.map((item) => item.active + 1) + await setContext('codewhisperer.activeLine', s) + } + + this.notifyLinesChanged('editor') + } + + // @VisibleForTesting + async onTextEditorSelectionChanged(e: vscode.TextEditorSelectionChangeEvent) { + // If this isn't for our cached editor and its not a real editor -- kick out + if (this._editor !== e.textEditor && !editorUtilities.isTextEditor(e.textEditor)) { + return + } + + const selections = toLineSelections(e.selections) + if (this._editor === e.textEditor && this.includes(selections)) { + return + } + + this._editor = e.textEditor + this._selections = selections + if (this._selections && this._selections[0]) { + const s = this._selections.map((item) => item.active + 1) + await setContext('codewhisperer.activeLine', s) + } + + this.notifyLinesChanged('selection') + } + + // @VisibleForTesting + onContentChanged(e: vscode.TextDocumentChangeEvent) { + const editor = vscode.window.activeTextEditor + if (e.document === editor?.document && e.contentChanges.length > 0 && editorUtilities.isTextEditor(editor)) { + this._editor = editor + this._selections = toLineSelections(this._editor?.selections) + + this.notifyLinesChanged('content') + } + } + + notifyLinesChanged(reason: 'editor' | 'selection' | 'content') { + const e: LinesChangeEvent = { editor: this._editor, selections: this.selections, reason: reason } + this._onDidChangeActiveLines.fire(e) + } + + includes(selections: LineSelection[]): boolean + includes(line: number, options?: { activeOnly: boolean }): boolean + includes(lineOrSelections: number | LineSelection[], options?: { activeOnly: boolean }): boolean { + if (typeof lineOrSelections !== 'number') { + return isIncluded(lineOrSelections, this._selections) + } + + if (this._selections === undefined || this._selections.length === 0) { + return false + } + + const line = lineOrSelections + const activeOnly = options?.activeOnly ?? true + + for (const selection of this._selections) { + if ( + line === selection.active || + (!activeOnly && + ((selection.anchor >= line && line >= selection.active) || + (selection.active >= line && line >= selection.anchor))) + ) { + return true + } + } + return false + } +} + +function isIncluded(selections: LineSelection[] | undefined, within: LineSelection[] | undefined): boolean { + if (selections === undefined && within === undefined) { + return true + } + if (selections === undefined || within === undefined || selections.length !== within.length) { + return false + } + + return selections.every((s, i) => { + const match = within[i] + return s.active === match.active && s.anchor === match.anchor + }) +} + +function toLineSelections(selections: readonly vscode.Selection[]): LineSelection[] +function toLineSelections(selections: readonly vscode.Selection[] | undefined): LineSelection[] | undefined +function toLineSelections(selections: readonly vscode.Selection[] | undefined) { + return selections?.map((s) => ({ active: s.active.line, anchor: s.anchor.line })) +} diff --git a/packages/amazonq/src/app/inline/telemetryHelper.ts b/packages/amazonq/src/app/inline/telemetryHelper.ts new file mode 100644 index 00000000000..41db4c7469a --- /dev/null +++ b/packages/amazonq/src/app/inline/telemetryHelper.ts @@ -0,0 +1,162 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AuthUtil, getSelectedCustomization } from 'aws-core-vscode/codewhisperer' +import { CodewhispererLanguage } from 'aws-core-vscode/shared' +import { CodewhispererTriggerType, telemetry } from 'aws-core-vscode/telemetry' +import { InlineCompletionTriggerKind } from 'vscode' + +export class TelemetryHelper { + // Variables needed for client component latency + private _invokeSuggestionStartTime = 0 + private _preprocessEndTime = 0 + private _sdkApiCallStartTime = 0 + private _sdkApiCallEndTime = 0 + private _allPaginationEndTime = 0 + private _firstSuggestionShowTime = 0 + private _firstResponseRequestId = '' + private _sessionId = '' + private _language: CodewhispererLanguage = 'java' + private _triggerType: CodewhispererTriggerType = 'OnDemand' + + constructor() {} + + static #instance: TelemetryHelper + + public static get instance() { + return (this.#instance ??= new this()) + } + + public resetClientComponentLatencyTime() { + this._invokeSuggestionStartTime = 0 + this._preprocessEndTime = 0 + this._sdkApiCallStartTime = 0 + this._sdkApiCallEndTime = 0 + this._firstSuggestionShowTime = 0 + this._allPaginationEndTime = 0 + this._firstResponseRequestId = '' + } + + public setInvokeSuggestionStartTime() { + this.resetClientComponentLatencyTime() + this._invokeSuggestionStartTime = Date.now() + } + + get invokeSuggestionStartTime(): number { + return this._invokeSuggestionStartTime + } + + public setPreprocessEndTime() { + this._preprocessEndTime = Date.now() + } + + get preprocessEndTime(): number { + return this._preprocessEndTime + } + + public setSdkApiCallStartTime() { + if (this._sdkApiCallStartTime === 0) { + this._sdkApiCallStartTime = Date.now() + } + } + + get sdkApiCallStartTime(): number { + return this._sdkApiCallStartTime + } + + public setSdkApiCallEndTime() { + if (this._sdkApiCallEndTime === 0 && this._sdkApiCallStartTime !== 0) { + this._sdkApiCallEndTime = Date.now() + } + } + + get sdkApiCallEndTime(): number { + return this._sdkApiCallEndTime + } + + public setAllPaginationEndTime() { + if (this._allPaginationEndTime === 0 && this._sdkApiCallEndTime !== 0) { + this._allPaginationEndTime = Date.now() + } + } + + get allPaginationEndTime(): number { + return this._allPaginationEndTime + } + + public setFirstSuggestionShowTime() { + if (this._firstSuggestionShowTime === 0 && this._sdkApiCallEndTime !== 0) { + this._firstSuggestionShowTime = Date.now() + } + } + + get firstSuggestionShowTime(): number { + return this._firstSuggestionShowTime + } + + public setFirstResponseRequestId(requestId: string) { + if (this._firstResponseRequestId === '') { + this._firstResponseRequestId = requestId + } + } + + get firstResponseRequestId(): string { + return this._firstResponseRequestId + } + + public setSessionId(sessionId: string) { + if (this._sessionId === '') { + this._sessionId = sessionId + } + } + + get sessionId(): string { + return this._sessionId + } + + public setLanguage(language: CodewhispererLanguage) { + this._language = language + } + + get language(): CodewhispererLanguage { + return this._language + } + + public setTriggerType(triggerType: InlineCompletionTriggerKind) { + if (triggerType === InlineCompletionTriggerKind.Invoke) { + this._triggerType = 'OnDemand' + } else if (triggerType === InlineCompletionTriggerKind.Automatic) { + this._triggerType = 'AutoTrigger' + } + } + + get triggerType(): string { + return this._triggerType + } + + // report client component latency after all pagination call finish + // and at least one suggestion is shown to the user + public tryRecordClientComponentLatency() { + if (this._firstSuggestionShowTime === 0 || this._allPaginationEndTime === 0) { + return + } + telemetry.codewhisperer_clientComponentLatency.emit({ + codewhispererAllCompletionsLatency: this._allPaginationEndTime - this._sdkApiCallStartTime, + codewhispererCompletionType: 'Line', + codewhispererCredentialFetchingLatency: 0, // no longer relevant, because we don't re-build the sdk. Flare already has that set + codewhispererCustomizationArn: getSelectedCustomization().arn, + codewhispererEndToEndLatency: this._firstSuggestionShowTime - this._invokeSuggestionStartTime, + codewhispererFirstCompletionLatency: this._sdkApiCallEndTime - this._sdkApiCallStartTime, + codewhispererLanguage: this._language, + codewhispererPostprocessingLatency: this._firstSuggestionShowTime - this._sdkApiCallEndTime, + codewhispererPreprocessingLatency: this._preprocessEndTime - this._invokeSuggestionStartTime, + codewhispererRequestId: this._firstResponseRequestId, + codewhispererSessionId: this._sessionId, + codewhispererTriggerType: this._triggerType, + credentialStartUrl: AuthUtil.instance.startUrl, + result: 'Succeeded', + }) + } +} diff --git a/packages/amazonq/src/app/inline/tutorials/inlineChatTutorialAnnotation.ts b/packages/amazonq/src/app/inline/tutorials/inlineChatTutorialAnnotation.ts new file mode 100644 index 00000000000..1208b4766af --- /dev/null +++ b/packages/amazonq/src/app/inline/tutorials/inlineChatTutorialAnnotation.ts @@ -0,0 +1,58 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { InlineTutorialAnnotation } from './inlineTutorialAnnotation' +import { globals } from 'aws-core-vscode/shared' + +export class InlineChatTutorialAnnotation { + private enabled: boolean = true + + constructor(private readonly inlineTutorialAnnotation: InlineTutorialAnnotation) { + globals.context.subscriptions.push( + vscode.window.onDidChangeTextEditorSelection(async ({ selections, textEditor }) => { + let showShow = false + + if (this.enabled) { + for (const selection of selections) { + if (selection.end.line === selection.start.line + 1 && selection.end.character === 0) { + // dont show if the selection is just a newline + } else if (selection.start.line !== selection.end.line) { + showShow = true + break + } + } + } + + await this.setVisible(textEditor, showShow) + }, this) + ) + } + + private async setVisible(editor: vscode.TextEditor, visible: boolean) { + let needsRefresh: boolean + if (visible) { + needsRefresh = await this.inlineTutorialAnnotation.tryShowInlineHint() + } else { + needsRefresh = await this.inlineTutorialAnnotation.tryHideInlineHint() + } + if (needsRefresh) { + await this.inlineTutorialAnnotation.refresh(editor, 'codewhisperer') + } + } + + async hide(editor: vscode.TextEditor) { + await this.setVisible(editor, false) + } + + enable() { + this.enabled = true + } + + async disable(editor: vscode.TextEditor) { + this.enabled = false + await this.setVisible(editor, false) + } +} diff --git a/packages/amazonq/src/app/inline/tutorials/inlineTutorialAnnotation.ts b/packages/amazonq/src/app/inline/tutorials/inlineTutorialAnnotation.ts new file mode 100644 index 00000000000..ad0807df94c --- /dev/null +++ b/packages/amazonq/src/app/inline/tutorials/inlineTutorialAnnotation.ts @@ -0,0 +1,519 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as os from 'os' +import { AnnotationChangeSource, AuthUtil, inlinehintKey, runtimeLanguageContext } from 'aws-core-vscode/codewhisperer' +import { editorUtilities, getLogger, globals, setContext, vscodeUtilities } from 'aws-core-vscode/shared' +import { LinesChangeEvent, LineSelection, LineTracker } from '../stateTracker/lineTracker' +import { telemetry } from 'aws-core-vscode/telemetry' +import { cancellableDebounce } from 'aws-core-vscode/utils' +import { SessionManager } from '../sessionManager' + +const case3TimeWindow = 30000 // 30 seconds + +const maxSmallIntegerV8 = 2 ** 30 // Max number that can be stored in V8's smis (small integers) + +function fromId(id: string | undefined, sessionManager: SessionManager): AnnotationState | undefined { + switch (id) { + case AutotriggerState.id: + return new AutotriggerState(sessionManager) + case PressTabState.id: + return new AutotriggerState(sessionManager) + case ManualtriggerState.id: + return new ManualtriggerState() + case TryMoreExState.id: + return new TryMoreExState() + case EndState.id: + return new EndState() + case InlineChatState.id: + return new InlineChatState() + default: + return undefined + } +} + +interface AnnotationState { + id: string + suppressWhileRunning: boolean + decorationRenderOptions?: vscode.ThemableDecorationAttachmentRenderOptions + + text: () => string + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState | undefined + isNextState(state: AnnotationState | undefined): boolean +} + +/** + * case 1: How Cwspr triggers + * Trigger Criteria: + * User opens an editor file && + * CW is not providing a suggestion && + * User has not accepted any suggestion + * + * Exit criteria: + * User accepts 1 suggestion + * + */ +export class AutotriggerState implements AnnotationState { + static id = 'codewhisperer_learnmore_case_1' + id = AutotriggerState.id + + suppressWhileRunning = true + text = () => 'Amazon Q Tip 1/3: Start typing to get suggestions ([ESC] to exit)' + static acceptedCount = 0 + + constructor(private readonly sessionManager: SessionManager) {} + + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState | undefined { + if (AutotriggerState.acceptedCount < this.sessionManager.acceptedSuggestionCount) { + return new ManualtriggerState() + } else if (this.sessionManager.getActiveRecommendation().length > 0) { + return new PressTabState(this.sessionManager) + } else { + return this + } + } + + isNextState(state: AnnotationState | undefined): boolean { + return state instanceof ManualtriggerState + } +} + +/** + * case 1-a: Tab to accept + * Trigger Criteria: + * Case 1 && + * Inline suggestion is being shown + * + * Exit criteria: + * User accepts 1 suggestion + */ +export class PressTabState implements AnnotationState { + static id = 'codewhisperer_learnmore_case_1a' + id = PressTabState.id + + suppressWhileRunning = false + + text = () => 'Amazon Q Tip 1/3: Press [TAB] to accept ([ESC] to exit)' + + constructor(private readonly sessionManager: SessionManager) {} + + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState | undefined { + return new AutotriggerState(this.sessionManager).updateState(changeSource, force) + } + + isNextState(state: AnnotationState | undefined): boolean { + return state instanceof ManualtriggerState + } +} + +/** + * case 2: Manual trigger + * Trigger Criteria: + * User exists case 1 && + * User navigates to a new line + * + * Exit criteria: + * User inokes manual trigger shortcut + */ +export class ManualtriggerState implements AnnotationState { + static id = 'codewhisperer_learnmore_case_2' + id = ManualtriggerState.id + + suppressWhileRunning = true + + text = () => { + if (os.platform() === 'win32') { + return 'Amazon Q Tip 2/3: Invoke suggestions with [Alt] + [C] ([ESC] to exit)' + } + + return 'Amazon Q Tip 2/3: Invoke suggestions with [Option] + [C] ([ESC] to exit)' + } + hasManualTrigger: boolean = false + hasValidResponse: boolean = false + + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState | undefined { + if (this.hasManualTrigger && this.hasValidResponse) { + if (changeSource !== 'codewhisperer') { + return new TryMoreExState() + } else { + return undefined + } + } else { + return this + } + } + + isNextState(state: AnnotationState | undefined): boolean { + return state instanceof TryMoreExState + } +} + +/** + * case 3: Learn more + * Trigger Criteria: + * User exists case 2 && + * User navigates to a new line + * + * Exit criteria: + * User accepts or rejects the suggestion + */ +export class TryMoreExState implements AnnotationState { + static id = 'codewhisperer_learnmore_case_3' + id = TryMoreExState.id + + suppressWhileRunning = true + + text = () => 'Amazon Q Tip 3/3: For settings, open the Amazon Q menu from the status bar ([ESC] to exit)' + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState { + if (force) { + return new EndState() + } + return this + } + + isNextState(state: AnnotationState | undefined): boolean { + return state instanceof EndState + } + + static learnmoeCount: number = 0 +} + +export class EndState implements AnnotationState { + static id = 'codewhisperer_learnmore_end' + id = EndState.id + + suppressWhileRunning = true + text = () => '' + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState { + return this + } + isNextState(state: AnnotationState): boolean { + return false + } +} + +export class InlineChatState implements AnnotationState { + static id = 'amazonq_annotation_inline_chat' + id = InlineChatState.id + suppressWhileRunning = false + + text = () => { + if (os.platform() === 'darwin') { + return 'Amazon Q: Edit \u2318I' + } + return 'Amazon Q: Edit (Ctrl+I)' + } + updateState(_changeSource: AnnotationChangeSource, _force: boolean): AnnotationState { + return this + } + isNextState(_state: AnnotationState | undefined): boolean { + return false + } +} + +/** + * There are + * - existing users + * - new users + * -- new users who has not seen tutorial + * -- new users who has seen tutorial + * + * "existing users" should have the context key "CODEWHISPERER_AUTO_TRIGGER_ENABLED" + * "new users who has seen tutorial" should have the context key "inlineKey" and "CODEWHISPERER_AUTO_TRIGGER_ENABLED" + * the remaining grouop of users should belong to "new users who has not seen tutorial" + */ +export class InlineTutorialAnnotation implements vscode.Disposable { + private readonly _disposable: vscode.Disposable + private _editor: vscode.TextEditor | undefined + + private _currentState: AnnotationState + + private readonly cwLineHintDecoration: vscode.TextEditorDecorationType = + vscode.window.createTextEditorDecorationType({ + after: { + margin: '0 0 0 3em', + // "borderRadius" and "padding" are not available on "after" type of decoration, this is a hack to inject these css prop to "after" content. Refer to https://github.com/microsoft/vscode/issues/68845 + textDecoration: ';border-radius:0.25rem;padding:0rem 0.5rem;', + width: 'fit-content', + }, + rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen, + }) + + constructor( + private readonly lineTracker: LineTracker, + private readonly sessionManager: SessionManager + ) { + const cachedState = fromId(globals.globalState.get(inlinehintKey), sessionManager) + const cachedAutotriggerEnabled = globals.globalState.get('CODEWHISPERER_AUTO_TRIGGER_ENABLED') + + // new users (has or has not seen tutorial) + if (cachedAutotriggerEnabled === undefined || cachedState !== undefined) { + this._currentState = cachedState ?? new AutotriggerState(this.sessionManager) + getLogger().debug( + `codewhisperer: new user login, activating inline tutorial. (autotriggerEnabled=${cachedAutotriggerEnabled}; inlineState=${cachedState?.id})` + ) + } else { + this._currentState = new EndState() + getLogger().debug(`codewhisperer: existing user login, disabling inline tutorial.`) + } + + this._disposable = vscode.Disposable.from( + vscodeUtilities.subscribeOnce(this.lineTracker.onReady)(async (_) => { + await this.onReady() + }), + this.lineTracker.onDidChangeActiveLines(async (e) => { + await this.onActiveLinesChanged(e) + }), + AuthUtil.instance.auth.onDidChangeConnectionState(async (e) => { + if (e.state !== 'authenticating') { + await this.refresh(vscode.window.activeTextEditor, 'editor') + } + }), + AuthUtil.instance.secondaryAuth.onDidChangeActiveConnection(async () => { + await this.refresh(vscode.window.activeTextEditor, 'editor') + }) + ) + } + + dispose() { + this._disposable.dispose() + } + + private _isReady: boolean = false + + private async onReady(): Promise { + this._isReady = !(this._currentState instanceof EndState) + await this._refresh(vscode.window.activeTextEditor, 'editor') + } + + async triggered(triggerType: vscode.InlineCompletionTriggerKind): Promise { + // TODO: this logic will take ~200ms each trigger, need to root cause and re-enable once it's fixed, or it should only be invoked when the tutorial is actually needed + // await telemetry.withTraceId(async () => { + // if (!this._isReady) { + // return + // } + // if (this._currentState instanceof ManualtriggerState) { + // if ( + // triggerType === vscode.InlineCompletionTriggerKind.Invoke && + // this._currentState.hasManualTrigger === false + // ) { + // this._currentState.hasManualTrigger = true + // } + // if ( + // this.sessionManager.getActiveRecommendation().length > 0 && + // this._currentState.hasValidResponse === false + // ) { + // this._currentState.hasValidResponse = true + // } + // } + // await this.refresh(vscode.window.activeTextEditor, 'codewhisperer') + // }, TelemetryHelper.instance.traceId) + } + + isTutorialDone(): boolean { + return this._currentState.id === new EndState().id + } + + isInlineChatHint(): boolean { + return this._currentState.id === new InlineChatState().id + } + + async dismissTutorial() { + this._currentState = new EndState() + await setContext('aws.codewhisperer.tutorial.workInProgress', false) + await globals.globalState.update(inlinehintKey, this._currentState.id) + } + + /** + * Trys to show the inline hint, if the tutorial is not finished it will not be shown + */ + async tryShowInlineHint(): Promise { + if (this.isTutorialDone()) { + this._isReady = true + this._currentState = new InlineChatState() + return true + } + return false + } + + async tryHideInlineHint(): Promise { + if (this._currentState instanceof InlineChatState) { + this._currentState = new EndState() + return true + } + return false + } + + private async onActiveLinesChanged(e: LinesChangeEvent) { + if (!this._isReady) { + return + } + + this.clear() + + await this.refresh(e.editor, e.reason) + } + + clear() { + this._editor?.setDecorations(this.cwLineHintDecoration, []) + } + + async refresh(editor: vscode.TextEditor | undefined, source: AnnotationChangeSource, force?: boolean) { + if (force) { + this.refreshDebounced.cancel() + await this._refresh(editor, source, true) + } else { + await this.refreshDebounced.promise(editor, source) + } + } + + private readonly refreshDebounced = cancellableDebounce( + async (editor: vscode.TextEditor | undefined, source: AnnotationChangeSource, force?: boolean) => { + await this._refresh(editor, source, force) + }, + 250 + ) + + private async _refresh(editor: vscode.TextEditor | undefined, source: AnnotationChangeSource, force?: boolean) { + if (!this._isReady) { + this.clear() + return + } + + if (this.isTutorialDone()) { + this.clear() + return + } + + if (editor === undefined && this._editor === undefined) { + this.clear() + return + } + + const selections = this.lineTracker.selections + if (editor === undefined || selections === undefined || !editorUtilities.isTextEditor(editor)) { + this.clear() + return + } + + if (this._editor !== editor) { + // Clear any annotations on the previously active editor + this.clear() + this._editor = editor + } + + // Make sure the editor hasn't died since the await above and that we are still on the same line(s) + if (editor.document === undefined || !this.lineTracker.includes(selections)) { + this.clear() + return + } + + if (!AuthUtil.instance.isConnectionValid()) { + this.clear() + return + } + + // Disable Tips when language is not supported by Amazon Q. + if (!runtimeLanguageContext.isLanguageSupported(editor.document)) { + return + } + + await this.updateDecorations(editor, selections, source, force) + } + + private async updateDecorations( + editor: vscode.TextEditor, + lines: LineSelection[], + source: AnnotationChangeSource, + force?: boolean + ) { + const range = editor.document.validateRange( + new vscode.Range(lines[0].active, maxSmallIntegerV8, lines[0].active, maxSmallIntegerV8) + ) + + const decorationOptions = this.getInlineDecoration(editor, lines, source, force) as + | vscode.DecorationOptions + | undefined + + if (decorationOptions === undefined) { + this.clear() + await setContext('aws.codewhisperer.tutorial.workInProgress', false) + return + } else if (this.isTutorialDone()) { + // special case + // Endstate is meaningless and doesnt need to be rendered + this.clear() + await this.dismissTutorial() + return + } else if (decorationOptions.renderOptions?.after?.contentText === new TryMoreExState().text()) { + // special case + // case 3 exit criteria is to fade away in 30s + setTimeout(async () => { + await this.refresh(editor, source, true) + }, case3TimeWindow) + } + + decorationOptions.range = range + + await globals.globalState.update(inlinehintKey, this._currentState.id) + if (!this.isInlineChatHint()) { + await setContext('aws.codewhisperer.tutorial.workInProgress', true) + } + editor.setDecorations(this.cwLineHintDecoration, [decorationOptions]) + } + + getInlineDecoration( + editor: vscode.TextEditor, + lines: LineSelection[], + source: AnnotationChangeSource, + force?: boolean + ): Partial | undefined { + const isCWRunning = this.sessionManager.getActiveSession()?.isRequestInProgress ?? false + + const textOptions: vscode.ThemableDecorationAttachmentRenderOptions = { + contentText: '', + fontWeight: 'normal', + fontStyle: 'normal', + textDecoration: 'none', + color: 'var(--vscode-editor-background)', + backgroundColor: 'var(--vscode-foreground)', + } + + if (isCWRunning && this._currentState.suppressWhileRunning) { + return undefined + } + + const updatedState: AnnotationState | undefined = this._currentState.updateState(source, force ?? false) + + if (updatedState === undefined) { + return undefined + } + + if (this._currentState.isNextState(updatedState)) { + // special case because PressTabState is part of case_1 (1a) which possibly jumps directly from case_1a to case_2 and miss case_1 + if (this._currentState instanceof PressTabState) { + telemetry.ui_click.emit({ elementId: AutotriggerState.id, passive: true }) + } + telemetry.ui_click.emit({ elementId: this._currentState.id, passive: true }) + } + + // update state + this._currentState = updatedState + + // take snapshot of accepted session so that we can compre if there is delta -> users accept 1 suggestion after seeing this state + AutotriggerState.acceptedCount = this.sessionManager.acceptedSuggestionCount + + textOptions.contentText = this._currentState.text() + + return { + renderOptions: { after: textOptions }, + } + } + + public get currentState(): AnnotationState { + return this._currentState + } +} diff --git a/packages/amazonq/src/app/inline/webViewPanel.ts b/packages/amazonq/src/app/inline/webViewPanel.ts new file mode 100644 index 00000000000..2effa94429c --- /dev/null +++ b/packages/amazonq/src/app/inline/webViewPanel.ts @@ -0,0 +1,450 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +/* eslint-disable no-restricted-imports */ +import fs from 'fs' +import { getLogger } from 'aws-core-vscode/shared' + +/** + * Interface for JSON request log data + */ +interface RequestLogEntry { + timestamp: string + request: string + response: string + endpoint: string + error: string + requestId: string + responseCode: number + applicationLogs?: { + rts?: string[] + ceo?: string[] + [key: string]: string[] | undefined + } + latency?: number + latencyBreakdown?: { + rts?: number + ceo?: number + [key: string]: number | undefined + } + miscellaneous?: any +} + +/** + * Manages the webview panel for displaying insert text content and request logs + */ +export class NextEditPredictionPanel implements vscode.Disposable { + public static readonly viewType = 'nextEditPrediction' + + private static instance: NextEditPredictionPanel | undefined + private panel: vscode.WebviewPanel | undefined + private disposables: vscode.Disposable[] = [] + private statusBarItem: vscode.StatusBarItem + private isVisible = false + private fileWatcher: vscode.FileSystemWatcher | undefined + private requestLogs: RequestLogEntry[] = [] + private logFilePath = '/tmp/request_log.jsonl' + private fileReadTimeout: NodeJS.Timeout | undefined + + private constructor() { + // Create status bar item - higher priority (1) to ensure visibility + this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 1) + this.statusBarItem.text = '$(eye) NEP' // Add icon for better visibility + this.statusBarItem.tooltip = 'Toggle Next Edit Prediction Panel' + this.statusBarItem.command = 'aws.amazonq.toggleNextEditPredictionPanel' + this.statusBarItem.show() + + // Register command for toggling the panel + this.disposables.push( + vscode.commands.registerCommand('aws.amazonq.toggleNextEditPredictionPanel', () => { + this.toggle() + }) + ) + } + + /** + * Get or create the NextEditPredictionPanel instance + */ + public static getInstance(): NextEditPredictionPanel { + if (!NextEditPredictionPanel.instance) { + NextEditPredictionPanel.instance = new NextEditPredictionPanel() + } + return NextEditPredictionPanel.instance + } + + /** + * Setup file watcher to monitor the request log file + */ + private setupFileWatcher(): void { + if (this.fileWatcher) { + return + } + + try { + // Create the watcher for the specific file + this.fileWatcher = vscode.workspace.createFileSystemWatcher(this.logFilePath) + + // When file is changed, read it after a delay + this.fileWatcher.onDidChange(() => { + this.scheduleFileRead() + }) + + // When file is created, read it after a delay + this.fileWatcher.onDidCreate(() => { + this.scheduleFileRead() + }) + + this.disposables.push(this.fileWatcher) + + // Initial read of the file if it exists + if (fs.existsSync(this.logFilePath)) { + this.scheduleFileRead() + } + + getLogger('nextEditPrediction').info(`File watcher set up for ${this.logFilePath}`) + } catch (error) { + getLogger('nextEditPrediction').error(`Error setting up file watcher: ${error}`) + } + } + + /** + * Schedule file read with a delay to ensure file is fully written + */ + private scheduleFileRead(): void { + // Clear any existing timeout + if (this.fileReadTimeout) { + clearTimeout(this.fileReadTimeout) + } + + // Schedule new read after 1 second delay + this.fileReadTimeout = setTimeout(() => { + this.readRequestLogFile() + }, 1000) + } + + /** + * Read the request log file and update the panel content + */ + private readRequestLogFile(): void { + getLogger('nextEditPrediction').info(`Attempting to read log file: ${this.logFilePath}`) + try { + if (!fs.existsSync(this.logFilePath)) { + getLogger('nextEditPrediction').info(`Log file does not exist: ${this.logFilePath}`) + return + } + + const content = fs.readFileSync(this.logFilePath, 'utf8') + this.requestLogs = [] + + // Process JSONL format (one JSON object per line) + const lines = content.split('\n').filter((line: string) => line.trim() !== '') + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim() + try { + // Try to parse the JSON, handling potential trailing characters + let jsonString = line + + // Find the last valid JSON by looking for the last closing brace/bracket + const lastClosingBrace = line.lastIndexOf('}') + const lastClosingBracket = line.lastIndexOf(']') + const lastValidChar = Math.max(lastClosingBrace, lastClosingBracket) + + if (lastValidChar > 0 && lastValidChar < line.length - 1) { + // If there are characters after the last valid JSON ending, trim them + jsonString = line.substring(0, lastValidChar + 1) + getLogger('nextEditPrediction').info(`Trimmed extra characters from line ${i + 1}`) + } + + // Step 1: Parse the JSON string to get an object + const parsed = JSON.parse(jsonString) + // Step 2: Stringify the object to normalize it + const normalized = JSON.stringify(parsed) + // Step 3: Parse the normalized string back to an object + const logEntry = JSON.parse(normalized) as RequestLogEntry + + // Parse request and response fields if they're JSON stringss + if (typeof logEntry.request === 'string') { + try { + // Apply the same double-parse technique to nested JSON + const requestObj = JSON.parse(logEntry.request) + const requestNormalized = JSON.stringify(requestObj) + logEntry.request = JSON.parse(requestNormalized) + } catch (e) { + // Keep as string if it's not valid JSON + getLogger('nextEditPrediction').info(`Could not parse request as JSON: ${e}`) + } + } + + if (typeof logEntry.response === 'string') { + try { + // Apply the same double-parse technique to nested JSON + const responseObj = JSON.parse(logEntry.response) + const responseNormalized = JSON.stringify(responseObj) + logEntry.response = JSON.parse(responseNormalized) + } catch (e) { + // Keep as string if it's not valid JSON + getLogger('nextEditPrediction').info(`Could not parse response as JSON: ${e}`) + } + } + + this.requestLogs.push(logEntry) + } catch (e) { + getLogger('nextEditPrediction').error(`Error parsing log entry ${i + 1}: ${e}`) + getLogger('nextEditPrediction').error( + `Problematic line: ${line.length > 100 ? line.substring(0, 100) + '...' : line}` + ) + } + } + + if (this.isVisible && this.panel) { + this.updateRequestLogsView() + } + + getLogger('nextEditPrediction').info(`Read ${this.requestLogs.length} log entries`) + } catch (error) { + getLogger('nextEditPrediction').error(`Error reading log file: ${error}`) + } + } + + /** + * Update the panel with request logs data + */ + private updateRequestLogsView(): void { + if (this.panel) { + this.panel.webview.html = this.getWebviewContent() + getLogger('nextEditPrediction').info('Webview panel updated with request logs') + } + } + + /** + * Toggle the panel visibility + */ + public toggle(): void { + if (this.isVisible) { + this.hide() + } else { + this.show() + } + } + + /** + * Show the panel + */ + public show(): void { + if (!this.panel) { + // Create the webview panel + this.panel = vscode.window.createWebviewPanel( + NextEditPredictionPanel.viewType, + 'Next Edit Prediction', + vscode.ViewColumn.Beside, + { + enableScripts: true, + retainContextWhenHidden: true, + } + ) + + // Set initial content + this.panel.webview.html = this.getWebviewContent() + + // Handle panel disposal + this.panel.onDidDispose( + () => { + this.panel = undefined + this.isVisible = false + this.updateStatusBarItem() + }, + undefined, + this.disposables + ) + + // Handle webview messages + this.panel.webview.onDidReceiveMessage( + (message) => { + switch (message.command) { + case 'refresh': + getLogger('nextEditPrediction').info(`Refresh button clicked`) + this.readRequestLogFile() + break + case 'clear': + getLogger('nextEditPrediction').info(`Clear logs button clicked`) + this.clearLogFile() + break + } + }, + undefined, + this.disposables + ) + } else { + this.panel.reveal() + } + + this.isVisible = true + this.updateStatusBarItem() + + // Setup file watcher when panel is shown + this.setupFileWatcher() + + // If we already have logs, update the view + if (this.requestLogs.length > 0) { + this.updateRequestLogsView() + } else { + // Try to read the log file + this.scheduleFileRead() + } + } + + /** + * Hide the panel + */ + private hide(): void { + if (this.panel) { + this.panel.dispose() + this.panel = undefined + this.isVisible = false + this.updateStatusBarItem() + } + } + + /** + * Update the status bar item appearance based on panel state + */ + private updateStatusBarItem(): void { + if (this.isVisible) { + this.statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground') + } else { + this.statusBarItem.backgroundColor = undefined + } + } + + /** + * Update the panel content with new text + */ + public updateContent(text: string): void { + if (this.panel) { + try { + // Store the text for display in a separate section + const customContent = text + + // Update the panel with both the custom content and the request logs + this.panel.webview.html = this.getWebviewContent(customContent) + getLogger('nextEditPrediction').info('Webview panel content updated') + } catch (error) { + getLogger('nextEditPrediction').error(`Error updating webview: ${error}`) + } + } + } + + /** + * Generate HTML content for the webview + */ + private getWebviewContent(customContent?: string): string { + // Path to the debug.html file + const debugHtmlPath = vscode.Uri.file( + vscode.Uri.joinPath( + vscode.Uri.file(__dirname), + '..', + '..', + '..', + 'app', + 'inline', + 'EditRendering', + 'debug.html' + ).fsPath + ) + + // Read the HTML file content + try { + const htmlContent = fs.readFileSync(debugHtmlPath.fsPath, 'utf8') + getLogger('nextEditPrediction').info(`Successfully loaded debug.html from ${debugHtmlPath.fsPath}`) + + // Modify the HTML to add vscode API initialization + return htmlContent.replace( + '', + ` + + ` + ) + } catch (error) { + getLogger('nextEditPrediction').error(`Error loading debug.html: ${error}`) + return ` + + +

Error loading visualization

+

Failed to load debug.html file: ${error}

+ + + ` + } + } + + /** + * Clear the log file and update the panel + */ + private clearLogFile(): void { + try { + getLogger('nextEditPrediction').info(`Clearing log file: ${this.logFilePath}`) + + // Write an empty string to clear the file + fs.writeFileSync(this.logFilePath, '') + + // Clear the in-memory logs + this.requestLogs = [] + + // Update the view + if (this.isVisible && this.panel) { + this.updateRequestLogsView() + } + + getLogger('nextEditPrediction').info(`Log file cleared successfully`) + } catch (error) { + getLogger('nextEditPrediction').error(`Error clearing log file: ${error}`) + } + } + + /** + * Dispose of resources + */ + public dispose(): void { + if (this.panel) { + this.panel.dispose() + } + + if (this.fileWatcher) { + this.fileWatcher.dispose() + } + + if (this.fileReadTimeout) { + clearTimeout(this.fileReadTimeout) + } + + this.statusBarItem.dispose() + + for (const d of this.disposables) { + d.dispose() + } + this.disposables = [] + + NextEditPredictionPanel.instance = undefined + } +} diff --git a/packages/amazonq/src/commands.ts b/packages/amazonq/src/commands.ts new file mode 100644 index 00000000000..494a3f6a3b7 --- /dev/null +++ b/packages/amazonq/src/commands.ts @@ -0,0 +1,19 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Amazon Q extension commands and implementations. + */ +import * as vscode from 'vscode' +import { Auth } from 'aws-core-vscode/auth' +import { Commands } from 'aws-core-vscode/shared' +import { clearCacheDeclaration } from './util/clearCache' + +export function registerCommands(context: vscode.ExtensionContext) { + context.subscriptions.push( + Commands.register('_aws.amazonq.auth.autoConnect', Auth.instance.tryAutoConnect), + clearCacheDeclaration.register() + ) +} diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts new file mode 100644 index 00000000000..9b83695205c --- /dev/null +++ b/packages/amazonq/src/extension.ts @@ -0,0 +1,202 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' +import { activate as activateCodeWhisperer, shutdown as shutdownCodeWhisperer } from 'aws-core-vscode/codewhisperer' +import { makeEndpointsProvider, registerGenericCommands } from 'aws-core-vscode' +import { CommonAuthWebview } from 'aws-core-vscode/login' +import { + amazonQDiffScheme, + DefaultAWSClientBuilder, + DefaultAwsContext, + ExtContext, + RegionProvider, + Settings, + VirtualFileSystem, + VirtualMemoryFile, + activateLogger, + activateTelemetry, + env, + errors, + fs, + getLogger, + getMachineId, + globals, + initialize, + initializeComputeRegion, + messages, + placeholder, + setContext, + setupUninstallHandler, + maybeShowMinVscodeWarning, + Experiments, + isSageMaker, + isAmazonLinux2, + ProxyUtil, +} from 'aws-core-vscode/shared' +import { ExtStartUpSources } from 'aws-core-vscode/telemetry' +import { VSCODE_EXTENSION_ID } from 'aws-core-vscode/utils' +import { join } from 'path' +import * as semver from 'semver' +import * as vscode from 'vscode' +import { registerCommands } from './commands' +import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat' +import { activate as activateAmazonqLsp } from './lsp/activation' +import { hasGlibcPatch } from './lsp/client' +import { activateAutoDebug } from './lsp/chat/autoDebug/activation' + +export const amazonQContextPrefix = 'amazonq' + +/** + * Activation code for Amazon Q that will we want in all environments (eg Node.js, web mode) + */ +export async function activateAmazonQCommon(context: vscode.ExtensionContext, isWeb: boolean) { + initialize(context, isWeb) + const homeDirLogs = await fs.init(context, (homeDir) => { + void messages.showViewLogsMessage(`Invalid home directory (check $HOME): "${homeDir}"`) + }) + errors.init(fs.getUsername(), env.isAutomation()) + await initializeComputeRegion() + + globals.contextPrefix = 'amazonq.' // todo: disconnect from above line + + // Avoid activation if older toolkit is installed + // Amazon Q is only compatible with AWS Toolkit >= 3.0.0 + // Or AWS Toolkit with a development version. Example: 2.19.0-3413gv + const toolkit = vscode.extensions.getExtension(VSCODE_EXTENSION_ID.awstoolkit) + if (toolkit) { + const toolkitVersion = semver.coerce(toolkit.packageJSON.version) + // XXX: can't use `SemVer.prerelease` because Toolkit "prerelease" (git sha) is not a valid + // semver prerelease: it may start with a number. + const isDevVersion = toolkit.packageJSON.version.toString().includes('-') + if (toolkitVersion && toolkitVersion.major < 3 && !isDevVersion) { + await vscode.commands + .executeCommand('workbench.extensions.installExtension', VSCODE_EXTENSION_ID.awstoolkit) + .then( + () => + vscode.window + .showInformationMessage( + `The Amazon Q extension is incompatible with AWS Toolkit ${ + toolkitVersion as any + } and older. Your AWS Toolkit was updated to version 3.0 or later.`, + 'Reload Now' + ) + .then(async (resp) => { + if (resp === 'Reload Now') { + await vscode.commands.executeCommand('workbench.action.reloadWindow') + } + }), + (reason) => { + getLogger().error('workbench.extensions.installExtension failed: %O', reason) + } + ) + return + } + } + + void maybeShowMinVscodeWarning('1.83.0') + + globals.machineId = await getMachineId() + globals.awsContext = new DefaultAwsContext() + globals.sdkClientBuilder = new DefaultAWSClientBuilder(globals.awsContext) + globals.manifestPaths.endpoints = context.asAbsolutePath(join('resources', 'endpoints.json')) + globals.regionProvider = RegionProvider.fromEndpointsProvider(makeEndpointsProvider()) + + const qLogChannel = vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) + await activateLogger(context, amazonQContextPrefix, qLogChannel) + globals.logOutputChannel = qLogChannel + globals.loginManager = new LoginManager(globals.awsContext, new CredentialsStore()) + + if (homeDirLogs.length > 0) { + getLogger().error('fs.init: invalid env vars found: %O', homeDirLogs) + } + + await activateTelemetry(context, globals.awsContext, Settings.instance, 'Amazon Q For VS Code') + + await initializeAuth(globals.loginManager) + + const extContext = { + extensionContext: context, + } + + // Configure proxy settings early + await ProxyUtil.configureProxyForLanguageServer() + + // This contains every lsp agnostic things (auth, security scan, code scan) + await activateCodeWhisperer(extContext as ExtContext) + + if (!isAmazonLinux2() || hasGlibcPatch()) { + // Activate Amazon Q LSP for everyone unless they're using AL2 without the glibc patch + await activateAmazonqLsp(context) + } + + // Activate AutoDebug feature at extension level + try { + const autoDebugFeature = await activateAutoDebug(context) + context.subscriptions.push(autoDebugFeature) + } catch (error) { + getLogger().error('Failed to activate AutoDebug feature at extension level: %s', error) + } + + // Generic extension commands + registerGenericCommands(context, amazonQContextPrefix) + + // Amazon Q specific commands + registerCommands(context) + + // Handle Amazon Q Extension un-installation. + setupUninstallHandler(VSCODE_EXTENSION_ID.amazonq, context.extension.packageJSON.version, context) + + const vfs = new VirtualFileSystem() + + // Register an empty file that's used when a to open a diff + vfs.registerProvider( + vscode.Uri.from({ scheme: amazonQDiffScheme, path: 'empty' }), + new VirtualMemoryFile(new Uint8Array()) + ) + + // Hide the Amazon Q tree in toolkit explorer + await setContext('aws.toolkit.amazonq.dismissed', true) + + // set context var to check if its SageMaker AI or not + await setContext('aws.isSageMaker', isSageMaker()) + + // set context var to check if its SageMaker Unified Studio or not + await setContext('aws.isSageMakerUnifiedStudio', isSageMaker('SMUS')) + + // reload webviews + await vscode.commands.executeCommand('workbench.action.webview.reloadWebviewAction') + + if (AuthUtils.ExtensionUse.instance.isFirstUse()) { + // Give time for the extension to finish initializing. + globals.clock.setTimeout(async () => { + CommonAuthWebview.authSource = ExtStartUpSources.firstStartUp + focusAmazonQPanel.execute(placeholder, ExtStartUpSources.firstStartUp).catch((e) => { + getLogger().error('focusAmazonQPanel failed: %s', e) + }) + }, 1000) + } + + context.subscriptions.push( + Experiments.instance.onDidChange(async (event) => { + if (event.key === 'amazonqLSP' || event.key === 'amazonqChatLSP' || event.key === 'amazonqLSPInline') { + await vscode.window + .showInformationMessage( + 'Amazon Q LSP setting has changed. Reload VS Code for the changes to take effect.', + 'Reload Now' + ) + .then(async (selection) => { + if (selection === 'Reload Now') { + await vscode.commands.executeCommand('workbench.action.reloadWindow') + } + }) + } + }) + ) +} + +export async function deactivateCommon() { + await shutdownCodeWhisperer() +} diff --git a/packages/amazonq/src/extensionNode.ts b/packages/amazonq/src/extensionNode.ts new file mode 100644 index 00000000000..d42fafea058 --- /dev/null +++ b/packages/amazonq/src/extensionNode.ts @@ -0,0 +1,185 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { activateAmazonQCommon, amazonQContextPrefix, deactivateCommon } from './extension' +import { DefaultAmazonQAppInitContext, AmazonQChatViewProvider } from 'aws-core-vscode/amazonq' +import { activate as activateTransformationHub } from 'aws-core-vscode/amazonqGumby' +import { + ExtContext, + globals, + CrashMonitoring, + getLogger, + isNetworkError, + isSageMaker, + Experiments, +} from 'aws-core-vscode/shared' +import { filetypes, SchemaService } from 'aws-core-vscode/sharedNode' +import { updateDevMode } from 'aws-core-vscode/dev' +import { CommonAuthViewProvider } from 'aws-core-vscode/login' +import { isExtensionActive, VSCODE_EXTENSION_ID } from 'aws-core-vscode/utils' +import { registerSubmitFeedback } from 'aws-core-vscode/feedback' +import { DevOptions } from 'aws-core-vscode/dev' +import { Auth, AuthUtils, getTelemetryMetadataForConn, isAnySsoConnection } from 'aws-core-vscode/auth' +import api from './api' +import { activate as activateCWChat } from './app/chat/activation' +import { beta } from 'aws-core-vscode/dev' +import { activate as activateNotifications, NotificationsController } from 'aws-core-vscode/notifications' +import { AuthState, AuthUtil } from 'aws-core-vscode/codewhisperer' +import { telemetry, AuthUserState } from 'aws-core-vscode/telemetry' +import { activateAgents } from './app/chat/node/activateAgents' + +export async function activate(context: vscode.ExtensionContext) { + // IMPORTANT: No other code should be added to this function. Place it in one of the following 2 functions where appropriate. + await activateAmazonQCommon(context, false) + await activateAmazonQNode(context) + + return api +} + +/** + * The code in this function is not common, implying it only works in Node.js and not web. + * The goal should be for this to not exist and that all code is "common". So if possible make + * the code compatible with web and move it to {@link activateAmazonQCommon}. + */ +async function activateAmazonQNode(context: vscode.ExtensionContext) { + // Intentionally do not await since this is slow and non-critical + void (await CrashMonitoring.instance())?.start() + + const extContext = { + extensionContext: context, + } + + if (!Experiments.instance.get('amazonqChatLSP', true)) { + const appInitContext = DefaultAmazonQAppInitContext.instance + const provider = new AmazonQChatViewProvider( + context, + appInitContext.getWebViewToAppsMessagePublishers(), + appInitContext.getAppsToWebViewMessageListener(), + appInitContext.onDidChangeAmazonQVisibility + ) + context.subscriptions.push( + vscode.window.registerWebviewViewProvider(AmazonQChatViewProvider.viewType, provider, { + webviewOptions: { + retainContextWhenHidden: true, + }, + }) + ) + // this is registered inside of lsp/chat/activation.ts when the chat experiment is enabled + await activateCWChat(context) + } + activateAgents() + await activateTransformationHub(extContext as ExtContext) + + const authProvider = new CommonAuthViewProvider( + context, + amazonQContextPrefix, + DefaultAmazonQAppInitContext.instance.onDidChangeAmazonQVisibility + ) + context.subscriptions.push( + vscode.window.registerWebviewViewProvider(authProvider.viewType, authProvider, { + webviewOptions: { + retainContextWhenHidden: true, + }, + }), + registerSubmitFeedback(context, 'Amazon Q', amazonQContextPrefix) + ) + + globals.schemaService = new SchemaService() + filetypes.activate() + + await setupDevMode(context) + await beta.activate(context) + + // TODO: Should probably emit for web as well. + // Will the web metric look the same? + telemetry.auth_userState.emit({ + passive: true, + result: 'Succeeded', + source: AuthUtils.ExtensionUse.instance.sourceForTelemetry(), + ...(await getAuthState()), + }) + + void activateNotifications(context, getAuthState) +} + +async function getAuthState(): Promise> { + let authState: AuthState = 'disconnected' + try { + // May call connection validate functions that try to refresh the token. + // This could result in network errors. + authState = (await AuthUtil.instance._getChatAuthState(false)).codewhispererChat + } catch (err) { + if ( + isNetworkError(err) && + AuthUtil.instance.conn && + AuthUtil.instance.auth.getConnectionState(AuthUtil.instance.conn) === 'valid' + ) { + authState = 'connectedWithNetworkError' + } else { + throw err + } + } + const currConn = AuthUtil.instance.conn + if (currConn !== undefined && !(isAnySsoConnection(currConn) || isSageMaker())) { + getLogger().error(`Current Amazon Q connection is not SSO, type is: %s`, currConn?.type) + } + + // Pending profile selection state means users already log in with Sso service + if (authState === 'pendingProfileSelection') { + authState = 'connected' + } + + return { + authStatus: + authState === 'connected' || authState === 'expired' || authState === 'connectedWithNetworkError' + ? authState + : 'notConnected', + authEnabledConnections: AuthUtils.getAuthFormIdsFromConnection(currConn).join(','), + ...(await getTelemetryMetadataForConn(currConn)), + } +} + +/** + * Some parts of this do not work in Web mode so we need to set Dev Mode up here. + * + * TODO: Get the following working in web mode as well and then move this function. + */ +async function setupDevMode(context: vscode.ExtensionContext) { + // At some point this imports CodeCatalyst code which breaks in web mode. + // TODO: Make this work in web mode and move it to extensionCommon.ts + await updateDevMode() + + const devOptions: DevOptions = { + context, + auth: () => Auth.instance, + notificationsController: () => NotificationsController.instance, + menuOptions: [ + 'editStorage', + 'resetState', + 'showEnvVars', + 'deleteSsoConnections', + 'expireSsoConnections', + 'editAuthConnections', + 'notificationsSend', + 'forceIdeCrash', + ], + } + + context.subscriptions.push( + vscode.commands.registerCommand('amazonq.dev.openMenu', async () => { + if (!isExtensionActive(VSCODE_EXTENSION_ID.awstoolkit)) { + void vscode.window.showErrorMessage('AWS Toolkit must be installed to access the Developer Menu.') + return + } + await vscode.commands.executeCommand('_aws.dev.invokeMenu', devOptions) + }) + ) +} + +export async function deactivate() { + // Run concurrently to speed up execution. stop() does not throw so it is safe + await Promise.all([(await CrashMonitoring.instance())?.shutdown(), deactivateCommon()]) +} diff --git a/packages/amazonq/src/extensionWeb.ts b/packages/amazonq/src/extensionWeb.ts new file mode 100644 index 00000000000..3091cda2da8 --- /dev/null +++ b/packages/amazonq/src/extensionWeb.ts @@ -0,0 +1,17 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import type { ExtensionContext } from 'vscode' +import { activateWebShared } from 'aws-core-vscode/webShared' +import { activateAmazonQCommon, deactivateCommon } from './extension' + +export async function activate(context: ExtensionContext) { + await activateWebShared(context) + await activateAmazonQCommon(context, true) +} + +export async function deactivate() { + await deactivateCommon() +} diff --git a/packages/amazonq/src/inlineChat/activation.ts b/packages/amazonq/src/inlineChat/activation.ts new file mode 100644 index 00000000000..52c826abb61 --- /dev/null +++ b/packages/amazonq/src/inlineChat/activation.ts @@ -0,0 +1,19 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { InlineChatController } from './controller/inlineChatController' +import { registerInlineCommands } from './command/registerInlineCommands' +import { BaseLanguageClient } from 'vscode-languageclient' +import { InlineChatTutorialAnnotation } from '../app/inline/tutorials/inlineChatTutorialAnnotation' + +export function activate( + context: vscode.ExtensionContext, + client: BaseLanguageClient, + encryptionKey: Buffer, + inlineChatTutorialAnnotation: InlineChatTutorialAnnotation +) { + const inlineChatController = new InlineChatController(context, client, encryptionKey, inlineChatTutorialAnnotation) + registerInlineCommands(context, inlineChatController) +} diff --git a/packages/amazonq/src/inlineChat/codeLenses/codeLenseProvider.ts b/packages/amazonq/src/inlineChat/codeLenses/codeLenseProvider.ts new file mode 100644 index 00000000000..34a85e10b38 --- /dev/null +++ b/packages/amazonq/src/inlineChat/codeLenses/codeLenseProvider.ts @@ -0,0 +1,76 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as os from 'os' +import { InlineTask, TaskState } from '../controller/inlineTask' + +export class CodelensProvider implements vscode.CodeLensProvider { + private codeLenses: vscode.CodeLens[] = [] + private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter() + public readonly onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event + + constructor(context: vscode.ExtensionContext) { + context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', this)) + this.provideCodeLenses = this.provideCodeLenses.bind(this) + } + + public provideCodeLenses(_document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.CodeLens[] { + return this.codeLenses + } + + public updateLenses(task: InlineTask): void { + if (task.state === TaskState.Complete) { + this.codeLenses = [] + this._onDidChangeCodeLenses.fire() + return + } + switch (task.state) { + case TaskState.InProgress: { + this.codeLenses = [] + this.codeLenses.push( + new vscode.CodeLens(new vscode.Range(task.selectedRange.start, task.selectedRange.start), { + title: 'Amazon Q is generating...', + command: '', + }) + ) + break + } + case TaskState.WaitingForDecision: { + let acceptTitle: string + let rejectTitle: string + if (os.platform() === 'darwin') { + acceptTitle = 'Accept ($(newline))' + rejectTitle = `Reject ( \u238B )` + } else { + acceptTitle = 'Accept (Enter)' + rejectTitle = `Reject (Esc)` + } + + this.codeLenses = [] + this.codeLenses.push( + new vscode.CodeLens(new vscode.Range(task.selectedRange.start, task.selectedRange.start), { + title: acceptTitle, + command: 'aws.amazonq.inline.waitForUserDecisionAcceptAll', + arguments: [task], + }) + ) + this.codeLenses.push( + new vscode.CodeLens(new vscode.Range(task.selectedRange.start, task.selectedRange.start), { + title: rejectTitle, + command: 'aws.amazonq.inline.waitForUserDecisionRejectAll', + arguments: [task], + }) + ) + break + } + default: { + this.codeLenses = [] + break + } + } + this._onDidChangeCodeLenses.fire() + } +} diff --git a/packages/amazonq/src/inlineChat/command/registerInlineCommands.ts b/packages/amazonq/src/inlineChat/command/registerInlineCommands.ts new file mode 100644 index 00000000000..48b1fbf5145 --- /dev/null +++ b/packages/amazonq/src/inlineChat/command/registerInlineCommands.ts @@ -0,0 +1,22 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import vscode from 'vscode' +import { InlineChatController } from '../controller/inlineChatController' +import { InlineTask } from '../controller/inlineTask' + +export function registerInlineCommands(context: vscode.ExtensionContext, inlineChatController: InlineChatController) { + context.subscriptions.push( + vscode.commands.registerCommand('aws.amazonq.inline.invokeChat', async () => { + await inlineChatController.inlineQuickPick() + }), + vscode.commands.registerCommand('aws.amazonq.inline.waitForUserDecisionAcceptAll', async (task: InlineTask) => { + await inlineChatController.acceptAllChanges(task, true) + }), + vscode.commands.registerCommand('aws.amazonq.inline.waitForUserDecisionRejectAll', async (task: InlineTask) => { + await inlineChatController.rejectAllChanges(task, true) + }) + ) +} diff --git a/packages/amazonq/src/inlineChat/controller/inlineChatController.ts b/packages/amazonq/src/inlineChat/controller/inlineChatController.ts new file mode 100644 index 00000000000..472591039f3 --- /dev/null +++ b/packages/amazonq/src/inlineChat/controller/inlineChatController.ts @@ -0,0 +1,466 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { randomUUID } from 'crypto' +import * as vscode from 'vscode' +import { InlineDecorator } from '../decorations/inlineDecorator' +import { InlineChatProvider } from '../provider/inlineChatProvider' +import { InlineTask, TaskState, TextDiff } from './inlineTask' +import { responseTransformer } from '../output/responseTransformer' +import { adjustTextDiffForEditing, computeDiff } from '../output/computeDiff' +import { computeDecorations } from '../decorations/computeDecorations' +import { CodelensProvider } from '../codeLenses/codeLenseProvider' +import { PromptMessage, ReferenceLogController } from 'aws-core-vscode/codewhispererChat' +import { CodeWhispererSettings } from 'aws-core-vscode/codewhisperer' +import { UserWrittenCodeTracker } from 'aws-core-vscode/codewhisperer' +import { BaseLanguageClient } from 'vscode-languageclient' +import { + codicon, + getIcon, + getLogger, + messages, + setContext, + Timeout, + textDocumentUtil, + isSageMaker, + Experiments, +} from 'aws-core-vscode/shared' +import { InlineChatTutorialAnnotation } from '../../app/inline/tutorials/inlineChatTutorialAnnotation' + +export class InlineChatController { + private task: InlineTask | undefined + private readonly decorator = new InlineDecorator() + private readonly inlineChatProvider: InlineChatProvider + private readonly codeLenseProvider: CodelensProvider + private readonly referenceLogController = new ReferenceLogController() + private readonly inlineChatTutorialAnnotation: InlineChatTutorialAnnotation + private readonly computeDiffAndRenderOnEditor: (query: string) => Promise + private userQuery: string | undefined + private listeners: vscode.Disposable[] = [] + + constructor( + context: vscode.ExtensionContext, + client: BaseLanguageClient, + encryptionKey: Buffer, + inlineChatTutorialAnnotation: InlineChatTutorialAnnotation + ) { + this.inlineChatProvider = new InlineChatProvider(client, encryptionKey) + this.inlineChatProvider.onErrorOccured(() => this.handleError()) + this.codeLenseProvider = new CodelensProvider(context) + this.inlineChatTutorialAnnotation = inlineChatTutorialAnnotation + this.computeDiffAndRenderOnEditor = Experiments.instance.get('amazonqLSPInlineChat', false) + ? this.computeDiffAndRenderOnEditorLSP.bind(this) + : this.computeDiffAndRenderOnEditorLocal.bind(this) + } + + public async createTask( + query: string, + document: vscode.TextDocument, + selectionRange: vscode.Selection + ): Promise { + const inlineTask = new InlineTask(query, document, selectionRange) + return inlineTask + } + + public async acceptAllChanges(task = this.task, userInvoked: boolean): Promise { + if (!task) { + return + } + const editor = vscode.window.visibleTextEditors.find( + (editor) => editor.document.uri.toString() === task.document.uri.toString() + ) + if (!editor) { + return + } + if (userInvoked) { + this.inlineChatProvider.sendTelemetryEvent( + { + userDecision: 'ACCEPT', + }, + this.task + ) + } + const deletions = task.diff.filter((diff) => diff.type === 'deletion') + await editor.edit( + (editBuilder) => { + for (const deletion of deletions) { + editBuilder.delete(deletion.range) + } + }, + { undoStopAfter: false, undoStopBefore: false } + ) + task.diff = [] + task.updateDecorations() + this.decorator.applyDecorations(task) + await this.updateTaskAndLenses(task) + this.referenceLogController.addReferenceLog(task.codeReferences, task.replacement ? task.replacement : '') + await this.reset() + UserWrittenCodeTracker.instance.onQFinishesEdits() + } + + public async rejectAllChanges(task = this.task, userInvoked: boolean): Promise { + if (!task) { + return + } + const editor = vscode.window.visibleTextEditors.find( + (editor) => editor.document.uri.toString() === task.document.uri.toString() + ) + if (!editor) { + return + } + if (userInvoked) { + this.inlineChatProvider.sendTelemetryEvent( + { + userDecision: 'REJECT', + }, + this.task + ) + } + const insertions = task.diff.filter((diff) => diff.type === 'insertion') + await editor.edit( + (editBuilder) => { + for (const insertion of insertions) { + editBuilder.delete(insertion.range) + } + }, + { undoStopAfter: false, undoStopBefore: false } + ) + task.diff = [] + task.updateDecorations() + this.decorator.applyDecorations(task) + await this.updateTaskAndLenses(task) + this.referenceLogController.addReferenceLog(task.codeReferences, task.replacement ? task.replacement : '') + await this.reset() + } + + public async updateTaskAndLenses(task?: InlineTask, taskState?: TaskState) { + if (!task) { + return + } + if (taskState) { + task.state = taskState + } else if (!task.diff || task.diff.length === 0) { + // If the previous state was waiting for a decision and the code diff is clean, then we mark the task as completed + if (task.state === TaskState.WaitingForDecision) { + task.state = TaskState.Complete + } + } + this.codeLenseProvider.updateLenses(task) + if (task.state === TaskState.InProgress) { + if (vscode.window.activeTextEditor) { + await this.inlineChatTutorialAnnotation.hide(vscode.window.activeTextEditor) + } + } + await this.refreshCodeLenses(task) + if (task.state === TaskState.Complete) { + await this.reset() + } + } + + private async handleError() { + if (!this.task) { + return + } + this.task.state = TaskState.Error + this.codeLenseProvider.updateLenses(this.task) + await this.refreshCodeLenses(this.task) + await this.reset() + } + + private async reset() { + for (const listener of this.listeners) { + listener.dispose() + } + this.listeners = [] + + this.task = undefined + this.inlineChatTutorialAnnotation.enable() + await setContext('amazonq.inline.codelensShortcutEnabled', undefined) + } + + private async refreshCodeLenses(task: InlineTask): Promise { + await vscode.commands.executeCommand('vscode.executeCodeLensProvider', task.document.uri) + } + + public async inlineQuickPick(previouseQuery?: string) { + const editor = vscode.window.activeTextEditor + if (!editor) { + return + } + + if (isSageMaker()) { + void vscode.window.showWarningMessage('Amazon Q: Inline chat is not supported in Sagemaker') + return + } + + if (this.task && this.task.isActiveState()) { + void vscode.window.showWarningMessage( + 'Amazon Q: Reject or Accept the current suggestion before creating a new one' + ) + return + } + + await vscode.window + .showInputBox({ + value: previouseQuery ?? '', + placeHolder: 'Enter instructions for Q', + prompt: codicon`${getIcon('aws-amazonq-q-white')} Edit code`, + }) + .then(async (query) => { + if (!query || query.trim() === '') { + getLogger().info('inlineQuickPick query is empty') + return + } + UserWrittenCodeTracker.instance.onQStartsMakingEdits() + this.userQuery = query + await textDocumentUtil.addEofNewline(editor) + this.task = await this.createTask(query, editor.document, editor.selection) + await this.inlineChatTutorialAnnotation.disable(editor) + await this.computeDiffAndRenderOnEditor(query).catch(async (err) => { + getLogger().error('computeDiffAndRenderOnEditor error: %s', (err as Error)?.message) + if (err instanceof Error) { + void vscode.window.showErrorMessage(`Amazon Q: ${err.message}`) + } else { + void vscode.window.showErrorMessage('Amazon Q encountered an error') + } + await this.handleError() + }) + }) + } + + private async computeDiffAndRenderOnEditorLSP(query: string) { + if (!this.task) { + return + } + + await this.updateTaskAndLenses(this.task, TaskState.InProgress) + getLogger().info(`inline chat query:\n${query}`) + const uuid = randomUUID() + const message: PromptMessage = { + message: query, + messageId: uuid, + command: undefined, + userIntent: undefined, + tabID: uuid, + } + + const response = await this.inlineChatProvider.processPromptMessageLSP(message) + + // TODO: add tests for this case. + if (!response.body) { + getLogger().warn('Empty body in inline chat response') + await this.handleError() + return + } + + // Update inline diff view + const textDiff = computeDiff(response.body, this.task, false) + const decorations = computeDecorations(this.task) + this.task.decorations = decorations + await this.applyDiff(this.task, textDiff ?? []) + this.decorator.applyDecorations(this.task) + + // Update Codelenses + await this.updateTaskAndLenses(this.task, TaskState.WaitingForDecision) + await setContext('amazonq.inline.codelensShortcutEnabled', true) + this.undoListener(this.task) + } + + // TODO: remove this implementation in favor of LSP + private async computeDiffAndRenderOnEditorLocal(query: string) { + if (!this.task) { + return + } + + await this.updateTaskAndLenses(this.task, TaskState.InProgress) + getLogger().info(`inline chat query:\n${query}`) + const uuid = randomUUID() + const message: PromptMessage = { + message: query, + messageId: uuid, + command: undefined, + userIntent: undefined, + tabID: uuid, + } + + const requestStart = performance.now() + let responseStartLatency: number | undefined + + const response = await this.inlineChatProvider.processPromptMessage(message) + this.task.requestId = response?.$metadata.requestId + + // Deselect all code + const editor = vscode.window.activeTextEditor + if (editor) { + const selection = editor.selection + if (!selection.isEmpty) { + const cursor = selection.active + const newSelection = new vscode.Selection(cursor, cursor) + editor.selection = newSelection + } + } + + if (response) { + let qSuggestedCodeResponse = '' + for await (const chatEvent of response.generateAssistantResponseResponse!) { + if ( + chatEvent.assistantResponseEvent?.content !== undefined && + chatEvent.assistantResponseEvent.content.length > 0 + ) { + if (responseStartLatency === undefined) { + responseStartLatency = performance.now() - requestStart + } + + qSuggestedCodeResponse += chatEvent.assistantResponseEvent.content + + const transformedResponse = responseTransformer(qSuggestedCodeResponse, this.task, false) + if (transformedResponse) { + const textDiff = computeDiff(transformedResponse, this.task, true) + const decorations = computeDecorations(this.task) + this.task.decorations = decorations + await this.applyDiff(this.task!, textDiff ?? [], { + undoStopBefore: false, + undoStopAfter: false, + }) + this.decorator.applyDecorations(this.task) + this.task.previouseDiff = textDiff + } + } + if ( + chatEvent.codeReferenceEvent?.references !== undefined && + chatEvent.codeReferenceEvent.references.length > 0 + ) { + this.task.codeReferences = this.task.codeReferences.concat(chatEvent.codeReferenceEvent?.references) + // clear diff if user settings is off for code reference + if (!CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled()) { + await this.rejectAllChanges(this.task, false) + void vscode.window.showInformationMessage( + 'Your settings do not allow code generation with references.' + ) + await this.updateTaskAndLenses(this.task, TaskState.Complete) + return + } + } + if (chatEvent.error) { + getLogger().error('generateAssistantResponse stream error: %s', chatEvent.error) + await this.rejectAllChanges(this.task, false) + void vscode.window.showErrorMessage(`Amazon Q: ${chatEvent.error.message}`) + await this.updateTaskAndLenses(this.task, TaskState.Complete) + return + } + } + + if (this.task) { + // Unclear why we need to check if task is defined, but occasionally an error occurs otherwise + this.task.responseStartLatency = responseStartLatency + this.task.responseEndLatency = performance.now() - requestStart + } + getLogger().info(`qSuggestedCodeResponse:\n${qSuggestedCodeResponse}`) + const transformedResponse = responseTransformer(qSuggestedCodeResponse, this.task, true) + if (transformedResponse) { + const textDiff = computeDiff(transformedResponse, this.task, false) + const decorations = computeDecorations(this.task) + this.task.decorations = decorations + await this.applyDiff(this.task, textDiff ?? []) + this.decorator.applyDecorations(this.task) + await this.updateTaskAndLenses(this.task, TaskState.WaitingForDecision) + await setContext('amazonq.inline.codelensShortcutEnabled', true) + this.undoListener(this.task) + } else { + void messages.showMessageWithCancel( + 'No suggestions from Q, please try different instructions.', + new Timeout(5000) + ) + await this.updateTaskAndLenses(this.task, TaskState.Complete) + await this.inlineQuickPick(this.userQuery) + await this.handleError() + } + } + } + + private async applyDiff( + task: InlineTask, + textDiff: TextDiff[], + undoOption?: { undoStopBefore: boolean; undoStopAfter: boolean } + ) { + const adjustedTextDiff = adjustTextDiffForEditing(textDiff) + const visibleEditor = vscode.window.visibleTextEditors.find( + (editor) => editor.document.uri === task.document.uri + ) + const previousDiff = task.previouseDiff?.filter((diff) => diff.type === 'insertion') + + if (visibleEditor) { + if (previousDiff) { + await visibleEditor.edit( + (editBuilder) => { + for (const insertion of previousDiff) { + editBuilder.delete(insertion.range) + } + }, + { undoStopAfter: false, undoStopBefore: false } + ) + } + await visibleEditor.edit( + (editBuilder) => { + for (const change of adjustedTextDiff) { + if (change.type === 'insertion') { + editBuilder.insert(change.range.start, change.replacementText) + } + } + }, + undoOption ?? { undoStopBefore: true, undoStopAfter: false } + ) + } else { + if (previousDiff) { + const edit = new vscode.WorkspaceEdit() + for (const insertion of previousDiff) { + edit.delete(task.document.uri, insertion.range) + } + await vscode.workspace.applyEdit(edit) + } + const edit = new vscode.WorkspaceEdit() + for (const change of textDiff) { + if (change.type === 'insertion') { + edit.insert(task.document.uri, change.range.start, change.replacementText) + } + } + await vscode.workspace.applyEdit(edit) + } + } + + private undoListener(task: InlineTask) { + const listener: vscode.Disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { + const { document, contentChanges } = event + + if (document.uri.toString() !== task.document.uri.toString()) { + return + } + + const changeIntersectsRange = contentChanges.some((change) => { + const { range } = change + if (task.selectedRange) { + return !( + range.end.isBefore(task.selectedRange.start) || range.start.isAfter(task.selectedRange.end) + ) + } + }) + + if (!changeIntersectsRange) { + return + } + + const updatedSelectedText = document.getText(task.selectedRange) + + if (updatedSelectedText.trim() === task.selectedText.trim()) { + task.diff = [] + await this.updateTaskAndLenses(task) + task.updateDecorations() + this.decorator.applyDecorations(task) + listener.dispose() + } + }) + + this.listeners.push(listener) + } +} diff --git a/packages/amazonq/src/inlineChat/controller/inlineTask.ts b/packages/amazonq/src/inlineChat/controller/inlineTask.ts new file mode 100644 index 00000000000..a6a169ad58c --- /dev/null +++ b/packages/amazonq/src/inlineChat/controller/inlineTask.ts @@ -0,0 +1,160 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import type { CodeReference } from 'aws-core-vscode/amazonq' +import type { InlineChatEvent } from 'aws-core-vscode/codewhisperer' +import type { Decorations } from '../decorations/inlineDecorator' +import { computeDecorations } from '../decorations/computeDecorations' +import { extractLanguageNameFromFile } from 'aws-core-vscode/codewhispererChat' +import { textDocumentUtil } from 'aws-core-vscode/shared' + +interface TextToInsert { + type: 'insertion' + replacementText: string + range: vscode.Range +} + +interface TextToDelete { + type: 'deletion' + originalText: string + range: vscode.Range +} + +interface DiffBlock { + originalText: string + replacementText: string + range: vscode.Range +} + +export type TextDiff = TextToInsert | TextToDelete + +export enum TaskState { + Idle = 'Idle', + InProgress = 'InProgress', + WaitingForDecision = 'WaitingForDecision', + Complete = 'Complete', + Error = 'Error', +} + +export class InlineTask { + public state: TaskState = TaskState.Idle + public diff: TextDiff[] = [] + public decorations: Decorations | undefined + public diffBlock: DiffBlock[] = [] + public codeReferences: CodeReference[] = [] + public selectedText: string + public languageName: string | undefined + + public partialSelectedText: string | undefined + public partialSelectedTextRight: string | undefined + + public previouseDiff: TextDiff[] | undefined + public selectedRange: vscode.Range + public inProgressReplacement: string | undefined + public replacement: string | undefined + + // Telemetry fields + public requestId?: string + public responseStartLatency?: number + public responseEndLatency?: number + + constructor( + public query: string, + public document: vscode.TextDocument, + selection: vscode.Selection + ) { + this.selectedRange = textDocumentUtil.expandSelectionToFullLines(document, selection) + this.selectedText = document.getText(this.selectedRange) + this.languageName = extractLanguageNameFromFile(document) + } + + public revertDiff(): void { + this.diff = [] + this.decorations = { + linesAdded: [], + linesRemoved: [], + } + } + + public removeDiffChangeByRange(range: vscode.Range): void { + if (this.diff) { + this.diff = this.diff.filter((change) => !change.range.isEqual(range)) + } + } + + public updateDecorations(): void { + const isEmpty = + !this.decorations || + (this.decorations?.linesAdded?.length === 0 && this.decorations?.linesRemoved?.length === 0) + + if (isEmpty) { + return + } + const updatedDecorations = computeDecorations(this) + this.decorations = updatedDecorations + } + + public updateDiff(affectedRange: vscode.Range, deletedLines: number) { + const diffsAfter = this.diff.filter((edit) => edit.range.start.isAfter(affectedRange.end)) + for (const diff of diffsAfter) { + diff.range = new vscode.Range( + diff.range.start.translate(-deletedLines), + diff.range.end.translate(-deletedLines) + ) + } + } + + // Telemetry methods + public get numSelectedLines() { + return this.selectedText.split('\n').length + } + + public get inputLength() { + return this.query.length + } + + public inlineChatEventBase() { + let numSuggestionAddChars = 0 + let numSuggestionAddLines = 0 + let numSuggestionDelChars = 0 + let numSuggestionDelLines = 0 + + for (const diff of this.diff) { + if (diff.type === 'insertion') { + numSuggestionAddChars += diff.replacementText.length + numSuggestionAddLines += diff.range.end.line - diff.range.start.line + 1 + } else { + numSuggestionDelChars += diff.originalText.length + numSuggestionDelLines += diff.range.end.line - diff.range.start.line + 1 + } + } + + const programmingLanguage = this.languageName + ? { + languageName: this.languageName, + } + : undefined + + const event: Partial = { + requestId: this.requestId, + timestamp: new Date(), + inputLength: this.inputLength, + numSelectedLines: this.numSelectedLines, + codeIntent: true, + responseStartLatency: this.responseStartLatency, + responseEndLatency: this.responseEndLatency, + numSuggestionAddChars, + numSuggestionAddLines, + numSuggestionDelChars, + numSuggestionDelLines, + programmingLanguage, + } + return event + } + + public isActiveState() { + return !(this.state === TaskState.Complete || this.state === TaskState.Error) + } +} diff --git a/packages/amazonq/src/inlineChat/decorations/computeDecorations.ts b/packages/amazonq/src/inlineChat/decorations/computeDecorations.ts new file mode 100644 index 00000000000..fb289494b3f --- /dev/null +++ b/packages/amazonq/src/inlineChat/decorations/computeDecorations.ts @@ -0,0 +1,32 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { InlineTask } from '../controller/inlineTask' +import { Decorations } from './inlineDecorator' + +export function computeDecorations(task: InlineTask): Decorations | undefined { + if (!task.diff) { + return + } + + const decorations: Decorations = { + linesAdded: [], + linesRemoved: [], + } + + for (const edit of task.diff) { + const countChanged = edit.range.end.line - edit.range.start.line - 1 + if (edit.type === 'deletion') { + decorations.linesRemoved.push({ + range: new vscode.Range(edit.range.start.line, 0, edit.range.start.line + countChanged, 0), + }) + } else if (edit.type === 'insertion') { + decorations.linesAdded.push({ + range: new vscode.Range(edit.range.start.line, 0, edit.range.start.line + countChanged, 0), + }) + } + } + return decorations +} diff --git a/packages/amazonq/src/inlineChat/decorations/inlineDecorator.ts b/packages/amazonq/src/inlineChat/decorations/inlineDecorator.ts new file mode 100644 index 00000000000..3c4e3899e13 --- /dev/null +++ b/packages/amazonq/src/inlineChat/decorations/inlineDecorator.ts @@ -0,0 +1,38 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { InlineTask } from '../controller/inlineTask' + +export interface Decorations { + linesAdded: vscode.DecorationOptions[] + linesRemoved: vscode.DecorationOptions[] +} + +const removedTextDecorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: 'rgba(255, 0, 0, 0.1)', + isWholeLine: true, +}) + +const AddedTextDecorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: 'rgba(0, 255, 0, 0.1)', + isWholeLine: true, +}) + +export class InlineDecorator { + public applyDecorations(task: InlineTask): void { + const decorations = task.decorations + if (!decorations) { + return + } + const editors = vscode.window.visibleTextEditors.filter( + (editor) => editor.document.uri.toString() === task.document.uri.toString() + ) + for (const editor of editors) { + editor.setDecorations(AddedTextDecorationType, decorations.linesAdded ?? []) + editor.setDecorations(removedTextDecorationType, decorations.linesRemoved ?? []) + } + } +} diff --git a/packages/amazonq/src/inlineChat/output/computeDiff.ts b/packages/amazonq/src/inlineChat/output/computeDiff.ts new file mode 100644 index 00000000000..a17a7914cf6 --- /dev/null +++ b/packages/amazonq/src/inlineChat/output/computeDiff.ts @@ -0,0 +1,115 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { type LinesOptions, diffLines } from 'diff' +import * as vscode from 'vscode' +import { InlineTask, TextDiff } from '../controller/inlineTask' + +export function computeDiff(response: string, inlineTask: InlineTask, isPartialDiff: boolean): TextDiff[] | undefined { + if (!response) { + return + } + const selectedRange = inlineTask.selectedRange + const partialSelectedText = inlineTask.partialSelectedText ?? '' + const selectedText = isPartialDiff ? partialSelectedText : inlineTask.selectedText + + const normalizedResponse = + getLeadingWhitespace(selectedText) + response.trim() + getTrailingWhitespace(selectedText) + + const diffs = diffLines(selectedText, normalizedResponse, { + stripTrailingCr: true, + ignoreNewlineAtEof: true, + } as LinesOptions) + + const textDiff: TextDiff[] = [] + let startLine = selectedRange.start.line + for (const part of diffs) { + const count = part.count ?? 0 + if (part.removed) { + if (part.value !== '\n') { + textDiff.push({ + type: 'deletion', + originalText: part.value, + range: new vscode.Range(startLine, 0, startLine + count, 0), + }) + } + } else if (part.added) { + if (part.value !== '\n') { + // The partial response sometimes doesn't have the correct ending newline character (\n), so we ensure that every insertion respects the code formatting. + if (isPartialDiff && !part.value.endsWith('\n')) { + part.value += '\n' + } + textDiff.push({ + type: 'insertion', + replacementText: part.value, + range: new vscode.Range(startLine, 0, startLine + count, 0), + }) + } + } + startLine += count + } + inlineTask.diff = textDiff + return textDiff +} + +export function adjustTextDiffForEditing(textDiff: TextDiff[]): TextDiff[] { + let linesAdded = 0 + const adjustedDiff: TextDiff[] = [] + + for (const edit of textDiff) { + const { range, type } = edit + const { start, end } = range + const linesChanged = end.line - start.line + + const adjustedRange = new vscode.Range( + new vscode.Position(start.line - linesAdded, start.character), + new vscode.Position(end.line - linesAdded, end.character) + ) + + adjustedDiff.push({ + ...edit, + range: adjustedRange, + }) + + if (type === 'insertion') { + linesAdded += linesChanged + } + } + + return adjustedDiff +} + +export function getDiffBlocks(inlineTask: InlineTask): vscode.Range[] { + const diff = inlineTask.diff + + if (!diff || diff.length === 0) { + return [] + } + + const diffBlocks: vscode.Range[] = [] + let currentRange: vscode.Range | undefined + + for (const change of diff) { + const { range } = change + if (!currentRange || range.start.line !== currentRange.end.line) { + currentRange = range + diffBlocks.push(range) + } else { + currentRange = new vscode.Range(currentRange.start, range.end) + diffBlocks[diffBlocks.length - 1] = currentRange + } + } + + return diffBlocks +} + +function getLeadingWhitespace(str: string): string { + const match = str.match(/^\s*/) + return match ? match[0] : '' +} + +function getTrailingWhitespace(str: string): string { + const match = str.match(/\s*$/) + return match ? match[0] : '' +} diff --git a/packages/amazonq/src/inlineChat/output/responseTransformer.ts b/packages/amazonq/src/inlineChat/output/responseTransformer.ts new file mode 100644 index 00000000000..b965ced519e --- /dev/null +++ b/packages/amazonq/src/inlineChat/output/responseTransformer.ts @@ -0,0 +1,55 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getLogger } from 'aws-core-vscode/shared' +import { decode } from 'he' +import { InlineTask } from '../controller/inlineTask' + +/** + * Transforms the response from the INLINE_CHAT GenerateAssistantResponse call. + * + * @param response - The raw response string from GenerateAssistantResponse. + * @param inlineTask - The inline task object containing information about the current task. + * @param isWholeResponse - A boolean indicating whether this is a complete response or a partial one. + * @returns The decoded response string, or undefined if an error occurs. + */ +export function responseTransformer( + response: string, + inlineTask: InlineTask, + isWholeResponse: boolean +): string | undefined { + try { + const decodedResponse = decode(response) + if (!isWholeResponse) { + const [partialSelectedCode, right] = extractPartialCode(decodedResponse, inlineTask) + inlineTask.partialSelectedText = partialSelectedCode + inlineTask.partialSelectedTextRight = right + return decodedResponse + } else { + return decodedResponse + } + } catch (err) { + getLogger().error('An unknown error occurred: %s', (err as Error).message) + return undefined + } +} + +/** + * This function is used to handle partial responses in inline tasks. It divides + * the selected text into two parts: + * 1. The "left" part, which contains the same number of lines as the response. + * 2. The "right" part, which contains the remaining lines. + * + * @param response - The response string from the assistant. + * @param inlineTask - The inline task object containing the full selected text. + * @returns A tuple with two strings: [leftPart, rightPart]. + */ +function extractPartialCode(response: string, inlineTask: InlineTask): [string, string] { + const lineCount = response.split('\n').length + const splitLines = inlineTask.selectedText.split('\n') + const left = splitLines.slice(0, lineCount).join('\n') + const right = splitLines.slice(lineCount).join('\n') + return [left, right] +} diff --git a/packages/amazonq/src/inlineChat/provider/inlineChatProvider.ts b/packages/amazonq/src/inlineChat/provider/inlineChatProvider.ts new file mode 100644 index 00000000000..c2dc94de36f --- /dev/null +++ b/packages/amazonq/src/inlineChat/provider/inlineChatProvider.ts @@ -0,0 +1,249 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { + CodeWhispererStreamingServiceException, + GenerateAssistantResponseCommandOutput, +} from '@amzn/codewhisperer-streaming' +import { BaseLanguageClient } from 'vscode-languageclient' +import { inlineChatRequestType } from '@aws/language-server-runtimes/protocol' +import { AuthUtil, getSelectedCustomization } from 'aws-core-vscode/codewhisperer' +import { + ChatSessionStorage, + ChatTriggerType, + EditorContextExtractor, + PromptMessage, + TriggerEventsStorage, + TriggerPayload, + triggerPayloadToChatRequest, + UserIntentRecognizer, +} from 'aws-core-vscode/codewhispererChat' +import { AwsClientResponseError, getLogger, isAwsError, ToolkitError } from 'aws-core-vscode/shared' +import { randomUUID } from 'crypto' +import { codeWhispererClient } from 'aws-core-vscode/codewhisperer' +import type { InlineChatEvent } from 'aws-core-vscode/codewhisperer' +import { InlineTask } from '../controller/inlineTask' +import { extractAuthFollowUp } from 'aws-core-vscode/amazonq' +import { InlineChatParams, InlineChatResult } from '@aws/language-server-runtimes-types' +import { decryptResponse, encryptRequest } from '../../lsp/encryption' +import { getCursorState } from '../../lsp/utils' + +export class InlineChatProvider { + private readonly editorContextExtractor: EditorContextExtractor + private readonly userIntentRecognizer: UserIntentRecognizer + private readonly sessionStorage: ChatSessionStorage + private readonly triggerEventsStorage: TriggerEventsStorage + private errorEmitter = new vscode.EventEmitter() + public onErrorOccured = this.errorEmitter.event + + public constructor( + private readonly client: BaseLanguageClient, + private readonly encryptionKey: Buffer + ) { + this.editorContextExtractor = new EditorContextExtractor() + this.userIntentRecognizer = new UserIntentRecognizer() + this.sessionStorage = new ChatSessionStorage() + this.triggerEventsStorage = new TriggerEventsStorage() + } + + private getCurrentEditorParams(prompt: string): InlineChatParams { + const editor = vscode.window.activeTextEditor + if (!editor) { + throw new ToolkitError('No active editor') + } + + const documentUri = editor.document.uri.toString() + const cursorState = getCursorState(editor.selections) + return { + prompt: { + prompt, + }, + cursorState, + textDocument: { + uri: documentUri, + }, + } + } + + public async processPromptMessageLSP(message: PromptMessage): Promise { + // TODO: handle partial responses. + getLogger().info('Making inline chat request with message %O', message) + const params = this.getCurrentEditorParams(message.message ?? '') + + const inlineChatRequest = await encryptRequest(params, this.encryptionKey) + const response = await this.client.sendRequest(inlineChatRequestType.method, inlineChatRequest) + const inlineChatResponse = await decryptResponse(response, this.encryptionKey) + this.client.info(`Logging response for inline chat ${JSON.stringify(inlineChatResponse)}`) + + return inlineChatResponse + } + + // TODO: remove in favor of LSP implementation. + public async processPromptMessage(message: PromptMessage) { + return this.editorContextExtractor + .extractContextForTrigger('ChatMessage') + .then((context) => { + const triggerID = randomUUID() + this.triggerEventsStorage.addTriggerEvent({ + id: triggerID, + tabID: message.tabID, + message: message.message, + type: 'inline_chat', + context, + }) + return this.generateResponse( + { + message: message.message ?? '', + trigger: ChatTriggerType.InlineChatMessage, + query: message.message, + codeSelection: context?.focusAreaContext?.selectionInsideExtendedCodeBlock, + fileText: context?.focusAreaContext?.extendedCodeBlock ?? '', + fileLanguage: context?.activeFileContext?.fileLanguage, + filePath: context?.activeFileContext?.filePath, + matchPolicy: context?.activeFileContext?.matchPolicy, + codeQuery: context?.focusAreaContext?.names, + userIntent: this.userIntentRecognizer.getFromPromptChatMessage(message), + customization: getSelectedCustomization(), + profile: AuthUtil.instance.regionProfileManager.activeRegionProfile, + context: [], + relevantTextDocuments: [], + additionalContents: [], + documentReferences: [], + useRelevantDocuments: false, + contextLengths: { + additionalContextLengths: { + fileContextLength: 0, + promptContextLength: 0, + ruleContextLength: 0, + }, + truncatedAdditionalContextLengths: { + fileContextLength: 0, + promptContextLength: 0, + ruleContextLength: 0, + }, + workspaceContextLength: 0, + truncatedWorkspaceContextLength: 0, + userInputContextLength: 0, + truncatedUserInputContextLength: 0, + focusFileContextLength: 0, + truncatedFocusFileContextLength: 0, + }, + }, + triggerID + ) + }) + .catch((e) => { + this.processException(e, message.tabID) + }) + } + + private async generateResponse( + triggerPayload: TriggerPayload & { projectContextQueryLatencyMs?: number }, + triggerID: string + ) { + const triggerEvent = this.triggerEventsStorage.getTriggerEvent(triggerID) + if (triggerEvent === undefined) { + return + } + + if (triggerEvent.tabID === 'no-available-tabs') { + return + } + + if (triggerEvent.tabID === undefined) { + setTimeout(() => { + this.generateResponse(triggerPayload, triggerID).catch((e) => { + getLogger().error('generateResponse failed: %s', (e as Error).message) + }) + }, 20) + return + } + + const tabID = triggerEvent.tabID + + const credentialsState = await AuthUtil.instance.getChatAuthState() + if ( + !(credentialsState.codewhispererChat === 'connected' && credentialsState.codewhispererCore === 'connected') + ) { + const { message } = extractAuthFollowUp(credentialsState) + this.errorEmitter.fire() + throw new ToolkitError(message) + } + triggerPayload.useRelevantDocuments = false + + const request = triggerPayloadToChatRequest(triggerPayload) + const session = this.sessionStorage.getSession(tabID) + getLogger().debug( + `request from tab: ${tabID} conversationID: ${session.sessionIdentifier} request: %O`, + request + ) + + let response: GenerateAssistantResponseCommandOutput | undefined = undefined + session.createNewTokenSource() + try { + response = await session.chatSso(request) + getLogger().info( + `response to tab: ${tabID} conversationID: ${session.sessionIdentifier} requestID: ${response.$metadata.requestId} metadata: %O`, + response.$metadata + ) + } catch (e: any) { + this.processException(e, tabID) + } + + return response + } + + private processException(e: any, tabID: string) { + let errorMessage: string | undefined + let requestID: string | undefined + if (typeof e === 'string') { + errorMessage = e.toUpperCase() + } else if (e instanceof SyntaxError) { + // Workaround to handle case when LB returns web-page with error and our client doesn't return proper exception + errorMessage = AwsClientResponseError.tryExtractReasonFromSyntaxError(e) + } else if (e instanceof CodeWhispererStreamingServiceException) { + errorMessage = e.message + requestID = e.$metadata.requestId + } else if (e instanceof Error) { + errorMessage = e.message + } + + this.errorEmitter.fire() + this.sessionStorage.deleteSession(tabID) + + throw ToolkitError.chain(e, errorMessage ?? 'Failed to get response', { + details: { + tabID, + requestID, + }, + }) + } + + public sendTelemetryEvent(inlineChatEvent: InlineChatEvent, currentTask?: InlineTask) { + codeWhispererClient + .sendTelemetryEvent({ + telemetryEvent: { + inlineChatEvent: { + ...inlineChatEvent, + ...(currentTask?.inlineChatEventBase() ?? {}), + }, + }, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + + getLogger().debug( + `Failed to sendTelemetryEvent to CodeWhisperer, requestId: ${ + requestId ?? '' + }, message: ${error.message}` + ) + }) + } +} diff --git a/packages/amazonq/src/lsp/activation.ts b/packages/amazonq/src/lsp/activation.ts new file mode 100644 index 00000000000..e2c1b6899d9 --- /dev/null +++ b/packages/amazonq/src/lsp/activation.ts @@ -0,0 +1,30 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import vscode from 'vscode' +import { startLanguageServer } from './client' +import { AmazonQLspInstaller, getBundledResourcePaths } from './lspInstaller' +import { lspSetupStage, ToolkitError, messages, getLogger } from 'aws-core-vscode/shared' + +export async function activate(ctx: vscode.ExtensionContext): Promise { + try { + await lspSetupStage('all', async () => { + const installResult = await new AmazonQLspInstaller().resolve() + await lspSetupStage('launch', async () => await startLanguageServer(ctx, installResult.resourcePaths)) + }) + } catch (err) { + const e = err as ToolkitError + getLogger('amazonqLsp').warn(`Failed to start downloaded LSP, falling back to bundled LSP: ${e.message}`) + try { + await lspSetupStage('all', async () => { + await lspSetupStage('launch', async () => await startLanguageServer(ctx, getBundledResourcePaths(ctx))) + }) + } catch (error) { + void messages.showViewLogsMessage( + `Failed to launch Amazon Q language server: ${(error as ToolkitError).message}` + ) + } + } +} diff --git a/packages/amazonq/src/lsp/auth.ts b/packages/amazonq/src/lsp/auth.ts new file mode 100644 index 00000000000..b4093362004 --- /dev/null +++ b/packages/amazonq/src/lsp/auth.ts @@ -0,0 +1,180 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + bearerCredentialsUpdateRequestType, + iamCredentialsUpdateRequestType, + ConnectionMetadata, + NotificationType, + RequestType, + ResponseMessage, + UpdateCredentialsParams, +} from '@aws/language-server-runtimes/protocol' +import * as jose from 'jose' +import * as crypto from 'crypto' +import { BaseLanguageClient } from 'vscode-languageclient' +import { AuthUtil } from 'aws-core-vscode/codewhisperer' +import { Writable } from 'stream' +import { onceChanged, onceChangedWithComparator } from 'aws-core-vscode/utils' +import { getLogger, oneMinute, isSageMaker } from 'aws-core-vscode/shared' +import { isSsoConnection, isIamConnection, areCredentialsEqual } from 'aws-core-vscode/auth' + +export const encryptionKey = crypto.randomBytes(32) + +/** + * Sends a json payload to the language server, who is waiting to know what the encryption key is. + * Code reference: https://github.com/aws/language-servers/blob/7da212185a5da75a72ce49a1a7982983f438651a/client/vscode/src/credentialsActivation.ts#L77 + */ +export function writeEncryptionInit(stream: Writable): void { + const request = { + version: '1.0', + mode: 'JWT', + key: encryptionKey.toString('base64'), + } + stream.write(JSON.stringify(request)) + stream.write('\n') +} + +/** + * Request for custom notifications that Update Credentials and tokens. + * See core\aws-lsp-core\src\credentials\updateCredentialsRequest.ts for details + */ +export interface UpdateCredentialsRequest { + /** + * Encrypted token (JWT or PASETO) + * The token's contents differ whether IAM or Bearer token is sent + */ + data: string + /** + * Used by the runtime based language servers. + * Signals that this client will encrypt its credentials payloads. + */ + encrypted: boolean +} + +export const notificationTypes = { + updateBearerToken: new RequestType( + 'aws/credentials/token/update' + ), + deleteBearerToken: new NotificationType('aws/credentials/token/delete'), + getConnectionMetadata: new RequestType( + 'aws/credentials/getConnectionMetadata' + ), +} + +/** + * Facade over our VSCode Auth that does crud operations on the language server auth + */ +export class AmazonQLspAuth { + #logErrorIfChanged = onceChanged((s) => getLogger('amazonqLsp').error(s)) + constructor( + private readonly client: BaseLanguageClient, + private readonly authUtil: AuthUtil = AuthUtil.instance + ) {} + + /** + * @param force bypass memoization, and forcefully update the bearer token + */ + async refreshConnection(force: boolean = false) { + const activeConnection = this.authUtil.conn + if (this.authUtil.isConnectionValid()) { + if (isSsoConnection(activeConnection)) { + // Existing SSO path + const token = await this.authUtil.getBearerToken() + await (force ? this._updateBearerToken(token) : this.updateBearerToken(token)) + } else if (isSageMaker() && isIamConnection(activeConnection)) { + // New SageMaker IAM path + const credentials = await this.authUtil.getCredentials() + await (force ? this._updateIamCredentials(credentials) : this.updateIamCredentials(credentials)) + } + } + } + + async logRefreshError(e: unknown) { + const err = e as Error + this.#logErrorIfChanged(`Unable to update bearer token: ${err.name}:${err.message}`) + } + + public updateBearerToken = onceChanged(this._updateBearerToken.bind(this)) + private async _updateBearerToken(token: string) { + const request = await this.createUpdateBearerCredentialsRequest(token) + + // "aws/credentials/token/update" + // https://github.com/aws/language-servers/blob/44d81f0b5754747d77bda60b40cc70950413a737/core/aws-lsp-core/src/credentials/credentialsProvider.ts#L27 + await this.client.sendRequest(bearerCredentialsUpdateRequestType.method, request) + + this.client.info(`UpdateBearerToken: ${JSON.stringify(request)}`) + } + + public updateIamCredentials = onceChangedWithComparator( + this._updateIamCredentials.bind(this), + ([prevCreds], [currentCreds]) => areCredentialsEqual(prevCreds, currentCreds) + ) + private async _updateIamCredentials(credentials: any) { + getLogger().info( + `[SageMaker Debug] Updating IAM credentials - credentials received: ${credentials ? 'YES' : 'NO'}` + ) + if (credentials) { + getLogger().info( + `[SageMaker Debug] IAM credentials structure: accessKeyId=${credentials.accessKeyId ? 'present' : 'missing'}, secretAccessKey=${credentials.secretAccessKey ? 'present' : 'missing'}, sessionToken=${credentials.sessionToken ? 'present' : 'missing'}, expiration=${credentials.expiration ? 'present' : 'missing'}` + ) + } + + const request = await this.createUpdateIamCredentialsRequest(credentials) + + // "aws/credentials/iam/update" + await this.client.sendRequest(iamCredentialsUpdateRequestType.method, request) + + this.client.info(`UpdateIamCredentials: ${JSON.stringify(request)}`) + getLogger().info(`[SageMaker Debug] IAM credentials update request sent successfully`) + } + + public startTokenRefreshInterval(pollingTime: number = oneMinute / 2) { + const interval = setInterval(async () => { + await this.refreshConnection().catch((e) => this.logRefreshError(e)) + }, pollingTime) + return interval + } + + private async createUpdateBearerCredentialsRequest(token: string): Promise { + const bearerCredentials = { token } + const payload = new TextEncoder().encode(JSON.stringify({ data: bearerCredentials })) + + const jwt = await new jose.CompactEncrypt(payload) + .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) + .encrypt(encryptionKey) + + return { + data: jwt, + metadata: { + sso: { + startUrl: AuthUtil.instance.startUrl, + }, + }, + encrypted: true, + } + } + + private async createUpdateIamCredentialsRequest(credentials: any): Promise { + // Extract IAM credentials structure + const iamCredentials = { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + sessionToken: credentials.sessionToken, + expiration: credentials.expiration, + } + const payload = new TextEncoder().encode(JSON.stringify({ data: iamCredentials })) + + const jwt = await new jose.CompactEncrypt(payload) + .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) + .encrypt(encryptionKey) + + return { + data: jwt, + // Omit metadata for IAM credentials since startUrl is undefined for non-SSO connections + encrypted: true, + } + } +} diff --git a/packages/amazonq/src/lsp/chat/activation.ts b/packages/amazonq/src/lsp/chat/activation.ts new file mode 100644 index 00000000000..93f4d68cf2a --- /dev/null +++ b/packages/amazonq/src/lsp/chat/activation.ts @@ -0,0 +1,104 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { window } from 'vscode' +import { BaseLanguageClient } from 'vscode-languageclient' +import { AmazonQChatViewProvider } from './webviewProvider' +import { focusAmazonQPanel, registerCommands } from './commands' +import { + registerActiveEditorChangeListener, + registerLanguageServerEventListener, + registerMessageListeners, +} from './messages' +import { Commands, getLogger, globals, undefinedIfEmpty } from 'aws-core-vscode/shared' +import { activate as registerLegacyChatListeners } from '../../app/chat/activation' +import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq' +import { AuthUtil, getSelectedCustomization } from 'aws-core-vscode/codewhisperer' +import { pushConfigUpdate } from '../config' +import { AutoDebugLspClient } from './autoDebug/lsp/autoDebugLspClient' + +export async function activate(languageClient: BaseLanguageClient, encryptionKey: Buffer, mynahUIPath: string) { + const disposables = globals.context.subscriptions + + const provider = new AmazonQChatViewProvider(mynahUIPath, languageClient) + + // Set the chat view provider for AutoDebug to use + AutoDebugLspClient.setChatViewProvider(provider) + + disposables.push( + window.registerWebviewViewProvider(AmazonQChatViewProvider.viewType, provider, { + webviewOptions: { + retainContextWhenHidden: true, + }, + }) + ) + + /** + * Commands are registered independent of the webview being open because when they're executed + * they focus the webview + **/ + registerCommands(provider) + registerLanguageServerEventListener(languageClient, provider) + registerActiveEditorChangeListener(languageClient) + + provider.onDidResolveWebview(() => { + const disposable = DefaultAmazonQAppInitContext.instance.getAppsToWebViewMessageListener().onMessage((msg) => { + /** + * codewhispers app handler is still registered because the activation flow hasn't been refactored. + * We need to explicitly deny events like restoreTabMessage, otherwise they will be forwarded to the frontend + * + */ + if (msg.sender === 'CWChat' && ['restoreTabMessage', 'contextCommandData'].includes(msg.type)) { + return + } + provider.webview?.postMessage(msg).then(undefined, (e) => { + getLogger().error('webView.postMessage failed: %s', (e as Error).message) + }) + }) + + if (provider.webviewView) { + disposables.push( + provider.webviewView.onDidDispose(() => { + disposable.dispose() + }) + ) + } + + registerMessageListeners(languageClient, provider, encryptionKey) + }) + + // register event listeners from the legacy agent flow + await registerLegacyChatListeners(globals.context) + + disposables.push( + AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(async () => { + await provider.refreshWebview() + }), + Commands.register('aws.amazonq.updateCustomizations', () => { + void pushConfigUpdate(languageClient, { + type: 'customization', + customization: undefinedIfEmpty(getSelectedCustomization().arn), + }) + }), + Commands.register('aws.amazonq.manageSubscription', () => { + focusAmazonQPanel().catch((e: Error) => languageClient.error(`[VSCode Client] focusAmazonQPanel() failed`)) + + languageClient + .sendRequest('workspace/executeCommand', { + command: 'aws/chat/manageSubscription', + // arguments: [], + }) + .catch((e: Error) => { + getLogger('amazonqLsp').error('failed request: aws/chat/manageSubscription: %O', e) + }) + }), + globals.logOutputChannel.onDidChangeLogLevel((logLevel) => { + getLogger('amazonqLsp').info(`Local log level changed to ${logLevel}, notifying LSP`) + void pushConfigUpdate(languageClient, { + type: 'logLevel', + }) + }) + ) +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/activation.ts b/packages/amazonq/src/lsp/chat/autoDebug/activation.ts new file mode 100644 index 00000000000..f18c82c237a --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/activation.ts @@ -0,0 +1,78 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger } from 'aws-core-vscode/shared' +import { AutoDebugCommands } from './commands' +import { AutoDebugCodeActionsProvider } from './codeActionsProvider' +import { AutoDebugController } from './controller' + +/** + * Auto Debug feature activation for Amazon Q + * This handles the complete lifecycle of the auto debug feature + */ +export class AutoDebugFeature implements vscode.Disposable { + private readonly logger = getLogger() + private readonly disposables: vscode.Disposable[] = [] + + private autoDebugCommands?: AutoDebugCommands + private codeActionsProvider?: AutoDebugCodeActionsProvider + private controller?: AutoDebugController + + constructor(private readonly context: vscode.ExtensionContext) {} + + /** + * Activate the auto debug feature + */ + async activate(): Promise { + try { + // Initialize the controller first + this.controller = new AutoDebugController() + + // Initialize commands and register them with the controller + this.autoDebugCommands = new AutoDebugCommands() + this.autoDebugCommands.registerCommands(this.context, this.controller) + + // Initialize code actions provider + this.codeActionsProvider = new AutoDebugCodeActionsProvider() + this.context.subscriptions.push(this.codeActionsProvider) + + // Add all to disposables + this.disposables.push(this.controller, this.autoDebugCommands, this.codeActionsProvider) + } catch (error) { + this.logger.error('AutoDebugFeature: Failed to activate auto debug feature: %s', error) + throw error + } + } + + /** + * Get the auto debug controller instance + */ + getController(): AutoDebugController | undefined { + return this.controller + } + + /** + * Dispose of all resources + */ + dispose(): void { + vscode.Disposable.from(...this.disposables).dispose() + } +} + +/** + * Factory function to activate auto debug feature with LSP client + * This is the main entry point for activating auto debug + */ +export async function activateAutoDebug( + context: vscode.ExtensionContext, + client?: any, + encryptionKey?: Buffer +): Promise { + const feature = new AutoDebugFeature(context) + await feature.activate() + + return feature +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/codeActionsProvider.ts b/packages/amazonq/src/lsp/chat/autoDebug/codeActionsProvider.ts new file mode 100644 index 00000000000..aed926eaacf --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/codeActionsProvider.ts @@ -0,0 +1,119 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' + +/** + * Provides code actions for Amazon Q Auto Debug features. + * Integrates with VS Code's quick fix system to offer debugging assistance. + */ +export class AutoDebugCodeActionsProvider implements vscode.CodeActionProvider, vscode.Disposable { + private readonly disposables: vscode.Disposable[] = [] + + public static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix, vscode.CodeActionKind.Refactor] + + constructor() { + this.registerProvider() + } + + private registerProvider(): void { + // Register for all file types + const selector: vscode.DocumentSelector = [{ scheme: 'file' }] + + this.disposables.push( + vscode.languages.registerCodeActionsProvider(selector, this, { + providedCodeActionKinds: AutoDebugCodeActionsProvider.providedCodeActionKinds, + }) + ) + } + + /** + * Provides code actions for the given document and range + */ + public provideCodeActions( + document: vscode.TextDocument, + range: vscode.Range | vscode.Selection, + context: vscode.CodeActionContext, + token: vscode.CancellationToken + ): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> { + if (token.isCancellationRequested) { + return [] + } + + const actions: vscode.CodeAction[] = [] + + // Get diagnostics for the current range + const diagnostics = context.diagnostics.filter( + (diagnostic) => diagnostic.range.intersection(range) !== undefined + ) + + if (diagnostics.length > 0) { + // Add "Fix with Amazon Q" action + actions.push(this.createFixWithQAction(document, range, diagnostics)) + + // Add "Fix All with Amazon Q" action + actions.push(this.createFixAllWithQAction(document)) + + // Add "Explain Problem" action + actions.push(this.createExplainProblemAction(document, range, diagnostics)) + } + return actions + } + + private createFixWithQAction( + document: vscode.TextDocument, + range: vscode.Range, + diagnostics: vscode.Diagnostic[] + ): vscode.CodeAction { + const action = new vscode.CodeAction( + `Amazon Q: Fix Problem (${diagnostics.length} issue${diagnostics.length !== 1 ? 's' : ''})`, + vscode.CodeActionKind.QuickFix + ) + + action.command = { + command: 'amazonq.01.fixWithQ', + title: 'Amazon Q: Fix Problem', + arguments: [range, diagnostics], + } + + action.diagnostics = diagnostics + action.isPreferred = true // Make this the preferred quick fix + + return action + } + + private createFixAllWithQAction(document: vscode.TextDocument): vscode.CodeAction { + const action = new vscode.CodeAction('Amazon Q: Fix All Errors', vscode.CodeActionKind.QuickFix) + + action.command = { + command: 'amazonq.02.fixAllWithQ', + title: 'Amazon Q: Fix All Errors', + } + + return action + } + + private createExplainProblemAction( + document: vscode.TextDocument, + range: vscode.Range, + diagnostics: vscode.Diagnostic[] + ): vscode.CodeAction { + const action = new vscode.CodeAction('Amazon Q: Explain Problem', vscode.CodeActionKind.QuickFix) + + action.command = { + command: 'amazonq.03.explainProblem', + title: 'Amazon Q: Explain Problem', + arguments: [range, diagnostics], + } + + action.diagnostics = diagnostics + + return action + } + + public dispose(): void { + vscode.Disposable.from(...this.disposables).dispose() + } +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/commands.ts b/packages/amazonq/src/lsp/chat/autoDebug/commands.ts new file mode 100644 index 00000000000..ecdbf80d1e0 --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/commands.ts @@ -0,0 +1,177 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { Commands, getLogger, messages } from 'aws-core-vscode/shared' +import { AutoDebugController } from './controller' +import { autoDebugTelemetry } from './telemetry' + +/** + * Auto Debug commands for Amazon Q + * Handles all command registrations and implementations + */ +export class AutoDebugCommands implements vscode.Disposable { + private readonly logger = getLogger() + private readonly disposables: vscode.Disposable[] = [] + private controller!: AutoDebugController + + /** + * Register all auto debug commands + */ + registerCommands(context: vscode.ExtensionContext, controller: AutoDebugController): void { + this.controller = controller + this.disposables.push( + // Fix with Amazon Q command + Commands.register( + { + id: 'amazonq.01.fixWithQ', + name: 'Amazon Q: Fix Problem', + }, + async (range?: vscode.Range, diagnostics?: vscode.Diagnostic[]) => { + await this.fixWithAmazonQ(range, diagnostics) + } + ), + + // Fix All with Amazon Q command + Commands.register( + { + id: 'amazonq.02.fixAllWithQ', + name: 'Amazon Q: Fix All Errors', + }, + async () => { + await this.fixAllWithAmazonQ() + } + ), + + // Explain Problem with Amazon Q command + Commands.register( + { + id: 'amazonq.03.explainProblem', + name: 'Amazon Q: Explain Problem', + }, + async (range?: vscode.Range, diagnostics?: vscode.Diagnostic[]) => { + await this.explainProblem(range, diagnostics) + } + ) + ) + + // Add all disposables to context + context.subscriptions.push(...this.disposables) + } + + /** + * Generic error handling wrapper for command execution + */ + private async executeWithErrorHandling( + action: () => Promise, + errorMessage: string, + logContext: string + ): Promise { + try { + return await action() + } catch (error) { + this.logger.error(`AutoDebugCommands: Error in ${logContext}: %s`, error) + + // Record telemetry failure based on context + const commandType = + logContext === 'fixWithAmazonQ' + ? 'fixWithQ' + : logContext === 'fixAllWithAmazonQ' + ? 'fixAllWithQ' + : 'explainProblem' + autoDebugTelemetry.recordCommandFailure(commandType, String(error)) + + void messages.showMessage('error', 'Amazon Q was not able to fix or explain the problem. Try again shortly') + } + } + + /** + * Check if there's an active editor and log warning if not + */ + private checkActiveEditor(): vscode.TextEditor | undefined { + const editor = vscode.window.activeTextEditor + if (!editor) { + this.logger.warn('AutoDebugCommands: No active editor found') + } + return editor + } + + /** + * Fix with Amazon Q - fixes only the specific issues the user selected + */ + private async fixWithAmazonQ(range?: vscode.Range, diagnostics?: vscode.Diagnostic[]): Promise { + const problemCount = diagnostics?.length + autoDebugTelemetry.recordCommandInvocation('fixWithQ', problemCount) + + await this.executeWithErrorHandling( + async () => { + const editor = this.checkActiveEditor() + if (!editor) { + return + } + const saved = await editor.document.save() + if (!saved) { + throw new Error('Failed to save document') + } + await this.controller.fixSpecificProblems(range, diagnostics) + autoDebugTelemetry.recordCommandSuccess('fixWithQ', problemCount) + }, + 'Fix with Amazon Q', + 'fixWithAmazonQ' + ) + } + + /** + * Fix All with Amazon Q - processes all errors in the current file + */ + private async fixAllWithAmazonQ(): Promise { + autoDebugTelemetry.recordCommandInvocation('fixAllWithQ') + + await this.executeWithErrorHandling( + async () => { + const editor = this.checkActiveEditor() + if (!editor) { + return + } + const saved = await editor.document.save() + if (!saved) { + throw new Error('Failed to save document') + } + const problemCount = await this.controller.fixAllProblemsInFile(10) // 10 errors per batch + autoDebugTelemetry.recordCommandSuccess('fixAllWithQ', problemCount) + }, + 'Fix All with Amazon Q', + 'fixAllWithAmazonQ' + ) + } + + /** + * Explains the problem using Amazon Q + */ + private async explainProblem(range?: vscode.Range, diagnostics?: vscode.Diagnostic[]): Promise { + const problemCount = diagnostics?.length + autoDebugTelemetry.recordCommandInvocation('explainProblem', problemCount) + + await this.executeWithErrorHandling( + async () => { + const editor = this.checkActiveEditor() + if (!editor) { + return + } + await this.controller.explainProblems(range, diagnostics) + autoDebugTelemetry.recordCommandSuccess('explainProblem', problemCount) + }, + 'Explain Problem', + 'explainProblem' + ) + } + + /** + * Dispose of all resources + */ + dispose(): void { + vscode.Disposable.from(...this.disposables).dispose() + } +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/controller.ts b/packages/amazonq/src/lsp/chat/autoDebug/controller.ts new file mode 100644 index 00000000000..66dcc83b21d --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/controller.ts @@ -0,0 +1,206 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger, randomUUID, messages } from 'aws-core-vscode/shared' +import { AutoDebugLspClient } from './lsp/autoDebugLspClient' +import { mapDiagnosticSeverity } from './shared/diagnosticUtils' +import { ErrorContextFormatter } from './diagnostics/errorContext' +import { Problem } from './diagnostics/problemDetector' +export interface AutoDebugConfig { + readonly enabled: boolean + readonly excludedSources: string[] + readonly severityFilter: ('error' | 'warning' | 'info' | 'hint')[] +} + +/** + * Simplified controller for Amazon Q Auto Debug system. + * Focuses on context menu and quick fix functionality without workspace-wide monitoring. + */ +export class AutoDebugController implements vscode.Disposable { + private readonly logger = getLogger() + private readonly lspClient: AutoDebugLspClient + private readonly errorFormatter: ErrorContextFormatter + private readonly disposables: vscode.Disposable[] = [] + + private config: AutoDebugConfig + + constructor(config?: Partial) { + this.config = { + enabled: true, + excludedSources: [], // No default exclusions - let users configure as needed + severityFilter: ['error'], // Only auto-fix errors, not warnings + ...config, + } + + this.lspClient = new AutoDebugLspClient() + this.errorFormatter = new ErrorContextFormatter() + } + + /** + * Extract common logic for getting problems from diagnostics + */ + private async getProblemsFromDiagnostics( + range?: vscode.Range, + diagnostics?: vscode.Diagnostic[] + ): Promise<{ editor: vscode.TextEditor; problems: Problem[] } | undefined> { + const editor = vscode.window.activeTextEditor + if (!editor) { + throw new Error('No active editor found') + } + + // Use provided diagnostics or get diagnostics for the range + let targetDiagnostics = diagnostics + if (!targetDiagnostics && range) { + const allDiagnostics = vscode.languages.getDiagnostics(editor.document.uri) + targetDiagnostics = allDiagnostics.filter((d) => d.range.intersection(range) !== undefined) + } + + if (!targetDiagnostics || targetDiagnostics.length === 0) { + return undefined + } + + // Convert diagnostics to problems + const problems = targetDiagnostics.map((diagnostic) => ({ + uri: editor.document.uri, + diagnostic, + severity: mapDiagnosticSeverity(diagnostic.severity), + source: diagnostic.source || 'unknown', + isNew: false, + })) + + return { editor, problems } + } + + /** + * Filter diagnostics to only errors and apply source filtering + */ + private filterErrorDiagnostics(diagnostics: vscode.Diagnostic[]): vscode.Diagnostic[] { + return diagnostics.filter((d) => { + if (d.severity !== vscode.DiagnosticSeverity.Error) { + return false + } + // Apply source filtering + if (this.config.excludedSources.length > 0 && d.source) { + return !this.config.excludedSources.includes(d.source) + } + return true + }) + } + + /** + * Fix specific problems in the code + */ + async fixSpecificProblems(range?: vscode.Range, diagnostics?: vscode.Diagnostic[]): Promise { + try { + const result = await this.getProblemsFromDiagnostics(range, diagnostics) + if (!result) { + return + } + const fixMessage = this.createFixMessage(result.editor.document.uri.fsPath, result.problems) + await this.sendMessageToChat(fixMessage) + } catch (error) { + this.logger.error('AutoDebugController: Error fixing specific problems: %s', error) + throw error + } + } + + /** + * Fix with Amazon Q - sends up to 15 error messages one time when user clicks the button + */ + public async fixAllProblemsInFile(maxProblems: number = 15): Promise { + try { + const editor = vscode.window.activeTextEditor + if (!editor) { + void messages.showMessage('warn', 'No active editor found') + return 0 + } + + // Get all diagnostics for the current file + const allDiagnostics = vscode.languages.getDiagnostics(editor.document.uri) + const errorDiagnostics = this.filterErrorDiagnostics(allDiagnostics) + if (errorDiagnostics.length === 0) { + return 0 + } + + // Take up to maxProblems errors (15 by default) + const diagnosticsToFix = errorDiagnostics.slice(0, maxProblems) + const result = await this.getProblemsFromDiagnostics(undefined, diagnosticsToFix) + if (!result) { + return 0 + } + + const fixMessage = this.createFixMessage(result.editor.document.uri.fsPath, result.problems) + await this.sendMessageToChat(fixMessage) + return result.problems.length + } catch (error) { + this.logger.error('AutoDebugController: Error in fix process: %s', error) + throw error + } + } + + /** + * Explain problems using Amazon Q + */ + async explainProblems(range?: vscode.Range, diagnostics?: vscode.Diagnostic[]): Promise { + try { + const result = await this.getProblemsFromDiagnostics(range, diagnostics) + if (!result) { + return + } + const explainMessage = this.createExplainMessage(result.editor.document.uri.fsPath, result.problems) + await this.sendMessageToChat(explainMessage) + } catch (error) { + this.logger.error('AutoDebugController: Error explaining problems: %s', error) + throw error + } + } + + private createFixMessage(filePath: string, problems: Problem[]): string { + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '' + const formattedProblems = this.errorFormatter.formatProblemsString(problems, workspaceRoot) + + return `Please help me fix the following errors in ${filePath}:${formattedProblems}` + } + + private createExplainMessage(filePath: string, problems: Problem[]): string { + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '' + const formattedProblems = this.errorFormatter.formatProblemsString(problems, workspaceRoot) + + return `Please explain the following problems in ${filePath}. DO NOT edit files. ONLY provide explanation:${formattedProblems}` + } + + /** + * Sends message directly to language server bypassing webview connectors + * This ensures messages go through the proper LSP chat system + */ + private async sendMessageToChat(message: string): Promise { + const triggerID = randomUUID() + try { + const success = await this.lspClient.sendChatMessage({ + message: message, + triggerType: 'autoDebug', + eventId: triggerID, + }) + + if (success) { + this.logger.debug('AutoDebugController: Chat message sent successfully through LSP client') + } else { + this.logger.error('AutoDebugController: Failed to send chat message through LSP client') + throw new Error('Failed to send message through LSP client') + } + } catch (error) { + this.logger.error( + 'AutoDebugController: Error sending message through LSP client with triggerID %s: %s', + triggerID, + error + ) + } + } + + public dispose(): void { + vscode.Disposable.from(...this.disposables).dispose() + } +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/diagnostics/diagnosticsMonitor.ts b/packages/amazonq/src/lsp/chat/autoDebug/diagnostics/diagnosticsMonitor.ts new file mode 100644 index 00000000000..8f4000bf217 --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/diagnostics/diagnosticsMonitor.ts @@ -0,0 +1,22 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' + +export interface DiagnosticCollection { + readonly diagnostics: [vscode.Uri, vscode.Diagnostic[]][] + readonly timestamp: number +} + +export interface DiagnosticSnapshot { + readonly diagnostics: DiagnosticCollection + readonly captureTime: number + readonly id: string +} + +export interface FileDiagnostics { + readonly uri: vscode.Uri + readonly diagnostics: vscode.Diagnostic[] +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/diagnostics/errorContext.ts b/packages/amazonq/src/lsp/chat/autoDebug/diagnostics/errorContext.ts new file mode 100644 index 00000000000..dee7bc0565a --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/diagnostics/errorContext.ts @@ -0,0 +1,75 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as path from 'path' +import { Problem } from './problemDetector' + +export interface ErrorContext { + readonly source: string + readonly severity: 'error' | 'warning' | 'info' | 'hint' + readonly location: { + readonly file: string + readonly line: number + readonly column: number + readonly range?: vscode.Range + } + readonly message: string + readonly code?: string | number + readonly relatedInformation?: vscode.DiagnosticRelatedInformation[] + readonly suggestedFixes?: vscode.CodeAction[] + readonly surroundingCode?: string +} + +export interface FormattedErrorReport { + readonly summary: string + readonly details: string + readonly contextualCode: string + readonly suggestions: string +} + +/** + * Formats diagnostic errors into contextual information for AI debugging assistance. + */ +export class ErrorContextFormatter { + /** + * Creates a problems string with Markdown formatting for better readability + */ + public formatProblemsString(problems: Problem[], cwd: string): string { + let result = '' + const fileGroups = this.groupProblemsByFile(problems) + + for (const [filePath, fileProblems] of fileGroups.entries()) { + if (fileProblems.length > 0) { + result += `\n\n**${path.relative(cwd, filePath)}**\n\n` + + // Group problems into a code block for better formatting + result += '```\n' + for (const problem of fileProblems) { + const line = problem.diagnostic.range.start.line + 1 + const source = problem.source ? `${problem.source}` : 'Unknown' + result += `[${source}] Line ${line}: ${problem.diagnostic.message}\n` + } + result += '```' + } + } + + return result.trim() + } + + private groupProblemsByFile(problems: Problem[]): Map { + const groups = new Map() + + for (const problem of problems) { + const filePath = problem.uri.fsPath + if (!groups.has(filePath)) { + groups.set(filePath, []) + } + groups.get(filePath)!.push(problem) + } + + return groups + } +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/diagnostics/problemDetector.ts b/packages/amazonq/src/lsp/chat/autoDebug/diagnostics/problemDetector.ts new file mode 100644 index 00000000000..44d00b55ca1 --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/diagnostics/problemDetector.ts @@ -0,0 +1,21 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' + +export interface Problem { + readonly uri: vscode.Uri + readonly diagnostic: vscode.Diagnostic + readonly severity: 'error' | 'warning' | 'info' | 'hint' + readonly source: string + readonly isNew: boolean +} + +export interface CategorizedProblems { + readonly errors: Problem[] + readonly warnings: Problem[] + readonly info: Problem[] + readonly hints: Problem[] +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/index.ts b/packages/amazonq/src/lsp/chat/autoDebug/index.ts new file mode 100644 index 00000000000..4819835066b --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/index.ts @@ -0,0 +1,18 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Auto Debug feature for Amazon Q + * + * This module provides auto debug functionality including: + * - Command registration for fixing problems with Amazon Q + * - Code actions provider for quick fixes + * - Integration with VSCode's diagnostic system + */ + +export { AutoDebugFeature, activateAutoDebug } from './activation' +export { AutoDebugCommands } from './commands' +export { AutoDebugCodeActionsProvider } from './codeActionsProvider' +export { AutoDebugController } from './controller' diff --git a/packages/amazonq/src/lsp/chat/autoDebug/lsp/autoDebugLspClient.ts b/packages/amazonq/src/lsp/chat/autoDebug/lsp/autoDebugLspClient.ts new file mode 100644 index 00000000000..2d2d0ca3664 --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/lsp/autoDebugLspClient.ts @@ -0,0 +1,75 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { getLogger, placeholder } from 'aws-core-vscode/shared' +import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat' + +export class AutoDebugLspClient { + private readonly logger = getLogger() + private static chatViewProvider: any // AmazonQChatViewProvider instance + + /** + * Sets the chat view provider instance + */ + public static setChatViewProvider(provider: any): void { + AutoDebugLspClient.chatViewProvider = provider + } + + public async sendChatMessage(params: { message: string; triggerType: string; eventId: string }): Promise { + try { + // Ensure the chat view provider and webview are available + await this.ensureWebviewReady() + + // Get the webview provider from the static reference + const amazonQChatViewProvider = AutoDebugLspClient.chatViewProvider + + if (!amazonQChatViewProvider?.webview) { + this.logger.error( + 'AutoDebugLspClient: Amazon Q Chat View Provider webview not available after initialization' + ) + return false + } + + // Focus Amazon Q panel first using the imported function + await focusAmazonQPanel.execute(placeholder, 'autoDebug') + + // Wait for panel to focus + await new Promise((resolve) => setTimeout(resolve, 200)) + await amazonQChatViewProvider.webview.postMessage({ + command: 'sendToPrompt', + params: { + selection: '', + triggerType: 'autoDebug', + prompt: { + prompt: params.message, // what gets sent to the user + escapedPrompt: params.message, // what gets sent to the backend + }, + autoSubmit: true, // Automatically submit the message + }, + }) + return true + } catch (error) { + this.logger.error('AutoDebugLspClient: Error sending message via webview: %s', error) + return false + } + } + + /** + * Ensures that the chat view provider and its webview are ready for use + */ + private async ensureWebviewReady(): Promise { + if (!AutoDebugLspClient.chatViewProvider) { + await focusAmazonQPanel.execute(placeholder, 'autoDebug') + // wait 1 second for focusAmazonQPanel to finish + await new Promise((resolve) => setTimeout(resolve, 500)) + } + + // Now ensure the webview is created + if (!AutoDebugLspClient.chatViewProvider.webview) { + await focusAmazonQPanel.execute(placeholder, 'autoDebug') + // wait 1 second for webview to be created + await new Promise((resolve) => setTimeout(resolve, 500)) + } + } +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/shared/diagnosticUtils.ts b/packages/amazonq/src/lsp/chat/autoDebug/shared/diagnosticUtils.ts new file mode 100644 index 00000000000..bf466168347 --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/shared/diagnosticUtils.ts @@ -0,0 +1,35 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { toIdeDiagnostics } from 'aws-core-vscode/codewhisperer' + +/** + * Maps VSCode DiagnosticSeverity to string representation + * Reuses the existing toIdeDiagnostics logic but returns lowercase format expected by Problem interface + */ +export function mapDiagnosticSeverity(severity: vscode.DiagnosticSeverity): 'error' | 'warning' | 'info' | 'hint' { + // Create a minimal diagnostic to use with toIdeDiagnostics + const tempDiagnostic: vscode.Diagnostic = { + range: new vscode.Range(0, 0, 0, 0), + message: '', + severity: severity, + } + + const ideDiagnostic = toIdeDiagnostics(tempDiagnostic) + // Convert uppercase severity to lowercase format expected by Problem interface + switch (ideDiagnostic.severity) { + case 'ERROR': + return 'error' + case 'WARNING': + return 'warning' + case 'INFORMATION': + return 'info' + case 'HINT': + return 'hint' + default: + return 'error' + } +} diff --git a/packages/amazonq/src/lsp/chat/autoDebug/telemetry.ts b/packages/amazonq/src/lsp/chat/autoDebug/telemetry.ts new file mode 100644 index 00000000000..dec3f424c5a --- /dev/null +++ b/packages/amazonq/src/lsp/chat/autoDebug/telemetry.ts @@ -0,0 +1,71 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { telemetry } from 'aws-core-vscode/telemetry' + +/** + * Auto Debug command types for telemetry tracking + */ +export type AutoDebugCommandType = 'fixWithQ' | 'fixAllWithQ' | 'explainProblem' + +/** + * Telemetry interface for Auto Debug feature + * Tracks usage counts and success rates for the three main commands + */ +export interface AutoDebugTelemetry { + /** + * Record when an auto debug command is invoked + */ + recordCommandInvocation(commandType: AutoDebugCommandType, problemCount?: number): void + + /** + * Record when an auto debug command succeeds + */ + recordCommandSuccess(commandType: AutoDebugCommandType, problemCount?: number): void + + /** + * Record when an auto debug command fails + */ + recordCommandFailure(commandType: AutoDebugCommandType, error?: string, problemCount?: number): void +} + +/** + * Implementation of Auto Debug telemetry tracking + */ +export class AutoDebugTelemetryImpl implements AutoDebugTelemetry { + recordCommandInvocation(commandType: AutoDebugCommandType, problemCount?: number): void { + telemetry.amazonq_autoDebugCommand.emit({ + amazonqAutoDebugCommandType: commandType, + amazonqAutoDebugAction: 'invoked', + amazonqAutoDebugProblemCount: problemCount, + result: 'Succeeded', + }) + } + + recordCommandSuccess(commandType: AutoDebugCommandType, problemCount?: number): void { + telemetry.amazonq_autoDebugCommand.emit({ + amazonqAutoDebugCommandType: commandType, + amazonqAutoDebugAction: 'completed', + amazonqAutoDebugProblemCount: problemCount, + result: 'Succeeded', + }) + } + + recordCommandFailure(commandType: AutoDebugCommandType, error?: string, problemCount?: number): void { + telemetry.amazonq_autoDebugCommand.emit({ + amazonqAutoDebugCommandType: commandType, + amazonqAutoDebugAction: 'completed', + amazonqAutoDebugProblemCount: problemCount, + result: 'Failed', + reason: error ? 'Error' : 'Unknown', + reasonDesc: error?.substring(0, 200), // Truncate to 200 chars as recommended + }) + } +} + +/** + * Global instance of auto debug telemetry + */ +export const autoDebugTelemetry: AutoDebugTelemetry = new AutoDebugTelemetryImpl() diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts new file mode 100644 index 00000000000..6e4f928f5f1 --- /dev/null +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -0,0 +1,186 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Commands, globals } from 'aws-core-vscode/shared' +import { window } from 'vscode' +import { AmazonQChatViewProvider } from './webviewProvider' +import { CodeScanIssue, AuthUtil } from 'aws-core-vscode/codewhisperer' +import { getLogger } from 'aws-core-vscode/shared' +import * as vscode from 'vscode' +import * as path from 'path' +import { telemetry, AmazonqCodeReviewTool } from 'aws-core-vscode/telemetry' + +/** + * TODO: Re-enable these once we can figure out which path they're going to live in + * In hybrid chat mode they were being registered twice causing a registration error + */ +export function registerCommands(provider: AmazonQChatViewProvider) { + globals.context.subscriptions.push( + registerGenericCommand('aws.amazonq.explainCode', 'Explain', provider), + registerGenericCommand('aws.amazonq.refactorCode', 'Refactor', provider), + registerGenericCommand('aws.amazonq.fixCode', 'Fix', provider), + registerGenericCommand('aws.amazonq.optimizeCode', 'Optimize', provider), + registerGenericCommand('aws.amazonq.generateUnitTests', 'Generate Tests', provider), + + Commands.register('aws.amazonq.explainIssue', (issue: CodeScanIssue, filePath: string) => + handleIssueCommand( + issue, + filePath, + 'Explain', + 'Provide a small description of the issue. You must not attempt to fix the issue. You should only give a small summary of it to the user. You must start with the information stored in the recommendation.text field if it is present.', + provider, + 'explainIssue' + ) + ), + Commands.register('aws.amazonq.generateFix', (issue: CodeScanIssue, filePath: string) => + handleIssueCommand( + issue, + filePath, + 'Fix', + 'Generate a fix for the following code issue. You must not explain the issue, just generate and explain the fix. The user should have the option to accept or reject the fix before any code is changed.', + provider, + 'applyFix' + ) + ), + Commands.register('aws.amazonq.sendToPrompt', (data) => { + const triggerType = getCommandTriggerType(data) + const selection = getSelectedText() + + void focusAmazonQPanel().then(() => { + void provider.webview?.postMessage({ + command: 'sendToPrompt', + params: { selection: selection, triggerType }, + }) + }) + }), + Commands.register('aws.amazonq.openTab', () => { + void focusAmazonQPanel().then(() => { + void provider.webview?.postMessage({ + command: 'aws/chat/openTab', + params: {}, + }) + }) + }), + registerShellCommandShortCut('aws.amazonq.runCmdExecution', 'run-shell-command', provider), + registerShellCommandShortCut('aws.amazonq.rejectCmdExecution', 'reject-shell-command', provider), + registerShellCommandShortCut('aws.amazonq.stopCmdExecution', 'stop-shell-command', provider) + ) +} + +async function handleIssueCommand( + issue: CodeScanIssue, + filePath: string, + action: string, + contextPrompt: string, + provider: AmazonQChatViewProvider, + metricName: string +) { + await focusAmazonQPanel() + + if (issue && filePath) { + await openFileWithSelection(issue, filePath) + } + + const lineRange = createLineRangeText(issue) + const visibleMessageInChat = `_${action} **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_` + const contextMessage = `${contextPrompt} Code issue - ${JSON.stringify(issue)}` + + void provider.webview?.postMessage({ + command: 'sendToPrompt', + params: { + selection: '', + triggerType: 'contextMenu', + prompt: { + prompt: visibleMessageInChat, + escapedPrompt: contextMessage, + }, + autoSubmit: true, + }, + }) + + telemetry.amazonq_codeReviewTool.emit({ + findingId: issue.findingId, + detectorId: issue.detectorId, + ruleId: issue.ruleId, + credentialStartUrl: AuthUtil.instance.startUrl, + autoDetected: issue.autoDetected, + result: 'Succeeded', + reason: metricName, + } as AmazonqCodeReviewTool) +} + +async function openFileWithSelection(issue: CodeScanIssue, filePath: string) { + try { + const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0) + const doc = await vscode.workspace.openTextDocument(filePath) + await vscode.window.showTextDocument(doc, { + selection: range, + viewColumn: vscode.ViewColumn.One, + preview: true, + }) + } catch (e) { + getLogger().error('openFileWithSelection: Failed to open file %s with selection: %O', filePath, e) + void vscode.window.showInformationMessage('Failed to display file with issue.') + } +} + +function createLineRangeText(issue: CodeScanIssue): string { + return issue.startLine === issue.endLine - 1 + ? `[${issue.startLine + 1}]` + : `[${issue.startLine + 1}, ${issue.endLine}]` +} + +function getSelectedText(): string { + const editor = window.activeTextEditor + if (editor) { + const selection = editor.selection + const selectedText = editor.document.getText(selection) + return selectedText + } + + return ' ' +} + +function getCommandTriggerType(data: any): string { + // data is undefined when commands triggered from keybinding or command palette. Currently no + // way to differentiate keybinding and command palette, so both interactions are recorded as keybinding + return data === undefined ? 'hotkeys' : 'contextMenu' +} + +function registerGenericCommand(commandName: string, genericCommand: string, provider: AmazonQChatViewProvider) { + return Commands.register(commandName, (data) => { + const triggerType = getCommandTriggerType(data) + const selection = getSelectedText() + + void focusAmazonQPanel().then(() => { + void provider.webview?.postMessage({ + command: 'genericCommand', + params: { genericCommand, selection, triggerType }, + }) + }) + }) +} + +/** + * Importing focusAmazonQPanel from aws-core-vscode/amazonq leads to several dependencies down the chain not resolving since AmazonQ chat + * is currently only activated on node, but the language server is activated on both web and node. + * + * Instead, we just create our own as a temporary solution + */ +export async function focusAmazonQPanel() { + await Commands.tryExecute('aws.amazonq.AmazonQChatView.focus') + await Commands.tryExecute('aws.amazonq.AmazonCommonAuth.focus') +} + +function registerShellCommandShortCut(commandName: string, buttonId: string, provider: AmazonQChatViewProvider) { + return Commands.register(commandName, async () => { + void focusAmazonQPanel().then(() => { + void provider.webview?.postMessage({ + command: 'aws/chat/executeShellCommandShortCut', + params: { id: buttonId }, + }) + }) + }) +} diff --git a/packages/amazonq/src/lsp/chat/error.ts b/packages/amazonq/src/lsp/chat/error.ts new file mode 100644 index 00000000000..fc2e0211b0f --- /dev/null +++ b/packages/amazonq/src/lsp/chat/error.ts @@ -0,0 +1,23 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { ChatResult } from '@aws/language-server-runtimes/protocol' +import { ResponseError } from '@aws/language-server-runtimes/protocol' +/** + * Perform a sanity check that the error we got from the LSP can be safely cast to the expected type. + * @param error + * @returns + */ +export function isValidResponseError(error: unknown): error is ResponseError & { data: ChatResult } { + return ( + typeof error === 'object' && + error !== null && + 'code' in error && + typeof error.code === 'number' && + 'message' in error && + typeof error.message === 'string' && + 'data' in error && + error.data !== undefined + ) +} diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts new file mode 100644 index 00000000000..1541e60b9c5 --- /dev/null +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -0,0 +1,856 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + isValidAuthFollowUpType, + INSERT_TO_CURSOR_POSITION, + AUTH_FOLLOW_UP_CLICKED, + CHAT_OPTIONS, + COPY_TO_CLIPBOARD, + AuthFollowUpType, + DISCLAIMER_ACKNOWLEDGED, + UiMessageResultParams, + CHAT_PROMPT_OPTION_ACKNOWLEDGED, + ChatPromptOptionAcknowledgedMessage, + STOP_CHAT_RESPONSE, + StopChatResponseMessage, + OPEN_FILE_DIALOG, +} from '@aws/chat-client-ui-types' +import { + ChatResult, + chatRequestType, + ChatParams, + followUpClickNotificationType, + quickActionRequestType, + QuickActionResult, + QuickActionParams, + insertToCursorPositionNotificationType, + ErrorCodes, + ResponseError, + openTabRequestType, + getSerializedChatRequestType, + listConversationsRequestType, + conversationClickRequestType, + listMcpServersRequestType, + mcpServerClickRequestType, + ShowSaveFileDialogRequestType, + ShowSaveFileDialogParams, + LSPErrorCodes, + tabBarActionRequestType, + ShowDocumentParams, + ShowDocumentResult, + ShowDocumentRequest, + contextCommandsNotificationType, + ContextCommandParams, + openFileDiffNotificationType, + OpenFileDiffParams, + LINK_CLICK_NOTIFICATION_METHOD, + LinkClickParams, + INFO_LINK_CLICK_NOTIFICATION_METHOD, + READY_NOTIFICATION_METHOD, + buttonClickRequestType, + ButtonClickResult, + CancellationTokenSource, + chatUpdateNotificationType, + ChatUpdateParams, + chatOptionsUpdateType, + ChatOptionsUpdateParams, + listRulesRequestType, + ruleClickRequestType, + pinnedContextNotificationType, + activeEditorChangedNotificationType, + listAvailableModelsRequestType, + ShowOpenDialogRequestType, + ShowOpenDialogParams, + openFileDialogRequestType, + OpenFileDialogResult, +} from '@aws/language-server-runtimes/protocol' +import { v4 as uuidv4 } from 'uuid' +import * as vscode from 'vscode' +import * as path from 'path' +import { Disposable, BaseLanguageClient, Position, TextDocumentIdentifier } from 'vscode-languageclient' +import { AmazonQChatViewProvider } from './webviewProvider' +import { + AggregatedCodeScanIssue, + AuthUtil, + CodeAnalysisScope, + CodeWhispererSettings, + initSecurityScanRender, + ReferenceLogViewProvider, + SecurityIssueTreeViewProvider, + CodeWhispererConstants, +} from 'aws-core-vscode/codewhisperer' +import { AmazonQPromptSettings, messages, openUrl, isTextEditor, globals, setContext } from 'aws-core-vscode/shared' +import { DefaultAmazonQAppInitContext, messageDispatcher, referenceLogText } from 'aws-core-vscode/amazonq' +import { telemetry } from 'aws-core-vscode/telemetry' +import { isValidResponseError } from './error' +import { decryptResponse, encryptRequest } from '../encryption' +import { getCursorState } from '../utils' +import { focusAmazonQPanel } from './commands' +import { ChatMessage } from '@aws/language-server-runtimes/server-interface' +import { CommentUtils } from 'aws-core-vscode/utils' + +export function registerActiveEditorChangeListener(languageClient: BaseLanguageClient) { + let debounceTimer: NodeJS.Timeout | undefined + vscode.window.onDidChangeActiveTextEditor((editor) => { + if (debounceTimer) { + clearTimeout(debounceTimer) + } + debounceTimer = setTimeout(() => { + let textDocument = undefined + let cursorState = undefined + if (editor) { + textDocument = { + uri: editor.document.uri.toString(), + } + cursorState = getCursorState(editor.selections) + } + void languageClient.sendNotification(activeEditorChangedNotificationType.method, { + textDocument, + cursorState, + }) + }, 100) + }) +} + +export function registerLanguageServerEventListener( + languageClient: BaseLanguageClient, + provider: AmazonQChatViewProvider +) { + languageClient.info( + 'Language client received initializeResult from server:', + JSON.stringify(languageClient.initializeResult) + ) + + const chatOptions = languageClient.initializeResult?.awsServerCapabilities?.chatOptions + + // overide the quick action commands provided by flare server initialization, which doesn't provide the group header + if (chatOptions?.quickActions?.quickActionsCommandGroups?.[0]) { + chatOptions.quickActions.quickActionsCommandGroups[0].groupName = 'Quick Actions' + } + + // This passes through metric data from LSP events to Toolkit telemetry with all fields from the LSP server + languageClient.onTelemetry((e: any) => { + const telemetryName: string = e.name + languageClient.info(`[VSCode Telemetry] Emitting ${telemetryName} telemetry: ${JSON.stringify(e.data)}`) + try { + // Flare is now the source of truth for metrics instead of depending on each IDE client and toolkit-common + const metric = (telemetry as any).getMetric(telemetryName) + metric?.emit(e.data) + } catch (error) { + languageClient.warn(`[VSCode Telemetry] Failed to emit ${telemetryName}: ${error}`) + } + }) +} + +export function registerMessageListeners( + languageClient: BaseLanguageClient, + provider: AmazonQChatViewProvider, + encryptionKey: Buffer +) { + const chatStreamTokens = new Map() // tab id -> token + + // Keep track of pending chat options to send when webview UI is ready + const pendingChatOptions = languageClient.initializeResult?.awsServerCapabilities?.chatOptions + + provider.webview?.onDidReceiveMessage(async (message) => { + languageClient.info(`[VSCode Client] Received ${JSON.stringify(message)} from chat`) + + if ((message.tabType && message.tabType !== 'cwc') || messageDispatcher.isLegacyEvent(message.command)) { + // handle the mynah ui -> agent legacy flow + messageDispatcher.handleWebviewEvent( + message, + DefaultAmazonQAppInitContext.instance.getWebViewToAppsMessagePublishers() + ) + return + } + + const webview = provider.webview + + switch (message.command) { + // Handle "aws/chat/ready" event + case READY_NOTIFICATION_METHOD: + languageClient.info(`[VSCode Client] "aws/chat/ready" event is received, sending chat options`) + if (webview && pendingChatOptions) { + try { + await webview.postMessage({ + command: CHAT_OPTIONS, + params: pendingChatOptions, + }) + + // Display a more readable representation of quick actions + const quickActionCommands = + pendingChatOptions?.quickActions?.quickActionsCommandGroups?.[0]?.commands || [] + const quickActionsDisplay = quickActionCommands.map((cmd: any) => cmd.command).join(', ') + languageClient.info( + `[VSCode Client] Chat options flags: mcpServers=${pendingChatOptions?.mcpServers}, history=${pendingChatOptions?.history}, export=${pendingChatOptions?.export}, quickActions=[${quickActionsDisplay}]` + ) + void languageClient.sendNotification(message.command, message.params) + } catch (err) { + languageClient.error( + `[VSCode Client] Failed to send CHAT_OPTIONS after "aws/chat/ready" event: ${(err as Error).message}` + ) + } + } + break + case COPY_TO_CLIPBOARD: + languageClient.info('[VSCode Client] Copy to clipboard event received') + try { + await messages.copyToClipboard(message.params.code) + } catch (e: unknown) { + languageClient.error(`[VSCode Client] Failed to copy to clipboard: ${(e as Error).message}`) + } + break + case INSERT_TO_CURSOR_POSITION: { + const editor = vscode.window.activeTextEditor + let textDocument: TextDocumentIdentifier | undefined = undefined + let cursorPosition: Position | undefined = undefined + if (editor) { + cursorPosition = editor.selection.active + textDocument = { uri: editor.document.uri.toString() } + } + + void languageClient.sendNotification(insertToCursorPositionNotificationType.method, { + ...message.params, + cursorPosition, + textDocument, + }) + break + } + case AUTH_FOLLOW_UP_CLICKED: { + languageClient.info('[VSCode Client] AuthFollowUp clicked') + const authType = message.params.authFollowupType + const reAuthTypes: AuthFollowUpType[] = ['re-auth', 'missing_scopes'] + const fullAuthTypes: AuthFollowUpType[] = ['full-auth', 'use-supported-auth'] + + if (reAuthTypes.includes(authType)) { + try { + await AuthUtil.instance.reauthenticate() + } catch (e) { + languageClient.error( + `[VSCode Client] Failed to re-authenticate after AUTH_FOLLOW_UP_CLICKED: ${(e as Error).message}` + ) + } + } + + if (fullAuthTypes.includes(authType)) { + try { + await AuthUtil.instance.secondaryAuth.deleteConnection() + } catch (e) { + languageClient.error( + `[VSCode Client] Failed to authenticate after AUTH_FOLLOW_UP_CLICKED: ${(e as Error).message}` + ) + } + } + break + } + case DISCLAIMER_ACKNOWLEDGED: { + void AmazonQPromptSettings.instance.update('amazonQChatDisclaimer', true) + break + } + case CHAT_PROMPT_OPTION_ACKNOWLEDGED: { + const acknowledgedMessage = message as ChatPromptOptionAcknowledgedMessage + switch (acknowledgedMessage.params.messageId) { + case 'programmerModeCardId': { + void AmazonQPromptSettings.instance.disablePrompt('amazonQChatPairProgramming') + } + } + break + } + case INFO_LINK_CLICK_NOTIFICATION_METHOD: + case LINK_CLICK_NOTIFICATION_METHOD: { + const linkParams = message.params as LinkClickParams + void openUrl(vscode.Uri.parse(linkParams.link)) + break + } + case STOP_CHAT_RESPONSE: { + const tabId = (message as StopChatResponseMessage).params.tabId + const token = chatStreamTokens.get(tabId) + token?.cancel() + token?.dispose() + chatStreamTokens.delete(tabId) + break + } + case chatRequestType.method: { + const chatParams: ChatParams = { ...message.params } + const partialResultToken = uuidv4() + let lastPartialResult: ChatResult | undefined + const cancellationToken = new CancellationTokenSource() + chatStreamTokens.set(chatParams.tabId, cancellationToken) + + const chatDisposable = languageClient.onProgress( + chatRequestType, + partialResultToken, + (partialResult: any) => + handlePartialResult(partialResult, encryptionKey, provider, chatParams.tabId).then( + (result) => { + lastPartialResult = result + } + ) + ) + + const editor = + vscode.window.activeTextEditor || + vscode.window.visibleTextEditors.find((editor) => editor.document.languageId !== 'Log') + if (editor) { + chatParams.cursorState = getCursorState(editor.selections) + chatParams.textDocument = { uri: editor.document.uri.toString() } + } + + const chatRequest = await encryptRequest(chatParams, encryptionKey) + + // Add detailed logging for SageMaker debugging + if (process.env.USE_IAM_AUTH === 'true') { + languageClient.info(`[SageMaker Debug] Making chat request with IAM auth`) + languageClient.info(`[SageMaker Debug] Chat request method: ${chatRequestType.method}`) + languageClient.info( + `[SageMaker Debug] Original chat params: ${JSON.stringify( + { + tabId: chatParams.tabId, + prompt: chatParams.prompt, + // Don't log full textDocument content, just metadata + textDocument: chatParams.textDocument + ? { uri: chatParams.textDocument.uri } + : undefined, + context: chatParams.context ? `${chatParams.context.length} context items` : undefined, + }, + undefined, + 2 + )}` + ) + languageClient.info( + `[SageMaker Debug] Environment context: USE_IAM_AUTH=${process.env.USE_IAM_AUTH}, AWS_REGION=${process.env.AWS_REGION}` + ) + } + + try { + const chatResult = await languageClient.sendRequest( + chatRequestType.method, + { + ...chatRequest, + partialResultToken, + }, + cancellationToken.token + ) + + // Add response content logging for SageMaker debugging + if (process.env.USE_IAM_AUTH === 'true') { + languageClient.info(`[SageMaker Debug] Chat response received - type: ${typeof chatResult}`) + if (typeof chatResult === 'string') { + languageClient.info( + `[SageMaker Debug] Chat response (string): ${chatResult.substring(0, 200)}...` + ) + } else if (chatResult && typeof chatResult === 'object') { + languageClient.info( + `[SageMaker Debug] Chat response (object keys): ${Object.keys(chatResult)}` + ) + if ('message' in chatResult) { + languageClient.info( + `[SageMaker Debug] Chat response message: ${JSON.stringify(chatResult.message).substring(0, 200)}...` + ) + } + } + } + + await handleCompleteResult( + chatResult, + encryptionKey, + provider, + chatParams.tabId, + chatDisposable, + languageClient + ) + } catch (e) { + const errorMsg = `Error occurred during chat request: ${e}` + languageClient.info(errorMsg) + languageClient.info( + `Last result from langauge server: ${JSON.stringify(lastPartialResult, undefined, 2)}` + ) + if (!isValidResponseError(e)) { + throw e + } + await handleCompleteResult( + e.data, + encryptionKey, + provider, + chatParams.tabId, + chatDisposable, + languageClient + ) + } finally { + chatStreamTokens.delete(chatParams.tabId) + } + break + } + case OPEN_FILE_DIALOG: { + // openFileDialog is the event emitted from webView to open + // file system + const result = await languageClient.sendRequest( + openFileDialogRequestType.method, + message.params + ) + void provider.webview?.postMessage({ + command: openFileDialogRequestType.method, + params: result, + }) + break + } + case quickActionRequestType.method: { + const quickActionPartialResultToken = uuidv4() + const quickActionDisposable = languageClient.onProgress( + quickActionRequestType, + quickActionPartialResultToken, + (partialResult: any) => + handlePartialResult( + partialResult, + encryptionKey, + provider, + message.params.tabId + ) + ) + + const quickActionRequest = await encryptRequest(message.params, encryptionKey) + const quickActionResult = (await languageClient.sendRequest(quickActionRequestType.method, { + ...quickActionRequest, + partialResultToken: quickActionPartialResultToken, + })) as string | ChatResult + void handleCompleteResult( + quickActionResult, + encryptionKey, + provider, + message.params.tabId, + quickActionDisposable, + languageClient + ) + break + } + case listRulesRequestType.method: + case ruleClickRequestType.method: + case listConversationsRequestType.method: + case conversationClickRequestType.method: + case listMcpServersRequestType.method: + case mcpServerClickRequestType.method: + case tabBarActionRequestType.method: + // handling for show_logs button + if (message.params.action === 'show_logs') { + languageClient.info('[VSCode Client] Received show_logs action, showing disclaimer') + + // Show warning message without buttons - just informational + void vscode.window.showWarningMessage( + 'Log files may contain sensitive information such as account IDs, resource names, and other data. Be careful when sharing these logs.' + ) + + // Get the log directory path + const logFolderPath = globals.context.logUri?.fsPath + const result = { ...message.params, success: false } + + if (logFolderPath) { + // Open the log directory in the OS file explorer directly + languageClient.info('[VSCode Client] Opening logs directory') + const path = require('path') + const logFilePath = path.join(logFolderPath, 'Amazon Q Logs.log') + await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(logFilePath)) + result.success = true + } else { + // Fallback: show error if log path is not available + void vscode.window.showErrorMessage('Log location not available.') + languageClient.error('[VSCode Client] Log location not available') + } + + void webview?.postMessage({ + command: message.command, + params: result, + }) + + break + } + // eslint-disable-next-line no-fallthrough + case listAvailableModelsRequestType.method: + await resolveChatResponse(message.command, message.params, languageClient, webview) + break + case followUpClickNotificationType.method: + if (!isValidAuthFollowUpType(message.params.followUp.type)) { + void languageClient.sendNotification(followUpClickNotificationType.method, message.params) + } + break + case buttonClickRequestType.method: { + const buttonResult = await languageClient.sendRequest( + buttonClickRequestType.method, + message.params + ) + if (!buttonResult.success) { + languageClient.error( + `[VSCode Client] Failed to execute button action: ${buttonResult.failureReason}` + ) + } + break + } + default: + if (isServerEvent(message.command)) { + if (enterFocus(message.params)) { + await setContext('aws.amazonq.amazonqChatLSP.isFocus', true) + } else if (exitFocus(message.params)) { + await setContext('aws.amazonq.amazonqChatLSP.isFocus', false) + } + void languageClient.sendNotification(message.command, message.params) + } + break + } + }, undefined) + + const registerHandlerWithResponseRouter = (command: string) => { + const handler = async (params: any, _: any) => { + const mapErrorType = (type: string | undefined): number => { + switch (type) { + case 'InvalidRequest': + return ErrorCodes.InvalidRequest + case 'InternalError': + return ErrorCodes.InternalError + case 'UnknownError': + default: + return ErrorCodes.UnknownErrorCode + } + } + const requestId = uuidv4() + + void provider.webview?.postMessage({ + requestId: requestId, + command: command, + params: params, + }) + const responsePromise = new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + disposable?.dispose() + reject(new Error('Request timed out')) + }, 30000) + + const disposable = provider.webview?.onDidReceiveMessage((message: any) => { + if (message.requestId === requestId) { + clearTimeout(timeout) + disposable?.dispose() + resolve(message.params) + } + }) + }) + + const result = await responsePromise + + if (result?.success) { + return result.result + } else { + return new ResponseError( + mapErrorType(result?.error.type), + result?.error.message ?? 'No response from client' + ) + } + } + + languageClient.onRequest(command, handler) + } + + registerHandlerWithResponseRouter(openTabRequestType.method) + registerHandlerWithResponseRouter(getSerializedChatRequestType.method) + + languageClient.onRequest(ShowSaveFileDialogRequestType.method, async (params: ShowSaveFileDialogParams) => { + const filters: Record = {} + const formatMappings = [ + { format: 'markdown', key: 'Markdown', extensions: ['md'] }, + { format: 'html', key: 'HTML', extensions: ['html'] }, + ] + + for (const format of params.supportedFormats ?? []) { + const mapping = formatMappings.find((m) => m.format === format) + if (mapping) { + filters[mapping.key] = mapping.extensions + } + } + + const saveAtUri = params.defaultUri ? vscode.Uri.parse(params.defaultUri) : vscode.Uri.file('export-chat.md') + const targetUri = await vscode.window.showSaveDialog({ + filters, + defaultUri: saveAtUri, + title: 'Export', + }) + + if (!targetUri) { + return new ResponseError(LSPErrorCodes.RequestFailed, 'Export failed') + } + + return { + targetUri: targetUri.toString(), + } + }) + + languageClient.onRequest(ShowOpenDialogRequestType.method, async (params: ShowOpenDialogParams) => { + try { + const uris = await vscode.window.showOpenDialog({ + canSelectFiles: params.canSelectFiles ?? true, + canSelectFolders: params.canSelectFolders ?? false, + canSelectMany: params.canSelectMany ?? false, + filters: params.filters, + defaultUri: params.defaultUri ? vscode.Uri.parse(params.defaultUri, false) : undefined, + title: params.title, + }) + const urisString = uris?.map((uri) => uri.fsPath) + return { uris: urisString || [] } + } catch (err) { + languageClient.error(`[VSCode Client] Failed to open file dialog: ${(err as Error).message}`) + return { uris: [] } + } + }) + + languageClient.onRequest( + ShowDocumentRequest.method, + async (params: ShowDocumentParams): Promise> => { + focusAmazonQPanel().catch((e: Error) => languageClient.error(`[VSCode Client] focusAmazonQPanel() failed`)) + + try { + const uri = vscode.Uri.parse(params.uri) + + if (params.external) { + // Note: Not using openUrl() because we probably don't want telemetry for these URLs. + // Also it doesn't yet support the required HACK below. + + // HACK: workaround vscode bug: https://github.com/microsoft/vscode/issues/85930 + vscode.env.openExternal(params.uri as any).then(undefined, (e) => { + // TODO: getLogger('?').error('failed vscode.env.openExternal: %O', e) + vscode.env.openExternal(uri).then(undefined, (e) => { + // TODO: getLogger('?').error('failed vscode.env.openExternal: %O', e) + }) + }) + return params + } + + const doc = await vscode.workspace.openTextDocument(uri) + await vscode.window.showTextDocument(doc, { preview: false }) + return params + } catch (e) { + return new ResponseError( + LSPErrorCodes.RequestFailed, + `Failed to open document: ${(e as Error).message}` + ) + } + } + ) + + languageClient.onNotification(contextCommandsNotificationType.method, (params: ContextCommandParams) => { + void provider.webview?.postMessage({ + command: contextCommandsNotificationType.method, + params: params, + }) + }) + languageClient.onNotification( + pinnedContextNotificationType.method, + (params: ContextCommandParams & { tabId: string; textDocument?: TextDocumentIdentifier }) => { + const editor = vscode.window.activeTextEditor + let textDocument = undefined + if (editor && isTextEditor(editor)) { + textDocument = { uri: vscode.workspace.asRelativePath(editor.document.uri) } + } + void provider.webview?.postMessage({ + command: pinnedContextNotificationType.method, + params: { ...params, textDocument }, + }) + } + ) + + languageClient.onNotification(openFileDiffNotificationType.method, async (params: OpenFileDiffParams) => { + // Handle both file:// URIs and raw file paths, ensuring proper Windows path handling + let currentFileUri: vscode.Uri + + // Check if it's already a proper file:// URI + if (params.originalFileUri.startsWith('file://')) { + currentFileUri = vscode.Uri.parse(params.originalFileUri) + } else { + // Decode URL-encoded characters and treat as file path + const decodedPath = decodeURIComponent(params.originalFileUri) + currentFileUri = vscode.Uri.file(decodedPath) + } + + const originalContent = params.originalFileContent ?? '' + const fileName = path.basename(currentFileUri.fsPath) + + // Use custom scheme to avoid adding to recent files + const originalFileUri = vscode.Uri.parse(`amazonq-diff:${fileName}_original_${Date.now()}`) + + // Register content provider for the custom scheme + const disposable = vscode.workspace.registerTextDocumentContentProvider('amazonq-diff', { + provideTextDocumentContent: () => originalContent, + }) + + try { + // Open diff view with custom scheme URI (left) vs current file (right) + await vscode.commands.executeCommand( + 'vscode.diff', + originalFileUri, + currentFileUri, + `${vscode.workspace.asRelativePath(currentFileUri)} (Original ↔ Current, Editable)`, + { preview: false } + ) + + // Clean up content provider when diff view is closed + const cleanupDisposable = vscode.window.onDidChangeVisibleTextEditors(() => { + const isDiffViewOpen = vscode.window.visibleTextEditors.some( + (editor) => editor.document.uri.toString() === originalFileUri.toString() + ) + if (!isDiffViewOpen) { + disposable.dispose() + cleanupDisposable.dispose() + } + }) + } catch (error) { + disposable.dispose() + languageClient.error(`[VSCode Client] Failed to open diff view: ${error}`) + } + }) + + languageClient.onNotification(chatUpdateNotificationType.method, (params: ChatUpdateParams) => { + void provider.webview?.postMessage({ + command: chatUpdateNotificationType.method, + params: params, + }) + }) + + languageClient.onNotification(chatOptionsUpdateType.method, (params: ChatOptionsUpdateParams) => { + void provider.webview?.postMessage({ + command: chatOptionsUpdateType.method, + params: params, + }) + }) +} + +function isServerEvent(command: string) { + return command.startsWith('aws/chat/') || command === 'telemetry/event' +} + +function enterFocus(params: any) { + return params.name === 'enterFocus' +} + +function exitFocus(params: any) { + return params.name === 'exitFocus' +} + +/** + * Decodes partial chat responses from the language server before sending them to mynah UI + */ +async function handlePartialResult( + partialResult: string | T, + encryptionKey: Buffer | undefined, + provider: AmazonQChatViewProvider, + tabId: string +) { + const decryptedMessage = await decryptResponse(partialResult, encryptionKey) + + // This is to filter out the message containing findings from CodeReview tool to update CodeIssues panel + decryptedMessage.additionalMessages = decryptedMessage.additionalMessages?.filter( + (message) => + !( + message.messageId !== undefined && + (message.messageId.endsWith(CodeWhispererConstants.codeReviewFindingsSuffix) || + message.messageId.endsWith(CodeWhispererConstants.displayFindingsSuffix)) + ) + ) + + if (decryptedMessage.body !== undefined) { + void provider.webview?.postMessage({ + command: chatRequestType.method, + params: decryptedMessage, + isPartialResult: true, + tabId: tabId, + }) + } + return decryptedMessage +} + +/** + * Decodes the final chat responses from the language server before sending it to mynah UI. + * Once this is called the answer response is finished + */ +async function handleCompleteResult( + result: string | T, + encryptionKey: Buffer | undefined, + provider: AmazonQChatViewProvider, + tabId: string, + disposable: Disposable, + languageClient: BaseLanguageClient +) { + const decryptedMessage = await decryptResponse(result, encryptionKey) + + await handleSecurityFindings(decryptedMessage, languageClient) + + void provider.webview?.postMessage({ + command: chatRequestType.method, + params: decryptedMessage, + tabId: tabId, + }) + + // only add the reference log once the request is complete, otherwise we will get duplicate log items + for (const ref of decryptedMessage.codeReference ?? []) { + ReferenceLogViewProvider.instance.addReferenceLog(referenceLogText(ref)) + } + disposable.dispose() +} + +async function handleSecurityFindings( + decryptedMessage: { additionalMessages?: ChatMessage[] }, + languageClient: BaseLanguageClient +): Promise { + if (decryptedMessage.additionalMessages === undefined || decryptedMessage.additionalMessages.length === 0) { + return + } + for (let i = decryptedMessage.additionalMessages.length - 1; i >= 0; i--) { + const message = decryptedMessage.additionalMessages[i] + if ( + message.messageId !== undefined && + (message.messageId.endsWith(CodeWhispererConstants.codeReviewFindingsSuffix) || + message.messageId.endsWith(CodeWhispererConstants.displayFindingsSuffix)) + ) { + if (message.body !== undefined) { + try { + const aggregatedCodeScanIssues: AggregatedCodeScanIssue[] = JSON.parse(message.body) + for (const aggregatedCodeScanIssue of aggregatedCodeScanIssues) { + const document = await vscode.workspace.openTextDocument(aggregatedCodeScanIssue.filePath) + for (const issue of aggregatedCodeScanIssue.issues) { + const isIssueTitleIgnored = CodeWhispererSettings.instance + .getIgnoredSecurityIssues() + .includes(issue.title) + const isSingleIssueIgnored = CommentUtils.detectCommentAboveLine( + document, + issue.startLine, + CodeWhispererConstants.amazonqIgnoreNextLine + ) + + issue.visible = !isIssueTitleIgnored && !isSingleIssueIgnored + } + } + initSecurityScanRender( + aggregatedCodeScanIssues, + undefined, + CodeAnalysisScope.AGENTIC, + message.messageId.endsWith(CodeWhispererConstants.codeReviewFindingsSuffix) + ) + SecurityIssueTreeViewProvider.focus() + } catch (e) { + languageClient.info('Failed to parse findings') + } + } + decryptedMessage.additionalMessages.splice(i, 1) + } + } +} + +async function resolveChatResponse( + requestMethod: string, + params: any, + languageClient: BaseLanguageClient, + webview: vscode.Webview | undefined +) { + const result = await languageClient.sendRequest(requestMethod, params) + void webview?.postMessage({ + command: requestMethod, + params: result, + }) +} diff --git a/packages/amazonq/src/lsp/chat/webviewProvider.ts b/packages/amazonq/src/lsp/chat/webviewProvider.ts new file mode 100644 index 00000000000..b0e0bddc195 --- /dev/null +++ b/packages/amazonq/src/lsp/chat/webviewProvider.ts @@ -0,0 +1,186 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EventEmitter, + CancellationToken, + WebviewView, + WebviewViewProvider, + WebviewViewResolveContext, + Uri, + Webview, +} from 'vscode' +import * as path from 'path' +import * as os from 'os' +import { + globals, + isSageMaker, + AmazonQPromptSettings, + LanguageServerResolver, + amazonqMark, + getLogger, +} from 'aws-core-vscode/shared' +import { AuthUtil, RegionProfile } from 'aws-core-vscode/codewhisperer' +import { featureConfig } from 'aws-core-vscode/amazonq' +import { getAmazonQLspConfig } from '../config' +import { BaseLanguageClient } from 'vscode-languageclient' + +export class AmazonQChatViewProvider implements WebviewViewProvider { + public static readonly viewType = 'aws.amazonq.AmazonQChatView' + private readonly onDidResolveWebviewEmitter = new EventEmitter() + public readonly onDidResolveWebview = this.onDidResolveWebviewEmitter.event + + webviewView?: WebviewView + webview?: Webview + + connectorAdapterPath?: string + uiPath?: string + + constructor( + private readonly mynahUIPath: string, + private readonly languageClient: BaseLanguageClient + ) {} + + public async resolveWebviewView( + webviewView: WebviewView, + context: WebviewViewResolveContext, + _token: CancellationToken + ) { + const lspDir = Uri.file(LanguageServerResolver.defaultDir()) + const dist = Uri.joinPath(globals.context.extensionUri, 'dist') + const bundledResources = Uri.joinPath(globals.context.extensionUri, 'resources/language-server') + let resourcesRoots = [lspDir, dist] + if (this.mynahUIPath?.startsWith(globals.context.extensionUri.fsPath)) { + getLogger('amazonqLsp').info(`Using bundled webview resources ${bundledResources.fsPath}`) + resourcesRoots = [bundledResources, dist] + } + /** + * if the mynah chat client is defined, then make sure to add it to the resource roots, otherwise + * it will 401 when trying to load + */ + const mynahUIPath = getAmazonQLspConfig().ui + if (process.env.WEBPACK_DEVELOPER_SERVER && mynahUIPath) { + const dir = path.dirname(mynahUIPath) + resourcesRoots.push(Uri.file(dir)) + } + + webviewView.webview.options = { + enableScripts: true, + enableCommandUris: true, + localResourceRoots: resourcesRoots, + } + + const source = 'vue/src/amazonq/webview/ui/amazonq-ui-connector-adapter.js' // Sent to dist/vue folder in webpack. + const serverHostname = process.env.WEBPACK_DEVELOPER_SERVER + + this.connectorAdapterPath = + serverHostname !== undefined + ? `${serverHostname}/${source}` + : webviewView.webview.asWebviewUri(Uri.joinPath(dist, source)).toString() + this.uiPath = webviewView.webview.asWebviewUri(Uri.file(this.mynahUIPath)).toString() + + webviewView.webview.html = await this.getWebviewContent() + + this.webviewView = webviewView + this.webview = this.webviewView.webview + + this.onDidResolveWebviewEmitter.fire() + performance.mark(amazonqMark.open) + } + + private async getWebviewContent() { + const featureConfigData = await featureConfig.getFeatureConfigs() + + const isSM = isSageMaker('SMAI') + const isSMUS = isSageMaker('SMUS') + const disabledCommands = isSM ? `['/dev', '/transform', '/test', '/review', '/doc']` : '[]' + const disclaimerAcknowledged = !AmazonQPromptSettings.instance.isPromptEnabled('amazonQChatDisclaimer') + const pairProgrammingAcknowledged = + !AmazonQPromptSettings.instance.isPromptEnabled('amazonQChatPairProgramming') + const welcomeCount = globals.globalState.tryGet('aws.amazonq.welcomeChatShowCount', Number, 0) + const modelSelectionEnabled = + this.languageClient.initializeResult?.awsServerCapabilities?.chatOptions?.modelSelection ?? false + + // only show profile card when the two conditions + // 1. profile count >= 2 + // 2. not default (fallback) which has empty arn + let regionProfile: RegionProfile | undefined = AuthUtil.instance.regionProfileManager.activeRegionProfile + if (AuthUtil.instance.regionProfileManager.profiles.length === 1) { + regionProfile = undefined + } + + const regionProfileString: string = JSON.stringify(regionProfile) + + const entrypoint = process.env.WEBPACK_DEVELOPER_SERVER + ? 'http://localhost:8080' + : 'https://file+.vscode-resource.vscode-cdn.net' + + const contentPolicy = `default-src ${entrypoint} data: blob: 'unsafe-inline'; + script-src ${entrypoint} filesystem: file: vscode-resource: https: ws: wss: 'unsafe-inline';` + + return ` + + + + + + + Chat + + + + + + + + ` + } + + async refreshWebview() { + if (this.webview) { + // post a message to the webview telling it to reload + void this.webview?.postMessage({ + command: 'reload', + }) + } + } +} diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts new file mode 100644 index 00000000000..2255fb80fee --- /dev/null +++ b/packages/amazonq/src/lsp/client.ts @@ -0,0 +1,552 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import vscode, { version } from 'vscode' +import * as nls from 'vscode-nls' +import { BaseLanguageClient, LanguageClientOptions, RequestType, State } from 'vscode-languageclient' +import { LanguageClient } from 'vscode-languageclient/node' +import { InlineCompletionManager } from '../app/inline/completion' +import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth' +import { + CreateFilesParams, + DeleteFilesParams, + DidChangeWorkspaceFoldersParams, + GetConfigurationFromServerParams, + RenameFilesParams, + ResponseMessage, + WorkspaceFolder, + ConnectionMetadata, +} from '@aws/language-server-runtimes/protocol' +import { + AuthUtil, + CodeWhispererSettings, + FeatureConfigProvider, + getSelectedCustomization, + TelemetryHelper, + vsCodeState, +} from 'aws-core-vscode/codewhisperer' +import { + Settings, + createServerOptions, + globals, + Experiments, + Commands, + oneSecond, + validateNodeExe, + getLogger, + undefinedIfEmpty, + getOptOutPreference, + isAmazonLinux2, + getClientId, + getClientName, + extensionVersion, + isSageMaker, + DevSettings, +} from 'aws-core-vscode/shared' +import { processUtils } from 'aws-core-vscode/shared' +import { activate } from './chat/activation' +import { activate as activateInline } from '../app/inline/activation' +import { AmazonQResourcePaths } from './lspInstaller' +import { ConfigSection, isValidConfigSection, pushConfigUpdate, toAmazonQLSPLogLevel } from './config' +import { activate as activateInlineChat } from '../inlineChat/activation' +import { telemetry } from 'aws-core-vscode/telemetry' +import { SessionManager } from '../app/inline/sessionManager' +import { LineTracker } from '../app/inline/stateTracker/lineTracker' +import { InlineTutorialAnnotation } from '../app/inline/tutorials/inlineTutorialAnnotation' +import { InlineChatTutorialAnnotation } from '../app/inline/tutorials/inlineChatTutorialAnnotation' +import { codeReviewInChat } from '../app/amazonqScan/models/constants' + +const localize = nls.loadMessageBundle() +const logger = getLogger('amazonqLsp.lspClient') + +export function hasGlibcPatch(): boolean { + // Skip GLIBC patching for SageMaker environments + if (isSageMaker()) { + getLogger('amazonqLsp').info('SageMaker environment detected in hasGlibcPatch, skipping GLIBC patching') + return false // Return false to ensure SageMaker doesn't try to use GLIBC patching + } + + // Check for environment variables (for CDM) + const glibcLinker = process.env.VSCODE_SERVER_CUSTOM_GLIBC_LINKER || '' + const glibcPath = process.env.VSCODE_SERVER_CUSTOM_GLIBC_PATH || '' + + if (glibcLinker.length > 0 && glibcPath.length > 0) { + getLogger('amazonqLsp').info('GLIBC patching environment variables detected') + return true + } + + // No environment variables, no patching needed + return false +} + +export async function startLanguageServer( + extensionContext: vscode.ExtensionContext, + resourcePaths: AmazonQResourcePaths +) { + const toDispose = extensionContext.subscriptions + + const serverModule = resourcePaths.lsp + + const argv = [ + '--nolazy', + '--preserve-symlinks', + '--stdio', + '--pre-init-encryption', + '--set-credentials-encryption-key', + ] + + const documentSelector = [{ scheme: 'file', language: '*' }] + + const clientId = 'amazonq' + const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`) + let executable: string[] = [] + // apply the GLIBC 2.28 path to node js runtime binary + if (isSageMaker()) { + // SageMaker doesn't need GLIBC patching + getLogger('amazonqLsp').info('SageMaker environment detected, skipping GLIBC patching') + executable = [resourcePaths.node] + } else if (isAmazonLinux2() && hasGlibcPatch()) { + // Use environment variables if available (for CDM) + if (process.env.VSCODE_SERVER_CUSTOM_GLIBC_LINKER && process.env.VSCODE_SERVER_CUSTOM_GLIBC_PATH) { + executable = [ + process.env.VSCODE_SERVER_CUSTOM_GLIBC_LINKER, + '--library-path', + process.env.VSCODE_SERVER_CUSTOM_GLIBC_PATH, + resourcePaths.node, + ] + getLogger('amazonqLsp').info(`Patched node runtime with GLIBC using env vars to ${executable}`) + } else { + // No environment variables, use the node executable directly + executable = [resourcePaths.node] + } + } else { + executable = [resourcePaths.node] + } + + const memoryWarnThreshold = 1024 * processUtils.oneMB + const serverOptions = createServerOptions({ + encryptionKey, + executable: executable, + serverModule, + execArgv: argv, + warnThresholds: { memory: memoryWarnThreshold }, + }) + + await validateNodeExe(executable, resourcePaths.lsp, argv, logger) + + const endpointOverride = DevSettings.instance.get('codewhispererService', {}).endpoint ?? undefined + const textDocSection = { + inlineEditSupport: Experiments.instance.get('amazonqLSPNEP', true), + } as any + + if (endpointOverride) { + textDocSection.endpointOverride = endpointOverride + } + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for json documents + documentSelector, + middleware: { + workspace: { + /** + * Convert VSCode settings format to be compatible with flare's configs + */ + configuration: async (params, token, next) => { + const config = await next(params, token) + const section = params.items[0].section + if (!isValidConfigSection(section)) { + return config + } + return getConfigSection(section) + }, + }, + }, + initializationOptions: { + aws: { + clientInfo: { + name: getClientName(), + version: version, + extension: { + name: 'AmazonQ-For-VSCode', + version: extensionVersion, + }, + clientId: getClientId(globals.globalState), + }, + awsClientCapabilities: { + q: { + developerProfiles: true, + pinnedContextEnabled: true, + imageContextEnabled: true, + mcp: true, + shortcut: true, + reroute: true, + modelSelection: true, + workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, + codeReviewInChat: codeReviewInChat, + // feature flag for displaying findings found not through CodeReview in the Code Issues Panel + displayFindings: true, + }, + window: { + notifications: true, + showSaveFileDialog: true, + showLogs: isSageMaker() ? false : true, + }, + textDocument: { + inlineCompletionWithReferences: textDocSection, + }, + }, + contextConfiguration: { + workspaceIdentifier: extensionContext.storageUri?.path, + }, + logLevel: isSageMaker() ? 'debug' : toAmazonQLSPLogLevel(globals.logOutputChannel.logLevel), + }, + credentials: { + providesBearerToken: true, + providesIam: isSageMaker(), // Enable IAM credentials for SageMaker environments + }, + }, + /** + * When the trace server is enabled it outputs a ton of log messages so: + * When trace server is enabled, logs go to a seperate "Amazon Q Language Server" output. + * Otherwise, logs go to the regular "Amazon Q Logs" channel. + */ + ...(traceServerEnabled + ? {} + : { + outputChannel: globals.logOutputChannel, + }), + } + + const client = new LanguageClient( + clientId, + localize('amazonq.server.name', 'Amazon Q Language Server'), + serverOptions, + clientOptions + ) + + await client.start() + + // Set up connection metadata handler + client.onRequest(notificationTypes.getConnectionMetadata.method, () => { + // For IAM auth, provide a default startUrl + if (process.env.USE_IAM_AUTH === 'true') { + getLogger().info( + `[SageMaker Debug] Connection metadata requested - returning hardcoded startUrl for IAM auth` + ) + return { + sso: { + // TODO P261194666 Replace with correct startUrl once identified + startUrl: 'https://amzn.awsapps.com/start', // Default for IAM auth + }, + } + } + + // For SSO auth, use the actual startUrl + getLogger().info( + `[SageMaker Debug] Connection metadata requested - returning actual startUrl for SSO auth: ${AuthUtil.instance.auth.startUrl}` + ) + return { + sso: { + startUrl: AuthUtil.instance.auth.startUrl, + }, + } + }) + + const auth = await initializeAuth(client) + + await onLanguageServerReady(extensionContext, auth, client, resourcePaths, toDispose) + + return client +} + +async function initializeAuth(client: BaseLanguageClient): Promise { + const auth = new AmazonQLspAuth(client) + await auth.refreshConnection(true) + return auth +} + +// jscpd:ignore-start +async function initializeLanguageServerConfiguration(client: BaseLanguageClient, context: string = 'startup') { + const logger = getLogger('amazonqLsp') + + if (AuthUtil.instance.isConnectionValid()) { + logger.info(`[${context}] Initializing language server configuration`) + // jscpd:ignore-end + + try { + // Send profile configuration + logger.debug(`[${context}] Sending profile configuration to language server`) + await sendProfileToLsp(client) + logger.debug(`[${context}] Profile configuration sent successfully`) + + // Send customization configuration + logger.debug(`[${context}] Sending customization configuration to language server`) + await pushConfigUpdate(client, { + type: 'customization', + customization: getSelectedCustomization(), + }) + logger.debug(`[${context}] Customization configuration sent successfully`) + + logger.info(`[${context}] Language server configuration completed successfully`) + } catch (error) { + logger.error(`[${context}] Failed to initialize language server configuration: ${error}`) + throw error + } + } else { + logger.warn( + `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` + ) + const activeConnection = AuthUtil.instance.auth.activeConnection + const connectionState = activeConnection + ? AuthUtil.instance.auth.getConnectionState(activeConnection) + : 'no-connection' + logger.warn(`[${context}] Connection state: ${connectionState}`) + } +} + +async function sendProfileToLsp(client: BaseLanguageClient) { + const logger = getLogger('amazonqLsp') + const profileArn = AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn + + logger.debug(`Sending profile to LSP: ${profileArn || 'undefined'}`) + + await pushConfigUpdate(client, { + type: 'profile', + profileArn: profileArn, + }) + + logger.debug(`Profile sent to LSP successfully`) +} + +async function onLanguageServerReady( + extensionContext: vscode.ExtensionContext, + auth: AmazonQLspAuth, + client: BaseLanguageClient, + resourcePaths: AmazonQResourcePaths, + toDispose: vscode.Disposable[] +) { + const sessionManager = new SessionManager() + + // keeps track of the line changes + const lineTracker = new LineTracker() + + // tutorial for inline suggestions + const inlineTutorialAnnotation = new InlineTutorialAnnotation(lineTracker, sessionManager) + + // tutorial for inline chat + const inlineChatTutorialAnnotation = new InlineChatTutorialAnnotation(inlineTutorialAnnotation) + + const enableInlineRollback = FeatureConfigProvider.instance.getPreFlareRollbackGroup() === 'treatment' + if (enableInlineRollback) { + // use VSC inline + getLogger().info('Entering preflare logic') + await activateInline(client) + } else { + // use language server for inline completion + getLogger().info('Entering postflare logic') + const inlineManager = new InlineCompletionManager(client, sessionManager, lineTracker, inlineTutorialAnnotation) + inlineManager.registerInlineCompletion() + toDispose.push( + inlineManager, + Commands.register('aws.amazonq.showPrev', async () => { + await sessionManager.maybeRefreshSessionUx() + await vscode.commands.executeCommand('editor.action.inlineSuggest.showPrevious') + sessionManager.onPrevSuggestion() + }), + Commands.register('aws.amazonq.showNext', async () => { + await sessionManager.maybeRefreshSessionUx() + await vscode.commands.executeCommand('editor.action.inlineSuggest.showNext') + sessionManager.onNextSuggestion() + }), + // this is a workaround since handleDidShowCompletionItem is not public API + Commands.register('aws.amazonq.checkInlineSuggestionVisibility', async () => { + sessionManager.checkInlineSuggestionVisibility() + }), + Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => { + vsCodeState.lastManualTriggerTime = performance.now() + await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') + }), + vscode.workspace.onDidCloseTextDocument(async () => { + await vscode.commands.executeCommand('aws.amazonq.rejectCodeSuggestion') + }) + ) + } + + activateInlineChat(extensionContext, client, encryptionKey, inlineChatTutorialAnnotation) + + if (Experiments.instance.get('amazonqChatLSP', true)) { + await activate(client, encryptionKey, resourcePaths.ui) + } + + const refreshInterval = auth.startTokenRefreshInterval(10 * oneSecond) + + // We manually push the cached values the first time since event handlers, which should push, may not have been setup yet. + // Execution order is weird and should be fixed in the flare implementation. + // TODO: Revisit if we need this if we setup the event handlers properly + await initializeLanguageServerConfiguration(client, 'startup') + + toDispose.push( + Commands.register('aws.amazonq.refreshAnnotation', async (forceProceed: boolean) => { + telemetry.record({ + traceId: TelemetryHelper.instance.traceId, + }) + + const editor = vscode.window.activeTextEditor + if (editor) { + if (forceProceed) { + await inlineTutorialAnnotation.refresh(editor, 'codewhisperer', true) + } else { + await inlineTutorialAnnotation.refresh(editor, 'codewhisperer') + } + } + }), + Commands.register('aws.amazonq.dismissTutorial', async () => { + const editor = vscode.window.activeTextEditor + if (editor) { + inlineTutorialAnnotation.clear() + try { + telemetry.ui_click.emit({ elementId: `dismiss_${inlineTutorialAnnotation.currentState.id}` }) + } catch (_) {} + await inlineTutorialAnnotation.dismissTutorial() + getLogger().debug(`codewhisperer: user dismiss tutorial.`) + } + }), + AuthUtil.instance.auth.onDidChangeActiveConnection(async () => { + await auth.refreshConnection() + }), + AuthUtil.instance.auth.onDidDeleteConnection(async () => { + void client.sendNotification(notificationTypes.deleteBearerToken.method) + }), + AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => sendProfileToLsp(client)), + vscode.commands.registerCommand('aws.amazonq.getWorkspaceId', async () => { + const requestType = new RequestType( + 'aws/getConfigurationFromServer' + ) + const workspaceIdResp = await client.sendRequest(requestType.method, { + section: 'aws.q.workspaceContext', + }) + return workspaceIdResp + }), + vscode.workspace.onDidCreateFiles((e) => { + void client.sendNotification('workspace/didCreateFiles', { + files: e.files.map((it) => { + return { uri: it.fsPath } + }), + } as CreateFilesParams) + }), + vscode.workspace.onDidDeleteFiles((e) => { + void client.sendNotification('workspace/didDeleteFiles', { + files: e.files.map((it) => { + return { uri: it.fsPath } + }), + } as DeleteFilesParams) + }), + vscode.workspace.onDidRenameFiles((e) => { + void client.sendNotification('workspace/didRenameFiles', { + files: e.files.map((it) => { + return { oldUri: it.oldUri.fsPath, newUri: it.newUri.fsPath } + }), + } as RenameFilesParams) + }), + vscode.workspace.onDidChangeWorkspaceFolders((e) => { + void client.sendNotification('workspace/didChangeWorkspaceFolder', { + event: { + added: e.added.map((it) => { + return { + name: it.name, + uri: it.uri.fsPath, + } as WorkspaceFolder + }), + removed: e.removed.map((it) => { + return { + name: it.name, + uri: it.uri.fsPath, + } as WorkspaceFolder + }), + }, + } as DidChangeWorkspaceFoldersParams) + }), + { dispose: () => clearInterval(refreshInterval) }, + // Set this inside onReady so that it only triggers on subsequent language server starts (not the first) + onServerRestartHandler(client, auth) + ) +} + +/** + * When the server restarts (likely due to a crash, then the LanguageClient automatically starts it again) + * we need to run some server intialization again. + */ +function onServerRestartHandler(client: BaseLanguageClient, auth: AmazonQLspAuth) { + return client.onDidChangeState(async (e) => { + // Ensure we are in a "restart" state + if (!(e.oldState === State.Starting && e.newState === State.Running)) { + return + } + + // Emit telemetry that a crash was detected. + // It is not guaranteed to 100% be a crash since somehow the server may have been intentionally restarted, + // but most of the time it probably will have been due to a crash. + // TODO: Port this metric override to common definitions + telemetry.languageServer_crash.emit({ id: 'AmazonQ' }) + + const logger = getLogger('amazonqLsp') + logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') + + try { + // Send bearer token + logger.debug('[crash-recovery] Refreshing connection and sending bearer token') + await auth.refreshConnection(true) + logger.debug('[crash-recovery] Bearer token sent successfully') + + // Send profile and customization configuration + await initializeLanguageServerConfiguration(client, 'crash-recovery') + logger.info('[crash-recovery] Authentication reinitialized successfully') + } catch (error) { + logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) + } + }) +} + +function getConfigSection(section: ConfigSection) { + getLogger('amazonqLsp').debug('Fetching config section %s for language server', section) + switch (section) { + case 'aws.q': + /** + * IMPORTANT: This object is parsed by the following code in the language server, **so + * it must match that expected shape**. + * https://github.com/aws/language-servers/blob/1d2ca018f2248106690438b860d40a7ee67ac728/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/configurationUtils.ts#L114 + */ + return [ + { + customization: undefinedIfEmpty(getSelectedCustomization().arn), + optOutTelemetry: getOptOutPreference() === 'OPTOUT', + projectContext: { + enableLocalIndexing: CodeWhispererSettings.instance.isLocalIndexEnabled(), + enableGpuAcceleration: CodeWhispererSettings.instance.isLocalIndexGPUEnabled(), + indexWorkerThreads: CodeWhispererSettings.instance.getIndexWorkerThreads(), + localIndexing: { + ignoreFilePatterns: CodeWhispererSettings.instance.getIndexIgnoreFilePatterns(), + maxFileSizeMB: CodeWhispererSettings.instance.getMaxIndexFileSize(), + maxIndexSizeMB: CodeWhispererSettings.instance.getMaxIndexSize(), + indexCacheDirPath: CodeWhispererSettings.instance.getIndexCacheDirPath(), + }, + }, + }, + ] + case 'aws.codeWhisperer': + return [ + { + includeSuggestionsWithCodeReferences: + CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled(), + shareCodeWhispererContentWithAWS: !CodeWhispererSettings.instance.isOptoutEnabled(), + includeImportsWithSuggestions: CodeWhispererSettings.instance.isImportRecommendationEnabled(), + sendUserWrittenCodeMetrics: true, + }, + ] + case 'aws.logLevel': + return [toAmazonQLSPLogLevel(globals.logOutputChannel.logLevel)] + } +} diff --git a/packages/amazonq/src/lsp/config.ts b/packages/amazonq/src/lsp/config.ts new file mode 100644 index 00000000000..d33f58d6988 --- /dev/null +++ b/packages/amazonq/src/lsp/config.ts @@ -0,0 +1,110 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { DevSettings, getServiceEnvVarConfig, BaseLspInstaller, getLogger } from 'aws-core-vscode/shared' +import { BaseLanguageClient } from 'vscode-languageclient' +import { + DidChangeConfigurationNotification, + updateConfigurationRequestType, +} from '@aws/language-server-runtimes/protocol' + +export interface ExtendedAmazonQLSPConfig extends BaseLspInstaller.LspConfig { + ui?: string +} + +// Taken from language server runtimes since they are not exported: +// https://github.com/aws/language-server-runtimes/blob/eae85672c345d8adaf4c8cbd741260b8a59750c4/runtimes/runtimes/util/loggingUtil.ts#L4-L10 +const validLspLogLevels = ['error', 'warn', 'info', 'log', 'debug'] as const +export type LspLogLevel = (typeof validLspLogLevels)[number] +const lspLogLevelMapping: Map = new Map([ + [vscode.LogLevel.Error, 'error'], + [vscode.LogLevel.Warning, 'warn'], + [vscode.LogLevel.Info, 'info'], + [vscode.LogLevel.Debug, 'log'], + [vscode.LogLevel.Trace, 'debug'], + [vscode.LogLevel.Off, 'error'], // TODO: once the language server supports a no-log setting, we can map to that. +]) + +const configSections = ['aws.q', 'aws.codeWhisperer', 'aws.logLevel'] as const +export type ConfigSection = (typeof configSections)[number] + +export function isValidConfigSection(section: unknown): section is ConfigSection { + return typeof section === 'string' && configSections.includes(section as ConfigSection) +} + +export const defaultAmazonQLspConfig: ExtendedAmazonQLSPConfig = { + manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/qAgenticChatServer/0/manifest.json', + supportedVersions: '1.*.*', + id: 'AmazonQ', // used across IDEs for identifying global storage/local disk locations. Do not change. + suppressPromptPrefix: 'amazonQ', + path: undefined, + ui: undefined, +} + +export function getAmazonQLspConfig(): ExtendedAmazonQLSPConfig { + return { + ...defaultAmazonQLspConfig, + ...(DevSettings.instance.getServiceConfig('amazonqLsp', {}) as ExtendedAmazonQLSPConfig), + ...getServiceEnvVarConfig('amazonqLsp', Object.keys(defaultAmazonQLspConfig)), + } +} +/** + * The language server logging levels do not directly match those used in VSC. Therefore, we must perform a mapping defined by {@link lspLogLevelMapping} + * @param logLevel vscode log level (0-5) + * @returns language server log level + */ +export function toAmazonQLSPLogLevel(logLevel: vscode.LogLevel): LspLogLevel { + return lspLogLevelMapping.get(logLevel) ?? 'info' +} + +/** + * Request/Notify a config value to the language server, effectively updating it with the + * latest configuration from the client. + * + * The issue is we need to push certain configs to different places, since there are + * different handlers for specific configs. So this determines the correct place to + * push the given config. + */ +export async function pushConfigUpdate(client: BaseLanguageClient, config: QConfigs) { + const logger = getLogger('amazonqLsp') + + switch (config.type) { + case 'profile': + logger.debug(`Pushing profile configuration: ${config.profileArn || 'undefined'}`) + await client.sendRequest(updateConfigurationRequestType.method, { + section: 'aws.q', + settings: { profileArn: config.profileArn }, + }) + logger.debug(`Profile configuration pushed successfully`) + break + case 'customization': + logger.debug(`Pushing customization configuration: ${config.customization || 'undefined'}`) + void client.sendNotification(DidChangeConfigurationNotification.type.method, { + section: 'aws.q', + settings: { customization: config.customization }, + }) + logger.debug(`Customization configuration pushed successfully`) + break + case 'logLevel': + logger.debug(`Pushing log level configuration`) + void client.sendNotification(DidChangeConfigurationNotification.type.method, { + section: 'aws.logLevel', + }) + logger.debug(`Log level configuration pushed successfully`) + break + } +} +type ProfileConfig = { + type: 'profile' + profileArn: string | undefined +} +type CustomizationConfig = { + type: 'customization' + customization: string | undefined +} +type LogLevelConfig = { + type: 'logLevel' +} +type QConfigs = ProfileConfig | CustomizationConfig | LogLevelConfig diff --git a/packages/amazonq/src/lsp/encryption.ts b/packages/amazonq/src/lsp/encryption.ts new file mode 100644 index 00000000000..246c64f476b --- /dev/null +++ b/packages/amazonq/src/lsp/encryption.ts @@ -0,0 +1,34 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as jose from 'jose' + +export async function encryptRequest(params: T, encryptionKey: Buffer): Promise<{ message: string } | T> { + const payload = new TextEncoder().encode(JSON.stringify(params)) + + const encryptedMessage = await new jose.CompactEncrypt(payload) + .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) + .encrypt(encryptionKey) + + return { message: encryptedMessage } +} + +export async function decryptResponse(response: unknown, key: Buffer | undefined) { + // Note that casts are required since language client requests return 'unknown' type. + // If we can't decrypt, return original response casted. + if (typeof response !== 'string' || key === undefined) { + return response as T + } + + const result = await jose.jwtDecrypt(response, key, { + clockTolerance: 60, // Allow up to 60 seconds to account for clock differences + contentEncryptionAlgorithms: ['A256GCM'], + keyManagementAlgorithms: ['dir'], + }) + + if (!result.payload) { + throw new Error('JWT payload not found') + } + return result.payload as T +} diff --git a/packages/amazonq/src/lsp/lspInstaller.ts b/packages/amazonq/src/lsp/lspInstaller.ts new file mode 100644 index 00000000000..9ac19601fe7 --- /dev/null +++ b/packages/amazonq/src/lsp/lspInstaller.ts @@ -0,0 +1,67 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import vscode from 'vscode' +import { fs, getNodeExecutableName, getRgExecutableName, BaseLspInstaller, ResourcePaths } from 'aws-core-vscode/shared' +import path from 'path' +import { ExtendedAmazonQLSPConfig, getAmazonQLspConfig } from './config' + +export interface AmazonQResourcePaths extends ResourcePaths { + ui: string + /** + * Path to `rg` (or `rg.exe`) executable/binary. + * Example: `"/aws/toolkits/language-servers/AmazonQ/3.3.0/servers/rg"` + */ + ripGrep: string +} + +export class AmazonQLspInstaller extends BaseLspInstaller.BaseLspInstaller< + AmazonQResourcePaths, + ExtendedAmazonQLSPConfig +> { + constructor(lspConfig: ExtendedAmazonQLSPConfig = getAmazonQLspConfig()) { + super(lspConfig, 'amazonqLsp') + } + + protected override async postInstall(assetDirectory: string): Promise { + const resourcePaths = this.resourcePaths(assetDirectory) + await fs.chmod(resourcePaths.node, 0o755) + if (await fs.exists(resourcePaths.ripGrep)) { + await fs.chmod(resourcePaths.ripGrep, 0o755) + } + } + + protected override resourcePaths(assetDirectory?: string): AmazonQResourcePaths { + if (!assetDirectory) { + return { + lsp: this.config.path ?? '', + node: getNodeExecutableName(), + ripGrep: `ripgrep/${getRgExecutableName()}`, + ui: this.config.ui ?? '', + } + } + + const nodePath = path.join(assetDirectory, `servers/${getNodeExecutableName()}`) + const rgPath = path.join(assetDirectory, `servers/ripgrep/${getRgExecutableName()}`) + return { + lsp: path.join(assetDirectory, 'servers/aws-lsp-codewhisperer.js'), + node: nodePath, + ripGrep: rgPath, + ui: path.join(assetDirectory, 'clients/amazonq-ui.js'), + } + } + + protected override downloadMessageOverride: string | undefined = 'Updating Amazon Q plugin' +} + +export function getBundledResourcePaths(ctx: vscode.ExtensionContext): AmazonQResourcePaths { + const assetDirectory = vscode.Uri.joinPath(ctx.extensionUri, 'resources', 'language-server').fsPath + return { + lsp: path.join(assetDirectory, 'servers', 'aws-lsp-codewhisperer.js'), + node: process.execPath, + ripGrep: '', + ui: path.join(assetDirectory, 'clients', 'amazonq-ui.js'), + } +} diff --git a/packages/amazonq/src/lsp/utils.ts b/packages/amazonq/src/lsp/utils.ts new file mode 100644 index 00000000000..f5b010c536b --- /dev/null +++ b/packages/amazonq/src/lsp/utils.ts @@ -0,0 +1,26 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { CursorState } from '@aws/language-server-runtimes-types' + +/** + * Convert from vscode selection type to the general CursorState expected by the AmazonQLSP. + * @param selection + * @returns + */ +export function getCursorState(selection: readonly vscode.Selection[]): CursorState[] { + return selection.map((s) => ({ + range: { + start: { + line: s.start.line, + character: s.start.character, + }, + end: { + line: s.end.line, + character: s.end.character, + }, + }, + })) +} diff --git a/packages/amazonq/src/util/clearCache.ts b/packages/amazonq/src/util/clearCache.ts new file mode 100644 index 00000000000..8c93b35ac12 --- /dev/null +++ b/packages/amazonq/src/util/clearCache.ts @@ -0,0 +1,49 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AuthUtil } from 'aws-core-vscode/codewhisperer' +import { Commands, fs, globals, LanguageServerResolver } from 'aws-core-vscode/shared' +import vscode from 'vscode' + +/** + * The purpose of this module is to provide a util to clear all extension cache so that it has a clean state + */ + +/** + * Clears "all" cache of the extension, effectively putting the user in a "net new" state. + * + * NOTE: This is a best attempt. There may be state like a file in the filesystem which is not deleted. + * We should aim to add all state clearing in to this method. + */ +async function clearCache() { + // Check a final time if they want to clear their cache + const doContinue = await vscode.window + .showInformationMessage( + 'This will wipe your Amazon Q extension state, then reload your VS Code window. This operation is not dangerous. ', + { modal: true }, + 'Continue' + ) + .then((value) => { + return value === 'Continue' + }) + if (!doContinue) { + return + } + + // SSO cache persists on disk, this should indirectly delete it + const conn = AuthUtil.instance.conn + if (conn) { + await AuthUtil.instance.auth.deleteConnection(conn) + } + + await globals.globalState.clear() + + // Clear the Language Server Cache + await fs.delete(LanguageServerResolver.defaultDir(), { recursive: true, force: true }) + + // Make the IDE reload so all new changes take effect + void vscode.commands.executeCommand('workbench.action.reloadWindow') +} +export const clearCacheDeclaration = Commands.declare({ id: 'aws.amazonq.clearCache' }, () => clearCache) diff --git a/packages/amazonq/src/util/timeoutUtil.ts b/packages/amazonq/src/util/timeoutUtil.ts new file mode 100644 index 00000000000..c42d1e3be01 --- /dev/null +++ b/packages/amazonq/src/util/timeoutUtil.ts @@ -0,0 +1,15 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export function asyncCallWithTimeout(asyncPromise: Promise, message: string, timeLimit: number): Promise { + let timeoutHandle: NodeJS.Timeout + const timeoutPromise = new Promise((_resolve, reject) => { + timeoutHandle = setTimeout(() => reject(new Error(message)), timeLimit) + }) + return Promise.race([asyncPromise, timeoutPromise]).then((result) => { + clearTimeout(timeoutHandle) + return result as T + }) +} diff --git a/packages/amazonq/test/e2e/amazonq/assert.ts b/packages/amazonq/test/e2e/amazonq/assert.ts new file mode 100644 index 00000000000..5bcec3fc0b4 --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/assert.ts @@ -0,0 +1,41 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { Messenger } from './framework/messenger' + +export function assertQuickActions(tab: Messenger, commands: string[]) { + const commandGroup = tab + .getCommands() + .map((groups) => groups.commands) + .flat() + if (!commandGroup) { + assert.fail(`Could not find commands for ${tab.tabID}`) + } + + const commandNames = commandGroup.map((cmd) => cmd.command) + + const missingCommands = [] + for (const command of commands) { + if (!commandNames.includes(command)) { + missingCommands.push(command) + } + } + + if (missingCommands.length > 0) { + assert.fail(`Could not find commands: ${missingCommands.join(', ')} for ${tab.tabID}`) + } +} + +export function assertContextCommands(tab: Messenger, contextCommands: string[]) { + assert.deepStrictEqual( + tab + .getStore() + .contextCommands?.map((x) => x.commands) + .flat() + .map((x) => x.command), + contextCommands + ) +} diff --git a/packages/amazonq/test/e2e/amazonq/chat.test.ts b/packages/amazonq/test/e2e/amazonq/chat.test.ts new file mode 100644 index 00000000000..c4f74c51376 --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/chat.test.ts @@ -0,0 +1,86 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { qTestingFramework } from './framework/framework' +import sinon from 'sinon' +import { Messenger } from './framework/messenger' +import { MynahUIDataModel } from '@aws/mynah-ui' +import { assertContextCommands, assertQuickActions } from './assert' +import { registerAuthHook, using } from 'aws-core-vscode/test' +import { loginToIdC } from './utils/setup' +import { webviewConstants, webviewTabConstants } from 'aws-core-vscode/amazonq' + +describe('Amazon Q Chat', function () { + this.retries(3) + let framework: qTestingFramework + let tab: Messenger + let store: MynahUIDataModel + + const availableCommands: string[] = ['/dev', '/test', '/review', '/doc', '/transform'] + + before(async function () { + /** + * Login to the amazonq-test-account. When running in CI this has unlimited + * calls to the backend api + */ + await using(registerAuthHook('amazonq-test-account'), async () => { + await loginToIdC() + }) + }) + + // jscpd:ignore-start + beforeEach(() => { + // Make sure you're logged in before every test + registerAuthHook('amazonq-test-account') + framework = new qTestingFramework('cwc', true, []) + tab = framework.createTab() + store = tab.getStore() + }) + + afterEach(() => { + framework.removeTab(tab.tabID) + framework.dispose() + sinon.restore() + }) + + it(`Shows quick actions: ${availableCommands.join(', ')}`, async () => { + assertQuickActions(tab, availableCommands) + }) + + it('Shows @workspace', () => { + assertContextCommands(tab, ['@workspace']) + }) + + // jscpd:ignore-end + + it('Shows title', () => { + assert.deepStrictEqual(store.tabTitle, 'Chat') + }) + + it('Shows placeholder', () => { + assert.deepStrictEqual(store.promptInputPlaceholder, webviewTabConstants.commonTabData.placeholder) + }) + + it('Sends message', async () => { + tab.addChatMessage({ + prompt: 'What is a lambda', + }) + await tab.waitForChatFinishesLoading() + const chatItems = tab.getChatItems() + // the last item should be an answer + assert.deepStrictEqual(chatItems[4].type, 'answer') + }) + + describe('Clicks examples', () => { + it('Click help', async () => { + tab.clickButton('help') + await tab.waitForText(webviewConstants.helpMessage) + const chatItems = tab.getChatItems() + assert.deepStrictEqual(chatItems[4].type, 'answer') + assert.deepStrictEqual(chatItems[4].body, webviewConstants.helpMessage) + }) + }) +}) diff --git a/packages/amazonq/test/e2e/amazonq/framework/framework.ts b/packages/amazonq/test/e2e/amazonq/framework/framework.ts new file mode 100644 index 00000000000..0cc9a9298ee --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/framework/framework.ts @@ -0,0 +1,136 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { injectJSDOM } from './jsdomInjector' + +// This needs to be ran before all other imports so that mynah ui gets loaded inside of jsdom +injectJSDOM() + +import assert from 'assert' +import * as vscode from 'vscode' +import { MynahUI, MynahUIProps } from '@aws/mynah-ui' +import { DefaultAmazonQAppInitContext, TabType, createMynahUI } from 'aws-core-vscode/amazonq' +import { Messenger, MessengerOptions } from './messenger' +import { FeatureContext } from 'aws-core-vscode/shared' + +/** + * Abstraction over Amazon Q to make e2e testing easier + */ +export class qTestingFramework { + private readonly mynahUI: MynahUI + private readonly mynahUIProps: MynahUIProps + private disposables: vscode.Disposable[] = [] + + lastEventId: string = '' + + constructor( + featureName: TabType, + amazonQEnabled: boolean, + featureConfigsSerialized: [string, FeatureContext][], + welcomeCount = Number.MAX_VALUE // by default don't show the welcome page + ) { + /** + * Instantiate the UI and override the postMessage to publish using the app message + * publishers directly. + * + * The postMessage function implements the MynahUI -> VSCode flow + */ + const ui = createMynahUI( + { + postMessage: (message: string) => { + const appMessagePublisher = DefaultAmazonQAppInitContext.instance + .getWebViewToAppsMessagePublishers() + .get(featureName) + if (appMessagePublisher === undefined) { + return + } + appMessagePublisher.publish(message) + }, + }, + amazonQEnabled, + featureConfigsSerialized, + welcomeCount + ) + this.mynahUI = ui.mynahUI + this.mynahUIProps = (this.mynahUI as any).props + + /** + * In order to successfully remove tabs we need the last event id + */ + const originalOnTabAdd = this.mynahUIProps.onTabAdd + this.mynahUIProps.onTabAdd = (tabId, eventId) => { + this.lastEventId = eventId ?? this.lastEventId + originalOnTabAdd && originalOnTabAdd(tabId) + } + + /** + * Listen to incoming events coming from VSCode and redirect them to MynahUI + * + * This implements the VSCode -> Mynah UI flow + */ + this.disposables.push( + DefaultAmazonQAppInitContext.instance.getAppsToWebViewMessageListener().onMessage(async (message) => { + // Emulate the json format of postMessage + const event = { + data: JSON.stringify(message), + } as any + await ui.messageReceiver(event) + }) + ) + + /** + * We need to manually indicate that the UI is ready since we are using a custom mynah UI event routing + * implementation instead of routing events through the real webview + **/ + DefaultAmazonQAppInitContext.instance.getAppsToWebViewMessagePublisher().setUiReady() + } + + /** + * Create a new tab and then return a new encapsulated tab messenger that makes it easier to directly call + * functionality against a specific tab + */ + public createTab(options?: MessengerOptions) { + const oldTabs = Object.keys(this.mynahUI.getAllTabs()) + + // simulate pressing the new tab button + ;(document.querySelectorAll('.mynah-nav-tabs-wrapper > button.mynah-button')[0] as HTMLButtonElement).click() + const newTabs = Object.keys(this.mynahUI.getAllTabs()) + + const newTabID = newTabs.find((tab) => !oldTabs.includes(tab)) + if (!newTabID) { + assert.fail('Could not find new tab') + } + + return new Messenger(newTabID, this.mynahUIProps, this.mynahUI, options) + } + + public getTabs() { + const tabs = this.mynahUI.getAllTabs() + return Object.entries(tabs).map(([tabId]) => new Messenger(tabId, this.mynahUIProps, this.mynahUI)) + } + + public getSelectedTab() { + const selectedTabId = this.mynahUI.getSelectedTabId() + const selectedTab = this.getTabs().find((tab) => tab.tabID === selectedTabId) + + if (!selectedTab) { + assert.fail('Selected tab not found') + } + return selectedTab + } + + public findTab(title: string) { + return Object.values(this.getTabs()).find((tab) => tab.getStore().tabTitle === title) + } + + public removeTab(tabId: string) { + this.mynahUI.removeTab(tabId, this.lastEventId) + } + + public dispose() { + vscode.Disposable.from(...this.disposables).dispose() + this.mynahUI.destroy() + } +} diff --git a/packages/amazonq/test/e2e/amazonq/framework/jsdomInjector.ts b/packages/amazonq/test/e2e/amazonq/framework/jsdomInjector.ts new file mode 100644 index 00000000000..24e2d361640 --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/framework/jsdomInjector.ts @@ -0,0 +1,63 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { JSDOM, VirtualConsole } from 'jsdom' + +/** + * JSDOM is used to help hoist MynahUI to running in a node environment vs in the browser (which is what it's made for) + */ +export function injectJSDOM() { + const virtualConsole = new VirtualConsole() + virtualConsole.on('error', (error) => { + // JSDOM can't load scss from mynah UI, just skip it + if (!error.includes('Could not parse CSS stylesheet')) { + console.error(error) + } + }) + + const dom = new JSDOM(undefined, { + pretendToBeVisual: true, + includeNodeLocations: true, + virtualConsole, + }) + global.window = dom.window as unknown as Window & typeof globalThis + global.document = dom.window.document + global.self = dom.window as unknown as Window & typeof globalThis + global.Element = dom.window.Element + global.HTMLElement = dom.window.HTMLElement + global.Node = dom.window.Node + + global.ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} + } + + // jsdom doesn't have support for innerText: https://github.com/jsdom/jsdom/issues/1245 which mynah ui uses + Object.defineProperty(global.Element.prototype, 'innerText', { + get() { + return this.textContent + }, + set(value) { + this.textContent = value + }, + }) + + // jsdom doesn't have support for structuredClone. See https://github.com/jsdom/jsdom/issues/3363 + global.structuredClone = (val: any) => JSON.parse(JSON.stringify(val)) + + global.IntersectionObserver = class IntersectionObserver { + observe() {} + unobserve() {} + disconnect() {} + takeRecords() { + return [] + } + // eslint-disable-next-line unicorn/no-null + root = null + rootMargin = '' + thresholds = [] + } +} diff --git a/packages/amazonq/test/e2e/amazonq/framework/messenger.ts b/packages/amazonq/test/e2e/amazonq/framework/messenger.ts new file mode 100644 index 00000000000..231e93b35be --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/framework/messenger.ts @@ -0,0 +1,219 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { MynahUI, MynahUIProps, MynahUIDataModel } from '@aws/mynah-ui' +import { waitUntil } from 'aws-core-vscode/shared' +import { FollowUpTypes } from 'aws-core-vscode/amazonq' + +export interface MessengerOptions { + waitIntervalInMs?: number + waitTimeoutInMs?: number +} + +/** + * Abstraction over tabIds to make it easier to send messages to specific tabs + */ +export class Messenger { + private defaultWaitIntervalInMs = 5000 + private defaultWaitTimeoutInMs = 600000 + + private waitIntervalInMs: number + private waitTimeoutInMs: number + + constructor( + public readonly tabID: string, + private readonly mynahUIProps: MynahUIProps, + private readonly mynahUI: MynahUI, + options?: MessengerOptions + ) { + this.waitIntervalInMs = options?.waitIntervalInMs ?? this.defaultWaitIntervalInMs + this.waitTimeoutInMs = options?.waitTimeoutInMs ?? this.defaultWaitTimeoutInMs + } + + addChatMessage({ prompt, command }: { prompt?: string; command?: string }) { + if (!this.mynahUIProps.onChatPrompt) { + assert.fail('onChatPrompt must be defined to use it in the tests') + } + + this.mynahUIProps.onChatPrompt(this.tabID, { + prompt, + escapedPrompt: prompt, + command, + }) + } + + clickButton(type: string) { + if (!this.mynahUIProps.onFollowUpClicked) { + assert.fail('onFollowUpClicked must be defined to use it in the tests') + } + + const lastChatItem = this.getChatItems().pop() + const followupOption = lastChatItem?.followUp?.options?.filter((option) => option.type === type) + if (followupOption && followupOption.length > 0) { + this.mynahUIProps.onFollowUpClicked(this.tabID, lastChatItem?.messageId ?? '', followupOption[0]) + return + } + + assert.fail(`Could not find a button with id ${type} on tabID: ${this.tabID}`) + } + + clickInBodyButton(type: string) { + if (!this.mynahUIProps.onInBodyButtonClicked) { + assert.fail('onInBodyButtonClicked must be defined to use it in the tests') + } + + const lastChatItem = this.getChatItems().pop() + const followupButton = lastChatItem?.buttons?.filter((option) => option.id === type) + if (followupButton && followupButton.length > 0) { + this.mynahUIProps.onInBodyButtonClicked(this.tabID, lastChatItem?.messageId ?? '', followupButton[0]) + return + } + + assert.fail(`Could not find a button with id ${type} on tabID: ${this.tabID}`) + } + + clickCustomFormButton(action: { id: string; text?: string; formItemValues?: Record }) { + if (!this.mynahUIProps.onCustomFormAction) { + assert.fail('onCustomFormAction must be defined to use it in the tests') + } + + this.mynahUIProps.onCustomFormAction(this.tabID, action) + } + + clickFileActionButton(filePath: string, actionName: string) { + if (!this.mynahUIProps.onFileActionClick) { + assert.fail('onFileActionClick must be defined to use it in the tests') + } + + this.mynahUIProps.onFileActionClick(this.tabID, this.getFileListMessageId(), filePath, actionName) + } + + findCommand(command: string) { + return this.getCommands() + .map((groups) => groups.commands) + .flat() + .filter((commands) => commands.command === command) + } + + getCommands() { + return [...(this.getStore().quickActionCommands ?? [])] + } + + getChatItems() { + return [...(this.getStore().chatItems ?? [])] + } + + getPlaceholder() { + return this.getStore().promptInputPlaceholder + } + + getFollowUpButton(type: FollowUpTypes) { + const followUpButton = this.getChatItems() + .pop() + ?.followUp?.options?.find((action) => action.type === type) + if (!followUpButton) { + assert.fail(`Could not find follow up button with type ${type}`) + } + return followUpButton + } + + getFileList() { + const chatItems = this.getChatItems() + const fileList = chatItems.find((item) => 'fileList' in item) + if (!fileList) { + assert.fail('Could not find file list') + } + return fileList + } + + getFileListMessageId() { + const fileList = this.getFileList() + const messageId = fileList?.messageId + if (!messageId) { + assert.fail('Could not find file list message id') + } + return messageId + } + + getFilePaths() { + const fileList = this.getFileList() + const filePaths = fileList?.fileList?.filePaths + if (!filePaths) { + assert.fail('Could not find file paths') + } + if (filePaths.length === 0) { + assert.fail('File paths list is empty') + } + return filePaths + } + + getActionsByFilePath(filePath: string) { + const fileList = this.getFileList() + const actions = fileList?.fileList?.actions + return actions?.[filePath] ?? [] + } + + hasButton(type: FollowUpTypes) { + return ( + this.getChatItems() + .pop() + ?.followUp?.options?.map((opt) => opt.type) + .includes(type) ?? false + ) + } + + hasAction(filePath: string, actionName: string) { + return this.getActionsByFilePath(filePath).some((action) => action.name === actionName) + } + + async waitForText(text: string, waitOverrides?: MessengerOptions) { + await this.waitForEvent(() => { + return this.getChatItems().some((chatItem) => chatItem.body === text) + }, waitOverrides) + } + + async waitForButtons(buttons: FollowUpTypes[]) { + return this.waitForEvent(() => { + return buttons.every((value) => this.hasButton(value)) + }) + } + + async waitForChatFinishesLoading() { + return this.waitForEvent(() => this.getStore().loadingChat === false || this.hasButton(FollowUpTypes.Retry)) + } + + async waitForEvent(event: () => boolean, waitOverrides?: MessengerOptions) { + /** + * Wait until the chat has finished loading. This happens when a backend request + * has finished and responded in the chat + */ + const ok = await waitUntil( + async () => { + return event() + }, + { + interval: waitOverrides ? waitOverrides.waitIntervalInMs : this.waitIntervalInMs, + timeout: waitOverrides ? waitOverrides.waitTimeoutInMs : this.waitTimeoutInMs, + truthy: true, + } + ) + + // Do another check just in case the waitUntil time'd out + if (!ok) { + assert.fail( + `Event has not finished loading in: ${waitOverrides ? waitOverrides.waitTimeoutInMs : this.waitTimeoutInMs} ms` + ) + } + } + + getStore(): MynahUIDataModel { + const store = this.mynahUI.getAllTabs()[this.tabID].store + if (!store) { + assert.fail(`${this.tabID} does not have a store`) + } + return store + } +} diff --git a/packages/amazonq/test/e2e/amazonq/framework/text.ts b/packages/amazonq/test/e2e/amazonq/framework/text.ts new file mode 100644 index 00000000000..020792c6835 --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/framework/text.ts @@ -0,0 +1,29 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as assert from 'assert' +import { ChatItem } from '@aws/mynah-ui' + +/** + * Verify that the text items in expectedText appears in the exact same order in chatItems + * @param chatItems An array of chat items in mynah UI + * @param expectedText An array of expected text items in order + * @returns true if the items in expectedText are found in the correct order in chatItems otherwise false + */ +export function verifyTextOrder(chatItems: ChatItem[], expectedText: RegExp[]) { + let currInd = 0 + for (const item of chatItems) { + const currentExpected = expectedText[currInd] + if (currentExpected && item.body?.match(currentExpected)) { + currInd++ + } + } + + if (currInd !== expectedText.length) { + const chatItemBodies = chatItems.filter((item) => item !== undefined).map((item) => item.body) + const expected = expectedText.join(', ') + assert.fail(`Items did not appear in expected order. Found ${chatItemBodies} but expected ${expected}`) + } +} diff --git a/packages/amazonq/test/e2e/amazonq/review.test.ts b/packages/amazonq/test/e2e/amazonq/review.test.ts new file mode 100644 index 00000000000..5914e3eaa29 --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/review.test.ts @@ -0,0 +1,611 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * Zuo + */ + +import assert from 'assert' +import * as vscode from 'vscode' +import { qTestingFramework } from './framework/framework' +import sinon from 'sinon' +import { Messenger } from './framework/messenger' +import { registerAuthHook, using, closeAllEditors } from 'aws-core-vscode/test' +import { loginToIdC } from './utils/setup' +import { + codewhispererDiagnosticSourceLabel, + invalidFileTypeChatMessage, + CodeAnalysisScope, + SecurityScanStep, + amazonqCodeIssueDetailsTabTitle, + CodeWhispererConstants, + CodeScanIssue, +} from 'aws-core-vscode/codewhisperer' +import path from 'path' +import { ScanAction, scanProgressMessage } from '../../../src/app/amazonqScan/models/constants' +import { SecurityIssueProvider } from 'aws-core-vscode/codewhisperer' +import { fs, waitUntil, processUtils } from 'aws-core-vscode/shared' + +function getWorkspaceFolder(): string { + return vscode.workspace.workspaceFolders![0].uri.fsPath +} + +describe('Amazon Q Code Review', function () { + let framework: qTestingFramework + let tab: Messenger + + function extractAndValidateIssues(reviewString: string): Record { + const issueRegex = /- (\w+): `(\d+) issues?`/g + const issues: Record = { + Critical: 0, + High: 0, + Medium: 0, + Low: 0, + Info: 0, + } + const foundCategories = new Set() + + let match + while ((match = issueRegex.exec(reviewString)) !== null) { + const [, severity, count] = match + if (severity in issues) { + issues[severity] = parseInt(count, 10) + foundCategories.add(severity) + } + } + + const expectedCategories = Object.keys(issues) + const missingCategories = expectedCategories.filter((category) => !foundCategories.has(category)) + + assert.deepStrictEqual( + missingCategories.length, + 0, + `Output chat issue format is not correct or it does not have these categories: ${missingCategories.join(', ')}` + ) + return issues + } + + function matchingSecurityDiagnosticCount(diagnostics: vscode.Diagnostic[], message: string, lineNumber?: number) { + const matchingDiagnostics = diagnostics.filter((diagnostic) => { + let matches = diagnostic.message === message + + // Only filter by startLine if it's provided + if (lineNumber !== undefined) { + matches = + matches && diagnostic.range.start.line <= lineNumber && diagnostic.range.end.line >= lineNumber + } + + return matches + }) + + return matchingDiagnostics.length + } + + async function waitForChatItems(index: number, waitTimeoutInMs: number = 5000, waitIntervalInMs: number = 1000) { + await tab.waitForEvent(() => tab.getChatItems().length > index, { + waitTimeoutInMs: waitTimeoutInMs, + waitIntervalInMs: waitIntervalInMs, + }) + } + + async function validateInitialChatMessage() { + tab.addChatMessage({ command: '/review' }) + await waitForChatItems(4) + const fileOrWorkspaceMessage = tab.getChatItems()[4] + assert.deepStrictEqual(fileOrWorkspaceMessage.type, 'ai-prompt') + } + + async function waitForReviewResults(tab: Messenger): Promise { + await waitForChatItems(7, 600_000, 10_000) + const scanResultsMessage = tab.getChatItems()[7] + assert.deepStrictEqual(scanResultsMessage.type, 'answer') + + const scanResultBody = scanResultsMessage.body ?? '' + assert.notDeepStrictEqual(scanResultBody, '') + return scanResultBody + } + + before(async function () { + await using(registerAuthHook('amazonq-test-account'), async () => { + await loginToIdC() + }) + }) + + beforeEach(async () => { + registerAuthHook('amazonq-test-account') + framework = new qTestingFramework('review', true, []) + tab = framework.createTab() + }) + + afterEach(async () => { + await closeAllEditors() + framework.removeTab(tab.tabID) + framework.dispose() + sinon.restore() + }) + + describe('Quick action availability', () => { + it('Shows /review when code review is enabled', async () => { + const command = tab.findCommand('/review') + if (!command.length) { + assert.fail('Could not find command') + } + if (command.length > 1) { + assert.fail('Found too many commands with the name /review') + } + }) + + it('Does NOT show /review when code review is NOT enabled', () => { + framework.dispose() + framework = new qTestingFramework('review', false, []) + const tab = framework.createTab() + const command = tab.findCommand('/review') + if (command.length > 0) { + assert.fail('Found command when it should not have been found') + } + }) + }) + + describe('/review initial chat output', () => { + it('Shows appropriate message when /review is entered', async () => { + tab.addChatMessage({ command: '/review' }) + + await waitForChatItems(4) + const fileOrWorkspaceMessage = tab.getChatItems()[4] + + assert.deepStrictEqual(fileOrWorkspaceMessage.type, 'ai-prompt') + assert.deepStrictEqual( + fileOrWorkspaceMessage.body, + 'Would you like to review your active file or the workspace you have open?' + ) + }) + }) + + describe('/review entry', () => { + describe('No file open when review active file', () => { + it('Shows appropriate message when no file is open', async () => { + await validateInitialChatMessage() + + tab.clickButton(ScanAction.RUN_FILE_SCAN) + + await waitForChatItems(5) + const noFileMessage = tab.getChatItems()[5] + assert.deepStrictEqual(noFileMessage.type, 'answer') + assert.deepStrictEqual(noFileMessage.body, invalidFileTypeChatMessage) + }) + }) + + describe('review insecure file and then fix file', async () => { + it('/review file gives correct critical and high security issues, clicks on view details, generate fix, verify diff, apply fix', async () => { + const testFolder = path.join(getWorkspaceFolder(), 'QCAFolder') + const fileName = 'ProblematicCode.java' + const filePath = path.join(testFolder, fileName) + + await validateInitialChatMessage() + const document = await vscode.workspace.openTextDocument(filePath) + const originalContent = document.getText() + await vscode.window.showTextDocument(document) + + tab.clickButton(ScanAction.RUN_FILE_SCAN) + + await waitForChatItems(6) + const scanningInProgressMessage = tab.getChatItems()[6] + assert.deepStrictEqual( + scanningInProgressMessage.body, + scanProgressMessage(SecurityScanStep.CREATE_SCAN_JOB, CodeAnalysisScope.FILE_ON_DEMAND, fileName) + ) + + const scanResultBody = await waitForReviewResults(tab) + + const issues = extractAndValidateIssues(scanResultBody) + assert.deepStrictEqual( + issues.Critical >= 1, + true, + `critical issue ${issues.Critical} is not larger or equal to 1` + ) + + const uri = vscode.Uri.file(filePath) + const securityDiagnostics = vscode.languages + .getDiagnostics(uri) + .filter((diagnostic) => diagnostic.source === codewhispererDiagnosticSourceLabel) + + assert.ok(securityDiagnostics.length > 0, 'No security diagnostics found') + + // at least 1 exact critical issue matches + assert.ok( + matchingSecurityDiagnosticCount(securityDiagnostics, 'CWE-798 - Hardcoded credentials', 21) >= 1 + ) + + // Find one diagnostic + const sampleDiagnostic = securityDiagnostics[0] + assert.ok(sampleDiagnostic, 'Could not find critical issue diagnostic') + + const range = new vscode.Range(sampleDiagnostic.range.start, sampleDiagnostic.range.end) + + const codeActions = await vscode.commands.executeCommand( + 'vscode.executeCodeActionProvider', + uri, + range + ) + + await new Promise((resolve) => setTimeout(resolve, 1000)) + + // Find the "View details" code action + const viewDetailsAction = codeActions?.find((action) => action.title.includes('View details')) + assert.ok(viewDetailsAction, 'Could not find View details code action') + + // Execute the view details command + if (viewDetailsAction?.command) { + await vscode.commands.executeCommand( + viewDetailsAction.command.command, + ...viewDetailsAction.command.arguments! + ) + } + + // Wait for the webview panel to open with polling + const webviewPanel = await waitUntil( + async () => { + const panels = vscode.window.tabGroups.all + .flatMap((group) => group.tabs) + .filter((tab) => tab.label === amazonqCodeIssueDetailsTabTitle) + .map((tab) => tab.input) + .filter((input): input is vscode.WebviewPanel => input !== undefined) + + return panels.length > 0 ? panels[0] : undefined + }, + { + timeout: 20_000, + interval: 1000, + truthy: false, + } + ) + + await new Promise((resolve) => setTimeout(resolve, 1000)) + + assert.ok(webviewPanel, 'Security issue webview panel did not open after waiting') + + // Wait until viewDetailsAction.command is defined + const viewDetailsActionDefined = await waitUntil( + async () => { + return viewDetailsAction.command?.arguments !== undefined + ? viewDetailsAction.command + : undefined + }, + { + timeout: 10_000, + interval: 500, + truthy: true, + } + ) + await new Promise((resolve) => setTimeout(resolve, 1000)) + + assert.ok(viewDetailsActionDefined, 'viewDetailsAction.command was not defined after waiting') + + const issue = viewDetailsActionDefined.arguments?.[0] as CodeScanIssue + console.log('issue', issue) + + // Wait for the fix to be generated with polling + const updatedIssue = await waitUntil( + async () => { + const foundIssue = SecurityIssueProvider.instance.issues + .flatMap(({ issues }) => issues) + .find((i) => i.findingId === issue.findingId) + + return foundIssue?.suggestedFixes?.length !== undefined && + foundIssue?.suggestedFixes?.length > 0 + ? foundIssue + : undefined + }, + { + timeout: CodeWhispererConstants.codeFixJobTimeoutMs + 20_000, + interval: CodeWhispererConstants.codeFixJobPollingIntervalMs, + truthy: true, + } + ) + + await new Promise((resolve) => setTimeout(resolve, 1000)) + + console.log('updated issue', updatedIssue) + console.log('original issue', issue) + + // Verify the fix was generated by checking if the issue has suggestedFixes + assert.ok(updatedIssue, 'Could not find updated issue') + assert.ok(updatedIssue.suggestedFixes.length > 0, 'No suggested fixes were generated') + + // Get the suggested fix and verify it contains diff markers + const suggestedFix = updatedIssue.suggestedFixes[0] + const suggestedFixDiff = suggestedFix.code + assert.ok(suggestedFixDiff, 'No suggested fix code was found') + assert.ok( + suggestedFixDiff.includes('-') && suggestedFixDiff.includes('+'), + 'Suggested fix does not contain diff markers' + ) + + // Parse the diff to extract removed and added lines + const diffLines = suggestedFixDiff.split('\n') + const removedLines = diffLines + .filter((line) => line.startsWith('-') && !line.startsWith('---')) + .map((line) => line.substring(1).trim()) + const addedLines = diffLines + .filter((line) => line.startsWith('+') && !line.startsWith('+++')) + .map((line) => line.substring(1).trim()) + + // Make sure we found some changes in the diff + assert.ok(addedLines.length + removedLines.length > 0, 'No added or deleted lines found in the diff') + + // Apply the fix + await vscode.commands.executeCommand('aws.amazonq.applySecurityFix', updatedIssue, filePath, 'webview') + + // Wait for the fix to be applied + await new Promise((resolve) => setTimeout(resolve, 1000)) + + // Verify the fix was applied to the file + const updatedDocument = await vscode.workspace.openTextDocument(filePath) + const updatedContent = updatedDocument.getText() + + // Check that the content has changed + assert.notStrictEqual( + updatedContent, + originalContent, + 'File content did not change after applying the fix' + ) + + // Count occurrences of each line in original and updated content + const countOccurrences = (text: string, line: string): number => { + const regex = new RegExp(`^\\s*${line.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*$`, 'gm') + const matches = text.match(regex) + return matches ? matches.length : 0 + } + + // Create a dictionary to track expected line count changes + const lineCountChanges: Record = {} + + // Process removed lines (decrement count) + for (const removedLine of removedLines) { + if (removedLine.trim()) { + // Skip empty lines + const trimmedLine = removedLine.trim() + lineCountChanges[trimmedLine] = (lineCountChanges[trimmedLine] || 0) - 1 + } + } + + // Process added lines (increment count) + for (const addedLine of addedLines) { + if (addedLine.trim()) { + // Skip empty lines + const trimmedLine = addedLine.trim() + lineCountChanges[trimmedLine] = (lineCountChanges[trimmedLine] || 0) + 1 + } + } + + // Verify all line count changes match expectations + for (const [line, expectedChange] of Object.entries(lineCountChanges)) { + const originalCount = countOccurrences(originalContent, line) + const updatedCount = countOccurrences(updatedContent, line) + const actualChange = updatedCount - originalCount + + assert.strictEqual( + actualChange, + expectedChange, + `Line "${line}" count change mismatch: expected ${expectedChange}, got ${actualChange} (original: ${originalCount}, updated: ${updatedCount})` + ) + } + + // Revert the changes + await fs.writeFile(uri, originalContent) + + // Wait a moment for the file system to update + await new Promise((resolve) => setTimeout(resolve, 1000)) + + // Verify the file was reverted + const revertedDocument = await vscode.workspace.openTextDocument(filePath) + const revertedContent = revertedDocument.getText() + + assert.deepStrictEqual(revertedContent, originalContent, 'File content was not properly reverted') + }) + }) + + describe('/review file and project scans should respect ignored line findings', async () => { + const testFolder = path.join(getWorkspaceFolder(), 'QCAFolder') + const fileName = 'ProblematicCode.java' + const filePath = path.join(testFolder, fileName) + let document: vscode.TextDocument + + beforeEach(async () => { + await validateInitialChatMessage() + + document = await vscode.workspace.openTextDocument(filePath) + await vscode.window.showTextDocument(document) + + const editor = vscode.window.activeTextEditor + + if (editor) { + const position = new vscode.Position(20, 0) + await editor.edit((editBuilder) => { + editBuilder.insert(position, '// amazonq-ignore-next-line\n') + }) + await document.save() + } + }) + + it('/review file respect ignored line findings', async () => { + tab.clickButton(ScanAction.RUN_FILE_SCAN) + }) + + it('/review project respect ignored line findings', async () => { + tab.clickButton(ScanAction.RUN_PROJECT_SCAN) + }) + + afterEach(async () => { + await waitForReviewResults(tab) + + const uri = vscode.Uri.file(filePath) + const securityDiagnostics: vscode.Diagnostic[] = vscode.languages + .getDiagnostics(uri) + .filter((diagnostic) => diagnostic.source === codewhispererDiagnosticSourceLabel) + + // cannot find this ignored issue + assert.equal( + matchingSecurityDiagnosticCount(securityDiagnostics, 'CWE-798 - Hardcoded credentials', 22), + 0 + ) + + const editor = vscode.window.activeTextEditor + if (editor) { + await editor.edit((editBuilder) => { + const lineRange = editor.document.lineAt(20).rangeIncludingLineBreak + editBuilder.delete(lineRange) + }) + await document.save() + } + }) + }) + + describe('Project and file scans should return at least 1 LLM findings', async () => { + const testFolder = path.join(getWorkspaceFolder(), 'QCAFolder') + const fileName = 'RLinker.java' + const filePath = path.join(testFolder, fileName) + let document: vscode.TextDocument + + function assertAtLeastOneLLMFindings(securityDiagnostics: vscode.Diagnostic[]) { + const readabilityIssuesCount = matchingSecurityDiagnosticCount( + securityDiagnostics, + 'Readability and maintainability issues detected.' + ) + const performanceIssuesCount = matchingSecurityDiagnosticCount( + securityDiagnostics, + 'Performance inefficiencies detected in code.' + ) + const errorHandlingIssuesCount = matchingSecurityDiagnosticCount( + securityDiagnostics, + 'Inadequate error handling detected.' + ) + const namingIssuesCount = matchingSecurityDiagnosticCount( + securityDiagnostics, + 'Inconsistent or unclear naming detected.' + ) + const loggingIssuesCount = matchingSecurityDiagnosticCount( + securityDiagnostics, + 'Insufficient or improper logging found.' + ) + assert.ok( + readabilityIssuesCount + + performanceIssuesCount + + errorHandlingIssuesCount + + namingIssuesCount + + loggingIssuesCount > + 0, + 'No LLM findings were found' + ) + } + + beforeEach(async () => { + await validateInitialChatMessage() + }) + + it('file scan returns at least 1 LLM findings', async () => { + document = await vscode.workspace.openTextDocument(filePath) + await vscode.window.showTextDocument(document) + + tab.clickButton(ScanAction.RUN_FILE_SCAN) + + await waitForChatItems(6) + const scanningInProgressMessage = tab.getChatItems()[6] + assert.deepStrictEqual( + scanningInProgressMessage.body, + scanProgressMessage(SecurityScanStep.CREATE_SCAN_JOB, CodeAnalysisScope.FILE_ON_DEMAND, fileName) + ) + + const scanResultBody = await waitForReviewResults(tab) + + const issues = extractAndValidateIssues(scanResultBody) + + assert.deepStrictEqual( + issues.Critical + issues.High + issues.Medium + issues.Low + issues.Info >= 1, + true, + `There are no issues detected when there should be at least 1` + ) + + const uri = vscode.Uri.file(filePath) + const securityDiagnostics: vscode.Diagnostic[] = vscode.languages + .getDiagnostics(uri) + .filter((diagnostic) => diagnostic.source === codewhispererDiagnosticSourceLabel) + + assertAtLeastOneLLMFindings(securityDiagnostics) + }) + + it('project scan returns at least 1 LLM findings', async () => { + const fileDir = path.join(getWorkspaceFolder()) + + try { + // Initialize git repository to make RLinker.java appear in git diff + await processUtils.ChildProcess.run('git', ['init'], { spawnOptions: { cwd: fileDir } }) + await processUtils.ChildProcess.run('git', ['add', 'QCAFolder/RLinker.java'], { + spawnOptions: { cwd: fileDir }, + }) + await processUtils.ChildProcess.run('git', ['config', 'user.name', 'Test'], { + spawnOptions: { cwd: fileDir }, + }) + await processUtils.ChildProcess.run('git', ['config', 'user.email', 'test@example.com'], { + spawnOptions: { cwd: fileDir }, + }) + await processUtils.ChildProcess.run('git', ['commit', '-m', 'Initial commit'], { + spawnOptions: { cwd: fileDir }, + }) + + document = await vscode.workspace.openTextDocument(filePath) + await vscode.window.showTextDocument(document) + + const editor = vscode.window.activeTextEditor + + if (editor) { + const position = new vscode.Position(20, 0) + await editor.edit((editBuilder) => { + editBuilder.insert(position, '\n') + }) + // save file + await document.save() + } + + // Run the project scan + tab.clickButton(ScanAction.RUN_PROJECT_SCAN) + + await waitForChatItems(6) + const scanningInProgressMessage = tab.getChatItems()[6] + assert.deepStrictEqual( + scanningInProgressMessage.body, + scanProgressMessage(SecurityScanStep.CREATE_SCAN_JOB, CodeAnalysisScope.PROJECT) + ) + + const scanResultBody = await waitForReviewResults(tab) + + const issues = extractAndValidateIssues(scanResultBody) + + assert.deepStrictEqual( + issues.Critical + issues.High + issues.Medium + issues.Low + issues.Info >= 1, + true, + `There are no issues detected when there should be at least 1` + ) + + const uri = vscode.Uri.file(filePath) + const securityDiagnostics: vscode.Diagnostic[] = vscode.languages + .getDiagnostics(uri) + .filter((diagnostic) => diagnostic.source === codewhispererDiagnosticSourceLabel) + + assertAtLeastOneLLMFindings(securityDiagnostics) + + const editor2 = vscode.window.activeTextEditor + if (editor2) { + await editor2.edit((editBuilder) => { + const lineRange = editor2.document.lineAt(20).rangeIncludingLineBreak + editBuilder.delete(lineRange) + }) + await document.save() + } + } finally { + // Clean up git repository + await fs.delete(path.join(fileDir, '.git'), { recursive: true }) + } + }) + }) + }) +}) diff --git a/packages/amazonq/test/e2e/amazonq/template.test.ts b/packages/amazonq/test/e2e/amazonq/template.test.ts new file mode 100644 index 00000000000..42857575583 --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/template.test.ts @@ -0,0 +1,67 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// jscpd:ignore-start +import assert from 'assert' +import { qTestingFramework } from './framework/framework' +import sinon from 'sinon' +import { Messenger } from './framework/messenger' +import { MynahUIDataModel } from '@aws/mynah-ui' +import { assertQuickActions } from './assert' +import { registerAuthHook, using } from 'aws-core-vscode/test' +import { loginToIdC } from './utils/setup' + +describe.skip('Amazon Q Test Template', function () { + let framework: qTestingFramework + let tab: Messenger + let store: MynahUIDataModel + + const availableCommands: string[] = [] + + before(async function () { + /** + * Login to the amazonq-test-account. When running in CI this has unlimited + * calls to the backend api + */ + await using(registerAuthHook('amazonq-test-account'), async () => { + await loginToIdC() + }) + }) + + beforeEach(() => { + // Make sure you're logged in before every test + registerAuthHook('amazonq-test-account') + + // TODO change unknown to the tab type you want to test + framework = new qTestingFramework('unknown', true, []) + tab = framework.getTabs()[0] // use the default tab that gets created + framework.createTab() // alternatively you can create a new tab + store = tab.getStore() + }) + + afterEach(() => { + framework.removeTab(tab.tabID) + framework.dispose() + sinon.restore() + }) + + it(`Shows quick actions: ${availableCommands.join(', ')}`, async () => { + assertQuickActions(tab, availableCommands) + }) + + it('Shows title', () => { + assert.deepStrictEqual(store.tabTitle, '') + }) + + it('Shows placeholder', () => { + assert.deepStrictEqual(store.promptInputPlaceholder, '') + }) + + describe('clicks examples', () => {}) + + describe('sends message', async () => {}) +}) + +// jscpd:ignore-end diff --git a/packages/amazonq/test/e2e/amazonq/transformByQ.test.ts b/packages/amazonq/test/e2e/amazonq/transformByQ.test.ts new file mode 100644 index 00000000000..7a9273a1e84 --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/transformByQ.test.ts @@ -0,0 +1,407 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { qTestingFramework } from './framework/framework' +import sinon from 'sinon' +import { Messenger } from './framework/messenger' +import { + CodeWhispererConstants, + JDKVersion, + TransformationType, + transformByQState, +} from 'aws-core-vscode/codewhisperer' +import { GumbyController, setMaven, startTransformByQ, TabsStorage } from 'aws-core-vscode/amazonqGumby' +import { using, registerAuthHook, TestFolder } from 'aws-core-vscode/test' +import { loginToIdC } from './utils/setup' +import { fs } from 'aws-core-vscode/shared' +import path from 'path' + +describe('Amazon Q Code Transformation', function () { + let framework: qTestingFramework + let tab: Messenger + + before(async function () { + await using(registerAuthHook('amazonq-test-account'), async () => { + await loginToIdC() + }) + }) + + beforeEach(() => { + registerAuthHook('amazonq-test-account') + framework = new qTestingFramework('gumby', true, []) + tab = framework.createTab() + }) + + afterEach(() => { + framework.removeTab(tab.tabID) + framework.dispose() + sinon.restore() + }) + + describe('Quick action availability', () => { + it('Can invoke /transform when QCT is enabled', async () => { + const command = tab.findCommand('/transform') + if (!command) { + assert.fail('Could not find command') + } + + if (command.length > 1) { + assert.fail('Found too many commands with the name /transform') + } + }) + + it('CANNOT invoke /transform when QCT is NOT enabled', () => { + framework.dispose() + framework = new qTestingFramework('gumby', false, []) + const tab = framework.createTab() + const command = tab.findCommand('/transform') + if (command.length > 0) { + assert.fail('Found command when it should not have been found') + } + }) + }) + + describe('Starting a transformation from chat', () => { + it('Can click through all user input forms for a Java upgrade', async () => { + sinon.stub(startTransformByQ, 'getValidSQLConversionCandidateProjects').resolves([]) + sinon.stub(GumbyController.prototype, 'validateLanguageUpgradeProjects' as keyof GumbyController).resolves([ + { + name: 'qct-sample-java-8-app-main', + path: '/Users/alias/Desktop/qct-sample-java-8-app-main', + JDKVersion: JDKVersion.JDK8, + }, + ]) + + transformByQState.setSourceJDKVersion(JDKVersion.JDK8) + transformByQState.setTargetJDKVersion(JDKVersion.JDK17) + + tab.addChatMessage({ command: '/transform' }) + + // wait for /transform to respond with some intro messages and the first user input form + await tab.waitForEvent(() => tab.getChatItems().length > 3, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const projectForm = tab.getChatItems().pop() + assert.strictEqual(projectForm?.formItems?.[0]?.id ?? undefined, 'GumbyTransformLanguageUpgradeProjectForm') + + const projectFormItemValues = { + GumbyTransformLanguageUpgradeProjectForm: '/Users/alias/Desktop/qct-sample-java-8-app-main', + GumbyTransformJdkFromForm: '8', + GumbyTransformJdkToForm: '17', + } + const projectFormValues: Record = { ...projectFormItemValues } + // TODO: instead of stubbing, can we create a tab in qTestingFramework with tabType passed in? + // Mynah-UI updates tab type like this: this.tabsStorage.updateTabTypeFromUnknown(affectedTabId, 'gumby') + sinon + .stub(TabsStorage.prototype, 'getTab') + .returns({ id: tab.tabID, status: 'free', type: 'gumby', isSelected: true }) + tab.clickCustomFormButton({ + id: 'gumbyLanguageUpgradeTransformFormConfirm', + text: 'Confirm', + formItemValues: projectFormValues, + }) + + // 3 additional chat messages (including message with 2nd form) get sent after 1st form submitted; wait for all of them + await tab.waitForEvent(() => tab.getChatItems().length > 6, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const skipTestsForm = tab.getChatItems().pop() + assert.strictEqual(skipTestsForm?.formItems?.[0]?.id ?? undefined, 'GumbyTransformSkipTestsForm') + + const skipTestsFormItemValues = { + GumbyTransformSkipTestsForm: 'Run unit tests', + } + const skipTestsFormValues: Record = { ...skipTestsFormItemValues } + tab.clickCustomFormButton({ + id: 'gumbyTransformSkipTestsFormConfirm', + text: 'Confirm', + formItemValues: skipTestsFormValues, + }) + + // 2 additional chat messages get sent after 3rd form submitted; wait for both of them + await tab.waitForEvent(() => tab.getChatItems().length > 8, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + + const customDependencyVersionPrompt = tab.getChatItems().pop() + assert.strictEqual( + customDependencyVersionPrompt?.body?.includes('You can optionally upload a YAML file'), + true + ) + tab.clickCustomFormButton({ id: 'gumbyTransformFormContinue' }) + + // 2 additional chat messages get sent after Continue button clicked; wait for both of them + await tab.waitForEvent(() => tab.getChatItems().length > 10, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + + const sourceJdkPathPrompt = tab.getChatItems().pop() + assert.strictEqual(sourceJdkPathPrompt?.body?.includes('Enter the path to JDK 8'), true) + + tab.addChatMessage({ prompt: '/dummy/path/to/jdk8' }) + + // 2 additional chat messages get sent after JDK path submitted; wait for both of them + await tab.waitForEvent(() => tab.getChatItems().length > 12, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const jdkPathResponse = tab.getChatItems().pop() + // this 'Sorry' message is OK - just making sure that the UI components are working correctly + assert.strictEqual(jdkPathResponse?.body?.includes("Sorry, I couldn't locate your Java installation"), true) + + const tmpDir = (await TestFolder.create()).path + + transformByQState.setSummaryFilePath(path.join(tmpDir, 'summary.md')) + transformByQState.setToPartiallySucceeded() + + transformByQState + .getChatMessenger() + ?.sendJobFinishedMessage(tab.tabID, CodeWhispererConstants.viewProposedChangesChatMessage) + + tab.clickCustomFormButton({ + id: 'gumbyViewSummary', + text: 'View summary', + }) + + await tab.waitForEvent(() => tab.getChatItems().length > 13, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + + const viewSummaryChatItem = tab.getChatItems().pop() + assert.strictEqual(viewSummaryChatItem?.body?.includes('view a summary'), true) + }) + + it('CANNOT do a Java 21 to Java 17 transformation', async () => { + sinon.stub(startTransformByQ, 'getValidSQLConversionCandidateProjects').resolves([]) + sinon.stub(GumbyController.prototype, 'validateLanguageUpgradeProjects' as keyof GumbyController).resolves([ + { + name: 'qct-sample-java-8-app-main', + path: '/Users/alias/Desktop/qct-sample-java-8-app-main', + JDKVersion: JDKVersion.JDK21, + }, + ]) + tab.addChatMessage({ command: '/transform' }) + await tab.waitForEvent(() => tab.getChatItems().length > 3, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const projectForm = tab.getChatItems().pop() + assert.strictEqual(projectForm?.formItems?.[0]?.id ?? undefined, 'GumbyTransformLanguageUpgradeProjectForm') + + const projectFormItemValues = { + GumbyTransformLanguageUpgradeProjectForm: '/Users/alias/Desktop/qct-sample-java-8-app-main', + GumbyTransformJdkFromForm: '21', + GumbyTransformJdkToForm: '17', + } + const projectFormValues: Record = { ...projectFormItemValues } + tab.clickCustomFormButton({ + id: 'gumbyLanguageUpgradeTransformFormConfirm', + text: 'Confirm', + formItemValues: projectFormValues, + }) + await tab.waitForEvent(() => tab.getChatItems().length > 4, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const errorMessage = tab.getChatItems().pop() + assert.strictEqual(errorMessage?.body, CodeWhispererConstants.invalidFromToJdkChatMessage) + }) + + it('Can provide metadata file for a SQL conversion', async () => { + sinon.stub(startTransformByQ, 'getValidSQLConversionCandidateProjects').resolves([ + { + name: 'OracleExample', + path: '/Users/alias/Desktop/OracleExample', + JDKVersion: JDKVersion.JDK17, + }, + ]) + sinon.stub(startTransformByQ, 'getValidLanguageUpgradeCandidateProjects').resolves([]) + sinon.stub(GumbyController.prototype, 'validateSQLConversionProjects' as keyof GumbyController).resolves([ + { + name: 'OracleExample', + path: '/Users/alias/Desktop/OracleExample', + JDKVersion: JDKVersion.JDK17, + }, + ]) + + tab.addChatMessage({ command: '/transform' }) + + // wait for /transform to respond with some intro messages and the first user input message + await tab.waitForEvent(() => tab.getChatItems().length > 3, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const selectMetadataMessage = tab.getChatItems().pop() + assert.strictEqual( + selectMetadataMessage?.body?.includes('I can convert the embedded SQL') ?? undefined, + true + ) + + // verify that we processed the metadata file + const processMetadataFileStub = sinon.stub( + GumbyController.prototype, + 'processMetadataFile' as keyof GumbyController + ) + tab.clickCustomFormButton({ + id: 'gumbySQLConversionMetadataTransformFormConfirm', + text: 'Select metadata file', + }) + sinon.assert.calledOnce(processMetadataFileStub) + }) + + it('Can choose "language upgrade" when eligible for a Java upgrade AND SQL conversion', async () => { + sinon.stub(startTransformByQ, 'getValidSQLConversionCandidateProjects').resolves([ + { + name: 'OracleExample', + path: '/Users/alias/Desktop/OracleExample', + JDKVersion: JDKVersion.JDK17, + }, + ]) + sinon.stub(startTransformByQ, 'getValidLanguageUpgradeCandidateProjects').resolves([ + { + name: 'qct-sample-java-8-app-main', + path: '/Users/alias/Desktop/qct-sample-java-8-app-main', + JDKVersion: JDKVersion.JDK8, + }, + ]) + + tab.addChatMessage({ command: '/transform' }) + + // wait for /transform to respond with some intro messages and a prompt asking user what they want to do + await tab.waitForEvent(() => tab.getChatItems().length > 2, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const prompt = tab.getChatItems().pop() + assert.strictEqual( + prompt?.body?.includes('You can enter "language upgrade" or "sql conversion"') ?? undefined, + true + ) + + // 3 additional chat messages get sent after user enters a choice; wait for all of them + tab.addChatMessage({ prompt: 'language upgrade' }) + await tab.waitForEvent(() => tab.getChatItems().length > 5, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const projectForm = tab.getChatItems().pop() + assert.strictEqual(projectForm?.formItems?.[0]?.id ?? undefined, 'GumbyTransformLanguageUpgradeProjectForm') + }) + + it('Can choose "sql conversion" when eligible for a Java upgrade AND SQL conversion', async () => { + sinon.stub(startTransformByQ, 'getValidSQLConversionCandidateProjects').resolves([ + { + name: 'OracleExample', + path: '/Users/alias/Desktop/OracleExample', + JDKVersion: JDKVersion.JDK17, + }, + ]) + sinon.stub(startTransformByQ, 'getValidLanguageUpgradeCandidateProjects').resolves([ + { + name: 'qct-sample-java-8-app-main', + path: '/Users/alias/Desktop/qct-sample-java-8-app-main', + JDKVersion: JDKVersion.JDK8, + }, + ]) + + tab.addChatMessage({ command: '/transform' }) + + // wait for /transform to respond with some intro messages and a prompt asking user what they want to do + await tab.waitForEvent(() => tab.getChatItems().length > 2, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const prompt = tab.getChatItems().pop() + assert.strictEqual( + prompt?.body?.includes('You can enter "language upgrade" or "sql conversion"') ?? undefined, + true + ) + + // 3 additional chat messages get sent after user enters a choice; wait for all of them + tab.addChatMessage({ prompt: 'sql conversion' }) + await tab.waitForEvent(() => tab.getChatItems().length > 5, { + waitTimeoutInMs: 5000, + waitIntervalInMs: 1000, + }) + const selectMetadataMessage = tab.getChatItems().pop() + assert.strictEqual( + selectMetadataMessage?.body?.includes('I can convert the embedded SQL') ?? undefined, + true + ) + }) + }) + + // TODO: enable when we no longer get throttled on CreateUploadUrl and other APIs + describe.skip('Running a Java upgrade from start to finish', async function () { + let tempDir = '' + let tempFileName = '' + let tempFilePath = '' + + const javaFileContents = `public class MyApp { + public static void main(String[] args) { + Integer temp = new Integer("1234"); + } + }` + + const pomXmlContents = ` + + 4.0.0 + + com.example + basic-java-app + 1.0-SNAPSHOT + + + 1.8 + 1.8 + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + + ` + + before(async function () { + tempDir = path.join((await TestFolder.create()).path, 'qct-java-upgrade-test') + tempFileName = 'MyApp.java' + tempFilePath = path.join(tempDir, tempFileName) + await fs.writeFile(tempFilePath, javaFileContents) + tempFileName = 'pom.xml' + tempFilePath = path.join(tempDir, tempFileName) + await fs.writeFile(tempFilePath, pomXmlContents) + }) + + after(async function () { + await fs.delete(tempDir, { recursive: true }) + }) + + it('WHEN transforming a Java 8 project E2E THEN job is successful', async function () { + transformByQState.setTransformationType(TransformationType.LANGUAGE_UPGRADE) + setMaven() + await startTransformByQ.processLanguageUpgradeTransformFormInput(tempDir, JDKVersion.JDK8, JDKVersion.JDK17) + await startTransformByQ.startTransformByQ() + assert.strictEqual(transformByQState.getPolledJobStatus(), 'COMPLETED') + }) + }) +}) diff --git a/packages/amazonq/test/e2e/amazonq/utils/setup.ts b/packages/amazonq/test/e2e/amazonq/utils/setup.ts new file mode 100644 index 00000000000..dd82b1f0b19 --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/utils/setup.ts @@ -0,0 +1,26 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { AuthUtil } from 'aws-core-vscode/codewhisperer' + +export async function loginToIdC() { + const authState = await AuthUtil.instance.getChatAuthState() + if (process.env['AWS_TOOLKIT_AUTOMATION'] === 'local') { + if (authState.amazonQ !== 'connected') { + throw new Error('You will need to login manually before running tests.') + } + return + } + + const startUrl = process.env['TEST_SSO_STARTURL'] + const region = process.env['TEST_SSO_REGION'] + + if (!startUrl || !region) { + throw new Error( + 'TEST_SSO_STARTURL and TEST_SSO_REGION are required environment variables when running Amazon Q E2E tests' + ) + } + + await AuthUtil.instance.connectToEnterpriseSso(startUrl, region) +} diff --git a/packages/amazonq/test/e2e/amazonq/welcome.test.ts b/packages/amazonq/test/e2e/amazonq/welcome.test.ts new file mode 100644 index 00000000000..d9f0ccd66bf --- /dev/null +++ b/packages/amazonq/test/e2e/amazonq/welcome.test.ts @@ -0,0 +1,103 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { qTestingFramework } from './framework/framework' +import sinon from 'sinon' +import { Messenger } from './framework/messenger' +import { MynahUIDataModel } from '@aws/mynah-ui' +import { FeatureContext } from 'aws-core-vscode/shared' +import { assertContextCommands, assertQuickActions } from './assert' + +describe('Amazon Q Welcome page', function () { + let framework: qTestingFramework + let tab: Messenger + let store: MynahUIDataModel + + const availableCommands = ['/dev', '/test', '/review', '/doc', '/transform'] + + const highlightCommand: FeatureContext = { + name: 'highlightCommand', + value: { + stringValue: '@highlight', + }, + variation: 'highlight command desc', + } + beforeEach(() => { + framework = new qTestingFramework('welcome', true, [['highlightCommand', highlightCommand]], 0) + tab = framework.getTabs()[0] // use the default tab that gets created + store = tab.getStore() + }) + + afterEach(() => { + framework.removeTab(tab.tabID) + framework.dispose() + sinon.restore() + }) + + it(`Shows quick actions: ${availableCommands.join(', ')}`, async () => { + assertQuickActions(tab, availableCommands) + }) + + it('Shows context commands', async () => { + assertContextCommands(tab, ['@workspace', '@highlight']) + }) + + describe('shows 3 times', async () => { + it('new tabs', () => { + framework.createTab() + framework.createTab() + framework.createTab() + framework.createTab() + + let welcomeCount = 0 + for (const tab of framework.getTabs()) { + if (tab.getStore().tabTitle === 'Welcome to Q') { + welcomeCount++ + } + } + // 3 welcome tabs + assert.deepStrictEqual(welcomeCount, 3) + + // 2 normal tabs + assert.deepStrictEqual(framework.getTabs().length - welcomeCount, 2) + }) + + it('new windows', () => { + // check the initial window + assert.deepStrictEqual(store.tabTitle, 'Welcome to Q') + framework.dispose() + + // check when theres already been two welcome tabs shown + framework = new qTestingFramework('welcome', true, [], 2) + const secondStore = framework.getTabs()[0].getStore() + assert.deepStrictEqual(secondStore.tabTitle, 'Welcome to Q') + framework.dispose() + + // check when theres already been three welcome tabs shown + framework = new qTestingFramework('welcome', true, [], 3) + const thirdStore = framework.getTabs()[0].getStore() + assert.deepStrictEqual(thirdStore.tabTitle, 'Chat') + framework.dispose() + }) + }) + + describe('Welcome actions', () => { + it('explore', () => { + tab.clickInBodyButton('explore') + + // explore opens in a new tab + const exploreTabStore = framework.findTab('Explore')?.getStore() + assert.strictEqual(exploreTabStore?.tabTitle, 'Explore') + }) + + it('quick-start', async () => { + tab.clickInBodyButton('quick-start') + + // clicking quick start opens in the current tab and changes the compact mode + assert.deepStrictEqual(tab.getStore().compactMode, false) + }) + }) +}) diff --git a/packages/amazonq/test/e2e/index.ts b/packages/amazonq/test/e2e/index.ts new file mode 100644 index 00000000000..065a2cd8bc8 --- /dev/null +++ b/packages/amazonq/test/e2e/index.ts @@ -0,0 +1,13 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { runTests } from 'aws-core-vscode/test' +import { VSCODE_EXTENSION_ID } from 'aws-core-vscode/utils' + +export function run(): Promise { + return runTests(process.env.TEST_DIR ?? 'test/e2e', VSCODE_EXTENSION_ID.amazonq, [ + '../../core/dist/src/testInteg/globalSetup.test.ts', + ]) +} diff --git a/packages/amazonq/test/e2e/inline/inline.test.ts b/packages/amazonq/test/e2e/inline/inline.test.ts new file mode 100644 index 00000000000..43a9f67ab73 --- /dev/null +++ b/packages/amazonq/test/e2e/inline/inline.test.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 assert from 'assert' +import { + closeAllEditors, + getTestWindow, + registerAuthHook, + resetCodeWhispererGlobalVariables, + TestFolder, + toTextEditor, + using, +} from 'aws-core-vscode/test' +import { RecommendationHandler, RecommendationService, session } from 'aws-core-vscode/codewhisperer' +import { Commands, globals, sleep, waitUntil, collectionUtil } from 'aws-core-vscode/shared' +import { loginToIdC } from '../amazonq/utils/setup' + +describe('Amazon Q Inline', async function () { + const retries = 3 + this.retries(retries) + + let tempFolder: string + const waitOptions = { + interval: 500, + timeout: 10000, + retryOnFail: false, + } + + before(async function () { + await using(registerAuthHook('amazonq-test-account'), async () => { + await loginToIdC() + }) + }) + + beforeEach(async function () { + registerAuthHook('amazonq-test-account') + const folder = await TestFolder.create() + tempFolder = folder.path + await closeAllEditors() + await resetCodeWhispererGlobalVariables() + }) + + afterEach(async function () { + await closeAllEditors() + if (this.currentTest?.state === undefined || this.currentTest?.isFailed() || this.currentTest?.isPending()) { + logUserDecisionStatus() + } + }) + + function logUserDecisionStatus() { + const events = getUserTriggerDecision() + console.table({ + 'telemetry events': JSON.stringify(events), + 'recommendation service status': RecommendationService.instance.isRunning, + }) + } + + async function setupEditor({ name, contents }: { name?: string; contents?: string } = {}) { + const fileName = name ?? 'test.ts' + const textContents = + contents ?? + `function fib() { + + +}` + await toTextEditor(textContents, fileName, tempFolder, { + selection: new vscode.Range(new vscode.Position(1, 4), new vscode.Position(1, 4)), + }) + } + + async function waitForRecommendations() { + const suggestionShown = await waitUntil(async () => session.getSuggestionState(0) === 'Showed', waitOptions) + if (!suggestionShown) { + throw new Error(`Suggestion did not show. Suggestion States: ${JSON.stringify(session.suggestionStates)}`) + } + const suggestionVisible = await waitUntil( + async () => RecommendationHandler.instance.isSuggestionVisible(), + waitOptions + ) + if (!suggestionVisible) { + throw new Error( + `Suggestions failed to become visible. Suggestion States: ${JSON.stringify(session.suggestionStates)}` + ) + } + console.table({ + 'suggestions states': JSON.stringify(session.suggestionStates), + 'valid recommendation': RecommendationHandler.instance.isValidResponse(), + 'recommendation service status': RecommendationService.instance.isRunning, + recommendations: session.recommendations, + }) + if (!RecommendationHandler.instance.isValidResponse()) { + throw new Error('Did not find a valid response') + } + } + + /** + * Waits for a specific telemetry event to be emitted with the expected suggestion state. + * It looks like there might be a potential race condition in codewhisperer causing telemetry + * events to be emitted in different orders + */ + async function waitForTelemetry(metricName: string, suggestionState: string) { + const ok = await waitUntil(async () => { + const events = globals.telemetry.logger.query({ + metricName, + }) + return events.some((event) => event.codewhispererSuggestionState === suggestionState) + }, waitOptions) + if (!ok) { + assert.fail(`Telemetry for ${metricName} with suggestionState ${suggestionState} was not emitted`) + } + const events = getUserTriggerDecision() + if (events.length > 1 && events[events.length - 1].codewhispererSuggestionState !== suggestionState) { + assert.fail(`Telemetry events were emitted in the wrong order`) + } + } + + function getUserTriggerDecision() { + return globals.telemetry.logger + .query({ + metricName: 'codewhisperer_userTriggerDecision', + }) + .map((e) => collectionUtil.partialClone(e, 3, ['credentialStartUrl'], { replacement: '[omitted]' })) + } + + for (const [name, invokeCompletion] of [ + ['automatic', async () => await vscode.commands.executeCommand('type', { text: '\n' })], + ['manual', async () => Commands.tryExecute('aws.amazonq.invokeInlineCompletion')], + ] as const) { + describe(`${name} invoke`, async function () { + let originalEditorContents: string | undefined + + describe('supported filetypes', () => { + async function setup() { + await setupEditor() + + /** + * Allow some time between when the editor is opened and when we start typing. + * If we don't do this then the time between the initial editor selection + * and invoking the "type" command is too low, causing completion to never + * activate. AFAICT there isn't anything we can use waitUntil on here. + * + * note: this number is entirely arbitrary + **/ + await sleep(1000) + + await invokeCompletion() + originalEditorContents = vscode.window.activeTextEditor?.document.getText() + + // wait until the ghost text appears + await waitForRecommendations() + } + + beforeEach(async () => { + /** + * Every once and a while the backend won't respond with any recommendations. + * In those cases, re-try the setup up-to ${retries} times + */ + let attempt = 0 + while (attempt < retries) { + try { + await setup() + console.log(`test run ${attempt} succeeded`) + logUserDecisionStatus() + break + } catch (e) { + console.log(`test run ${attempt} failed`) + console.log(e) + logUserDecisionStatus() + attempt++ + await resetCodeWhispererGlobalVariables() + } + } + if (attempt === retries) { + assert.fail(`Failed to invoke ${name} tests after ${attempt} attempts`) + } + }) + + it(`${name} invoke accept`, async function () { + /** + * keep accepting the suggestion until the text contents change + * this is required because we have no access to the inlineSuggest panel + **/ + const suggestionAccepted = await waitUntil(async () => { + // Accept the suggestion + await vscode.commands.executeCommand('editor.action.inlineSuggest.commit') + return vscode.window.activeTextEditor?.document.getText() !== originalEditorContents + }, waitOptions) + + assert.ok(suggestionAccepted, 'Editor contents should have changed') + + await waitForTelemetry('codewhisperer_userTriggerDecision', 'Accept') + }) + + it(`${name} invoke reject`, async function () { + // Reject the suggestion + await vscode.commands.executeCommand('aws.amazonq.rejectCodeSuggestion') + + // Contents haven't changed + assert.deepStrictEqual(vscode.window.activeTextEditor?.document.getText(), originalEditorContents) + await waitForTelemetry('codewhisperer_userTriggerDecision', 'Reject') + }) + + it(`${name} invoke discard`, async function () { + // Discard the suggestion by moving it back to the original position + const position = new vscode.Position(1, 4) + const editor = vscode.window.activeTextEditor + if (!editor) { + assert.fail('Could not find text editor') + } + editor.selection = new vscode.Selection(position, position) + + // Contents are the same + assert.deepStrictEqual(vscode.window.activeTextEditor?.document.getText(), originalEditorContents) + }) + }) + + it(`${name} invoke on unsupported filetype`, async function () { + await setupEditor({ + name: 'test.zig', + contents: `fn doSomething() void { + + }`, + }) + + /** + * Add delay between editor loading and invoking completion + * @see beforeEach in supported filetypes for more information + */ + await sleep(1000) + await invokeCompletion() + + if (name === 'automatic') { + // It should never get triggered since its not a supported file type + assert.deepStrictEqual(RecommendationService.instance.isRunning, false) + } else { + await getTestWindow().waitForMessage('currently not supported by Amazon Q inline suggestions') + } + }) + }) + } +}) diff --git a/packages/amazonq/test/e2e/lsp/amazonqLsp.test.ts b/packages/amazonq/test/e2e/lsp/amazonqLsp.test.ts new file mode 100644 index 00000000000..f4a60ff282b --- /dev/null +++ b/packages/amazonq/test/e2e/lsp/amazonqLsp.test.ts @@ -0,0 +1,31 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AmazonQLspInstaller } from '../../../src/lsp/lspInstaller' +import { defaultAmazonQLspConfig } from '../../../src/lsp/config' +import { createLspInstallerTests } from './lspInstallerUtil' +import { BaseLspInstaller } from 'aws-core-vscode/shared' + +describe('AmazonQLSP', () => { + createLspInstallerTests({ + suiteName: 'AmazonQLSPInstaller', + lspConfig: defaultAmazonQLspConfig, + createInstaller: (lspConfig?: BaseLspInstaller.LspConfig) => new AmazonQLspInstaller(lspConfig), + targetContents: [ + { + bytes: 0, + filename: 'servers.zip', + hashes: [], + url: 'http://fakeurl', + }, + ], + setEnv: (path: string) => { + process.env.__AMAZONQLSP_PATH = path + }, + resetEnv: () => { + delete process.env.__AMAZONQLSP_PATH + }, + }) +}) diff --git a/packages/amazonq/test/e2e/lsp/lspInstallerUtil.ts b/packages/amazonq/test/e2e/lsp/lspInstallerUtil.ts new file mode 100644 index 00000000000..d4251959756 --- /dev/null +++ b/packages/amazonq/test/e2e/lsp/lspInstallerUtil.ts @@ -0,0 +1,307 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import sinon from 'sinon' +import { + BaseLspInstaller, + DevSettings, + fs, + LanguageServerResolver, + makeTemporaryToolkitFolder, + ManifestResolver, + request, + TargetContent, + ToolkitError, +} from 'aws-core-vscode/shared' +import * as semver from 'semver' +import { assertTelemetry } from 'aws-core-vscode/test' +import { LanguageServerSetup } from 'aws-core-vscode/telemetry' + +function createVersion(version: string, contents: TargetContent[]) { + return { + isDelisted: false, + serverVersion: version, + targets: [ + { + arch: process.arch, + platform: process.platform, + contents, + }, + ], + } +} + +export function createLspInstallerTests({ + suiteName, + lspConfig, + createInstaller, + targetContents, + setEnv, + resetEnv, +}: { + suiteName: string + lspConfig: BaseLspInstaller.LspConfig + createInstaller: (lspConfig?: BaseLspInstaller.LspConfig) => BaseLspInstaller.BaseLspInstaller + targetContents: TargetContent[] + setEnv: (path: string) => void + resetEnv: () => void +}) { + describe(suiteName, () => { + let installer: BaseLspInstaller.BaseLspInstaller + let sandbox: sinon.SinonSandbox + let tempDir: string + + beforeEach(async () => { + sandbox = sinon.createSandbox() + installer = createInstaller() + tempDir = await makeTemporaryToolkitFolder() + sandbox.stub(LanguageServerResolver.prototype, 'defaultDownloadFolder').returns(tempDir) + }) + + afterEach(async () => { + resetEnv() + sandbox.restore() + await fs.delete(tempDir, { + recursive: true, + }) + }) + + describe('resolve()', () => { + it('uses dev setting override', async () => { + const path = '/custom/path/to/lsp' + sandbox.stub(DevSettings.instance, 'getServiceConfig').returns({ + path, + }) + /** + * The installer pre-evaluates the config, so if we want to override the config + * we need to stub then re-create it + */ + const result = await createInstaller().resolve() + + assert.strictEqual(result.assetDirectory, path) + assert.strictEqual(result.location, 'override') + assert.strictEqual(result.version, '0.0.0') + }) + + it('uses environment variable override', async () => { + const overridePath = '/custom/path/to/lsp' + setEnv(overridePath) + + /** + * The installer pre-evaluates the config, so if we want to override the environment variables + * we need to override the env then re-create it + */ + const result = await createInstaller().resolve() + + assert.strictEqual(result.assetDirectory, overridePath) + assert.strictEqual(result.location, 'override') + assert.strictEqual(result.version, '0.0.0') + }) + + it('resolves', async () => { + // First try - should download the file + const download = await installer.resolve() + + assert.ok(download.assetDirectory.startsWith(tempDir)) + assert.deepStrictEqual(download.location, 'remote') + assert.ok(semver.satisfies(download.version, lspConfig.supportedVersions)) + + // Second try - Should see the contents in the cache + const cache = await installer.resolve() + + assert.ok(cache.assetDirectory.startsWith(tempDir)) + assert.deepStrictEqual(cache.location, 'cache') + assert.ok(semver.satisfies(cache.version, lspConfig.supportedVersions)) + + /** + * Always make sure the latest version is one patch higher. This stops a problem + * where the fallback can't be used because the latest compatible version + * is equal to the min version, so if the cache isn't valid, then there + * would be no fallback location + * + * Instead, increasing the latest compatible lsp version means we can just + * use the one we downloaded earlier in the test as the fallback + */ + const nextVer = semver.inc(cache.version, 'patch', true) + if (!nextVer) { + throw new Error('Could not increment version') + } + sandbox.stub(ManifestResolver.prototype, 'resolve').resolves({ + manifestSchemaVersion: '0.0.0', + artifactId: 'foo', + artifactDescription: 'foo', + isManifestDeprecated: false, + versions: [createVersion(nextVer, targetContents), createVersion(cache.version, targetContents)], + }) + + // fail the next http request for the language server + sandbox.stub(request, 'fetch').returns({ + response: Promise.resolve({ + ok: false, + }), + } as any) + + const config = { + ...lspConfig, + // contains the old version thats actually on disk + the new version + supportedVersions: `${cache.version} || ${nextVer}`, + } + + // Third try - Cache doesn't exist and we couldn't download from the internet, fallback to a local version + const fallback = await createInstaller(config).resolve() + + assert.ok(fallback.assetDirectory.startsWith(tempDir)) + assert.deepStrictEqual(fallback.location, 'fallback') + assert.ok(semver.satisfies(fallback.version, lspConfig.supportedVersions)) + + /* First Try Telemetry + getManifest: remote succeeds + getServer: cache fails then remote succeeds. + validate: succeeds. + */ + const firstTryTelemetry: Partial[] = [ + { + id: lspConfig.id, + manifestLocation: 'remote', + languageServerSetupStage: 'getManifest', + result: 'Succeeded', + }, + { + id: lspConfig.id, + languageServerLocation: 'cache', + languageServerSetupStage: 'getServer', + result: 'Failed', + }, + { + id: lspConfig.id, + languageServerLocation: 'remote', + languageServerSetupStage: 'validate', + result: 'Succeeded', + }, + { + id: lspConfig.id, + languageServerLocation: 'remote', + languageServerSetupStage: 'getServer', + result: 'Succeeded', + }, + ] + + /* Second Try Telemetry + getManifest: remote fails, then cache succeeds. + getServer: cache succeeds + validate: doesn't run since its cached. + */ + const secondTryTelemetry: Partial[] = [ + { + id: lspConfig.id, + manifestLocation: 'remote', + languageServerSetupStage: 'getManifest', + result: 'Succeeded', + }, + { + id: lspConfig.id, + languageServerLocation: 'cache', + languageServerSetupStage: 'getServer', + result: 'Succeeded', + }, + ] + + /* Third Try Telemetry + getManifest: (stubbed to fail, no telemetry) + getServer: remote and cache fail + validate: no validation since not remote. + */ + const thirdTryTelemetry: Partial[] = [ + { + id: lspConfig.id, + languageServerLocation: 'cache', + languageServerSetupStage: 'getServer', + result: 'Failed', + }, + { + id: lspConfig.id, + languageServerLocation: 'remote', + languageServerSetupStage: 'getServer', + result: 'Failed', + }, + { + id: lspConfig.id, + languageServerLocation: 'fallback', + languageServerSetupStage: 'getServer', + result: 'Succeeded', + }, + ] + + const expectedTelemetry = firstTryTelemetry.concat(secondTryTelemetry, thirdTryTelemetry) + + assertTelemetry('languageServer_setup', expectedTelemetry) + }) + + it('resolves release candidiates', async () => { + const original = new ManifestResolver(lspConfig.manifestUrl, lspConfig.id, '').resolve() + sandbox.stub(ManifestResolver.prototype, 'resolve').callsFake(async () => { + const originalManifest = await original + + const latestVersion = originalManifest.versions.reduce((latest, current) => { + return semver.gt(current.serverVersion, latest.serverVersion) ? current : latest + }, originalManifest.versions[0]) + + // These convert something like 3.1.1 to 3.1.2-rc.0 + const incrementedVersion = semver.inc(latestVersion.serverVersion, 'patch') + if (!incrementedVersion) { + assert.fail('Failed to increment minor version') + } + + const prereleaseVersion = semver.inc(incrementedVersion, 'prerelease', 'rc') + if (!prereleaseVersion) { + assert.fail('Failed to create pre-release version') + } + + const newVersion = { + ...latestVersion, + serverVersion: prereleaseVersion, + } + + originalManifest.versions = [newVersion, ...originalManifest.versions] + return originalManifest + }) + + const version = lspConfig.supportedVersions + lspConfig.supportedVersions = version.startsWith('^') ? version : `^${version}` + const download = await createInstaller(lspConfig).resolve() + assert.ok(download.assetDirectory.endsWith('-rc.0')) + }) + + it('throws on firewall error', async () => { + // Stub the manifest resolver to return a valid manifest + sandbox.stub(ManifestResolver.prototype, 'resolve').resolves({ + manifestSchemaVersion: '0.0.0', + artifactId: 'foo', + artifactDescription: 'foo', + isManifestDeprecated: false, + versions: [createVersion('1.0.0', targetContents)], + }) + + // Fail all HTTP requests for the language server + sandbox.stub(request, 'fetch').returns({ + response: Promise.resolve({ + ok: false, + }), + } as any) + + // This should now throw a NetworkConnectivityError + await assert.rejects( + async () => await installer.resolve(), + (err: ToolkitError) => { + assert.strictEqual(err.code, 'NetworkConnectivityError') + assert.ok(err.message.includes('Unable to download dependencies')) + return true + } + ) + }) + }) + }) +} diff --git a/packages/amazonq/test/unit/amazonq/apps/initContext.test.ts b/packages/amazonq/test/unit/amazonq/apps/initContext.test.ts new file mode 100644 index 00000000000..71aa82d7818 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/apps/initContext.test.ts @@ -0,0 +1,42 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EventEmitter } from 'vscode' +import { DefaultAmazonQAppInitContext, MessagePublisher } from 'aws-core-vscode/amazonq' +import assert from 'assert' + +describe('DefaultAmazonQAppInitContext', () => { + let context: DefaultAmazonQAppInitContext + + beforeEach(() => { + context = new DefaultAmazonQAppInitContext() + }) + + describe('registerWebViewToAppMessagePublisher', () => { + it('should add the publisher to the map', () => { + const publisher = new MessagePublisher(new EventEmitter()) + context.registerWebViewToAppMessagePublisher(publisher, 'unknown') + assert.strictEqual(context.getWebViewToAppsMessagePublishers().get('unknown'), publisher) + }) + }) + + describe('getAppsToWebViewMessagePublisher', () => { + it('should return the publisher', () => { + assert.notDeepStrictEqual(context.getAppsToWebViewMessagePublisher(), undefined) + }) + }) + + describe('getAppsToWebViewMessageListener', () => { + it('should return the listener', () => { + assert.notDeepStrictEqual(context.getAppsToWebViewMessageListener(), undefined) + }) + }) + + describe('onDidChangeAmazonQVisibility', () => { + it('should be an EventEmitter', () => { + assert.strictEqual(context.onDidChangeAmazonQVisibility instanceof EventEmitter, true) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/EditRendering/stringUtils.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/EditRendering/stringUtils.test.ts new file mode 100644 index 00000000000..09c33fb0c80 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/apps/inline/EditRendering/stringUtils.test.ts @@ -0,0 +1,47 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import assert from 'assert' +import { stripCommonIndentation } from '../../../../../../src/app/inline/EditRendering/stringUtils' + +describe('stripCommonIndentation', () => { + it('should strip common leading whitespace', () => { + const input = [' line1 ', ' line2 ', ' line3 '] + const expected = ['line1 ', 'line2 ', ' line3 '] + assert.deepStrictEqual(stripCommonIndentation(input), expected) + }) + + it('should handle HTML tags', () => { + const input = [ + ' line2 ', + ] + const expected = ['line2 '] + assert.deepStrictEqual(stripCommonIndentation(input), expected) + }) + + it('should handle mixed indentation', () => { + const input = [' line1', ' line2', ' line3'] + const expected = ['line1', ' line2', ' line3'] + assert.deepStrictEqual(stripCommonIndentation(input), expected) + }) + + it('should handle empty lines', () => { + const input = [' line1', '', ' line2'] + const expected = [' line1', '', ' line2'] + assert.deepStrictEqual(stripCommonIndentation(input), expected) + }) + + it('should handle no indentation', () => { + const input = ['line1', 'line2'] + const expected = ['line1', 'line2'] + assert.deepStrictEqual(stripCommonIndentation(input), expected) + }) + + it('should handle single line', () => { + const input = [' single line'] + const expected = ['single line'] + assert.deepStrictEqual(stripCommonIndentation(input), expected) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts new file mode 100644 index 00000000000..8e0d2719428 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts @@ -0,0 +1,424 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import sinon from 'sinon' +import { + CancellationToken, + commands, + InlineCompletionItem, + languages, + Position, + window, + Range, + InlineCompletionTriggerKind, +} from 'vscode' +import assert from 'assert' +import { BaseLanguageClient } from 'vscode-languageclient' +import { StringValue } from 'vscode-languageserver-types' +import { AmazonQInlineCompletionItemProvider, InlineCompletionManager } from '../../../../../src/app/inline/completion' +import { RecommendationService } from '../../../../../src/app/inline/recommendationService' +import { SessionManager } from '../../../../../src/app/inline/sessionManager' +import { createMockDocument, createMockTextEditor, getTestWindow, installFakeClock } from 'aws-core-vscode/test' +import { + noInlineSuggestionsMsg, + ReferenceHoverProvider, + ReferenceLogViewProvider, + vsCodeState, +} from 'aws-core-vscode/codewhisperer' +import { LineTracker } from '../../../../../src/app/inline/stateTracker/lineTracker' +import { InlineTutorialAnnotation } from '../../../../../src/app/inline/tutorials/inlineTutorialAnnotation' +import { DocumentEventListener } from '../../../../../src/app/inline/documentEventListener' + +describe('InlineCompletionManager', () => { + let manager: InlineCompletionManager + let languageClient: BaseLanguageClient + let sendNotificationStub: sinon.SinonStub + let registerProviderStub: sinon.SinonStub + let registerCommandStub: sinon.SinonStub + let executeCommandStub: sinon.SinonStub + let disposableStub: sinon.SinonStub + let sandbox: sinon.SinonSandbox + let getActiveSessionStub: sinon.SinonStub + let getActiveRecommendationStub: sinon.SinonStub + let logReferenceStub: sinon.SinonStub + let getReferenceStub: sinon.SinonStub + let hoverReferenceStub: sinon.SinonStub + const mockDocument = createMockDocument() + const mockEditor = createMockTextEditor() + const mockPosition = new Position(0, 0) + const mockContext = { triggerKind: 1, selectedCompletionInfo: undefined } + const mockToken = { isCancellationRequested: false } as CancellationToken + const fakeReferences = [ + { + message: '', + licenseName: 'TEST_LICENSE', + repository: 'TEST_REPO', + recommendationContentSpan: { + start: 0, + end: 10, + }, + }, + ] + const mockSuggestions = [ + { + itemId: 'test-item', + insertText: 'test', + references: fakeReferences, + }, + { + itemId: 'test-item2', + insertText: 'import math\ndef two_sum(nums, target):\n', + references: fakeReferences, + }, + ] + + beforeEach(() => { + sandbox = sinon.createSandbox() + + registerProviderStub = sandbox.stub(languages, 'registerInlineCompletionItemProvider') + registerCommandStub = sandbox.stub(commands, 'registerCommand') + executeCommandStub = sandbox.stub(commands, 'executeCommand') + sendNotificationStub = sandbox.stub() + + const mockDisposable = { + dispose: sandbox.stub(), + } + disposableStub = mockDisposable.dispose + registerProviderStub.returns(mockDisposable) + + languageClient = { + sendNotification: sendNotificationStub, + } as unknown as BaseLanguageClient + + const sessionManager = new SessionManager() + const lineTracker = new LineTracker() + const inlineTutorialAnnotation = new InlineTutorialAnnotation(lineTracker, sessionManager) + manager = new InlineCompletionManager(languageClient, sessionManager, lineTracker, inlineTutorialAnnotation) + getActiveSessionStub = sandbox.stub(manager['sessionManager'], 'getActiveSession') + getActiveRecommendationStub = sandbox.stub(manager['sessionManager'], 'getActiveRecommendation') + getReferenceStub = sandbox.stub(ReferenceLogViewProvider, 'getReferenceLog') + logReferenceStub = sandbox.stub(ReferenceLogViewProvider.instance, 'addReferenceLog') + hoverReferenceStub = sandbox.stub(ReferenceHoverProvider.instance, 'addCodeReferences') + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('registerInlineCompletion', () => { + beforeEach(() => { + manager.registerInlineCompletion() + }) + + it('should register accept and reject commands', () => { + assert(registerCommandStub.calledWith('aws.amazonq.acceptInline')) + assert(registerCommandStub.calledWith('aws.amazonq.rejectCodeSuggestion')) + }) + + describe('onInlineAcceptance', () => { + it('should send notification and re-register provider on acceptance', async () => { + // Get the acceptance handler + const acceptanceHandler = registerCommandStub + .getCalls() + ?.find((call) => call.args[0] === 'aws.amazonq.acceptInline')?.args[1] + + const sessionId = 'test-session' + const requestStartTime = Date.now() - 1000 + const firstCompletionDisplayLatency = 500 + + await acceptanceHandler( + sessionId, + mockSuggestions[0], + mockEditor, + requestStartTime, + firstCompletionDisplayLatency + ) + + assert(sendNotificationStub.calledOnce) + assert( + sendNotificationStub.calledWith( + 'aws/logInlineCompletionSessionResults', + sinon.match({ + sessionId, + completionSessionResult: { + [mockSuggestions[0].itemId]: { + seen: true, + accepted: true, + discarded: false, + }, + }, + }) + ) + ) + + assert(disposableStub.calledOnce) + assert(registerProviderStub.calledTwice) // Once in constructor, once after acceptance + }) + + it('should log reference if there is any', async () => { + const acceptanceHandler = registerCommandStub + .getCalls() + ?.find((call) => call.args[0] === 'aws.amazonq.acceptInline')?.args[1] + + const sessionId = 'test-session' + const requestStartTime = Date.now() - 1000 + const firstCompletionDisplayLatency = 500 + const mockReferenceLog = 'test reference log' + getReferenceStub.returns(mockReferenceLog) + + await acceptanceHandler( + sessionId, + mockSuggestions[0], + mockEditor, + requestStartTime, + firstCompletionDisplayLatency + ) + + assert(getReferenceStub.calledOnce) + assert( + getReferenceStub.calledWith( + mockSuggestions[0].insertText, + mockSuggestions[0].references, + mockEditor + ) + ) + assert(logReferenceStub.calledOnce) + assert(logReferenceStub.calledWith(mockReferenceLog)) + assert(hoverReferenceStub.calledOnce) + assert(hoverReferenceStub.calledWith(mockSuggestions[0].insertText, mockSuggestions[0].references)) + }) + }) + + describe('onInlineRejection', () => { + it('should hide suggestion and send notification on rejection', async () => { + // Get the rejection handler + const rejectionHandler = registerCommandStub + .getCalls() + .find((call) => call.args[0] === 'aws.amazonq.rejectCodeSuggestion')?.args[1] + + const sessionId = 'test-session' + const itemId = 'test-item' + const mockSuggestion = { + itemId, + insertText: 'test', + } + + getActiveSessionStub.returns({ + sessionId: 'test-session', + suggestions: [mockSuggestion], + isRequestInProgress: false, + requestStartTime: Date.now(), + }) + getActiveRecommendationStub.returns([mockSuggestion]) + await rejectionHandler() + + assert(executeCommandStub.calledWith('editor.action.inlineSuggest.hide')) + assert(sendNotificationStub.calledOnce) + assert( + sendNotificationStub.calledWith( + 'aws/logInlineCompletionSessionResults', + sinon.match({ + sessionId, + completionSessionResult: { + [itemId]: { + seen: true, + accepted: false, + discarded: false, + }, + }, + }) + ) + ) + + assert(disposableStub.calledOnce) + assert(registerProviderStub.calledTwice) // Once in constructor, once after rejection + }) + }) + }) + + describe('AmazonQInlineCompletionItemProvider', () => { + describe('provideInlineCompletionItems', () => { + let mockSessionManager: SessionManager + let provider: AmazonQInlineCompletionItemProvider + let getAllRecommendationsStub: sinon.SinonStub + let recommendationService: RecommendationService + let inlineTutorialAnnotation: InlineTutorialAnnotation + let documentEventListener: DocumentEventListener + + beforeEach(() => { + const lineTracker = new LineTracker() + inlineTutorialAnnotation = new InlineTutorialAnnotation(lineTracker, mockSessionManager) + recommendationService = new RecommendationService(mockSessionManager) + documentEventListener = new DocumentEventListener() + vsCodeState.isRecommendationsActive = false + mockSessionManager = { + getActiveSession: getActiveSessionStub, + getActiveRecommendation: getActiveRecommendationStub, + clear: () => {}, + updateCodeReferenceAndImports: () => {}, + } as unknown as SessionManager + + getActiveSessionStub.returns({ + sessionId: 'test-session', + suggestions: mockSuggestions, + isRequestInProgress: false, + requestStartTime: Date.now(), + }) + getActiveRecommendationStub.returns(mockSuggestions) + getAllRecommendationsStub = sandbox.stub(recommendationService, 'getAllRecommendations') + getAllRecommendationsStub.resolves() + sandbox.stub(window, 'activeTextEditor').value(createMockTextEditor()) + }), + it('should call recommendation service to get new suggestions(matching typeahead) for new sessions', async () => { + provider = new AmazonQInlineCompletionItemProvider( + languageClient, + recommendationService, + mockSessionManager, + inlineTutorialAnnotation, + documentEventListener + ) + const items = await provider.provideInlineCompletionItems( + mockDocument, + mockPosition, + mockContext, + mockToken + ) + assert(getAllRecommendationsStub.calledOnce) + assert.deepStrictEqual(items, [mockSuggestions[1]]) + }), + it('should handle reference if there is any', async () => { + provider = new AmazonQInlineCompletionItemProvider( + languageClient, + recommendationService, + mockSessionManager, + inlineTutorialAnnotation, + documentEventListener + ) + await provider.provideInlineCompletionItems(mockDocument, mockPosition, mockContext, mockToken) + }), + it('should add a range to the completion item when missing', async function () { + provider = new AmazonQInlineCompletionItemProvider( + languageClient, + recommendationService, + mockSessionManager, + inlineTutorialAnnotation, + documentEventListener + ) + getActiveRecommendationStub.returns([ + { + insertText: 'testText', + itemId: 'itemId', + }, + { + insertText: 'testText2', + itemId: 'itemId2', + range: undefined, + }, + ]) + const cursorPosition = new Position(5, 6) + const result = await provider.provideInlineCompletionItems( + mockDocument, + cursorPosition, + mockContext, + mockToken + ) + + for (const item of result) { + assert.deepStrictEqual(item.range, new Range(cursorPosition, cursorPosition)) + } + }), + it('should handle StringValue instead of strings', async function () { + provider = new AmazonQInlineCompletionItemProvider( + languageClient, + recommendationService, + mockSessionManager, + inlineTutorialAnnotation, + documentEventListener + ) + const expectedText = `${mockSuggestions[1].insertText}this is my text` + getActiveRecommendationStub.returns([ + { + insertText: { + kind: 'snippet', + value: `${mockSuggestions[1].insertText}this is my text`, + } satisfies StringValue, + itemId: 'itemId', + }, + ]) + const result = await provider.provideInlineCompletionItems( + mockDocument, + mockPosition, + mockContext, + mockToken + ) + + assert.strictEqual(result[0].insertText, expectedText) + }), + it('shows message to user when manual invoke fails to produce results', async function () { + provider = new AmazonQInlineCompletionItemProvider( + languageClient, + recommendationService, + mockSessionManager, + inlineTutorialAnnotation, + documentEventListener + ) + getActiveRecommendationStub.returns([]) + const messageShown = new Promise((resolve) => + getTestWindow().onDidShowMessage((e) => { + assert.strictEqual(e.message, noInlineSuggestionsMsg) + resolve(true) + }) + ) + await provider.provideInlineCompletionItems( + mockDocument, + mockPosition, + { triggerKind: InlineCompletionTriggerKind.Invoke, selectedCompletionInfo: undefined }, + mockToken + ) + await messageShown + }) + describe.skip('debounce behavior', function () { + let clock: ReturnType + + beforeEach(function () { + clock = installFakeClock() + }) + + after(function () { + clock.uninstall() + }) + + it.skip('should only trigger once on rapid events', async () => { + provider = new AmazonQInlineCompletionItemProvider( + languageClient, + recommendationService, + mockSessionManager, + inlineTutorialAnnotation, + documentEventListener + ) + const p1 = provider.provideInlineCompletionItems(mockDocument, mockPosition, mockContext, mockToken) + const p2 = provider.provideInlineCompletionItems(mockDocument, mockPosition, mockContext, mockToken) + const p3 = provider.provideInlineCompletionItems( + mockDocument, + new Position(1, 26), + mockContext, + mockToken + ) + + await clock.tickAsync(1000) + + // All promises should be the same object when debounced properly. + assert.strictEqual(p1, p2) + assert.strictEqual(p1, p3) + await p1 + await p2 + const r3 = await p3 + + // calls the function with the latest provided args. + assert.deepStrictEqual((r3 as InlineCompletionItem[])[0].range?.end, new Position(1, 26)) + }) + }) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/inlineTracker.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/inlineTracker.test.ts new file mode 100644 index 00000000000..6b9490c72a5 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/apps/inline/inlineTracker.test.ts @@ -0,0 +1,299 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { LineSelection, LineTracker, AuthUtil } from 'aws-core-vscode/codewhisperer' +import sinon from 'sinon' +import { Disposable, TextEditor, Position, Range, Selection } from 'vscode' +import { toTextEditor } from 'aws-core-vscode/test' +import assert from 'assert' +import { waitUntil } from 'aws-core-vscode/shared' + +describe('LineTracker class', function () { + let sut: LineTracker + let disposable: Disposable + let editor: TextEditor + let sandbox: sinon.SinonSandbox + let counts = { + editor: 0, + selection: 0, + content: 0, + } + + beforeEach(async function () { + sut = new LineTracker() + sandbox = sinon.createSandbox() + counts = { + editor: 0, + selection: 0, + content: 0, + } + disposable = sut.onDidChangeActiveLines((e) => { + if (e.reason === 'content') { + counts.content++ + } else if (e.reason === 'selection') { + counts.selection++ + } else if (e.reason === 'editor') { + counts.editor++ + } + }) + + sandbox.stub(AuthUtil.instance, 'isConnected').returns(true) + sandbox.stub(AuthUtil.instance, 'isConnectionExpired').returns(false) + }) + + afterEach(function () { + disposable.dispose() + sut.dispose() + sandbox.restore() + }) + + function assertEmptyCounts() { + assert.deepStrictEqual(counts, { + editor: 0, + selection: 0, + content: 0, + }) + } + + it('ready will emit onReady event', async function () { + let messageReceived = 0 + disposable = sut.onReady((_) => { + messageReceived++ + }) + + assert.strictEqual(sut.isReady, false) + sut.ready() + + await waitUntil( + async () => { + if (messageReceived !== 0) { + return + } + }, + { interval: 1000 } + ) + + assert.strictEqual(sut.isReady, true) + assert.strictEqual(messageReceived, 1) + }) + + describe('includes', function () { + // util function to help set up LineTracker.selections + async function setEditorSelection(selections: LineSelection[]): Promise { + const editor = await toTextEditor('\n\n\n\n\n\n\n\n\n\n', 'foo.py', undefined, { + preview: false, + }) + + const vscodeSelections = selections.map((s) => { + return new Selection(new Position(s.anchor, 0), new Position(s.active, 0)) + }) + + await sut.onTextEditorSelectionChanged({ + textEditor: editor, + selections: vscodeSelections, + kind: undefined, + }) + + assert.deepStrictEqual(sut.selections, selections) + return editor + } + + it('exact match when array of selections are provided', async function () { + const selections = [ + { + anchor: 1, + active: 1, + }, + { + anchor: 3, + active: 3, + }, + ] + + editor = await setEditorSelection(selections) + assert.deepStrictEqual(sut.selections, selections) + + let actual = sut.includes([ + { active: 1, anchor: 1 }, + { active: 3, anchor: 3 }, + ]) + assert.strictEqual(actual, true) + + actual = sut.includes([ + { active: 2, anchor: 2 }, + { active: 4, anchor: 4 }, + ]) + assert.strictEqual(actual, false) + + // both active && anchor have to be the same + actual = sut.includes([ + { active: 1, anchor: 0 }, + { active: 3, anchor: 0 }, + ]) + assert.strictEqual(actual, false) + + // different length would simply return false + actual = sut.includes([ + { active: 1, anchor: 1 }, + { active: 3, anchor: 3 }, + { active: 5, anchor: 5 }, + ]) + assert.strictEqual(actual, false) + }) + + it('match active line if line number and activeOnly option are provided', async function () { + const selections = [ + { + anchor: 1, + active: 1, + }, + { + anchor: 3, + active: 3, + }, + ] + + editor = await setEditorSelection(selections) + assert.deepStrictEqual(sut.selections, selections) + + let actual = sut.includes(1, { activeOnly: true }) + assert.strictEqual(actual, true) + + actual = sut.includes(2, { activeOnly: true }) + assert.strictEqual(actual, false) + }) + + it('range match if line number and activeOnly is set to false', async function () { + const selections = [ + { + anchor: 0, + active: 2, + }, + { + anchor: 4, + active: 6, + }, + ] + + editor = await setEditorSelection(selections) + assert.deepStrictEqual(sut.selections, selections) + + for (const line of [0, 1, 2]) { + const actual = sut.includes(line, { activeOnly: false }) + assert.strictEqual(actual, true) + } + + for (const line of [4, 5, 6]) { + const actual = sut.includes(line, { activeOnly: false }) + assert.strictEqual(actual, true) + } + + let actual = sut.includes(3, { activeOnly: false }) + assert.strictEqual(actual, false) + + actual = sut.includes(7, { activeOnly: false }) + assert.strictEqual(actual, false) + }) + }) + + describe('onContentChanged', function () { + it('should fire lineChangedEvent and set current line selection', async function () { + editor = await toTextEditor('\n\n\n\n\n', 'foo.py', undefined, { preview: false }) + editor.selection = new Selection(new Position(5, 0), new Position(5, 0)) + assertEmptyCounts() + + sut.onContentChanged({ + document: editor.document, + contentChanges: [{ text: 'a', range: new Range(0, 0, 0, 0), rangeOffset: 0, rangeLength: 0 }], + reason: undefined, + }) + + assert.deepStrictEqual(counts, { ...counts, content: 1 }) + assert.deepStrictEqual(sut.selections, [ + { + anchor: 5, + active: 5, + }, + ]) + }) + }) + + describe('onTextEditorSelectionChanged', function () { + it('should fire lineChangedEvent if selection changes and set current line selection', async function () { + editor = await toTextEditor('\n\n\n\n\n', 'foo.py', undefined, { preview: false }) + editor.selection = new Selection(new Position(3, 0), new Position(3, 0)) + assertEmptyCounts() + + await sut.onTextEditorSelectionChanged({ + textEditor: editor, + selections: [new Selection(new Position(3, 0), new Position(3, 0))], + kind: undefined, + }) + + assert.deepStrictEqual(counts, { ...counts, selection: 1 }) + assert.deepStrictEqual(sut.selections, [ + { + anchor: 3, + active: 3, + }, + ]) + + // if selection is included in the existing selections, won't emit an event + await sut.onTextEditorSelectionChanged({ + textEditor: editor, + selections: [new Selection(new Position(3, 0), new Position(3, 0))], + kind: undefined, + }) + + assert.deepStrictEqual(counts, { ...counts, selection: 1 }) + assert.deepStrictEqual(sut.selections, [ + { + anchor: 3, + active: 3, + }, + ]) + }) + + it('should not fire lineChangedEvent if uri scheme is debug || output', async function () { + // if the editor is not a text editor, won't emit an event and selection will be set to undefined + async function assertLineChanged(schema: string) { + const anotherEditor = await toTextEditor('', 'bar.log', undefined, { preview: false }) + const uri = anotherEditor.document.uri + sandbox.stub(uri, 'scheme').get(() => schema) + + await sut.onTextEditorSelectionChanged({ + textEditor: anotherEditor, + selections: [new Selection(new Position(3, 0), new Position(3, 0))], + kind: undefined, + }) + + assert.deepStrictEqual(counts, { ...counts }) + } + + await assertLineChanged('debug') + await assertLineChanged('output') + }) + }) + + describe('onActiveTextEditorChanged', function () { + it('shoudl fire lineChangedEvent', async function () { + const selections: Selection[] = [new Selection(0, 0, 1, 1)] + + editor = { selections: selections } as any + + assertEmptyCounts() + + await sut.onActiveTextEditorChanged(editor) + + assert.deepStrictEqual(counts, { ...counts, editor: 1 }) + assert.deepStrictEqual(sut.selections, [ + { + anchor: 0, + active: 1, + }, + ]) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts new file mode 100644 index 00000000000..c8473022118 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts @@ -0,0 +1,400 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import sinon from 'sinon' +import { BaseLanguageClient } from 'vscode-languageclient' +import { Position, CancellationToken, InlineCompletionItem, InlineCompletionTriggerKind } from 'vscode' +import assert from 'assert' +import { RecommendationService } from '../../../../../src/app/inline/recommendationService' +import { SessionManager } from '../../../../../src/app/inline/sessionManager' +import { createMockDocument } from 'aws-core-vscode/test' +// Import CursorUpdateManager directly instead of the interface +import { CursorUpdateManager } from '../../../../../src/app/inline/cursorUpdateManager' +import { CodeWhispererStatusBarManager } from 'aws-core-vscode/codewhisperer' +import { globals } from 'aws-core-vscode/shared' +import { DocumentEventListener } from '../../../../../src/app/inline/documentEventListener' +import { EditSuggestionState } from '../../../../../src/app/inline/editSuggestionState' + +const completionApi = 'aws/textDocument/inlineCompletionWithReferences' +const editApi = 'aws/textDocument/editCompletion' + +describe('RecommendationService', () => { + let languageClient: BaseLanguageClient + let sendRequestStub: sinon.SinonStub + let sandbox: sinon.SinonSandbox + let sessionManager: SessionManager + let service: RecommendationService + let cursorUpdateManager: CursorUpdateManager + let statusBarStub: any + let clockStub: sinon.SinonFakeTimers + const mockDocument = createMockDocument() + const mockPosition = { line: 0, character: 0 } as Position + const mockContext = { triggerKind: InlineCompletionTriggerKind.Automatic, selectedCompletionInfo: undefined } + const mockToken = { isCancellationRequested: false } as CancellationToken + const mockDocumentEventListener = { + isLastEventDeletion: (filepath: string) => false, + getLastDocumentChangeEvent: (filepath: string) => undefined, + } as DocumentEventListener + const mockInlineCompletionItemOne = { + insertText: 'ItemOne', + } as InlineCompletionItem + + const mockInlineCompletionItemTwo = { + insertText: 'ItemTwo', + } as InlineCompletionItem + const mockPartialResultToken = 'some-random-token' + + beforeEach(async () => { + sandbox = sinon.createSandbox() + + // Create a fake clock for testing time-based functionality + clockStub = sandbox.useFakeTimers({ + now: 1000, + shouldAdvanceTime: true, + }) + + // Stub globals.clock + sandbox.stub(globals, 'clock').value({ + Date: { + now: () => clockStub.now, + }, + setTimeout: clockStub.setTimeout.bind(clockStub), + clearTimeout: clockStub.clearTimeout.bind(clockStub), + setInterval: clockStub.setInterval.bind(clockStub), + clearInterval: clockStub.clearInterval.bind(clockStub), + }) + + sendRequestStub = sandbox.stub() + + languageClient = { + sendRequest: sendRequestStub, + warn: sandbox.stub(), + } as unknown as BaseLanguageClient + + sessionManager = new SessionManager() + + // Create cursor update manager mock + cursorUpdateManager = { + recordCompletionRequest: sandbox.stub(), + logger: { debug: sandbox.stub(), warn: sandbox.stub(), error: sandbox.stub() }, + updateIntervalMs: 250, + isActive: false, + lastRequestTime: 0, + dispose: sandbox.stub(), + start: sandbox.stub(), + stop: sandbox.stub(), + updatePosition: sandbox.stub(), + } as unknown as CursorUpdateManager + + // Create status bar stub + statusBarStub = { + setLoading: sandbox.stub().resolves(), + refreshStatusBar: sandbox.stub().resolves(), + } + + sandbox.stub(CodeWhispererStatusBarManager, 'instance').get(() => statusBarStub) + + // Create the service without cursor update recorder initially + service = new RecommendationService(sessionManager) + }) + + afterEach(() => { + sandbox.restore() + sessionManager.clear() + }) + + describe('constructor', () => { + it('should initialize with optional cursorUpdateRecorder', () => { + const serviceWithRecorder = new RecommendationService(sessionManager, cursorUpdateManager) + + // Verify the service was created with the recorder + assert.strictEqual(serviceWithRecorder['cursorUpdateRecorder'], cursorUpdateManager) + }) + }) + + describe('setCursorUpdateRecorder', () => { + it('should set the cursor update recorder', () => { + // Initially the recorder should be undefined + assert.strictEqual(service['cursorUpdateRecorder'], undefined) + + // Set the recorder + service.setCursorUpdateRecorder(cursorUpdateManager) + + // Verify it was set correctly + assert.strictEqual(service['cursorUpdateRecorder'], cursorUpdateManager) + }) + }) + + describe('getAllRecommendations', () => { + it('should handle single request with no partial result token', async () => { + // Mock EditSuggestionState to return false (no edit suggestion active) + sandbox.stub(EditSuggestionState, 'isEditSuggestionActive').returns(false) + + const mockFirstResult = { + sessionId: 'test-session', + items: [mockInlineCompletionItemOne], + partialResultToken: undefined, + } + + sendRequestStub.resolves(mockFirstResult) + + await service.getAllRecommendations( + languageClient, + mockDocument, + mockPosition, + mockContext, + mockToken, + true, + mockDocumentEventListener + ) + + // Verify sendRequest was called with correct parameters + const cs = sendRequestStub.getCalls() + const completionCalls = cs.filter((c) => c.firstArg === completionApi) + const editCalls = cs.filter((c) => c.firstArg === editApi) + assert.strictEqual(cs.length, 2) + assert.strictEqual(completionCalls.length, 1) + assert.strictEqual(editCalls.length, 1) + + const requestArgs = completionCalls[0].args[1] + assert.deepStrictEqual(requestArgs, { + textDocument: { + uri: 'file:///test.py', + }, + position: mockPosition, + context: mockContext, + documentChangeParams: undefined, + openTabFilepaths: [], + }) + + // Verify session management + const items = sessionManager.getActiveRecommendation() + assert.deepStrictEqual(items, [mockInlineCompletionItemOne]) + }) + + it('should handle multiple request with partial result token', async () => { + // Mock EditSuggestionState to return false (no edit suggestion active) + sandbox.stub(EditSuggestionState, 'isEditSuggestionActive').returns(false) + + const mockFirstResult = { + sessionId: 'test-session', + items: [mockInlineCompletionItemOne], + partialResultToken: mockPartialResultToken, + } + + const mockSecondResult = { + sessionId: 'test-session', + items: [mockInlineCompletionItemTwo], + partialResultToken: undefined, + } + + sendRequestStub.onFirstCall().resolves(mockFirstResult) + sendRequestStub.onSecondCall().resolves(mockSecondResult) + + await service.getAllRecommendations( + languageClient, + mockDocument, + mockPosition, + mockContext, + mockToken, + true, + mockDocumentEventListener + ) + + // Verify sendRequest was called with correct parameters + const cs = sendRequestStub.getCalls() + const completionCalls = cs.filter((c) => c.firstArg === completionApi) + const editCalls = cs.filter((c) => c.firstArg === editApi) + assert.strictEqual(cs.length, 3) + assert.strictEqual(completionCalls.length, 2) + assert.strictEqual(editCalls.length, 1) + + const firstRequestArgs = completionCalls[0].args[1] + const expectedRequestArgs = { + textDocument: { + uri: 'file:///test.py', + }, + position: mockPosition, + context: mockContext, + documentChangeParams: undefined, + openTabFilepaths: [], + } + const secondRequestArgs = completionCalls[1].args[1] + assert.deepStrictEqual(firstRequestArgs, expectedRequestArgs) + assert.deepStrictEqual(secondRequestArgs, { + ...expectedRequestArgs, + partialResultToken: mockPartialResultToken, + }) + }) + + it('should record completion request when cursorUpdateRecorder is set', async () => { + // Set the cursor update recorder + service.setCursorUpdateRecorder(cursorUpdateManager) + + const mockFirstResult = { + sessionId: 'test-session', + items: [mockInlineCompletionItemOne], + partialResultToken: undefined, + } + + sendRequestStub.resolves(mockFirstResult) + + await service.getAllRecommendations( + languageClient, + mockDocument, + mockPosition, + mockContext, + mockToken, + true, + mockDocumentEventListener + ) + + // Verify recordCompletionRequest was called + // eslint-disable-next-line @typescript-eslint/unbound-method + sinon.assert.calledOnce(cursorUpdateManager.recordCompletionRequest as sinon.SinonStub) + }) + + it('should not show UI indicators when showUi option is false', async () => { + // Call with showUi: false option + await service.getAllRecommendations( + languageClient, + mockDocument, + mockPosition, + mockContext, + mockToken, + true, + mockDocumentEventListener, + { + showUi: false, + emitTelemetry: true, + } + ) + + // Verify UI methods were not called + sinon.assert.notCalled(statusBarStub.setLoading) + sinon.assert.notCalled(statusBarStub.refreshStatusBar) + }) + + it('should show UI indicators when showUi option is true (default)', async () => { + // Call with default options (showUi: true) + await service.getAllRecommendations( + languageClient, + mockDocument, + mockPosition, + mockContext, + mockToken, + true, + mockDocumentEventListener + ) + + // Verify UI methods were called + sinon.assert.calledOnce(statusBarStub.setLoading) + sinon.assert.calledOnce(statusBarStub.refreshStatusBar) + }) + + it('should handle errors gracefully', async () => { + // Set the cursor update recorder + service.setCursorUpdateRecorder(cursorUpdateManager) + + // Make the request throw an error + const testError = new Error('Test error') + sendRequestStub.rejects(testError) + + // Set up UI options + const options = { showUi: true } + + // Temporarily replace console.error with a no-op function to prevent test failure + const originalConsoleError = console.error + console.error = () => {} + + try { + // Call the method and expect it to handle the error + const result = await service.getAllRecommendations( + languageClient, + mockDocument, + mockPosition, + mockContext, + mockToken, + true, + mockDocumentEventListener, + options + ) + + // Assert that error handling was done correctly + assert.deepStrictEqual(result, []) + + // Verify the UI indicators were hidden even when an error occurs + sinon.assert.calledOnce(statusBarStub.refreshStatusBar) + } finally { + // Restore the original console.error function + console.error = originalConsoleError + } + }) + + it('should not make completion request when edit suggestion is active', async () => { + // Mock EditSuggestionState to return true (edit suggestion is active) + sandbox.stub(EditSuggestionState, 'isEditSuggestionActive').returns(true) + + const mockResult = { + sessionId: 'test-session', + items: [mockInlineCompletionItemOne], + partialResultToken: undefined, + } + + sendRequestStub.resolves(mockResult) + + await service.getAllRecommendations( + languageClient, + mockDocument, + mockPosition, + mockContext, + mockToken, + true, + mockDocumentEventListener + ) + + // Verify sendRequest was called only for edit API, not completion API + const cs = sendRequestStub.getCalls() + const completionCalls = cs.filter((c) => c.firstArg === completionApi) + const editCalls = cs.filter((c) => c.firstArg === editApi) + + assert.strictEqual(cs.length, 1) // Only edit call + assert.strictEqual(completionCalls.length, 0) // No completion calls + assert.strictEqual(editCalls.length, 1) // One edit call + }) + + it('should make completion request when edit suggestion is not active', async () => { + // Mock EditSuggestionState to return false (no edit suggestion active) + sandbox.stub(EditSuggestionState, 'isEditSuggestionActive').returns(false) + + const mockResult = { + sessionId: 'test-session', + items: [mockInlineCompletionItemOne], + partialResultToken: undefined, + } + + sendRequestStub.resolves(mockResult) + + await service.getAllRecommendations( + languageClient, + mockDocument, + mockPosition, + mockContext, + mockToken, + true, + mockDocumentEventListener + ) + + // Verify sendRequest was called for both APIs + const cs = sendRequestStub.getCalls() + const completionCalls = cs.filter((c) => c.firstArg === completionApi) + const editCalls = cs.filter((c) => c.firstArg === editApi) + + assert.strictEqual(cs.length, 2) // Both calls + assert.strictEqual(completionCalls.length, 1) // One completion call + assert.strictEqual(editCalls.length, 1) // One edit call + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/auth/controller.test.ts b/packages/amazonq/test/unit/amazonq/auth/controller.test.ts new file mode 100644 index 00000000000..e18032d9424 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/auth/controller.test.ts @@ -0,0 +1,36 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import sinon from 'sinon' +import { AuthController } from 'aws-core-vscode/amazonq' +import { reconnect, amazonQChatSource } from 'aws-core-vscode/codewhisperer' +import assert from 'assert' +import { placeholder } from 'aws-core-vscode/shared' + +describe('AuthController', () => { + let authController: AuthController + let reconnectStub: any + + beforeEach(() => { + authController = new AuthController() + reconnectStub = sinon.stub(reconnect, 'execute') + }) + + afterEach(() => { + sinon.restore() + }) + + it('should call reconnect for "missing_scopes"', () => { + authController.handleAuth('missing_scopes') + + assert.strictEqual(reconnectStub.calledWith(placeholder, amazonQChatSource), true) + }) + + it('should call reconnect for "re-auth"', () => { + authController.handleAuth('re-auth') + + assert.strictEqual(reconnectStub.calledWith(placeholder, amazonQChatSource), true) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/auth.test.ts b/packages/amazonq/test/unit/amazonq/lsp/auth.test.ts new file mode 100644 index 00000000000..835a19b65f1 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/lsp/auth.test.ts @@ -0,0 +1,33 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import assert from 'assert' +import { AmazonQLspAuth } from '../../../../src/lsp/auth' +import { BaseLanguageClient } from 'vscode-languageclient' + +describe('AmazonQLspAuth', function () { + describe('updateBearerToken', function () { + it('makes request to LSP when token changes', async function () { + // Note: this token will be encrypted + let lastSentToken = {} + const auth = new AmazonQLspAuth({ + sendRequest: (_method: string, param: any) => { + lastSentToken = param + }, + info: (_message: string, _data: any) => {}, + } as BaseLanguageClient) + + await auth.updateBearerToken('firstToken') + assert.notDeepStrictEqual(lastSentToken, {}) + const encryptedFirstToken = lastSentToken + + await auth.updateBearerToken('secondToken') + assert.notDeepStrictEqual(lastSentToken, encryptedFirstToken) + const encryptedSecondToken = lastSentToken + + await auth.updateBearerToken('secondToken') + assert.deepStrictEqual(lastSentToken, encryptedSecondToken) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/chat/error.test.ts b/packages/amazonq/test/unit/amazonq/lsp/chat/error.test.ts new file mode 100644 index 00000000000..80bfe657cc1 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/lsp/chat/error.test.ts @@ -0,0 +1,15 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { isValidResponseError } from '../../../../../src/lsp/chat/error' +import { ResponseError } from '@aws/language-server-runtimes/protocol' +import * as assert from 'assert' + +describe('isValidResponseError', async function () { + it('requires the data field', function () { + assert.ok(isValidResponseError(new ResponseError(0, 'this one has data', {}))) + assert.ok(!isValidResponseError(new ResponseError(0, 'this one does not have data'))) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/chat/messages.test.ts b/packages/amazonq/test/unit/amazonq/lsp/chat/messages.test.ts new file mode 100644 index 00000000000..4c412a17676 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/lsp/chat/messages.test.ts @@ -0,0 +1,163 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import { BaseLanguageClient } from 'vscode-languageclient' +import { AuthUtil } from 'aws-core-vscode/codewhisperer' +import { registerMessageListeners } from '../../../../../src/lsp/chat/messages' +import { AmazonQChatViewProvider } from '../../../../../src/lsp/chat/webviewProvider' +import { secondaryAuth, authConnection, AuthFollowUpType } from 'aws-core-vscode/amazonq' +import { messages } from 'aws-core-vscode/shared' + +describe('registerMessageListeners', () => { + let languageClient: BaseLanguageClient + let provider: AmazonQChatViewProvider + let sandbox: sinon.SinonSandbox + let messageHandler: (message: any) => void | Promise + let errorStub: sinon.SinonStub + + beforeEach(() => { + sandbox = sinon.createSandbox() + errorStub = sandbox.stub() + + languageClient = { + info: sandbox.stub(), + error: errorStub, + sendNotification: sandbox.stub(), + onRequest: sandbox.stub(), + onNotification: sandbox.stub(), + } as unknown as BaseLanguageClient + + provider = { + webview: { + onDidReceiveMessage: (callback: (message: any) => void | Promise) => { + messageHandler = callback + return { + dispose: (): void => {}, + } + }, + }, + } as any + + registerMessageListeners(languageClient, provider, Buffer.from('test-key')) + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('AUTH_FOLLOW_UP_CLICKED', () => { + let mockAuthUtil: AuthUtil + let deleteConnectionStub: sinon.SinonStub + let reauthenticateStub: sinon.SinonStub + + const authFollowUpClickedCommand = 'authFollowUpClicked' + + interface TestCase { + authType: AuthFollowUpType + stubToReject: sinon.SinonStub + errorMessage: string + } + + const testFailure = async (testCase: TestCase) => { + testCase.stubToReject.rejects(new Error()) + + await messageHandler({ + command: authFollowUpClickedCommand, + params: { + authFollowupType: testCase.authType, + }, + }) + + sinon.assert.calledOnce(errorStub) + sinon.assert.calledWith(errorStub, sinon.match(testCase.errorMessage)) + } + + beforeEach(() => { + deleteConnectionStub = sandbox.stub().resolves() + reauthenticateStub = sandbox.stub().resolves() + + mockAuthUtil = { + reauthenticate: reauthenticateStub, + secondaryAuth: { + deleteConnection: deleteConnectionStub, + } as unknown as secondaryAuth.SecondaryAuth, + } as unknown as AuthUtil + + sandbox.replaceGetter(AuthUtil, 'instance', () => mockAuthUtil) + }) + + it('handles re-authentication request', async () => { + await messageHandler({ + command: authFollowUpClickedCommand, + params: { + authFollowupType: 're-auth', + }, + }) + + sinon.assert.calledOnce(reauthenticateStub) + sinon.assert.notCalled(deleteConnectionStub) + }) + + it('handles full authentication request', async () => { + await messageHandler({ + command: authFollowUpClickedCommand, + params: { + authFollowupType: 'full-auth', + }, + }) + + sinon.assert.notCalled(reauthenticateStub) + sinon.assert.calledOnce(deleteConnectionStub) + }) + + it('logs error if re-authentication fails', async () => { + await testFailure({ + authType: 're-auth', + stubToReject: reauthenticateStub, + errorMessage: 'Failed to re-authenticate', + }) + }) + + it('logs error if full authentication fails', async () => { + await testFailure({ + authType: 'full-auth', + stubToReject: deleteConnectionStub, + errorMessage: 'Failed to authenticate', + }) + }) + }) + + describe('COPY_TO_CLIPBOARD', () => { + let copyToClipboardStub: sinon.SinonStub + const testCode = 'test' + const copyMessage = { + command: 'copyToClipboard', + params: { + code: testCode, + }, + } + + beforeEach(() => { + copyToClipboardStub = sandbox.stub().resolves() + sandbox.stub(messages, 'copyToClipboard').get(() => copyToClipboardStub) + }) + + it('successfully copies code to clipboard', async () => { + await messageHandler(copyMessage) + + sinon.assert.calledWith(copyToClipboardStub, testCode) + }) + + it('handles clipboard copy failure', async () => { + const errorMessage = 'Failed to copy' + copyToClipboardStub.rejects(new Error(errorMessage)) + + await messageHandler(copyMessage) + + sinon.assert.calledWith(errorStub, `[VSCode Client] Failed to copy to clipboard: ${errorMessage}`) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/client.test.ts b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts new file mode 100644 index 00000000000..8f11c8eaa35 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts @@ -0,0 +1,268 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import sinon from 'sinon' +import { BaseLanguageClient } from 'vscode-languageclient' +import { AuthUtil } from 'aws-core-vscode/codewhisperer' +import { AmazonQLspAuth } from '../../../../src/lsp/auth' + +// These tests verify the behavior of the authentication functions +// Since the actual functions are module-level and use real dependencies, +// we test the expected behavior through mock implementations + +describe('Language Server Client Authentication', function () { + let sandbox: sinon.SinonSandbox + let mockClient: any + let mockAuth: any + let authUtilStub: sinon.SinonStub + let loggerStub: any + let getLoggerStub: sinon.SinonStub + let pushConfigUpdateStub: sinon.SinonStub + + beforeEach(() => { + sandbox = sinon.createSandbox() + + // Mock LanguageClient + mockClient = { + sendRequest: sandbox.stub().resolves(), + sendNotification: sandbox.stub(), + onDidChangeState: sandbox.stub(), + } + + // Mock AmazonQLspAuth + mockAuth = { + refreshConnection: sandbox.stub().resolves(), + } + + // Mock AuthUtil + authUtilStub = sandbox.stub(AuthUtil, 'instance').get(() => ({ + isConnectionValid: sandbox.stub().returns(true), + regionProfileManager: { + activeRegionProfile: { arn: 'test-profile-arn' }, + }, + auth: { + getConnectionState: sandbox.stub().returns('valid'), + activeConnection: { id: 'test-connection' }, + }, + })) + + // Create logger stub + loggerStub = { + info: sandbox.stub(), + debug: sandbox.stub(), + warn: sandbox.stub(), + error: sandbox.stub(), + } + + // Clear all relevant module caches + const sharedModuleId = require.resolve('aws-core-vscode/shared') + const configModuleId = require.resolve('../../../../src/lsp/config') + delete require.cache[sharedModuleId] + delete require.cache[configModuleId] + + // jscpd:ignore-start + // Create getLogger stub + getLoggerStub = sandbox.stub().returns(loggerStub) + + // Create a mock shared module with stubbed getLogger + const mockSharedModule = { + getLogger: getLoggerStub, + } + + // Override the require cache with our mock + require.cache[sharedModuleId] = { + id: sharedModuleId, + filename: sharedModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockSharedModule, + paths: [], + } as any + // jscpd:ignore-end + + // Mock pushConfigUpdate + pushConfigUpdateStub = sandbox.stub().resolves() + const mockConfigModule = { + pushConfigUpdate: pushConfigUpdateStub, + } + + require.cache[configModuleId] = { + id: configModuleId, + filename: configModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockConfigModule, + paths: [], + } as any + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('initializeLanguageServerConfiguration behavior', function () { + it('should initialize configuration when connection is valid', async function () { + // Test the expected behavior of the function + const mockInitializeFunction = async (client: BaseLanguageClient, context: string) => { + const { getLogger } = require('aws-core-vscode/shared') + const { pushConfigUpdate } = require('../../../../src/lsp/config') + const logger = getLogger('amazonqLsp') + + if (AuthUtil.instance.isConnectionValid()) { + logger.info(`[${context}] Initializing language server configuration`) + + // Send profile configuration + logger.debug(`[${context}] Sending profile configuration to language server`) + await pushConfigUpdate(client, { + type: 'profile', + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + logger.debug(`[${context}] Profile configuration sent successfully`) + + // Send customization configuration + logger.debug(`[${context}] Sending customization configuration to language server`) + await pushConfigUpdate(client, { + type: 'customization', + customization: 'test-customization', + }) + logger.debug(`[${context}] Customization configuration sent successfully`) + + logger.info(`[${context}] Language server configuration completed successfully`) + } else { + logger.warn(`[${context}] Connection invalid, skipping configuration`) + } + } + + await mockInitializeFunction(mockClient as any, 'startup') + + // Verify logging + assert(loggerStub.info.calledWith('[startup] Initializing language server configuration')) + assert(loggerStub.debug.calledWith('[startup] Sending profile configuration to language server')) + assert(loggerStub.debug.calledWith('[startup] Profile configuration sent successfully')) + assert(loggerStub.debug.calledWith('[startup] Sending customization configuration to language server')) + assert(loggerStub.debug.calledWith('[startup] Customization configuration sent successfully')) + assert(loggerStub.info.calledWith('[startup] Language server configuration completed successfully')) + + // Verify pushConfigUpdate was called twice + assert.strictEqual(pushConfigUpdateStub.callCount, 2) + + // Verify profile configuration + assert( + pushConfigUpdateStub.calledWith(mockClient, { + type: 'profile', + profileArn: 'test-profile-arn', + }) + ) + + // Verify customization configuration + assert( + pushConfigUpdateStub.calledWith(mockClient, { + type: 'customization', + customization: 'test-customization', + }) + ) + }) + + it('should log warning when connection is invalid', async function () { + // Mock invalid connection + authUtilStub.get(() => ({ + isConnectionValid: sandbox.stub().returns(false), + auth: { + getConnectionState: sandbox.stub().returns('invalid'), + activeConnection: { id: 'test-connection' }, + }, + })) + + const mockInitializeFunction = async (client: BaseLanguageClient, context: string) => { + const { getLogger } = require('aws-core-vscode/shared') + const logger = getLogger('amazonqLsp') + + // jscpd:ignore-start + if (AuthUtil.instance.isConnectionValid()) { + // Should not reach here + } else { + logger.warn( + `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` + ) + const activeConnection = AuthUtil.instance.auth.activeConnection + const connectionState = activeConnection + ? AuthUtil.instance.auth.getConnectionState(activeConnection) + : 'no-connection' + logger.warn(`[${context}] Connection state: ${connectionState}`) + // jscpd:ignore-end + } + } + + await mockInitializeFunction(mockClient as any, 'crash-recovery') + + // Verify warning logs + assert( + loggerStub.warn.calledWith( + '[crash-recovery] Connection invalid, skipping language server configuration - this will cause authentication failures' + ) + ) + assert(loggerStub.warn.calledWith('[crash-recovery] Connection state: invalid')) + + // Verify pushConfigUpdate was not called + assert.strictEqual(pushConfigUpdateStub.callCount, 0) + }) + }) + + describe('crash recovery handler behavior', function () { + it('should reinitialize authentication after crash', async function () { + const mockCrashHandler = async (client: BaseLanguageClient, auth: AmazonQLspAuth) => { + const { getLogger } = require('aws-core-vscode/shared') + const { pushConfigUpdate } = require('../../../../src/lsp/config') + const logger = getLogger('amazonqLsp') + + logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') + + try { + logger.debug('[crash-recovery] Refreshing connection and sending bearer token') + await auth.refreshConnection(true) + logger.debug('[crash-recovery] Bearer token sent successfully') + + // Mock the configuration initialization + if (AuthUtil.instance.isConnectionValid()) { + await pushConfigUpdate(client, { + type: 'profile', + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + } + + logger.info('[crash-recovery] Authentication reinitialized successfully') + } catch (error) { + logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) + } + } + + await mockCrashHandler(mockClient as any, mockAuth as any) + + // Verify crash recovery logging + assert( + loggerStub.info.calledWith( + '[crash-recovery] Language server crash detected, reinitializing authentication' + ) + ) + assert(loggerStub.debug.calledWith('[crash-recovery] Refreshing connection and sending bearer token')) + assert(loggerStub.debug.calledWith('[crash-recovery] Bearer token sent successfully')) + assert(loggerStub.info.calledWith('[crash-recovery] Authentication reinitialized successfully')) + + // Verify auth.refreshConnection was called + assert(mockAuth.refreshConnection.calledWith(true)) + + // Verify profile configuration was sent + assert( + pushConfigUpdateStub.calledWith(mockClient, { + type: 'profile', + profileArn: 'test-profile-arn', + }) + ) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts new file mode 100644 index 00000000000..c31e873e181 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts @@ -0,0 +1,227 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { DevSettings } from 'aws-core-vscode/shared' +import sinon from 'sinon' +import { defaultAmazonQLspConfig, ExtendedAmazonQLSPConfig, getAmazonQLspConfig } from '../../../../src/lsp/config' + +describe('getAmazonQLspConfig', () => { + let sandbox: sinon.SinonSandbox + let serviceConfigStub: sinon.SinonStub + const settingConfig: ExtendedAmazonQLSPConfig = { + manifestUrl: 'https://custom.url/manifest.json', + supportedVersions: '4.0.0', + id: 'AmazonQSetting', + suppressPromptPrefix: getAmazonQLspConfig().suppressPromptPrefix, + path: '/custom/path', + ui: '/chat/client/location', + } + + beforeEach(() => { + sandbox = sinon.createSandbox() + + serviceConfigStub = sandbox.stub() + sandbox.stub(DevSettings, 'instance').get(() => ({ + getServiceConfig: serviceConfigStub, + })) + }) + + afterEach(() => { + sandbox.restore() + resetEnv() + }) + + it('uses default config', () => { + serviceConfigStub.returns({}) + assert.deepStrictEqual(getAmazonQLspConfig(), defaultAmazonQLspConfig) + }) + + it('overrides path', () => { + const path = '/custom/path/to/lsp' + serviceConfigStub.returns({ path }) + + assert.deepStrictEqual(getAmazonQLspConfig(), { + ...defaultAmazonQLspConfig, + path, + }) + }) + + it('overrides default settings', () => { + serviceConfigStub.returns(settingConfig) + + assert.deepStrictEqual(getAmazonQLspConfig(), settingConfig) + }) + + it('environment variable takes precedence over settings', () => { + setEnv(settingConfig) + serviceConfigStub.returns({}) + assert.deepStrictEqual(getAmazonQLspConfig(), settingConfig) + }) + + function setEnv(envConfig: ExtendedAmazonQLSPConfig) { + process.env.__AMAZONQLSP_MANIFEST_URL = envConfig.manifestUrl + process.env.__AMAZONQLSP_SUPPORTED_VERSIONS = envConfig.supportedVersions + process.env.__AMAZONQLSP_ID = envConfig.id + process.env.__AMAZONQLSP_PATH = envConfig.path + process.env.__AMAZONQLSP_UI = envConfig.ui + } + + function resetEnv() { + delete process.env.__AMAZONQLSP_MANIFEST_URL + delete process.env.__AMAZONQLSP_SUPPORTED_VERSIONS + delete process.env.__AMAZONQLSP_ID + delete process.env.__AMAZONQLSP_PATH + delete process.env.__AMAZONQLSP_UI + } +}) + +describe('pushConfigUpdate', () => { + let sandbox: sinon.SinonSandbox + let mockClient: any + let loggerStub: any + let getLoggerStub: sinon.SinonStub + let pushConfigUpdate: any + + beforeEach(() => { + sandbox = sinon.createSandbox() + + // Mock LanguageClient + mockClient = { + sendRequest: sandbox.stub().resolves(), + sendNotification: sandbox.stub(), + } + + // Create logger stub + loggerStub = { + debug: sandbox.stub(), + } + + // Clear all relevant module caches + const configModuleId = require.resolve('../../../../src/lsp/config') + const sharedModuleId = require.resolve('aws-core-vscode/shared') + delete require.cache[configModuleId] + delete require.cache[sharedModuleId] + + // jscpd:ignore-start + // Create getLogger stub and store reference for test verification + getLoggerStub = sandbox.stub().returns(loggerStub) + + // Create a mock shared module with stubbed getLogger + const mockSharedModule = { + getLogger: getLoggerStub, + } + + // Override the require cache with our mock + require.cache[sharedModuleId] = { + id: sharedModuleId, + filename: sharedModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockSharedModule, + paths: [], + } as any + + // Now require the module - it should use our mocked getLogger + // jscpd:ignore-end + const configModule = require('../../../../src/lsp/config') + pushConfigUpdate = configModule.pushConfigUpdate + }) + + afterEach(() => { + sandbox.restore() + }) + + it('should send profile configuration with logging', async () => { + const config = { + type: 'profile' as const, + profileArn: 'test-profile-arn', + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging + assert(loggerStub.debug.calledWith('Pushing profile configuration: test-profile-arn')) + assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) + + // Verify client call + assert(mockClient.sendRequest.calledOnce) + assert( + mockClient.sendRequest.calledWith(sinon.match.string, { + section: 'aws.q', + settings: { profileArn: 'test-profile-arn' }, + }) + ) + }) + + it('should send customization configuration with logging', async () => { + const config = { + type: 'customization' as const, + customization: 'test-customization-arn', + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging + assert(loggerStub.debug.calledWith('Pushing customization configuration: test-customization-arn')) + assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) + + // Verify client call + assert(mockClient.sendNotification.calledOnce) + assert( + mockClient.sendNotification.calledWith(sinon.match.string, { + section: 'aws.q', + settings: { customization: 'test-customization-arn' }, + }) + ) + }) + + it('should handle undefined profile ARN', async () => { + const config = { + type: 'profile' as const, + profileArn: undefined, + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging with undefined + assert(loggerStub.debug.calledWith('Pushing profile configuration: undefined')) + assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) + }) + + it('should handle undefined customization ARN', async () => { + const config = { + type: 'customization' as const, + customization: undefined, + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging with undefined + assert(loggerStub.debug.calledWith('Pushing customization configuration: undefined')) + assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) + }) + + it('should send logLevel configuration with logging', async () => { + const config = { + type: 'logLevel' as const, + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging + assert(loggerStub.debug.calledWith('Pushing log level configuration')) + assert(loggerStub.debug.calledWith('Log level configuration pushed successfully')) + + // Verify client call + assert(mockClient.sendNotification.calledOnce) + assert( + mockClient.sendNotification.calledWith(sinon.match.string, { + section: 'aws.logLevel', + }) + ) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/encryption.test.ts b/packages/amazonq/test/unit/amazonq/lsp/encryption.test.ts new file mode 100644 index 00000000000..06a901edde6 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/lsp/encryption.test.ts @@ -0,0 +1,27 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as assert from 'assert' +import { decryptResponse, encryptRequest } from '../../../../src/lsp/encryption' +import { encryptionKey } from '../../../../src/lsp/auth' + +describe('LSP encryption', function () { + it('encrypt and decrypt invert eachother with same key', async function () { + const key = encryptionKey + const request = { + id: 0, + name: 'my Request', + isRealRequest: false, + metadata: { + tags: ['tag1', 'tag2'], + }, + } + const encryptedPayload = await encryptRequest(request, key) + const message = (encryptedPayload as { message: string }).message + const decrypted = await decryptResponse(message, key) + + assert.deepStrictEqual(decrypted, request) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/onboardingPage/walkthrough.test.ts b/packages/amazonq/test/unit/amazonq/onboardingPage/walkthrough.test.ts new file mode 100644 index 00000000000..23be7643128 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/onboardingPage/walkthrough.test.ts @@ -0,0 +1,22 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { showAmazonQWalkthroughOnce } from 'aws-core-vscode/amazonq' +import sinon from 'sinon' + +describe('showAmazonQWalkthroughOnce', function () { + it('only shows once', async function () { + const showWalkthroughStub = sinon.stub() + assert.deepStrictEqual(showWalkthroughStub.callCount, 0) + await showAmazonQWalkthroughOnce(showWalkthroughStub) + // Show walkthrough since our state indicates we haven't shown before + assert.deepStrictEqual(showWalkthroughStub.callCount, 1) + + await showAmazonQWalkthroughOnce(showWalkthroughStub) + // On the second call we do not show again since we've shown before. + assert.deepStrictEqual(showWalkthroughStub.callCount, 1) + }) +}) diff --git a/packages/amazonq/test/unit/amazonqGumby/amazonQGumbyUtil.ts b/packages/amazonq/test/unit/amazonqGumby/amazonQGumbyUtil.ts new file mode 100644 index 00000000000..b6b6af3eaf9 --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/amazonQGumbyUtil.ts @@ -0,0 +1,11 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import path from 'path' + +export const getTestResourceFilePath = (relativePathToFile: string) => { + return path.resolve(__dirname, relativePathToFile) +} + +export const stripStringWhitespace = (str: string) => str.replace(/\s+/g, '') diff --git a/packages/amazonq/test/unit/amazonqGumby/chat/controller/messenger/messengerUtils.test.ts b/packages/amazonq/test/unit/amazonqGumby/chat/controller/messenger/messengerUtils.test.ts new file mode 100644 index 00000000000..8792510db79 --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/chat/controller/messenger/messengerUtils.test.ts @@ -0,0 +1,45 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as assert from 'assert' +import { DependencyVersions, MessengerUtils } from 'aws-core-vscode/amazonqGumby' + +const TestDependencyVersions = new DependencyVersions('4.2', ['3.8', '4.2', '2.4'], ['1.12', '1.09', '1.10'], '1.3') + +describe('MessengerUtils', () => { + describe('createAvailableDependencyVersionString', async () => { + it('returns a string containing the highest major version available', async () => { + const message = MessengerUtils.createAvailableDependencyVersionString(TestDependencyVersions) + assert.strictEqual( + message.includes('Latest major version:'), + true, + "'Latest major version' was not found in string" + ) + assert.strictEqual( + message.includes('4.2'), + true, + 'Expected the actual latest major version to be present in string' + ) + }) + + it('returns a string that does not contain a major version when major versions are not defined', async () => { + const testDependencyVersions = new DependencyVersions('4.2', [], ['1.9', '1.11', '1.10'], '1.3') + const message = MessengerUtils.createAvailableDependencyVersionString(testDependencyVersions) + assert.strictEqual(message.includes('Latest major version:'), false) + }) + + it('returns a string containing the highest minor version available', async () => { + const message = MessengerUtils.createAvailableDependencyVersionString(TestDependencyVersions) + assert.strictEqual(message.includes('Latest minor version:'), true) + assert.strictEqual(message.includes('1.12'), true) + }) + + it('returns a string that does not contain a minor version when minor versions are not defined', async () => { + const testDependencyVersions = new DependencyVersions('4.2', ['3.8', '4.2', '2.4'], [], '1.3') + const message = MessengerUtils.createAvailableDependencyVersionString(testDependencyVersions) + assert.strictEqual(message.includes('Latest minor version:'), false) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonqGumby/humanInTheLoopManager.test.ts b/packages/amazonq/test/unit/amazonqGumby/humanInTheLoopManager.test.ts new file mode 100644 index 00000000000..e94838ba3c2 --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/humanInTheLoopManager.test.ts @@ -0,0 +1,64 @@ +/*! + * 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 path from 'path' +import { HumanInTheLoopManager } from 'aws-core-vscode/codewhisperer/node' +import { getTestResourceFilePath, stripStringWhitespace } from './amazonQGumbyUtil' +import { fs } from 'aws-core-vscode/shared' +import { assertEqualPaths } from 'aws-core-vscode/test' + +describe('HumanInTheLoopManager', async function () { + it('will getUserDependencyUpdateDir()', async function () { + const updateDirPath = HumanInTheLoopManager.instance.getUserDependencyUpdateDir() + assert.ok(updateDirPath, 'q-pom-dependency-update') + }) + it('will getUploadFolderInfo()', async function () { + const uploadFolderInfo = HumanInTheLoopManager.instance.getUploadFolderInfo() + assert.strictEqual(uploadFolderInfo.name, 'q-pom-dependency-update') + assert.strictEqual(uploadFolderInfo.path, HumanInTheLoopManager.instance.getUserDependencyUpdateDir()) + }) + it('will getCompileDependencyListFolderInfo()', async function () { + const compileFolderInfo = HumanInTheLoopManager.instance.getCompileDependencyListFolderInfo() + assert.strictEqual(compileFolderInfo.name, 'q-pom-dependency-list') + assert.strictEqual(compileFolderInfo.path, HumanInTheLoopManager.instance.getTmpDependencyListDir()) + }) + it('will createPomFileCopy() and delete artifact', async function () { + const pomFileVirtualFileReference = vscode.Uri.file( + getTestResourceFilePath('resources/files/humanInTheLoop/downloadResults/pom.xml') + ) + const outputDirectoryPath = path.resolve(__dirname, 'testOutput') + const newPomFilePath = await HumanInTheLoopManager.instance.createPomFileCopy( + outputDirectoryPath, + pomFileVirtualFileReference + ) + const outputPathResult = path.join(outputDirectoryPath, 'pom.xml') + assertEqualPaths(newPomFilePath.fsPath, outputPathResult) + const newPomFileContents = await fs.readFileText(newPomFilePath.path) + assert.strictEqual( + stripStringWhitespace(newPomFileContents), + stripStringWhitespace(` + + 4.0.0 + GROUP_ID + ARTIFACT_ID + VERSION + + + + org.projectlombok + lombok + ***** + + + + `) + ) + await HumanInTheLoopManager.instance.cleanUpArtifacts() + const newPomFileDoesNotExistFlag = await fs.existsFile(newPomFilePath) + assert.equal(newPomFileDoesNotExistFlag, false) + }) +}) diff --git a/packages/amazonq/test/unit/amazonqGumby/models/dependencies.test.ts b/packages/amazonq/test/unit/amazonqGumby/models/dependencies.test.ts new file mode 100644 index 00000000000..e56ec1853c3 --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/models/dependencies.test.ts @@ -0,0 +1,48 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as assert from 'assert' +import { DependencyVersions } from 'aws-core-vscode/amazonqGumby' + +describe('DependencyVersions', () => { + describe('length', async () => { + it('returns the correct number of versions when empty arrays are passed in', async () => { + const testDependencyVersions = new DependencyVersions('', [], [], '1') + assert.strictEqual(testDependencyVersions.length, 0) + }) + + it('returns the correct number of versions when full arrays', async () => { + const testDependencyVersions = new DependencyVersions('1', ['1', '2', '3'], ['4', '5', '6'], '1') + assert.strictEqual(testDependencyVersions.length, 6) + }) + + it('ignores duplicates when calculating the length', async () => { + const testDependencyVersions = new DependencyVersions('1', ['1', '2'], ['1', '3', '4'], '1') + assert.strictEqual(testDependencyVersions.length, 4) + }) + }) + + describe('allVersions', async () => { + it('returns an empty array when constructed with empty arrays', async () => { + const testDependencyVersions = new DependencyVersions('1', [], [], '1') + assert.deepStrictEqual(Array.from(testDependencyVersions.allVersions), []) + }) + + it('returns a flat array containing all versions passed in the constructor', async () => { + const testDependencyVersions = new DependencyVersions('1', ['1', '2', '3'], ['4', '5', '6'], '1') + assert.deepStrictEqual(Array.from(testDependencyVersions.allVersions), ['1', '2', '3', '4', '5', '6']) + }) + + it('returns an array containing only unique members', async () => { + const testDependencyVersions = new DependencyVersions('1', ['1', '2'], ['1', '2', '3'], '1') + assert.deepStrictEqual(Array.from(testDependencyVersions.allVersions), ['1', '2', '3']) + }) + + it('returns an array where major versions are sorted in ascending order, followed by minor versions', async () => { + const testDependencyVersions = new DependencyVersions('4.4', ['4.4', '3.2'], ['1.1', '1.3', '1.2'], '1') + assert.deepStrictEqual(Array.from(testDependencyVersions.allVersions), ['3.2', '4.4', '1.1', '1.2', '1.3']) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonqGumby/resources/files/addedFile.diff b/packages/amazonq/test/unit/amazonqGumby/resources/files/addedFile.diff new file mode 100644 index 00000000000..7eb35b5fa0e --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/resources/files/addedFile.diff @@ -0,0 +1,6 @@ +diff --git a/testfile.txt b/testfile.txt +index e69de29..9f4b6d8 100644 +--- a/testfile.txt ++++ b/testfile.txt +@@ -0,0 +1 @@ ++This is a test file diff --git a/packages/amazonq/test/unit/amazonqGumby/resources/files/humanInTheLoop/dependency-updates-aggregate-report.xml b/packages/amazonq/test/unit/amazonqGumby/resources/files/humanInTheLoop/dependency-updates-aggregate-report.xml new file mode 100644 index 00000000000..fd105d8e66c --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/resources/files/humanInTheLoop/dependency-updates-aggregate-report.xml @@ -0,0 +1,35 @@ + + + + 0 + 0 + 1 + 0 + 0 + + + + org.projectlombok + lombok + compile + jar + 0.11.4 + 1.18.32 + + 0.11.6 + 0.11.8 + + + 0.12.0 + + + 1.12.2 + 1.12.4 + 1.12.6 + + incremental available + + + diff --git a/packages/amazonq/test/unit/amazonqGumby/resources/files/humanInTheLoop/downloadResults/manifest.json b/packages/amazonq/test/unit/amazonqGumby/resources/files/humanInTheLoop/downloadResults/manifest.json new file mode 100644 index 00000000000..048276f5237 --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/resources/files/humanInTheLoop/downloadResults/manifest.json @@ -0,0 +1,7 @@ +{ + "hilCapability": "HIL_1pDependency_VersionUpgrade", + "pomFolderName": "pomFolder", + "pomGroupId": "org.projectlombok", + "pomArtifactId": "lombok", + "sourcePomVersion": "0.11.4" +} diff --git a/packages/amazonq/test/unit/amazonqGumby/resources/files/humanInTheLoop/downloadResults/pom.xml b/packages/amazonq/test/unit/amazonqGumby/resources/files/humanInTheLoop/downloadResults/pom.xml new file mode 100644 index 00000000000..c5cd78ac24d --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/resources/files/humanInTheLoop/downloadResults/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + GROUP_ID + ARTIFACT_ID + VERSION + + + + org.projectlombok + lombok + ***** + + + diff --git a/packages/amazonq/test/unit/amazonqGumby/resources/files/modifiedFile.diff b/packages/amazonq/test/unit/amazonqGumby/resources/files/modifiedFile.diff new file mode 100644 index 00000000000..ee3a0a44008 --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/resources/files/modifiedFile.diff @@ -0,0 +1,8 @@ +diff --git a/README.md b/README.md +index 084f378..1705101 100644 +--- a/README.md ++++ b/README.md +@@ -1,3 +1,5 @@ +This guide walks you through using Gradle to build a simple Java project. ++This line is added. +\ No newline at end of file \ No newline at end of file diff --git a/packages/amazonq/test/unit/amazonqGumby/resources/mocks/transformFixtures.ts b/packages/amazonq/test/unit/amazonqGumby/resources/mocks/transformFixtures.ts new file mode 100644 index 00000000000..60053108efb --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/resources/mocks/transformFixtures.ts @@ -0,0 +1,33 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { TransformationStep } from 'aws-core-vscode/codewhisperer/node' + +export const downloadArtifactIdFixture = 'hil-test-artifact-id' +export const downloadArtifactTypeFixture = 'BuiltJars' +export const transformationStepsHumanInTheLoopFixture: TransformationStep[] = [ + { + id: 'fake-step-id-1', + name: 'Building Code', + description: 'Building dependencies', + status: 'COMPLETED', + progressUpdates: [ + { + name: 'Status step', + status: 'FAILED', + description: 'This step should be hil identifier', + startTime: new Date(), + endTime: new Date(), + downloadArtifacts: [ + { + downloadArtifactId: downloadArtifactIdFixture, + downloadArtifactType: downloadArtifactTypeFixture, + }, + ], + }, + ], + startTime: new Date(), + endTime: new Date(), + }, +] diff --git a/packages/amazonq/test/unit/amazonqGumby/transformApiHandler.test.ts b/packages/amazonq/test/unit/amazonqGumby/transformApiHandler.test.ts new file mode 100644 index 00000000000..1441171bafc --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/transformApiHandler.test.ts @@ -0,0 +1,150 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import assert from 'assert' +import { + TransformationProgressUpdate, + TransformationStep, + findDownloadArtifactProgressUpdate, + findDownloadArtifactStep, + getArtifactsFromProgressUpdate, +} from 'aws-core-vscode/codewhisperer/node' + +describe('Amazon Q Transform - transformApiHandler tests', function () { + describe('getArtifactIdentifiers', function () { + it('will return downloaded artifact values from transformationStep', function () { + const downloadArtifactId = 'hil-test-artifact-id' + const downloadArtifactType = 'BuiltJars' + const transformationStepsFixture: TransformationProgressUpdate = { + name: 'Status step', + status: 'FAILED', + description: 'This step should be hil identifier', + startTime: new Date(), + endTime: new Date(), + downloadArtifacts: [ + { + downloadArtifactId, + downloadArtifactType, + }, + ], + } + const { artifactId, artifactType } = getArtifactsFromProgressUpdate(transformationStepsFixture) + + assert.strictEqual(artifactId, downloadArtifactId) + assert.strictEqual(artifactType, downloadArtifactType) + }) + }) + describe('findDownloadArtifactStep', function () { + it('will return downloaded artifact values from transformationStep', function () { + const downloadArtifactId = 'hil-test-artifact-id' + const downloadArtifactType = 'BuiltJars' + const transformationStepsFixture: TransformationStep[] = [ + { + id: 'fake-step-id-1', + name: 'Building Code', + description: 'Building dependencies', + status: 'COMPLETED', + progressUpdates: [ + { + name: 'Status step', + status: 'FAILED', + description: 'This step should be hil identifier', + startTime: new Date(), + endTime: new Date(), + downloadArtifacts: [ + { + downloadArtifactId, + downloadArtifactType, + }, + ], + }, + ], + startTime: new Date(), + endTime: new Date(), + }, + ] + const { transformationStep, progressUpdate } = findDownloadArtifactStep(transformationStepsFixture) + + assert.strictEqual(transformationStep, transformationStepsFixture[0]) + assert.strictEqual(progressUpdate, transformationStepsFixture[0].progressUpdates?.[0]) + }) + it('will return undefined if no downloadArtifactId found', function () { + const transformationStepsFixture: TransformationStep[] = [ + { + id: 'fake-step-id-1', + name: 'Building Code', + description: 'Building dependencies', + status: 'COMPLETED', + progressUpdates: [ + { + name: 'Status step', + status: 'FAILED', + description: 'This step should be hil identifier', + startTime: new Date(), + endTime: new Date(), + downloadArtifacts: undefined, + }, + ], + startTime: new Date(), + endTime: new Date(), + }, + ] + const { transformationStep, progressUpdate } = findDownloadArtifactStep(transformationStepsFixture) + + assert.strictEqual(transformationStep, undefined) + assert.strictEqual(progressUpdate, undefined) + }) + }) + + describe('findDownloadArtifactProgressUpdate', function () { + it('will return correct progress update from transformationStep', function () { + const transformationStepsFixture: TransformationStep[] = [ + { + id: 'dummy-id', + name: 'Step name', + description: 'Step description', + status: 'TRANSFORMING', + progressUpdates: [ + { + name: 'Progress update name', + status: 'AWAITING_CLIENT_ACTION', + description: 'Client-side build happening now', + startTime: new Date(), + endTime: new Date(), + downloadArtifacts: [ + { + downloadArtifactId: 'some-download-artifact-id', + downloadArtifactType: 'some-download-artifact-type', + }, + ], + }, + ], + startTime: new Date(), + endTime: new Date(), + }, + ] + const progressUpdate = findDownloadArtifactProgressUpdate(transformationStepsFixture) + assert.strictEqual(progressUpdate, transformationStepsFixture[0].progressUpdates?.[0]) + }) + + it('will return undefined if step status is NOT AWAITING_CLIENT_ACTION', function () { + const transformationStepsFixture: TransformationStep[] = [ + { + id: 'random-id', + name: 'not-awaiting-client-action step name', + description: 'not-awaiting-client-action step description', + status: 'TRANSFORMING', + progressUpdates: [ + { + name: 'some progress update name', + status: 'SOMETHING-BESIDES-AWAITING_CLIENT_ACTION', + }, + ], + }, + ] + const progressUpdate = findDownloadArtifactProgressUpdate(transformationStepsFixture) + assert.strictEqual(progressUpdate, undefined) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonqGumby/transformFileHandler.test.ts b/packages/amazonq/test/unit/amazonqGumby/transformFileHandler.test.ts new file mode 100644 index 00000000000..d89e97af7f0 --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/transformFileHandler.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 { parseVersionsListFromPomFile } from 'aws-core-vscode/codewhisperer/node' + +describe('Amazon Q Transform - transformFileHandler tests', function () { + describe('parseXmlDependenciesReport', function () { + it('Will return parsed values', async function () { + const testXmlReport = ` + + + + 0 + 0 + 1 + 0 + 0 + + + + org.projectlombok + lombok + compile + jar + 0.11.4 + 1.18.32 + + 0.11.6 + 0.11.8 + + + 0.12.0 + + + 1.12.2 + 1.12.4 + 1.12.6 + + incremental available + + + + ` + const { latestVersion, majorVersions, minorVersions, status } = + await parseVersionsListFromPomFile(testXmlReport) + + assert.strictEqual(latestVersion, '1.18.32') + assert.strictEqual(minorVersions[0], '0.12.0') + assert.strictEqual(majorVersions[0], '1.12.2') + assert.strictEqual(status, 'incremental available') + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonqGumby/transformationResultsHandler.test.ts b/packages/amazonq/test/unit/amazonqGumby/transformationResultsHandler.test.ts new file mode 100644 index 00000000000..143346674d9 --- /dev/null +++ b/packages/amazonq/test/unit/amazonqGumby/transformationResultsHandler.test.ts @@ -0,0 +1,66 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import assert from 'assert' +import sinon from 'sinon' +import { DiffModel, AddedChangeNode, ModifiedChangeNode } from 'aws-core-vscode/codewhisperer/node' +import path from 'path' +import { getTestResourceFilePath } from './amazonQGumbyUtil' +import { fs } from 'aws-core-vscode/shared' +import { createTestWorkspace } from 'aws-core-vscode/test' + +describe('DiffModel', function () { + afterEach(() => { + sinon.restore() + }) + + it('WHEN parsing a diff patch where a file was added THEN returns an array representing the added file', async function () { + const testDiffModel = new DiffModel() + + const workspacePath = 'workspace' + + sinon.replace(fs, 'exists', async (path) => { + const pathStr = path.toString() + if (pathStr.includes(workspacePath)) { + return false + } + + return true + }) + testDiffModel.parseDiff(getTestResourceFilePath('resources/files/addedFile.diff'), workspacePath) + + assert.strictEqual( + testDiffModel.patchFileNodes[0].patchFilePath, + getTestResourceFilePath('resources/files/addedFile.diff') + ) + const change = testDiffModel.patchFileNodes[0].children[0] + + assert.strictEqual(change instanceof AddedChangeNode, true) + }) + + it('WHEN parsing a diff patch where a file was modified THEN returns an array representing the modified file', async function () { + const testDiffModel = new DiffModel() + + const fileAmount = 1 + const workspaceFolder = await createTestWorkspace(fileAmount, { fileContent: '' }) + + await fs.writeFile( + path.join(workspaceFolder.uri.fsPath, 'README.md'), + 'This guide walks you through using Gradle to build a simple Java project.' + ) + + testDiffModel.parseDiff( + getTestResourceFilePath('resources/files/modifiedFile.diff'), + workspaceFolder.uri.fsPath + ) + + assert.strictEqual( + testDiffModel.patchFileNodes[0].patchFilePath, + getTestResourceFilePath('resources/files/modifiedFile.diff') + ) + const change = testDiffModel.patchFileNodes[0].children[0] + + assert.strictEqual(change instanceof ModifiedChangeNode, true) + }) +}) diff --git a/packages/amazonq/test/unit/app/inline/EditRendering/diffUtils.test.ts b/packages/amazonq/test/unit/app/inline/EditRendering/diffUtils.test.ts new file mode 100644 index 00000000000..dee096d7f57 --- /dev/null +++ b/packages/amazonq/test/unit/app/inline/EditRendering/diffUtils.test.ts @@ -0,0 +1,83 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as assert from 'assert' +import { applyUnifiedDiff } from '../../../../../src/app/inline/EditRendering/diffUtils' + +describe('diffUtils', function () { + describe('applyUnifiedDiff', function () { + it('should correctly apply a unified diff to original text', function () { + // Original code + const originalCode = 'function add(a, b) {\n return a + b;\n}' + + // Unified diff that adds a comment and modifies the return statement + const unifiedDiff = + '--- a/file.js\n' + + '+++ b/file.js\n' + + '@@ -1,3 +1,4 @@\n' + + ' function add(a, b) {\n' + + '+ // Add two numbers\n' + + '- return a + b;\n' + + '+ return a + b; // Return the sum\n' + + ' }' + + // Expected result after applying the diff + const expectedResult = 'function add(a, b) {\n // Add two numbers\n return a + b; // Return the sum\n}' + + // Apply the diff + const appliedCode = applyUnifiedDiff(originalCode, unifiedDiff) + + // Verify the result + assert.strictEqual(appliedCode, expectedResult) + }) + }) + + describe('applyUnifiedDiff with complex changes', function () { + it('should handle multiple hunks in a diff', function () { + // Original code with multiple functions + const originalCode = + 'function add(a, b) {\n' + + ' return a + b;\n' + + '}\n' + + '\n' + + 'function subtract(a, b) {\n' + + ' return a - b;\n' + + '}' + + // Unified diff that modifies both functions + const unifiedDiff = + '--- a/file.js\n' + + '+++ b/file.js\n' + + '@@ -1,3 +1,4 @@\n' + + ' function add(a, b) {\n' + + '+ // Addition function\n' + + ' return a + b;\n' + + ' }\n' + + '@@ -5,3 +6,4 @@\n' + + ' function subtract(a, b) {\n' + + '+ // Subtraction function\n' + + ' return a - b;\n' + + ' }' + + // Expected result after applying the diff + const expectedResult = + 'function add(a, b) {\n' + + ' // Addition function\n' + + ' return a + b;\n' + + '}\n' + + '\n' + + 'function subtract(a, b) {\n' + + ' // Subtraction function\n' + + ' return a - b;\n' + + '}' + + // Apply the diff + const appliedCode = applyUnifiedDiff(originalCode, unifiedDiff) + + // Verify the result + assert.strictEqual(appliedCode, expectedResult) + }) + }) +}) diff --git a/packages/amazonq/test/unit/app/inline/EditRendering/displayImage.test.ts b/packages/amazonq/test/unit/app/inline/EditRendering/displayImage.test.ts new file mode 100644 index 00000000000..28155811f50 --- /dev/null +++ b/packages/amazonq/test/unit/app/inline/EditRendering/displayImage.test.ts @@ -0,0 +1,417 @@ +/*! + * 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 { EditDecorationManager, displaySvgDecoration } from '../../../../../src/app/inline/EditRendering/displayImage' +import { EditSuggestionState } from '../../../../../src/app/inline/editSuggestionState' + +// Shared helper function to create common stubs +function createCommonStubs(sandbox: sinon.SinonSandbox) { + const documentStub = { + getText: sandbox.stub().returns('Original code content'), + uri: vscode.Uri.file('/test/file.ts'), + lineAt: sandbox.stub().returns({ + text: 'Line text content', + range: new vscode.Range(0, 0, 0, 18), + rangeIncludingLineBreak: new vscode.Range(0, 0, 0, 19), + firstNonWhitespaceCharacterIndex: 0, + isEmptyOrWhitespace: false, + }), + } as unknown as sinon.SinonStubbedInstance + + const editorStub = { + document: documentStub, + setDecorations: sandbox.stub(), + } as unknown as sinon.SinonStubbedInstance + + return { documentStub, editorStub } +} + +describe('EditDecorationManager', function () { + let sandbox: sinon.SinonSandbox + let editorStub: sinon.SinonStubbedInstance + let documentStub: sinon.SinonStubbedInstance + let windowStub: sinon.SinonStubbedInstance + let commandsStub: sinon.SinonStubbedInstance + let decorationTypeStub: sinon.SinonStubbedInstance + let manager: EditDecorationManager + + beforeEach(function () { + sandbox = sinon.createSandbox() + + // Create stubs for vscode objects + decorationTypeStub = { + dispose: sandbox.stub(), + } as unknown as sinon.SinonStubbedInstance + + const commonStubs = createCommonStubs(sandbox) + documentStub = commonStubs.documentStub + editorStub = commonStubs.editorStub + + // Add additional properties needed for this test suite - extend the stub objects + Object.assign(documentStub, { lineCount: 5 }) + Object.assign(editorStub, { edit: sandbox.stub().resolves(true) }) + + windowStub = sandbox.stub(vscode.window) + windowStub.createTextEditorDecorationType.returns(decorationTypeStub as any) + + commandsStub = sandbox.stub(vscode.commands) + commandsStub.registerCommand.returns({ dispose: sandbox.stub() }) + + // Create a new instance of EditDecorationManager for each test + manager = new EditDecorationManager() + }) + + afterEach(function () { + sandbox.restore() + }) + + it('should display SVG decorations in the editor', async function () { + // Create a fake SVG image URI + const svgUri = vscode.Uri.parse('file:///path/to/image.svg') + + // Create accept and reject handlers + const acceptHandler = sandbox.stub() + const rejectHandler = sandbox.stub() + + // Reset the setDecorations stub to clear any previous calls + editorStub.setDecorations.reset() + + // Call displayEditSuggestion + await manager.displayEditSuggestion( + editorStub as unknown as vscode.TextEditor, + svgUri, + 0, + acceptHandler, + rejectHandler, + 'Original code', + 'New code', + [{ line: 0, start: 0, end: 0 }] + ) + + // Verify decorations were set (we expect 4 calls because clearDecorations is called first) + assert.strictEqual(editorStub.setDecorations.callCount, 4) + + // Verify the third call is for the image decoration (after clearDecorations) + const imageCall = editorStub.setDecorations.getCall(2) + assert.strictEqual(imageCall.args[0], manager['imageDecorationType']) + assert.strictEqual(imageCall.args[1].length, 1) + + // Verify the fourth call is for the removed code decoration + const removedCodeCall = editorStub.setDecorations.getCall(3) + assert.strictEqual(removedCodeCall.args[0], manager['removedCodeDecorationType']) + }) + + // Helper function to setup edit suggestion test + async function setupEditSuggestionTest() { + // Create a fake SVG image URI + const svgUri = vscode.Uri.parse('file:///path/to/image.svg') + + // Create accept and reject handlers + const acceptHandler = sandbox.stub() + const rejectHandler = sandbox.stub() + + // Display the edit suggestion + await manager.displayEditSuggestion( + editorStub as unknown as vscode.TextEditor, + svgUri, + 0, + acceptHandler, + rejectHandler, + 'Original code', + 'New code', + [{ line: 0, start: 0, end: 0 }] + ) + + return { acceptHandler, rejectHandler } + } + + it('should trigger accept handler when command is executed', async function () { + const { acceptHandler, rejectHandler } = await setupEditSuggestionTest() + + // Find the command handler that was registered for accept + const acceptCommandArgs = commandsStub.registerCommand.args.find( + (args) => args[0] === 'aws.amazonq.inline.acceptEdit' + ) + + // Execute the accept command handler if found + if (acceptCommandArgs && acceptCommandArgs[1]) { + const acceptCommandHandler = acceptCommandArgs[1] + acceptCommandHandler() + + // Verify the accept handler was called + sinon.assert.calledOnce(acceptHandler) + sinon.assert.notCalled(rejectHandler) + } else { + assert.fail('Accept command handler not found') + } + }) + + it('should trigger reject handler when command is executed', async function () { + const { acceptHandler, rejectHandler } = await setupEditSuggestionTest() + + // Find the command handler that was registered for reject + const rejectCommandArgs = commandsStub.registerCommand.args.find( + (args) => args[0] === 'aws.amazonq.inline.rejectEdit' + ) + + // Execute the reject command handler if found + if (rejectCommandArgs && rejectCommandArgs[1]) { + const rejectCommandHandler = rejectCommandArgs[1] + rejectCommandHandler() + + // Verify the reject handler was called + sinon.assert.calledOnce(rejectHandler) + sinon.assert.notCalled(acceptHandler) + } else { + assert.fail('Reject command handler not found') + } + }) + + it('should clear decorations when requested', async function () { + // Reset the setDecorations stub to clear any previous calls + editorStub.setDecorations.reset() + + // Call clearDecorations + await manager.clearDecorations(editorStub as unknown as vscode.TextEditor) + + // Verify decorations were cleared + assert.strictEqual(editorStub.setDecorations.callCount, 2) + + // Verify both decoration types were cleared + sinon.assert.calledWith(editorStub.setDecorations.firstCall, manager['imageDecorationType'], []) + sinon.assert.calledWith(editorStub.setDecorations.secondCall, manager['removedCodeDecorationType'], []) + }) +}) + +describe('displaySvgDecoration cursor distance auto-discard', function () { + let sandbox: sinon.SinonSandbox + let editorStub: sinon.SinonStubbedInstance + let languageClientStub: any + let sessionStub: any + let itemStub: any + + beforeEach(function () { + sandbox = sinon.createSandbox() + const commonStubs = createCommonStubs(sandbox) + editorStub = commonStubs.editorStub + + languageClientStub = { + sendNotification: sandbox.stub(), + } + + sessionStub = { + sessionId: 'test-session', + requestStartTime: Date.now(), + firstCompletionDisplayLatency: 100, + } + + itemStub = { + itemId: 'test-item', + insertText: 'test content', + } + }) + + afterEach(function () { + sandbox.restore() + }) + + it('should send discard telemetry and return early when edit is 10+ lines away from cursor', async function () { + // Set cursor at line 5 + editorStub.selection = { + active: new vscode.Position(5, 0), + } as any + // Try to display edit at line 20 (15 lines away) + await displaySvgDecoration( + editorStub as unknown as vscode.TextEditor, + vscode.Uri.parse('data:image/svg+xml;base64,test'), + 20, + 'new code', + [], + sessionStub, + languageClientStub, + itemStub + ) + + // Verify discard telemetry was sent + sinon.assert.calledOnce(languageClientStub.sendNotification) + const call = languageClientStub.sendNotification.getCall(0) + assert.strictEqual(call.args[0], 'aws/logInlineCompletionSessionResults') + assert.strictEqual(call.args[1].sessionId, 'test-session') + assert.strictEqual(call.args[1].completionSessionResult['test-item'].discarded, true) + }) + + it('should proceed normally when edit is within 10 lines of cursor', async function () { + // Set cursor at line 5 + editorStub.selection = { + active: new vscode.Position(5, 0), + } as any + // Mock required dependencies for normal flow + sandbox.stub(vscode.workspace, 'onDidChangeTextDocument').returns({ dispose: sandbox.stub() }) + sandbox.stub(vscode.window, 'onDidChangeTextEditorSelection').returns({ dispose: sandbox.stub() }) + + // Try to display edit at line 10 (5 lines away) + await displaySvgDecoration( + editorStub as unknown as vscode.TextEditor, + vscode.Uri.parse('data:image/svg+xml;base64,test'), + 10, + 'new code', + [], + sessionStub, + languageClientStub, + itemStub + ) + + // Verify no discard telemetry was sent (function should proceed normally) + sinon.assert.notCalled(languageClientStub.sendNotification) + }) +}) + +describe('displaySvgDecoration cursor distance auto-reject', function () { + let sandbox: sinon.SinonSandbox + let editorStub: sinon.SinonStubbedInstance + let windowStub: sinon.SinonStub + let commandsStub: sinon.SinonStub + let editSuggestionStateStub: sinon.SinonStub + let onDidChangeTextEditorSelectionStub: sinon.SinonStub + let selectionChangeListener: (e: vscode.TextEditorSelectionChangeEvent) => void + + // Helper function to setup displaySvgDecoration + async function setupDisplaySvgDecoration(startLine: number) { + return await displaySvgDecoration( + editorStub as unknown as vscode.TextEditor, + vscode.Uri.parse('data:image/svg+xml;base64,test'), + startLine, + 'new code', + [], + {} as any, + {} as any, + { itemId: 'test', insertText: 'patch' } as any + ) + } + + // Helper function to create selection change event + function createSelectionChangeEvent(line: number): vscode.TextEditorSelectionChangeEvent { + const position = new vscode.Position(line, 0) + const selection = new vscode.Selection(position, position) + return { + textEditor: editorStub, + selections: [selection], + kind: vscode.TextEditorSelectionChangeKind.Mouse, + } as vscode.TextEditorSelectionChangeEvent + } + + beforeEach(function () { + sandbox = sinon.createSandbox() + + const commonStubs = createCommonStubs(sandbox) + editorStub = commonStubs.editorStub + + // Mock vscode.window.onDidChangeTextEditorSelection + onDidChangeTextEditorSelectionStub = sandbox.stub() + onDidChangeTextEditorSelectionStub.returns({ dispose: sandbox.stub() }) + windowStub = sandbox.stub(vscode.window, 'onDidChangeTextEditorSelection') + windowStub.callsFake((callback) => { + selectionChangeListener = callback + return { dispose: sandbox.stub() } + }) + + // Mock vscode.commands.executeCommand + commandsStub = sandbox.stub(vscode.commands, 'executeCommand') + + // Mock EditSuggestionState + editSuggestionStateStub = sandbox.stub(EditSuggestionState, 'isEditSuggestionActive') + editSuggestionStateStub.returns(true) + + // Mock other required dependencies + sandbox.stub(vscode.workspace, 'onDidChangeTextDocument').returns({ dispose: sandbox.stub() }) + }) + + afterEach(function () { + sandbox.restore() + }) + + it('should not reject when cursor moves less than 25 lines away', async function () { + // Set cursor at line 50 + editorStub.selection = { + active: new vscode.Position(50, 0), + } as any + const startLine = 50 + await setupDisplaySvgDecoration(startLine) + + selectionChangeListener(createSelectionChangeEvent(startLine + 24)) + + sinon.assert.notCalled(commandsStub) + }) + + it('should not reject when cursor moves exactly 25 lines away', async function () { + // Set cursor at line 50 + editorStub.selection = { + active: new vscode.Position(50, 0), + } as any + const startLine = 50 + await setupDisplaySvgDecoration(startLine) + + selectionChangeListener(createSelectionChangeEvent(startLine + 25)) + + sinon.assert.notCalled(commandsStub) + }) + + it('should reject when cursor moves more than 25 lines away', async function () { + // Set cursor at line 50 + editorStub.selection = { + active: new vscode.Position(50, 0), + } as any + const startLine = 50 + await setupDisplaySvgDecoration(startLine) + + selectionChangeListener(createSelectionChangeEvent(startLine + 26)) + + sinon.assert.calledOnceWithExactly(commandsStub, 'aws.amazonq.inline.rejectEdit') + }) + + it('should reject when cursor moves more than 25 lines before the edit', async function () { + // Set cursor at line 50 + editorStub.selection = { + active: new vscode.Position(50, 0), + } as any + const startLine = 50 + await setupDisplaySvgDecoration(startLine) + + selectionChangeListener(createSelectionChangeEvent(startLine - 26)) + + sinon.assert.calledOnceWithExactly(commandsStub, 'aws.amazonq.inline.rejectEdit') + }) + + it('should not reject when edit is near beginning of file and cursor cannot move far enough', async function () { + // Set cursor at line 10 + editorStub.selection = { + active: new vscode.Position(10, 0), + } as any + const startLine = 10 + await setupDisplaySvgDecoration(startLine) + + selectionChangeListener(createSelectionChangeEvent(0)) + + sinon.assert.notCalled(commandsStub) + }) + + it('should not reject when edit suggestion is not active', async function () { + // Set cursor at line 50 + editorStub.selection = { + active: new vscode.Position(50, 0), + } as any + editSuggestionStateStub.returns(false) + + const startLine = 50 + await setupDisplaySvgDecoration(startLine) + + selectionChangeListener(createSelectionChangeEvent(startLine + 100)) + + sinon.assert.notCalled(commandsStub) + }) +}) diff --git a/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts new file mode 100644 index 00000000000..e1c32778d83 --- /dev/null +++ b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts @@ -0,0 +1,269 @@ +/*! + * 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' +// Remove static import - we'll use dynamic import instead +// import { showEdits } from '../../../../../src/app/inline/EditRendering/imageRenderer' +import { SvgGenerationService } from '../../../../../src/app/inline/EditRendering/svgGenerator' +import { InlineCompletionItemWithReferences } from '@aws/language-server-runtimes/protocol' + +describe('showEdits', function () { + let sandbox: sinon.SinonSandbox + let editorStub: sinon.SinonStubbedInstance + let documentStub: sinon.SinonStubbedInstance + let svgGenerationServiceStub: sinon.SinonStubbedInstance + let displaySvgDecorationStub: sinon.SinonStub + let loggerStub: sinon.SinonStubbedInstance + let getLoggerStub: sinon.SinonStub + let showEdits: any // Will be dynamically imported + let languageClientStub: any + let sessionStub: any + let itemStub: InlineCompletionItemWithReferences + + // Helper function to create mock SVG result + function createMockSvgResult(overrides: Partial = {}) { + return { + svgImage: vscode.Uri.file('/path/to/generated.svg'), + startLine: 5, + newCode: 'console.log("Hello World");', + originalCodeHighlightRange: [{ line: 5, start: 0, end: 10 }], + ...overrides, + } + } + + beforeEach(function () { + sandbox = sinon.createSandbox() + + // Create logger stub + loggerStub = { + error: sandbox.stub(), + info: sandbox.stub(), + debug: sandbox.stub(), + warn: sandbox.stub(), + } + + // Clear all relevant module caches + const moduleId = require.resolve('../../../../../src/app/inline/EditRendering/imageRenderer') + const sharedModuleId = require.resolve('aws-core-vscode/shared') + delete require.cache[moduleId] + delete require.cache[sharedModuleId] + + // jscpd:ignore-start + // Create getLogger stub and store reference for test verification + getLoggerStub = sandbox.stub().returns(loggerStub) + + // Create a mock shared module with stubbed getLogger + const mockSharedModule = { + getLogger: getLoggerStub, + } + + // Override the require cache with our mock + require.cache[sharedModuleId] = { + id: sharedModuleId, + filename: sharedModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockSharedModule, + paths: [], + } as any + + // Now require the module - it should use our mocked getLogger + // jscpd:ignore-end + const imageRendererModule = require('../../../../../src/app/inline/EditRendering/imageRenderer') + showEdits = imageRendererModule.showEdits + + // Create document stub + documentStub = { + uri: { + fsPath: '/path/to/test/file.ts', + }, + getText: sandbox.stub().returns('Original code content'), + lineCount: 5, + } as unknown as sinon.SinonStubbedInstance + + // Create editor stub + editorStub = { + document: documentStub, + setDecorations: sandbox.stub(), + edit: sandbox.stub().resolves(true), + } as unknown as sinon.SinonStubbedInstance + + // Create SVG generation service stub + svgGenerationServiceStub = { + generateDiffSvg: sandbox.stub(), + } as unknown as sinon.SinonStubbedInstance + + // Stub the SvgGenerationService constructor + sandbox + .stub(SvgGenerationService.prototype, 'generateDiffSvg') + .callsFake(svgGenerationServiceStub.generateDiffSvg) + + // Create display SVG decoration stub + displaySvgDecorationStub = sandbox.stub() + sandbox.replace( + require('../../../../../src/app/inline/EditRendering/displayImage'), + 'displaySvgDecoration', + displaySvgDecorationStub + ) + + // Create language client stub + languageClientStub = {} as any + + // Create session stub + sessionStub = { + sessionId: 'test-session-id', + suggestions: [], + isRequestInProgress: false, + requestStartTime: Date.now(), + startPosition: new vscode.Position(0, 0), + } as any + + // Create item stub + itemStub = { + insertText: 'console.log("Hello World");', + range: new vscode.Range(0, 0, 0, 0), + itemId: 'test-item-id', + } as any + }) + + afterEach(function () { + sandbox.restore() + }) + + it('should return early when editor is undefined', async function () { + await showEdits(itemStub, undefined, sessionStub, languageClientStub) + + // Verify that no SVG generation or display methods were called + sinon.assert.notCalled(svgGenerationServiceStub.generateDiffSvg) + sinon.assert.notCalled(displaySvgDecorationStub) + sinon.assert.notCalled(loggerStub.error) + }) + + it('should successfully generate and display SVG when all parameters are valid', async function () { + // Setup successful SVG generation + const mockSvgResult = createMockSvgResult() + svgGenerationServiceStub.generateDiffSvg.resolves(mockSvgResult) + + await showEdits(itemStub, editorStub as unknown as vscode.TextEditor, sessionStub, languageClientStub) + + // Verify SVG generation was called with correct parameters + sinon.assert.calledOnce(svgGenerationServiceStub.generateDiffSvg) + sinon.assert.calledWith( + svgGenerationServiceStub.generateDiffSvg, + '/path/to/test/file.ts', + 'console.log("Hello World");' + ) + + // Verify display decoration was called with correct parameters + sinon.assert.calledOnce(displaySvgDecorationStub) + sinon.assert.calledWith( + displaySvgDecorationStub, + editorStub, + mockSvgResult.svgImage, + mockSvgResult.startLine, + mockSvgResult.newCode, + mockSvgResult.originalCodeHighlightRange, + sessionStub, + languageClientStub, + itemStub + ) + + // Verify no errors were logged + sinon.assert.notCalled(loggerStub.error) + }) + + it('should log error when SVG generation returns empty result', async function () { + // Setup SVG generation to return undefined svgImage + const mockSvgResult = createMockSvgResult({ svgImage: undefined as any }) + svgGenerationServiceStub.generateDiffSvg.resolves(mockSvgResult) + + await showEdits(itemStub, editorStub as unknown as vscode.TextEditor, sessionStub, languageClientStub) + + // Verify SVG generation was called + sinon.assert.calledOnce(svgGenerationServiceStub.generateDiffSvg) + + // Verify display decoration was NOT called + sinon.assert.notCalled(displaySvgDecorationStub) + + // Verify error was logged + sinon.assert.calledOnce(loggerStub.error) + sinon.assert.calledWith(loggerStub.error, 'SVG image generation returned an empty result.') + }) + + it('should catch and log error when SVG generation throws exception', async function () { + // Setup SVG generation to throw an error + const testError = new Error('SVG generation failed') + svgGenerationServiceStub.generateDiffSvg.rejects(testError) + + await showEdits(itemStub, editorStub as unknown as vscode.TextEditor, sessionStub, languageClientStub) + + // Verify SVG generation was called + sinon.assert.calledOnce(svgGenerationServiceStub.generateDiffSvg) + + // Verify display decoration was NOT called + sinon.assert.notCalled(displaySvgDecorationStub) + + // Verify error was logged with correct message + sinon.assert.calledOnce(loggerStub.error) + const errorCall = loggerStub.error.getCall(0) + assert.strictEqual(errorCall.args[0], `Error generating SVG image: ${testError}`) + }) + + it('should catch and log error when displaySvgDecoration throws exception', async function () { + // Setup successful SVG generation + const mockSvgResult = createMockSvgResult() + svgGenerationServiceStub.generateDiffSvg.resolves(mockSvgResult) + + // Setup displaySvgDecoration to throw an error + const testError = new Error('Display decoration failed') + displaySvgDecorationStub.rejects(testError) + + await showEdits(itemStub, editorStub as unknown as vscode.TextEditor, sessionStub, languageClientStub) + + // Verify SVG generation was called + sinon.assert.calledOnce(svgGenerationServiceStub.generateDiffSvg) + + // Verify display decoration was called + sinon.assert.calledOnce(displaySvgDecorationStub) + + // Verify error was logged with correct message + sinon.assert.calledOnce(loggerStub.error) + const errorCall = loggerStub.error.getCall(0) + assert.strictEqual(errorCall.args[0], `Error generating SVG image: ${testError}`) + }) + + it('should use correct logger name', async function () { + await showEdits(itemStub, editorStub as unknown as vscode.TextEditor, sessionStub, languageClientStub) + + // Verify getLogger was called with correct name + sinon.assert.calledWith(getLoggerStub, 'nextEditPrediction') + }) + + it('should handle item with undefined insertText', async function () { + // Create item with undefined insertText + const itemWithUndefinedText = { + ...itemStub, + insertText: undefined, + } as any + + // Setup successful SVG generation + const mockSvgResult = createMockSvgResult() + svgGenerationServiceStub.generateDiffSvg.resolves(mockSvgResult) + + await showEdits( + itemWithUndefinedText, + editorStub as unknown as vscode.TextEditor, + sessionStub, + languageClientStub + ) + + // Verify SVG generation was called with undefined as string + sinon.assert.calledOnce(svgGenerationServiceStub.generateDiffSvg) + sinon.assert.calledWith(svgGenerationServiceStub.generateDiffSvg, '/path/to/test/file.ts', undefined) + }) +}) diff --git a/packages/amazonq/test/unit/app/inline/EditRendering/svgGenerator.test.ts b/packages/amazonq/test/unit/app/inline/EditRendering/svgGenerator.test.ts new file mode 100644 index 00000000000..657ff5c2915 --- /dev/null +++ b/packages/amazonq/test/unit/app/inline/EditRendering/svgGenerator.test.ts @@ -0,0 +1,252 @@ +/*! + * 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 { SvgGenerationService } from '../../../../../src/app/inline/EditRendering/svgGenerator' + +describe('SvgGenerationService', function () { + let sandbox: sinon.SinonSandbox + let service: SvgGenerationService + let documentStub: sinon.SinonStubbedInstance + let workspaceStub: sinon.SinonStubbedInstance + let editorConfigStub: any + + beforeEach(function () { + sandbox = sinon.createSandbox() + + // Create stubs for vscode objects and utilities + documentStub = { + getText: sandbox.stub().returns('function example() {\n return 42;\n}'), + lineCount: 3, + lineAt: sandbox.stub().returns({ + text: 'Line content', + range: new vscode.Range(0, 0, 0, 12), + }), + } as unknown as sinon.SinonStubbedInstance + + workspaceStub = sandbox.stub(vscode.workspace) + workspaceStub.openTextDocument.resolves(documentStub as unknown as vscode.TextDocument) + workspaceStub.getConfiguration = sandbox.stub() + + editorConfigStub = { + get: sandbox.stub(), + } + editorConfigStub.get.withArgs('fontSize').returns(14) + editorConfigStub.get.withArgs('lineHeight').returns(0) + + // Create the service instance + service = new SvgGenerationService() + }) + + afterEach(function () { + sandbox.restore() + }) + + describe('generateDiffSvg', function () { + it('should handle empty original code', async function () { + // Create a new document stub for this test with empty content + const emptyDocStub = { + getText: sandbox.stub().returns(''), + lineCount: 0, + lineAt: sandbox.stub().returns({ + text: '', + range: new vscode.Range(0, 0, 0, 0), + }), + } as unknown as vscode.TextDocument + + // Make openTextDocument return our empty document + workspaceStub.openTextDocument.resolves(emptyDocStub as unknown as vscode.TextDocument) + + // A simple unified diff + const udiff = '--- a/example.js\n+++ b/example.js\n@@ -0,0 +1,1 @@\n+function example() {}\n' + + // Expect an error to be thrown + try { + await service.generateDiffSvg('example.js', udiff) + assert.fail('Expected an error to be thrown') + } catch (error) { + assert.ok(error) + assert.strictEqual((error as Error).message, 'udiff format error') + } + }) + }) + + describe('theme handling', function () { + it('should generate correct styles for dark theme', function () { + // Configure for dark theme + workspaceStub.getConfiguration.withArgs('editor').returns(editorConfigStub) + workspaceStub.getConfiguration.withArgs('workbench').returns({ + get: sandbox.stub().withArgs('colorTheme', 'Default').returns('Dark+ (default dark)'), + } as any) + + const getEditorTheme = (service as any).getEditorTheme.bind(service) + const theme = getEditorTheme() + + assert.strictEqual(theme.fontSize, 14) + assert.strictEqual(theme.lingHeight, 21) // 1.5 * 14 + assert.strictEqual(theme.foreground, 'rgba(212, 212, 212, 1)') + assert.strictEqual(theme.background, 'rgba(30, 30, 30, 1)') + }) + + it('should generate correct styles for light theme', function () { + // Reconfigure for light theme + editorConfigStub.get.withArgs('fontSize', 12).returns(12) + + workspaceStub.getConfiguration.withArgs('editor').returns(editorConfigStub) + workspaceStub.getConfiguration.withArgs('workbench').returns({ + get: sandbox.stub().withArgs('colorTheme', 'Default').returns('Light+ (default light)'), + } as any) + + const getEditorTheme = (service as any).getEditorTheme.bind(service) + const theme = getEditorTheme() + + assert.strictEqual(theme.fontSize, 12) + assert.strictEqual(theme.lingHeight, 18) // 1.5 * 12 + assert.strictEqual(theme.foreground, 'rgba(0, 0, 0, 1)') + assert.strictEqual(theme.background, 'rgba(255, 255, 255, 1)') + }) + + it('should handle custom line height settings', function () { + // Reconfigure for custom line height + editorConfigStub.get.withArgs('fontSize').returns(16) + editorConfigStub.get.withArgs('lineHeight').returns(2.5) + + workspaceStub.getConfiguration.withArgs('editor').returns(editorConfigStub) + workspaceStub.getConfiguration.withArgs('workbench').returns({ + get: sandbox.stub().withArgs('colorTheme', 'Default').returns('Dark+ (default dark)'), + } as any) + + const getEditorTheme = (service as any).getEditorTheme.bind(service) + const theme = getEditorTheme() + + assert.strictEqual(theme.fontSize, 16) + assert.strictEqual(theme.lingHeight, 40) // 2.5 * 16 + }) + + it('should generate CSS styles correctly', function () { + const theme = { + fontSize: 14, + lingHeight: 21, + foreground: 'rgba(212, 212, 212, 1)', + background: 'rgba(30, 30, 30, 1)', + diffAdded: 'rgba(231, 245, 231, 0.2)', + diffRemoved: 'rgba(255, 0, 0, 0.2)', + } + + const generateStyles = (service as any).generateStyles.bind(service) + const styles = generateStyles(theme) + + assert.ok(styles.includes('font-size: 14px')) + assert.ok(styles.includes('line-height: 21px')) + assert.ok(styles.includes('color: rgba(212, 212, 212, 1)')) + assert.ok(styles.includes('background-color: rgba(30, 30, 30, 1)')) + assert.ok(styles.includes('.diff-changed')) + assert.ok(styles.includes('.diff-removed')) + }) + }) + + describe('highlight ranges', function () { + it('should generate highlight ranges for word-level changes', function () { + const originalCode = ['function test() {', ' return 42;', '}'] + const afterCode = ['function test() {', ' return 100;', '}'] + const modifiedLines = new Map([[' return 42;', ' return 100;']]) + + const generateHighlightRanges = (service as any).generateHighlightRanges.bind(service) + const result = generateHighlightRanges(originalCode, afterCode, modifiedLines) + + // Should have ranges for the changed characters + assert.ok(result.removedRanges.length > 0) + assert.ok(result.addedRanges.length > 0) + + // Check that ranges are properly formatted + const removedRange = result.removedRanges[0] + assert.ok(removedRange.line >= 0) + assert.ok(removedRange.start >= 0) + assert.ok(removedRange.end > removedRange.start) + + const addedRange = result.addedRanges[0] + assert.ok(addedRange.line >= 0) + assert.ok(addedRange.start >= 0) + assert.ok(addedRange.end > addedRange.start) + }) + + it('should handle HTML escaping in highlight edits', function () { + const newLines = ['function test() {', ' return "";', '}'] + const highlightRanges = [{ line: 1, start: 10, end: 35 }] + + const getHighlightEdit = (service as any).getHighlightEdit.bind(service) + const result = getHighlightEdit(newLines, highlightRanges) + + assert.ok(result[1].includes('<script>')) + assert.ok(result[1].includes('</script>')) + assert.ok(result[1].includes('diff-changed')) + }) + }) + + describe('dimensions and positioning', function () { + it('should calculate dimensions correctly', function () { + const newLines = ['function test() {', ' return 42;', '}'] + const theme = { + fontSize: 14, + lingHeight: 21, + foreground: 'rgba(212, 212, 212, 1)', + background: 'rgba(30, 30, 30, 1)', + } + + const calculateDimensions = (service as any).calculateDimensions.bind(service) + const result = calculateDimensions(newLines, theme) + + assert.strictEqual(result.width, 287) + assert.strictEqual(result.height, 109) + assert.ok(result.height >= (newLines.length + 1) * theme.lingHeight) + }) + + it('should calculate position offset correctly', function () { + const originalLines = ['function test() {', ' return 42;', '}'] + const newLines = ['function test() {', ' return 100;', '}'] + const diffLines = [' return 100;'] + const theme = { + fontSize: 14, + lingHeight: 21, + foreground: 'rgba(212, 212, 212, 1)', + background: 'rgba(30, 30, 30, 1)', + } + + const calculatePosition = (service as any).calculatePosition.bind(service) + const result = calculatePosition(originalLines, newLines, diffLines, theme) + + assert.strictEqual(result.offset, 10) + assert.strictEqual(result.editStartLine, 1) + }) + }) + + describe('HTML content generation', function () { + it('should generate HTML content with proper structure', function () { + const diffLines = ['function test() {', ' return 42;', '}'] + const styles = '.code-container { color: white; }' + const offset = 20 + + const generateHtmlContent = (service as any).generateHtmlContent.bind(service) + const result = generateHtmlContent(diffLines, styles, offset) + + assert.ok(result.includes('
')) + assert.ok(result.includes(' + + +
+
+
${title}
+
${message}
+
+ +` + + 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 new file mode 100644 index 00000000000..0c9ce74ad30 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts @@ -0,0 +1,59 @@ +/*! + * 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, isSmusConnection } 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) + 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) + return + } + + const { region } = parseArn(connectionIdentifier) + // Detect if this is a SMUS connection for specialized error handling + const isSmus = await isSmusConnection(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) { + const error = err as SageMakerServiceException + console.error(`Failed to start SageMaker session for ${connectionIdentifier}:`, err) + const errorTitle = getVSCodeErrorTitle(error) + const errorText = getVSCodeErrorText(error, isSmus) + 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/detached-server/routes/getSessionAsync.ts b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts new file mode 100644 index 00000000000..f8dad504067 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts @@ -0,0 +1,70 @@ +/*! + * 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 { open, parseArn, readServerInfo } 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 { spaceName } = parseArn(connectionIdentifier) + + const url = `${refreshUrl}/${encodeURIComponent(spaceName)}?remote_access_token_refresh=true&reconnect_identifier=${encodeURIComponent( + connectionIdentifier + )}&reconnect_request_id=${encodeURIComponent(requestId)}&reconnect_callback_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..04098f68c89 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/sessionStore.ts @@ -0,0 +1,136 @@ +/*! + * 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') { + delete requests[requestId] + await writeMapping(mapping) + 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..d4c963c40ff --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/utils.ts @@ -0,0 +1,176 @@ +/*! + * 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' +import { ConfiguredRetryStrategy } from '@smithy/util-retry' +export { open } + +export const mappingFilePath = join(os.homedir(), '.aws', '.sagemaker-space-profiles') +const tempFilePath = `${mappingFilePath}.tmp` + +// Simple file lock to prevent concurrent writes +let isWriting = false +const writeQueue: Array<() => Promise> = [] + +// Currently SSM registration happens asynchronously with App launch, which can lead to +// StartSession Internal Failure when connecting to a fresly-started Space. +// To mitigate, spread out retries over multiple seconds instead of sending all retries within a second. +// Backoff sequence: 1500ms, 2250ms, 3375ms +// Retry timing: 1500ms, 3750ms, 7125ms +const startSessionRetryStrategy = new ConfiguredRetryStrategy(3, (attempt: number) => 1000 * 1.5 ** attempt) + +/** + * 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, account ID, and space name. + * 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, accountId, and spaceName + * @throws If the ARN format is invalid + */ +export function parseArn(arn: string): { region: string; accountId: string; spaceName: 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}"`) + } + + // Extract space name from the end of the ARN (after the last forward slash) + const spaceName = cleanedArn.split('/').pop() + if (!spaceName) { + throw new Error(`Could not extract space name from ARN: "${arn}"`) + } + + return { + region: match.groups.region, + accountId: match.groups.account_id, + spaceName: spaceName, + } +} + +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, retryStrategy: startSessionRetryStrategy }) + 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}`) + return JSON.parse(content) + } catch (err) { + throw new Error(`Failed to read mapping file: ${err instanceof Error ? err.message : String(err)}`) + } +} + +/** + * Processes the write queue to ensure only one write operation happens at a time. + */ +async function processWriteQueue() { + if (isWriting || writeQueue.length === 0) { + return + } + + isWriting = true + try { + while (writeQueue.length > 0) { + const writeOperation = writeQueue.shift()! + await writeOperation() + } + } finally { + isWriting = false + } +} + +/** + * Detects if the connection identifier is using SMUS credentials + * @param connectionIdentifier - The connection identifier to check + * @returns Promise - true if SMUS, false otherwise + */ +export async function isSmusConnection(connectionIdentifier: string): Promise { + try { + const mapping = await readMapping() + const profile = mapping.localCredential?.[connectionIdentifier] + + // Check if profile exists and has smusProjectId + return profile && 'smusProjectId' in profile + } catch (err) { + // If we can't read the mapping, assume not SMUS to avoid breaking existing functionality + return false + } +} + +/** + * Writes the mapping to a temp file and atomically renames it to the target path. + * Uses a queue to prevent race conditions when multiple requests try to write simultaneously. + */ +export async function writeMapping(mapping: SpaceMappings) { + return new Promise((resolve, reject) => { + const writeOperation = async () => { + try { + // Generate unique temp file name to avoid conflicts + const uniqueTempPath = `${tempFilePath}.${process.pid}.${Date.now()}` + + const json = JSON.stringify(mapping, undefined, 2) + await fs.writeFile(uniqueTempPath, json) + await fs.rename(uniqueTempPath, mappingFilePath) + resolve() + } catch (err) { + reject(new Error(`Failed to write mapping file: ${err instanceof Error ? err.message : String(err)}`)) + } + } + + writeQueue.push(writeOperation) + + // ProcessWriteQueue handles its own errors via individual operation callbacks + // eslint-disable-next-line @typescript-eslint/no-floating-promises + processWriteQueue() + }) +} 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 new file mode 100644 index 00000000000..104eb7662a2 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts @@ -0,0 +1,214 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { GetCallerIdentityCommandOutput } from '@aws-sdk/client-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 { 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 { getDomainSpaceKey, getDomainUserProfileKey, getSpaceAppsForUserProfile } from '../utils' +import { PollingSet } from '../../../shared/utilities/pollingSet' +import { getRemoteAppMetadata } from '../remoteUtils' + +export const parentContextValue = 'awsSagemakerParentNode' + +export type SelectedDomainUsers = [string, string[]][] +export type SelectedDomainUsersByRegion = [string, SelectedDomainUsers][] + +export interface UserProfileMetadata { + domain: DescribeDomainResponse +} +export class SagemakerParentNode extends AWSTreeNodeBase { + protected sagemakerSpaceNodes: Map + protected stsClient: DefaultStsClient + public override readonly contextValue: string = parentContextValue + domainUserProfiles: Map = new Map() + spaceApps: Map = new Map() + callerIdentity: Partial = {} + public readonly pollingSet: PollingSet = new PollingSet(5000, this.updatePendingNodes.bind(this)) + + public constructor( + public override readonly regionCode: string, + protected readonly sagemakerClient: SagemakerClient + ) { + super('SageMaker AI', vscode.TreeItemCollapsibleState.Collapsed) + this.sagemakerSpaceNodes = new Map() + this.stsClient = new DefaultStsClient(regionCode) + } + + public override async getChildren(): Promise { + const result = await makeChildrenNodes({ + getChildNodes: async () => { + await this.updateChildren() + return [...this.sagemakerSpaceNodes.values()] + }, + getNoChildrenPlaceholderNode: async () => new PlaceholderNode(this, SagemakerConstants.PlaceHolderMessage), + sort: (nodeA, nodeB) => nodeA.name.localeCompare(nodeB.name), + }) + + 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 + * 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 = await getRemoteAppMetadata() + return getSpaceAppsForUserProfile( + [...this.spaceApps.values()], + remoteAppMetadata.UserProfileName, + remoteAppMetadata.DomainId + ) + } + + public async getDefaultSelectedDomainUsers(): Promise { + if (isRemoteWorkspace()) { + return this.getRemoteSelectedDomainUsers() + } else { + return this.getLocalSelectedDomainUsers() + } + } + + public async getSelectedDomainUsers(): Promise> { + const selectedDomainUsersByRegionMap = new Map( + globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) + ) + + const selectedDomainUsersMap = new Map(selectedDomainUsersByRegionMap.get(this.regionCode)) + const defaultSelectedDomainUsers = await this.getDefaultSelectedDomainUsers() + const cachedDomainUsers = selectedDomainUsersMap.get(this.callerIdentity.Arn || '') + + if (cachedDomainUsers && cachedDomainUsers.length > 0) { + return new Set(cachedDomainUsers) + } else { + return new Set(defaultSelectedDomainUsers) + } + } + + public saveSelectedDomainUsers(selectedDomainUsers: string[]) { + const selectedDomainUsersByRegionMap = new Map( + globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) + ) + + const selectedDomainUsersMap = new Map(selectedDomainUsersByRegionMap.get(this.regionCode)) + + if (this.callerIdentity.Arn) { + selectedDomainUsersMap?.set(this.callerIdentity.Arn, selectedDomainUsers) + selectedDomainUsersByRegionMap?.set(this.regionCode, [...selectedDomainUsersMap]) + + globals.globalState.tryUpdate(SagemakerConstants.SelectedDomainUsersState, [ + ...selectedDomainUsersByRegionMap, + ]) + } + } + + public async updateChildren(): Promise { + 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, + spaceApps.keys(), + (key) => this.sagemakerSpaceNodes.get(key)!.updateSpace(spaceApps.get(key)!), + (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 new file mode 100644 index 00000000000..1d93d325193 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts @@ -0,0 +1,104 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +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 { getLogger } from '../../../shared/logger/logger' +import { SagemakerUnifiedStudioSpaceNode } from '../../../sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpaceNode' +import { SagemakerSpace } from '../sagemakerSpace' + +export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNode { + private smSpace: SagemakerSpace + public constructor( + public readonly parent: SagemakerParentNode, + public readonly client: SagemakerClient, + public override readonly regionCode: string, + public readonly spaceApp: SagemakerSpaceApp + ) { + super('') + this.smSpace = new SagemakerSpace(this.client, this.regionCode, this.spaceApp) + this.updateSpace(spaceApp) + this.contextValue = this.smSpace.getContext() + } + + public updateSpace(spaceApp: SagemakerSpaceApp) { + this.smSpace.updateSpace(spaceApp) + this.updateFromSpace() + if (this.isPending()) { + this.parent.trackPendingNode(this.DomainSpaceKey) + } + } + + private updateFromSpace() { + this.label = this.smSpace.label + this.description = this.smSpace.description + this.tooltip = this.smSpace.tooltip + this.iconPath = this.smSpace.iconPath + this.contextValue = this.smSpace.contextValue + } + + public isPending(): boolean { + return this.smSpace.isPending() + } + + public getStatus(): string { + return this.smSpace.getStatus() + } + + public async getAppStatus() { + return this.smSpace.getAppStatus() + } + + public get name(): string { + return this.smSpace.name + } + + public get arn(): string { + return this.smSpace.arn + } + + public async getAppArn() { + return this.smSpace.getAppArn() + } + + public async getSpaceArn() { + return this.smSpace.getSpaceArn() + } + + public async updateSpaceAppStatus() { + await this.smSpace.updateSpaceAppStatus() + this.updateFromSpace() + if (this.isPending()) { + this.parent.trackPendingNode(this.DomainSpaceKey) + } + } + + public get DomainSpaceKey(): string { + return this.spaceApp.DomainSpaceKey! + } + + public async refreshNode(): Promise { + await this.updateSpaceAppStatus() + await vscode.commands.executeCommand('aws.refreshAwsExplorerNode', this) + } +} + +export async function tryRefreshNode(node?: SagemakerSpaceNode | SagemakerUnifiedStudioSpaceNode) { + if (node) { + try { + // For SageMaker spaces, refresh just the individual space node to avoid expensive + // operation of refreshing all spaces in the domain + await node.updateSpaceAppStatus() + node instanceof SagemakerSpaceNode + ? await vscode.commands.executeCommand('aws.refreshAwsExplorerNode', node) + : await node.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 new file mode 100644 index 00000000000..e25e8791d4f --- /dev/null +++ b/packages/core/src/awsService/sagemaker/model.ts @@ -0,0 +1,247 @@ +/*! + * 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, startVscodeRemote } from '../../shared/extensions/ssh' +import { createBoundProcess, ensureDependencies } from '../../shared/remoteSession' +import { SshConfig } from '../../shared/sshConfig' +import * as path from 'path' +import { persistLocalCredentials, persistSmusProjectCreds, 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' +import { SagemakerSpaceNode } from './explorer/sagemakerSpaceNode' +import { sleep } from '../../shared/utilities/timeoutUtils' +import { SagemakerUnifiedStudioSpaceNode } from '../../sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpaceNode' + +const logger = getLogger('sagemaker') + +export async function tryRemoteConnection( + node: SagemakerSpaceNode | SagemakerUnifiedStudioSpaceNode, + ctx: vscode.ExtensionContext, + progress: vscode.Progress<{ message?: string; increment?: number }> +) { + const spaceArn = (await node.getSpaceArn()) as string + const isSMUS = node instanceof SagemakerUnifiedStudioSpaceNode + const remoteEnv = await prepareDevEnvConnection(spaceArn, ctx, 'sm_lc', isSMUS, node) + try { + progress.report({ message: 'Opening remote session' }) + 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( + spaceArn: string, + ctx: vscode.ExtensionContext, + connectionType: string, + isSMUS: boolean, + node: SagemakerSpaceNode | SagemakerUnifiedStudioSpaceNode | undefined, + session?: string, + wsUrl?: string, + token?: string, + domain?: string, + appType?: 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 < 120) { + await remoteSshConfig.update('connectTimeout', 120, vscode.ConfigurationTarget.Global) + void vscode.window.showInformationMessage( + 'Updated "remote.SSH.connectTimeout" to 120 seconds to improve stability.' + ) + } + + const hostnamePrefix = connectionType + const hostname = `${hostnamePrefix}_${spaceArn.replace(/\//g, '__').replace(/:/g, '_._')}` + + // save space credential mapping + if (connectionType === 'sm_lc') { + if (!isSMUS) { + await persistLocalCredentials(spaceArn) + } else { + await persistSmusProjectCreds(spaceArn, node as SagemakerUnifiedStudioSpaceNode) + } + } else if (connectionType === 'sm_dl') { + await persistSSMConnection(spaceArn, domain ?? '', session, wsUrl, token, appType) + } + + 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(`sagemaker-local-server.*.log at ${storagePath}`) + + 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() + + // 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 { + 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) => { + const entryHostname = line.split(' ')[0].split(',') + // Hostnames in the known_hosts file seem to be always lowercase, but keeping the case-sensitive check just in + // case. Originally we were only doing the case-sensitive check which caused users to get a host + // identification error when reconnecting to a Space after it was restarted. + return !entryHostname.includes(hostname) && !entryHostname.includes(hostname.toLowerCase()) + }) + + 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/remoteUtils.ts b/packages/core/src/awsService/sagemaker/remoteUtils.ts new file mode 100644 index 00000000000..9ff8d8ca177 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/remoteUtils.ts @@ -0,0 +1,48 @@ +/*! + * 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 { RemoteAppMetadata } from './utils' +import { getLogger } from '../../shared/logger/logger' +import { parseArn } from './detached-server/utils' + +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 } = parseArn(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/sagemakerSpace.ts b/packages/core/src/awsService/sagemaker/sagemakerSpace.ts new file mode 100644 index 00000000000..14ac03d9c0e --- /dev/null +++ b/packages/core/src/awsService/sagemaker/sagemakerSpace.ts @@ -0,0 +1,229 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as path from 'path' +import { AppType } from '@aws-sdk/client-sagemaker' +import { SagemakerClient, SagemakerSpaceApp } from '../../shared/clients/sagemaker' +import { getIcon, IconPath } from '../../shared/icons' +import { generateSpaceStatus, updateIdleFile, startMonitoringTerminalActivity, ActivityCheckInterval } from './utils' +import { UserActivity } from '../../shared/extensionUtilities' +import { getLogger } from '../../shared/logger/logger' + +export class SagemakerSpace { + public label: string = '' + public contextValue: string = '' + public description?: string + private spaceApp: SagemakerSpaceApp + public tooltip?: vscode.MarkdownString + public iconPath?: IconPath + public refreshCallback?: () => Promise + + public constructor( + private readonly client: SagemakerClient, + public readonly regionCode: string, + spaceApp: SagemakerSpaceApp, + private readonly isSMUSSpace: boolean = false + ) { + this.spaceApp = spaceApp + this.updateSpace(spaceApp) + this.contextValue = this.getContext() + } + + public updateSpace(spaceApp: SagemakerSpaceApp) { + this.setSpaceStatus(spaceApp.Status ?? '', spaceApp.App?.Status ?? '') + // Only update RemoteAccess property to minimize impact due to minor structural differences between variables + if (this.spaceApp.SpaceSettingsSummary && spaceApp.SpaceSettingsSummary?.RemoteAccess) { + this.spaceApp.SpaceSettingsSummary.RemoteAccess = spaceApp.SpaceSettingsSummary.RemoteAccess + } + this.label = this.buildLabel() + this.description = this.isSMUSSpace ? undefined : this.buildDescription() + this.tooltip = new vscode.MarkdownString(this.buildTooltip()) + this.iconPath = this.getAppIcon() + this.contextValue = this.getContext() + } + + 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 { + return this.spaceApp.SpaceName ?? `(no name)` + } + + public get arn(): string { + return 'placeholder-arn' + } + + // TODO: Verify this method is still needed to retrieve the app ARN or build based on provided details + 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 + } + + // TODO: Verify this method is still needed to retrieve the app ARN or build based on provided details + public async getSpaceArn() { + const spaceDetails = await this.client.describeSpace({ + DomainId: this.spaceApp.DomainId, + SpaceName: this.spaceApp.SpaceName, + }) + + return spaceDetails.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, + }) + + // AWS DescribeSpace API returns full details with property names like 'SpaceSettings' + // but our internal SagemakerSpaceApp type expects 'SpaceSettingsSummary' (from ListSpaces API) + // We destructure and rename properties to maintain type compatibility + const { + SpaceSettings: spaceSettingsSummary, + OwnershipSettings: ownershipSettingsSummary, + SpaceSharingSettings: spaceSharingSettingsSummary, + ...spaceDetails + } = space + this.updateSpace({ + SpaceSettingsSummary: spaceSettingsSummary, + OwnershipSettingsSummary: ownershipSettingsSummary, + SpaceSharingSettingsSummary: spaceSharingSettingsSummary, + ...spaceDetails, + App: app, + DomainSpaceKey: this.spaceApp.DomainSpaceKey, + }) + } + + public buildLabel(): string { + const status = generateSpaceStatus(this.spaceApp.Status, this.spaceApp.App?.Status) + return `${this.name} (${status})` + } + + public buildDescription(): string { + return `${this.spaceApp.SpaceSharingSettingsSummary?.SharingType ?? 'Unknown'} space` + } + + public buildTooltip() { + const spaceName = this.spaceApp?.SpaceName ?? '-' + const appType = this.spaceApp?.SpaceSettingsSummary?.AppType || '-' + const domainId = this.spaceApp?.DomainId ?? '-' + const owner = this.spaceApp?.OwnershipSettingsSummary?.OwnerUserProfileName || '-' + const instanceType = this.spaceApp?.App?.ResourceSpec?.InstanceType ?? '-' + if (this.isSMUSSpace) { + return `**Space:** ${spaceName} \n\n**Application:** ${appType} \n\n**Instance Type:** ${instanceType}` + } + return `**Space:** ${spaceName} \n\n**Application:** ${appType} \n\n**Domain ID:** ${domainId} \n\n**User Profile:** ${owner}` + } + + public getAppIcon() { + const appType = this.spaceApp.SpaceSettingsSummary?.AppType + if (appType === AppType.JupyterLab) { + return getIcon('aws-sagemaker-jupyter-lab') + } + if (appType === AppType.CodeEditor) { + return getIcon('aws-sagemaker-code-editor') + } + } + + public getContext(): string { + 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 === 'Running' && this.isSMUSSpace) { + return 'awsSagemakerSpaceRunningNode' + } 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 this.isSMUSSpace ? 'smusSpaceNode' : 'awsSagemakerSpaceNode' + } + + public get DomainSpaceKey(): string { + return this.spaceApp.DomainSpaceKey! + } +} + +/** + * Sets up user activity monitoring for SageMaker spaces + */ +export async function setupUserActivityMonitoring(extensionContext: vscode.ExtensionContext): Promise { + const logger = getLogger() + logger.info('setupUserActivityMonitoring: Starting user activity monitoring setup') + + const tmpDirectory = '/tmp/' + const idleFilePath = path.join(tmpDirectory, '.sagemaker-last-active-timestamp') + logger.debug(`setupUserActivityMonitoring: Using idle file path: ${idleFilePath}`) + + try { + const userActivity = new UserActivity(ActivityCheckInterval) + userActivity.onUserActivity(() => { + logger.debug('setupUserActivityMonitoring: User activity detected, updating idle file') + void updateIdleFile(idleFilePath) + }) + + let terminalActivityInterval: NodeJS.Timeout | undefined = startMonitoringTerminalActivity(idleFilePath) + logger.debug('setupUserActivityMonitoring: Started terminal activity monitoring') + // Write initial timestamp + await updateIdleFile(idleFilePath) + logger.info('setupUserActivityMonitoring: Initial timestamp written successfully') + extensionContext.subscriptions.push(userActivity, { + dispose: () => { + logger.info('setupUserActivityMonitoring: Disposing user activity monitoring') + if (terminalActivityInterval) { + clearInterval(terminalActivityInterval) + terminalActivityInterval = undefined + } + }, + }) + + logger.info('setupUserActivityMonitoring: User activity monitoring setup completed successfully') + } catch (error) { + logger.error(`setupUserActivityMonitoring: Error during setup: ${error}`) + throw error + } +} diff --git a/packages/core/src/awsService/sagemaker/types.ts b/packages/core/src/awsService/sagemaker/types.ts new file mode 100644 index 00000000000..82f4d4f92d6 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/types.ts @@ -0,0 +1,32 @@ +/*! + * 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 } + smusProjects?: { [smusProjectId: string]: { accessKey: string; secret: string; token: string } } +} + +export type LocalCredentialProfile = + | { type: 'iam'; profileName: string } + | { type: 'sso'; accessKey: string; secret: string; token: string } + | { type: 'sso'; smusProjectId: 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..6f1143d9054 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/uriHandlers.ts @@ -0,0 +1,43 @@ +/*! + * 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' +import { telemetry } from '../../shared/telemetry/telemetry' + +export function register(ctx: ExtContext) { + async function connectHandler(params: ReturnType) { + await telemetry.sagemaker_deeplinkConnect.run(async () => { + await deeplinkConnect( + ctx, + params.connection_identifier, + params.session, + `${params.ws_url}&cell-number=${params['cell-number']}`, + params.token, + params.domain, + params.app_type + ) + }) + } + + return vscode.Disposable.from(ctx.uriHandler.onPath('/connect/sagemaker', connectHandler, parseConnectParams)) +} + +export function parseConnectParams(query: SearchParams) { + const requiredParams = query.getFromKeysOrThrow( + 'connection_identifier', + 'domain', + 'user_profile', + 'session', + 'ws_url', + 'cell-number', + 'token' + ) + const optionalParams = query.getFromKeys('app_type') + + return { ...requiredParams, ...optionalParams } +} diff --git a/packages/core/src/awsService/sagemaker/utils.ts b/packages/core/src/awsService/sagemaker/utils.ts new file mode 100644 index 00000000000..33cc5880bee --- /dev/null +++ b/packages/core/src/awsService/sagemaker/utils.ts @@ -0,0 +1,156 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as cp from 'child_process' // eslint-disable-line no-restricted-imports +import * as path from 'path' +import { AppStatus, SpaceStatus } from '@aws-sdk/client-sagemaker' +import { SagemakerSpaceApp } from '../../shared/clients/sagemaker' +import { sshLogFileLocation } from '../../shared/sshConfig' +import { fs } from '../../shared/fs/fs' +import { getLogger } from '../../shared/logger/logger' + +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 ( + 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' +} + +export interface RemoteAppMetadata { + DomainId: string + UserProfileName: 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 || '') + ) + } + + 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 const ActivityCheckInterval = 60000 + +/** + * Updates the idle file with the current timestamp + */ +export async function updateIdleFile(idleFilePath: string): Promise { + try { + const timestamp = new Date().toISOString() + await fs.writeFile(idleFilePath, timestamp) + } catch (error) { + getLogger().error(`Failed to update SMAI idle file: ${error}`) + } +} + +/** + * Checks for terminal activity by reading the /dev/pts directory and comparing modification times of the files. + * + * The /dev/pts directory is used in Unix-like operating systems to represent pseudo-terminal (PTY) devices. + * Each active terminal session is assigned a PTY device. These devices are represented as files within the /dev/pts directory. + * When a terminal session has activity, such as when a user inputs commands or output is written to the terminal, + * the modification time (mtime) of the corresponding PTY device file is updated. By monitoring the modification + * times of the files in the /dev/pts directory, we can detect terminal activity. + * + * If activity is detected (i.e., if any PTY device file was modified within the CHECK_INTERVAL), this function + * updates the last activity timestamp. + */ +export async function checkTerminalActivity(idleFilePath: string): Promise { + try { + const files = await fs.readdir('/dev/pts') + const now = Date.now() + + for (const [fileName] of files) { + const filePath = path.join('/dev/pts', fileName) + try { + const stats = await fs.stat(filePath) + const mtime = new Date(stats.mtime).getTime() + if (now - mtime < ActivityCheckInterval) { + await updateIdleFile(idleFilePath) + return + } + } catch (err) { + getLogger().error(`Error reading file stats:`, err) + } + } + } catch (err) { + getLogger().error(`Error reading /dev/pts directory:`, err) + } +} + +/** + * Starts monitoring terminal activity by setting an interval to check for activity in the /dev/pts directory. + */ +export function startMonitoringTerminalActivity(idleFilePath: string): NodeJS.Timeout { + return setInterval(async () => { + await checkTerminalActivity(idleFilePath) + }, ActivityCheckInterval) +} diff --git a/packages/core/src/awsexplorer/activation.ts b/packages/core/src/awsexplorer/activation.ts new file mode 100644 index 00000000000..ec4c23ccd79 --- /dev/null +++ b/packages/core/src/awsexplorer/activation.ts @@ -0,0 +1,217 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { deleteCloudFormation } from '../lambda/commands/deleteCloudFormation' +import { CloudFormationStackNode } from '../lambda/explorer/cloudFormationNodes' +import globals from '../shared/extensionGlobals' +import { isCloud9, isSageMaker } from '../shared/extensionUtilities' +import { ExtContext, VSCODE_EXTENSION_ID } from '../shared/extensions' +import { getLogger } from '../shared/logger/logger' +import { RegionProvider } from '../shared/regions/regionProvider' +import { AWSResourceNode } from '../shared/treeview/nodes/awsResourceNode' +import { AWSTreeNodeBase } from '../shared/treeview/nodes/awsTreeNodeBase' +import { Commands } from '../shared/vscode/commands2' +import { downloadStateMachineDefinition } from '../stepFunctions/commands/downloadStateMachineDefinition' +import { executeStateMachine } from '../stepFunctions/vue/executeStateMachine/executeStateMachine' +import { StateMachineNode } from '../stepFunctions/explorer/stepFunctionsNodes' +import { AwsExplorer } from './awsExplorer' +import { copyTextCommand } from './commands/copyText' +import { loadMoreChildrenCommand } from './commands/loadMoreChildren' +import { checkExplorerForDefaultRegion } from './defaultRegion' +import { ToolView } from './toolView' +import { telemetry } from '../shared/telemetry/telemetry' +import { CdkRootNode } from '../awsService/cdk/explorer/rootNode' +import { CodeCatalystRootNode } from '../codecatalyst/explorer' +import { CodeCatalystAuthenticationProvider } from '../codecatalyst/auth' +import { S3FolderNode } from '../awsService/s3/explorer/s3FolderNode' +import { AmazonQNode, refreshAmazonQ, refreshAmazonQRootNode } from '../amazonq/explorer/amazonQTreeNode' +import { activateViewsShared, registerToolView } from './activationShared' +import { isExtensionInstalled } from '../shared/utilities/vsCodeUtils' +import { CommonAuthViewProvider } from '../login/webview/commonAuthViewProvider' +import { setContext } from '../shared/vscode/setContext' +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. + * + * IMPORTANT: Views that should work in all vscode environments (node or web) + * should be setup in {@link activateViewsShared}. + */ +export async function activate(args: { + context: ExtContext + regionProvider: RegionProvider + toolkitOutputChannel: vscode.OutputChannel +}): Promise { + const awsExplorer = new AwsExplorer(globals.context, args.regionProvider) + + const view = vscode.window.createTreeView(awsExplorer.viewProviderId, { + treeDataProvider: awsExplorer, + showCollapseAll: true, + }) + view.onDidExpandElement((element) => { + if (element.element instanceof S3FolderNode) { + globals.globalState.tryUpdate('aws.lastTouchedS3Folder', { + bucket: element.element.bucket, + folder: element.element.folder, + }) + } + if (element.element.serviceId) { + telemetry.aws_expandExplorerNode.emit({ serviceType: element.element.serviceId, result: 'Succeeded' }) + } + }) + globals.context.subscriptions.push( + view, + vscode.window.registerFileDecorationProvider(LambdaFunctionNodeDecorationProvider.getInstance()) + ) + + await registerAwsExplorerCommands(args.context, awsExplorer, args.toolkitOutputChannel) + + telemetry.vscode_activeRegions.emit({ value: args.regionProvider.getExplorerRegions().length }) + + args.context.extensionContext.subscriptions.push( + args.context.awsContext.onDidChangeContext(async (credentialsChangedEvent) => { + getLogger().verbose(`Credentials changed (${credentialsChangedEvent.profileName}), updating AWS Explorer`) + awsExplorer.refresh() + + if (credentialsChangedEvent.profileName) { + await checkExplorerForDefaultRegion(args.regionProvider, awsExplorer) + } + }) + ) + + const authProvider = CodeCatalystAuthenticationProvider.fromContext(args.context.extensionContext) + const codecatalystViewNode: ToolView[] = [] + let codecatalystNode: CodeCatalystRootNode | undefined + + const shouldShowCodeCatalyst = !(isCloud9('classic') || isSageMaker()) + if (shouldShowCodeCatalyst) { + codecatalystNode = new CodeCatalystRootNode(authProvider) + codecatalystViewNode.push({ + nodes: [codecatalystNode], + view: 'aws.codecatalyst', + refreshCommands: [ + (provider) => { + codecatalystNode!.addRefreshEmitter(() => provider.refresh()) + }, + ], + }) + } + // CodeCatalyst view may not be present. Wrap VS Code-owned command to avoid warning toasts if missing + args.context.extensionContext.subscriptions.push( + Commands.register(`aws.codecatalyst.maybeFocus`, async () => { + if (shouldShowCodeCatalyst) { + // vs code-owned command + await vscode.commands.executeCommand('aws.codecatalyst.focus') + } + }) + ) + + const amazonQViewNode: ToolView[] = [] + if ( + isExtensionInstalled(VSCODE_EXTENSION_ID.amazonq) || + globals.globalState.get('aws.toolkit.amazonq.dismissed') + ) { + await setContext('aws.toolkit.amazonq.dismissed', true) + } + + // We should create the tree even if it's dismissed, in case the user installs Amazon Q later. + amazonQViewNode.push({ + nodes: [AmazonQNode.instance], + view: 'aws.amazonq.codewhisperer', + refreshCommands: [refreshAmazonQ, refreshAmazonQRootNode], + }) + + const viewNodes: ToolView[] = [ + ...amazonQViewNode, + ...codecatalystViewNode, + { nodes: [CdkRootNode.instance], view: 'aws.cdk', refreshCommands: [CdkRootNode.instance.refreshCdkExplorer] }, + ] + for (const viewNode of viewNodes) { + registerToolView(viewNode, args.context.extensionContext) + } + + const toolkitAuthProvider = new CommonAuthViewProvider(args.context.extensionContext, 'toolkit') + args.context.extensionContext.subscriptions.push( + vscode.window.registerWebviewViewProvider(toolkitAuthProvider.viewType, toolkitAuthProvider, { + webviewOptions: { + retainContextWhenHidden: true, + }, + }), + // Hacky way for a webview to call setLoginService(). + vscode.commands.registerCommand('aws.explorer.setLoginService', (serviceToShow?: string) => { + if (toolkitAuthProvider.webView && 'setLoginService' in toolkitAuthProvider.webView.server) { + toolkitAuthProvider.webView.server.setLoginService(serviceToShow) + } + }) + ) +} + +async function registerAwsExplorerCommands( + context: ExtContext, + awsExplorer: AwsExplorer, + toolkitOutputChannel: vscode.OutputChannel +): Promise { + context.extensionContext.subscriptions.push( + Commands.register({ id: 'aws.showRegion', autoconnect: false }, async () => { + try { + await globals.awsContextCommands.onCommandShowRegion() + } finally { + telemetry.aws_setRegion.emit() + telemetry.vscode_activeRegions.emit({ value: awsExplorer.getRegionNodesSize() }) + } + }), + Commands.register({ id: 'aws.refreshAwsExplorer', autoconnect: true }, async (passive: boolean = false) => { + awsExplorer.refresh() + + if (!passive) { + telemetry.aws_refreshExplorer.emit() + } + }), + Commands.register( + { id: 'aws.deleteCloudFormation', autoconnect: true }, + async (node: CloudFormationStackNode) => + await deleteCloudFormation(() => awsExplorer.refresh(node.parent), node) + ), + Commands.register( + { id: 'aws.downloadStateMachineDefinition', autoconnect: true }, + async (node: StateMachineNode) => + await downloadStateMachineDefinition({ + stateMachineNode: node, + outputChannel: toolkitOutputChannel, + }) + ) + ) + + context.extensionContext.subscriptions.push( + Commands.register( + 'aws.executeStateMachine', + async (node: StateMachineNode) => await executeStateMachine(context, node) + ), + Commands.register('aws.copyArn', async (node: AWSResourceNode | TreeNode) => { + const sourceNode = getSourceNode(node) + await copyTextCommand(sourceNode, 'ARN') + }), + Commands.register('aws.copyName', async (node: AWSResourceNode | TreeNode) => { + const sourceNode = getSourceNode(node) + await copyTextCommand(sourceNode, 'name') + }), + Commands.register('aws.openAwsConsole', async (node: AWSResourceNode | TreeNode) => { + const sourceNode = getSourceNode(node) + await openAwsConsoleCommand(sourceNode) + }), + Commands.register('aws.openAwsCFNConsole', async (node: StackNameNode) => { + await openAwsCFNConsoleCommand(node) + }), + Commands.register('aws.refreshAwsExplorerNode', async (element: AWSTreeNodeBase | undefined) => { + awsExplorer.refresh(element) + }), + loadMoreChildrenCommand.register(awsExplorer) + ) +} diff --git a/packages/core/src/awsexplorer/activationShared.ts b/packages/core/src/awsexplorer/activationShared.ts new file mode 100644 index 00000000000..9f488a35c36 --- /dev/null +++ b/packages/core/src/awsexplorer/activationShared.ts @@ -0,0 +1,32 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { createToolView, ToolView } from './toolView' +import { telemetry } from '../shared/telemetry/telemetry' +import { CdkRootNode } from '../awsService/cdk/explorer/rootNode' + +/** + * Activates vscode Views (eg tree view) that work in any vscode environment (nodejs or browser). + */ +export async function activateViewsShared(context: vscode.ExtensionContext): Promise {} + +export function registerToolView(viewNode: ToolView, context: vscode.ExtensionContext) { + const toolView = createToolView(viewNode) + context.subscriptions.push(toolView) + if (viewNode.view === 'aws.cdk') { + // Legacy CDK behavior. Mostly useful for C9 as they do not have inline buttons. + toolView.onDidChangeVisibility(({ visible }) => visible && CdkRootNode.instance.refresh()) + } + + toolView.onDidExpandElement((e) => { + if (e.element.resource instanceof CdkRootNode) { + // Legacy CDK metric, remove this when we add something generic + telemetry.cdk_appExpanded.emit() + } + }) + + return toolView +} diff --git a/src/awsexplorer/awsExplorer.ts b/packages/core/src/awsexplorer/awsExplorer.ts similarity index 82% rename from src/awsexplorer/awsExplorer.ts rename to packages/core/src/awsexplorer/awsExplorer.ts index 7b207137178..63707527173 100644 --- a/src/awsexplorer/awsExplorer.ts +++ b/packages/core/src/awsexplorer/awsExplorer.ts @@ -1,13 +1,13 @@ /*! - * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ import * as vscode from 'vscode' -import { Auth, AuthNode, useIamCredentials } from '../credentials/auth' +import { Auth } from '../auth/auth' import { getIdeProperties } from '../shared/extensionUtilities' import { getIcon } from '../shared/icons' -import { getLogger, Logger } from '../shared/logger' +import { getLogger, Logger } from '../shared/logger/logger' import { RegionProvider } from '../shared/regions/regionProvider' import { RefreshableAwsTreeProvider } from '../shared/treeview/awsTreeProvider' import { AWSCommandTreeNode } from '../shared/treeview/nodes/awsCommandTreeNode' @@ -17,6 +17,8 @@ import { intersection, toMap, updateInPlace } from '../shared/utilities/collecti import { once } from '../shared/utilities/functionUtils' import { localize } from '../shared/utilities/vsCodeUtils' import { RegionNode } from './regionNode' +import { AuthNode } from '../auth/utils' +import { Commands } from '../shared/vscode/commands2' export class AwsExplorer implements vscode.TreeDataProvider, RefreshableAwsTreeProvider { public viewProviderId: string = 'aws.explorer' @@ -74,7 +76,7 @@ export class AwsExplorer implements vscode.TreeDataProvider, Re } } catch (err) { const error = err as Error - this.logger.error(`Error getting children for node ${element?.label ?? 'Root Node'}: %s`, error) + this.logger.error(`Error getting children for node %O: %s`, element?.label ?? 'Root Node', error) childNodes.splice( 0, @@ -105,12 +107,14 @@ export class AwsExplorer implements vscode.TreeDataProvider, Re const conn = this.auth.activeConnection if (conn !== undefined && conn.type !== 'iam') { // TODO: this should show up as a child node? - const selectIamNode = useIamCredentials.build(this.auth).asTreeNode({ - // label: `No IAM credentials linked to ${conn.label}`, - // iconPath: getIcon('vscode-circle-slash'), - label: 'Select IAM Credentials to View Resources', - iconPath: getIcon('vscode-sync'), - }) + const selectIamNode = (await Commands.getOrThrow('_aws.toolkit.auth.useIamCredentials')) + .build(this.auth) + .asTreeNode({ + // label: `No IAM credentials linked to ${conn.label}`, + // iconPath: getIcon('vscode-circle-slash'), + label: 'Select IAM Credentials to View Resources', + iconPath: getIcon('vscode-sync'), + }) return [this.getAuthNode(), new TreeShim(selectIamNode)] } else if (conn === undefined || conn.state !== 'valid') { @@ -119,13 +123,13 @@ export class AwsExplorer implements vscode.TreeDataProvider, Re const partitionRegions = this.regionProvider.getRegions() const userVisibleRegionCodes = this.regionProvider.getExplorerRegions() - const regionMap = toMap(partitionRegions, r => r.id) + const regionMap = toMap(partitionRegions, (r) => r.id) updateInPlace( this.regionNodes, intersection(regionMap.keys(), userVisibleRegionCodes), - key => this.regionNodes.get(key)!.update(regionMap.get(key)!), - key => new RegionNode(regionMap.get(key)!, this.regionProvider) + (key) => this.regionNodes.get(key)!.update(regionMap.get(key)!), + (key) => new RegionNode(regionMap.get(key)!, this.regionProvider) ) if (this.regionNodes.size === 0) { diff --git a/src/awsexplorer/childNodeCache.ts b/packages/core/src/awsexplorer/childNodeCache.ts similarity index 96% rename from src/awsexplorer/childNodeCache.ts rename to packages/core/src/awsexplorer/childNodeCache.ts index 97aa8a2e09b..da4bbbd8519 100644 --- a/src/awsexplorer/childNodeCache.ts +++ b/packages/core/src/awsexplorer/childNodeCache.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/src/awsexplorer/childNodeLoader.ts b/packages/core/src/awsexplorer/childNodeLoader.ts similarity index 96% rename from src/awsexplorer/childNodeLoader.ts rename to packages/core/src/awsexplorer/childNodeLoader.ts index 41c50f7e98d..bb3997a558b 100644 --- a/src/awsexplorer/childNodeLoader.ts +++ b/packages/core/src/awsexplorer/childNodeLoader.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -7,7 +7,7 @@ import { AWSTreeNodeBase } from '../shared/treeview/nodes/awsTreeNodeBase' import { LoadMoreNode } from '../shared/treeview/nodes/loadMoreNode' import { MoreResultsNode } from './moreResultsNode' import { ChildNodeCache } from './childNodeCache' -import * as AsyncLock from 'async-lock' +import AsyncLock from 'async-lock' const lockKey = 'ChildNodeLoader' diff --git a/packages/core/src/awsexplorer/commands/copyText.ts b/packages/core/src/awsexplorer/commands/copyText.ts new file mode 100644 index 00000000000..4fbb0244bb8 --- /dev/null +++ b/packages/core/src/awsexplorer/commands/copyText.ts @@ -0,0 +1,28 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { copyToClipboard } from '../../shared/utilities/messages' +import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode' +import { TreeShim } from '../../shared/treeview/utils' + +export type copyableText = 'ARN' | 'name' | 'id' + +export async function copyTextCommand( + node: AWSResourceNode | TreeShim, + text: copyableText +): Promise { + node = node instanceof TreeShim ? node.node.resource : node + switch (text) { + case 'ARN': + await copyToClipboard(node.arn, text) + break + case 'name': + await copyToClipboard(node.name, text) + break + case 'id': + await copyToClipboard(node.id!, text) + break + } +} diff --git a/src/awsexplorer/commands/loadMoreChildren.ts b/packages/core/src/awsexplorer/commands/loadMoreChildren.ts similarity index 91% rename from src/awsexplorer/commands/loadMoreChildren.ts rename to packages/core/src/awsexplorer/commands/loadMoreChildren.ts index fe98777401b..6bc37b770e2 100644 --- a/src/awsexplorer/commands/loadMoreChildren.ts +++ b/packages/core/src/awsexplorer/commands/loadMoreChildren.ts @@ -1,10 +1,10 @@ /*! - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ import { localize } from '../../shared/utilities/vsCodeUtils' -import { getLogger } from '../../shared/logger' +import { getLogger } from '../../shared/logger/logger' import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase' import { LoadMoreNode } from '../../shared/treeview/nodes/loadMoreNode' import { AwsExplorer } from '../awsExplorer' diff --git a/packages/core/src/awsexplorer/defaultRegion.ts b/packages/core/src/awsexplorer/defaultRegion.ts new file mode 100644 index 00000000000..be28e4ec6d6 --- /dev/null +++ b/packages/core/src/awsexplorer/defaultRegion.ts @@ -0,0 +1,22 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AwsExplorer } from './awsExplorer' +import { RegionProvider } from '../shared/regions/regionProvider' + +export async function checkExplorerForDefaultRegion( + regionProvider: RegionProvider, + awsExplorer: AwsExplorer +): Promise { + const profileRegion = regionProvider.defaultRegionId + + const explorerRegions = new Set(regionProvider.getExplorerRegions()) + if (explorerRegions.has(profileRegion)) { + return + } + + await regionProvider.updateExplorerRegions([...explorerRegions, profileRegion]) + awsExplorer.refresh() +} diff --git a/src/awsexplorer/moreResultsNode.ts b/packages/core/src/awsexplorer/moreResultsNode.ts similarity index 93% rename from src/awsexplorer/moreResultsNode.ts rename to packages/core/src/awsexplorer/moreResultsNode.ts index a533e8cc02a..de09541aed7 100644 --- a/src/awsexplorer/moreResultsNode.ts +++ b/packages/core/src/awsexplorer/moreResultsNode.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/packages/core/src/awsexplorer/regionNode.ts b/packages/core/src/awsexplorer/regionNode.ts new file mode 100644 index 00000000000..d78bcbec2a4 --- /dev/null +++ b/packages/core/src/awsexplorer/regionNode.ts @@ -0,0 +1,182 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { TreeItemCollapsibleState } from 'vscode' +import { ApiGatewayNode } from '../awsService/apigateway/explorer/apiGatewayNodes' +import { SchemasNode } from '../eventSchemas/explorer/schemasNode' +import { CloudFormationNode } from '../lambda/explorer/cloudFormationNodes' +import { CloudWatchLogsNode } from '../awsService/cloudWatchLogs/explorer/cloudWatchLogsNode' +import { LambdaNode } from '../lambda/explorer/lambdaNodes' +import { S3Node } from '../awsService/s3/explorer/s3Nodes' +import { EcrNode } from '../awsService/ecr/explorer/ecrNode' +import { RedshiftNode } from '../awsService/redshift/explorer/redshiftNode' +import { IotNode } from '../awsService/iot/explorer/iotNodes' +import { Region } from '../shared/regions/endpoints' +import { defaultPartition, RegionProvider } from '../shared/regions/regionProvider' +import { AWSTreeNodeBase } from '../shared/treeview/nodes/awsTreeNodeBase' +import { StepFunctionsNode } from '../stepFunctions/explorer/stepFunctionsNodes' +import { SsmDocumentNode } from '../ssmDocument/explorer/ssmDocumentNode' +import { ResourcesNode } from '../dynamicResources/explorer/nodes/resourcesNode' +import { AppRunnerNode } from '../awsService/apprunner/explorer/apprunnerNode' +import { DocumentDBNode } from '../docdb/explorer/docdbNode' +import { DefaultDocumentDBClient } from '../shared/clients/docdbClient' +import { AppRunnerClient } from '../shared/clients/apprunner' +import { DefaultEcrClient } from '../shared/clients/ecrClient' +import { DefaultRedshiftClient } from '../shared/clients/redshiftClient' +import { DefaultIotClient } from '../shared/clients/iotClient' +import { S3Client } from '../shared/clients/s3' +import { DefaultSchemaClient } from '../shared/clients/schemaClient' +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 + serviceId: string + /** + * Decides if the node should be shown. Example: + * ``` + * when: () => DevSettings.instance.isDevMode() + * ``` + */ + when?: () => boolean + createFn: (regionCode: string, partitionId: string) => any +} + +const serviceCandidates: ServiceNode[] = [ + { + serviceId: 'apigateway', + createFn: (regionCode: string, partitionId: string) => new ApiGatewayNode(partitionId, regionCode), + }, + { + serviceId: 'apprunner', + createFn: (regionCode: string) => new AppRunnerNode(regionCode, new AppRunnerClient(regionCode)), + }, + { + serviceId: 'cloudformation', + createFn: (regionCode: string) => new CloudFormationNode(regionCode), + }, + { + serviceId: 'docdb', + createFn: (regionCode: string) => new DocumentDBNode(DefaultDocumentDBClient.create(regionCode)), + }, + { + serviceId: 'logs', + createFn: (regionCode: string) => new CloudWatchLogsNode(regionCode), + }, + { + serviceId: 'ec2', + createFn: (regionCode: string, partitionId: string) => + new Ec2ParentNode(regionCode, partitionId, new Ec2Client(regionCode)), + }, + { + serviceId: 'ecr', + createFn: (regionCode: string) => new EcrNode(new DefaultEcrClient(regionCode)), + }, + { + serviceId: 'redshift', + createFn: (regionCode: string) => new RedshiftNode(new DefaultRedshiftClient(regionCode)), + }, + { + serviceId: 'ecs', + createFn: (regionCode: string) => new TreeShim(getEcsRootNode(regionCode)), + }, + { + serviceId: 'iot', + createFn: (regionCode: string) => new IotNode(new DefaultIotClient(regionCode)), + }, + { + serviceId: 'lambda', + createFn: (regionCode: string) => new LambdaNode(regionCode), + }, + { + 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)), + }, + { + serviceId: 'states', + createFn: (regionCode: string) => new StepFunctionsNode(regionCode), + }, + { + serviceId: 'ssm', + createFn: (regionCode: string) => new SsmDocumentNode(regionCode), + }, + { + allRegions: true, + serviceId: 'cloudcontrol', + createFn: (regionCode: string) => new ResourcesNode(regionCode), + }, +] + +/** + * An AWS Explorer node representing a region. + * Contains resource types as child nodes (for example, nodes representing + * an account's Lambda Functions and CloudFormation stacks for this region) + */ +export class RegionNode extends AWSTreeNodeBase { + private region: Region + public override readonly regionCode: string + + public get regionName(): string { + return this.region.name + } + + public constructor( + region: Region, + private readonly regionProvider: RegionProvider + ) { + super(region.name, TreeItemCollapsibleState.Expanded) + this.contextValue = 'awsRegionNode' + this.region = region + this.regionCode = region.id + this.update(region) + } + + public override async getChildren(): Promise { + // Services that are candidates to add to the region explorer. + // `serviceId`s are checked against ~/resources/endpoints.json to see whether or not the service is available in the given region. + // If the service is available, we use the `createFn` to generate the node for the region. + // This interface exists so we can add additional nodes to the array (otherwise Typescript types the array to what's already in the array at creation) + const partitionId = this.regionProvider.getPartitionId(this.regionCode) ?? defaultPartition + const childNodes: AWSTreeNodeBase[] = [] + for (const service of serviceCandidates) { + if (service.when !== undefined && !service.when()) { + continue + } + if (service.allRegions || this.regionProvider.isServiceInRegion(service.serviceId, this.regionCode)) { + const node = service.createFn(this.regionCode, partitionId) + if (node !== undefined) { + node.serviceId = service.serviceId + childNodes.push(node) + } + } + } + + return this.sortNodes(childNodes) + } + + private sortNodes(nodes: AWSTreeNodeBase[]) { + return nodes.sort((a, b) => { + // Always sort `ResourcesNode` at the bottom + return a instanceof ResourcesNode ? 1 : b instanceof ResourcesNode ? -1 : compareTreeItems(a, b) + }) + } + public update(region: Region): void { + this.region = region + this.label = this.regionName + this.tooltip = `${this.regionName} [${this.regionCode}]` + } +} diff --git a/packages/core/src/awsexplorer/toolView.ts b/packages/core/src/awsexplorer/toolView.ts new file mode 100644 index 00000000000..99d20fbdb3a --- /dev/null +++ b/packages/core/src/awsexplorer/toolView.ts @@ -0,0 +1,41 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { ResourceTreeDataProvider, TreeNode } from '../shared/treeview/resourceTreeDataProvider' + +export interface ToolView { + nodes: TreeNode[] + view: string + refreshCommands: ((provider: ResourceTreeDataProvider) => void)[] +} + +/** + * The 'local' explorer is represented as 'Developer Tools' in the UI. We use a different name within + * source code to differentiate between _Toolkit developers_ and _Toolkit users_. + * + * Components placed under this view do not strictly need to be 'local'. They just need to place greater + * emphasis on the developer's local development environment. + */ +export function createToolView(viewNode: ToolView): vscode.TreeView { + const treeDataProvider = new ResourceTreeDataProvider({ getChildren: () => getChildren(viewNode.nodes) }) + for (const refreshCommand of viewNode.refreshCommands ?? []) { + refreshCommand(treeDataProvider) + } + + return vscode.window.createTreeView(viewNode.view, { treeDataProvider }) +} + +async function getChildren(roots: TreeNode[]) { + const nodes: TreeNode[] = [] + + for (const node of roots) { + if (node.getChildren) { + nodes.push(...(await node.getChildren())) + } + } + + return nodes +} diff --git a/packages/core/src/codecatalyst/activation.ts b/packages/core/src/codecatalyst/activation.ts new file mode 100644 index 00000000000..db5ff9e12f0 --- /dev/null +++ b/packages/core/src/codecatalyst/activation.ts @@ -0,0 +1,179 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as uriHandlers from './uriHandlers' +import * as vscode from 'vscode' +import * as nls from 'vscode-nls' +import { ExtContext } from '../shared/extensions' +import { CodeCatalystRemoteSourceProvider } from './repos/remoteSourceProvider' +import { CodeCatalystCommands, codecatalystConnectionsCmd } from './commands' +import { GitExtension } from '../shared/extensions/git' +import { CodeCatalystAuthenticationProvider } from './auth' +import { registerDevfileWatcher, updateDevfileCommand } from './devfile' +import { DevEnvClient } from '../shared/clients/devenvClient' +import { watchRestartingDevEnvs } from './reconnect' +import { ToolkitPromptSettings } from '../shared/settings' +import { dontShow } from '../shared/localizedText' +import { getIdeProperties } from '../shared/extensionUtilities' +import { Commands } from '../shared/vscode/commands2' +import { getCodeCatalystConfig } from '../shared/clients/codecatalystClient' +import { isDevenvVscode } from './utils' +import { codeCatalystConnectCommand, getThisDevEnv } from './model' +import { getLogger } from '../shared/logger/logger' +import { DevEnvActivityStarter } from './devEnv' +import { learnMoreCommand, onboardCommand, reauth } from './explorer' +import { isInDevEnv } from '../shared/vscode/env' +import { hasScopes, scopesCodeWhispererCore, getTelemetryMetadataForConn } from '../auth/connection' +import { telemetry } from '../shared/telemetry/telemetry' +import { asStringifiedStack } from '../shared/telemetry/spans' + +const localize = nls.loadMessageBundle() + +/** + * Activate CodeCatalyst functionality. + */ +export async function activate(ctx: ExtContext): Promise { + const authProvider = CodeCatalystAuthenticationProvider.fromContext(ctx.extensionContext) + const commands = new CodeCatalystCommands(authProvider) + const remoteSourceProvider = new CodeCatalystRemoteSourceProvider(commands, authProvider) + + codeCatalystConnectCommand.register() + reauth.register() + onboardCommand.register() + updateDevfileCommand.register() + learnMoreCommand.register() + + await authProvider.restore() + + // Forget Amazon Q connections while we transition to separate auth sessions per extension. + // Note: credentials on disk in the dev env cannot have Q scopes, so it will never be forgotten. + // TODO: Remove after some time? + if (authProvider.isConnected() && hasScopes(authProvider.activeConnection!, scopesCodeWhispererCore)) { + await telemetry.function_call.run( + async () => { + await telemetry.auth_modifyConnection.run(async () => { + const conn = authProvider.activeConnection + telemetry.record({ + action: 'forget', + source: asStringifiedStack(telemetry.getFunctionStack()), + connectionState: conn ? authProvider.auth.getConnectionState(conn) : undefined, + ...(await getTelemetryMetadataForConn(conn)), + }) + + await authProvider.secondaryAuth.forgetConnection() + }) + }, + { emit: false, functionId: { name: 'activate', class: 'CodeCatalyst' } } + ) + } + + ctx.extensionContext.subscriptions.push( + uriHandlers.register(ctx.uriHandler, CodeCatalystCommands.declared), + ...Object.values(CodeCatalystCommands.declared).map((c) => c.register(commands)), + codecatalystConnectionsCmd.register(), + Commands.register('aws.codecatalyst.signout', () => { + return authProvider.secondaryAuth.deleteConnection() + }) + ) + + await GitExtension.instance.registerRemoteSourceProvider(remoteSourceProvider).then((disposable) => { + ctx.extensionContext.subscriptions.push(disposable) + }) + + await GitExtension.instance + .registerCredentialsProvider({ + getCredentials(uri: vscode.Uri) { + if (uri.authority.endsWith(getCodeCatalystConfig().gitHostname)) { + return commands.withClient((client) => authProvider.getCredentialsForGit(client)) + } + }, + }) + .then((disposable) => ctx.extensionContext.subscriptions.push(disposable)) + + watchRestartingDevEnvs(ctx, authProvider) + + const thisDevenv = (await getThisDevEnv(authProvider))?.unwrapOrElse((err) => { + getLogger().error('codecatalyst: failed to get current Dev Enviroment: %s', err) + return undefined + }) + + if (!thisDevenv) { + if (isInDevEnv()) { + getLogger().info('codecatalyst: Dev Environment timeout=unknown') + } else { + getLogger().verbose('codecatalyst: not a Dev Environment ($__DEV_ENVIRONMENT_ID is undefined)') + } + } else { + ctx.extensionContext.subscriptions.push(DevEnvClient.instance) + if (DevEnvClient.instance.id) { + ctx.extensionContext.subscriptions.push(registerDevfileWatcher(DevEnvClient.instance)) + } + + const timeoutMin = thisDevenv.summary.inactivityTimeoutMinutes + const timeout = timeoutMin === 0 ? 'never' : `${timeoutMin} min` + getLogger().info('codecatalyst: Dev Environment timeout=%s, ides=%O', timeout, thisDevenv.summary.ides) + if (thisDevenv && !isDevenvVscode(thisDevenv.summary.ides)) { + // Prevent Toolkit from reconnecting to a "non-vscode" devenv by actively closing it. + // Can happen if devenv is switched to ides="cloud9", etc. + // TODO: Is this needed without cloud9 check? + void vscode.commands.executeCommand('workbench.action.remote.close') + return + } + + await showReadmeFileOnFirstLoad(ctx.extensionContext.workspaceState) + + const settings = ToolkitPromptSettings.instance + if (settings.isPromptEnabled('remoteConnected')) { + const message = localize( + 'AWS.codecatalyst.connectedMessage', + 'Welcome to your Amazon CodeCatalyst Dev Environment. For more options and information, view Dev Environment settings ({0} Extension > CodeCatalyst).', + getIdeProperties().company + ) + const openDevEnvSettings = localize('AWS.codecatalyst.openDevEnvSettings', 'Open Dev Environment Settings') + void vscode.window.showInformationMessage(message, dontShow, openDevEnvSettings).then(async (selection) => { + if (selection === dontShow) { + await settings.disablePrompt('remoteConnected') + } else if (selection === openDevEnvSettings) { + await CodeCatalystCommands.declared.openDevEnvSettings.execute() + } + }) + } + } + + // This must always be called on activation + DevEnvActivityStarter.init(authProvider) +} + +async function showReadmeFileOnFirstLoad(workspaceState: vscode.ExtensionContext['workspaceState']): Promise { + getLogger().debug('codecatalyst: showReadmeFileOnFirstLoad()') + // Check dev env state to see if this is the first time the user has connected to a dev env + const isFirstLoad = workspaceState.get('aws.codecatalyst.devEnv.isFirstLoad', true) + + if (!isFirstLoad) { + getLogger().info('codecatalyst: is not first load, skipping showing README.md') + return + } + + // Determine expected readme file location + const readmePath = `README.md` + + // Find readme file in workspace + const readmeUri = await vscode.workspace.findFiles(readmePath).then((files) => { + if (files.length === 0) { + return undefined + } + return files[0] + }) + + if (readmeUri === undefined) { + getLogger().debug(`codecatalyst: README.md not found in path '${readmePath}'`) + return + } + + // Show rendered readme file to user + await vscode.commands.executeCommand('markdown.showPreview', readmeUri) + + await workspaceState.update('aws.codecatalyst.devEnv.isFirstLoad', false) +} diff --git a/packages/core/src/codecatalyst/auth.ts b/packages/core/src/codecatalyst/auth.ts new file mode 100644 index 00000000000..f1d3ea82954 --- /dev/null +++ b/packages/core/src/codecatalyst/auth.ts @@ -0,0 +1,430 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { onAccessDeniedException, CodeCatalystClient, createClient } from '../shared/clients/codecatalystClient' +import { Auth } from '../auth/auth' +import * as localizedText from '../shared/localizedText' +import { getSecondaryAuth, setScopes } from '../auth/secondaryAuth' +import { getLogger } from '../shared/logger/logger' +import globals from '../shared/extensionGlobals' +import { ToolkitError, isAwsError } from '../shared/errors' +import { MetricName, MetricShapes, telemetry } from '../shared/telemetry/telemetry' +import { openUrl } from '../shared/utilities/vsCodeUtils' +import { + scopesSsoAccountAccess, + scopesCodeCatalyst, + SsoConnection, + Connection, + isBuilderIdConnection, + isSsoConnection, + createSsoProfile, + isValidCodeCatalystConnection, + isIdcSsoConnection, + hasExactScopes, +} from '../auth/connection' +import { createBuilderIdConnection } from '../auth/utils' +import { showReauthenticateMessage } from '../shared/utilities/messages' +import { ToolkitPromptSettings } from '../shared/settings' +import { setContext } from '../shared/vscode/setContext' +import { withTelemetryContext } from '../shared/telemetry/util' +import { builderIdStartUrl } from '../auth/sso/constants' + +// Secrets stored on the macOS keychain appear as individual entries for each key +// This is fine so long as the user has only a few accounts. Otherwise this should +// store secrets as a map. +export class CodeCatalystAuthStorage { + public constructor(private readonly secrets: vscode.SecretStorage) {} + + public async getPat(username: string): Promise { + return this.secrets.get(`codecatalyst.pat.${username}`) + } + + public async storePat(username: string, pat: string): Promise { + await this.secrets.store(`codecatalyst.pat.${username}`, pat) + } +} + +export const onboardingUrl = vscode.Uri.parse('https://codecatalyst.aws/onboarding/view') + +/** + * AWS account scopes are intended to be included. Some codepaths that use defaultScopes may depend on these scopes. + */ +export const defaultScopes = [...scopesSsoAccountAccess, ...scopesCodeCatalyst] + +export const isUpgradeableConnection = (conn: Connection): conn is SsoConnection => + isSsoConnection(conn) && !isValidCodeCatalystConnection(conn) + +export function setCodeCatalystConnectedContext(isConnected: boolean) { + return setContext('aws.codecatalyst.connected', isConnected) +} + +type ConnectionState = { + onboarded: boolean + scopeExpired: boolean +} + +const authClassName = 'AuthCodeCatalyst' + +export class CodeCatalystAuthenticationProvider { + public readonly onDidChangeActiveConnection = this.secondaryAuth.onDidChangeActiveConnection + public readonly onAccessDeniedException = onAccessDeniedException + private readonly onDidChangeEmitter = new vscode.EventEmitter() + public readonly onDidChange = this.onDidChangeEmitter.event + + public constructor( + protected readonly storage: CodeCatalystAuthStorage, + public readonly auth = Auth.instance, + public readonly secondaryAuth = getSecondaryAuth( + auth, + 'codecatalyst', + 'CodeCatalyst', + isValidCodeCatalystConnection + ) + ) { + this.onDidChangeActiveConnection(async () => { + if (this.activeConnection) { + await this.setScopeExpired(this.activeConnection, false) + } + await setCodeCatalystConnectedContext(this.isConnectionValid()) + this.onDidChangeEmitter.fire() + }) + + this.onAccessDeniedException(async (showReauthPrompt: boolean) => { + await this.accessDeniedExceptionHandler(showReauthPrompt) + this.onDidChangeEmitter.fire() + }) + + // set initial context in case event does not trigger + void setCodeCatalystConnectedContext(this.isConnectionValid()) + } + + public get activeConnection() { + return this.secondaryAuth.activeConnection + } + + public get isUsingSavedConnection() { + return this.secondaryAuth.hasSavedConnection + } + + public async setScopeExpired(conn: SsoConnection, isExpired: boolean) { + await this.updateConnectionState(conn, { scopeExpired: isExpired }) + } + + public isScopeExpired(conn: SsoConnection): boolean { + return this.getConnectionState(conn).scopeExpired + } + + public isConnectionValid(): boolean { + return ( + this.activeConnection !== undefined && + !this.secondaryAuth.isConnectionExpired && + !this.isScopeExpired(this.activeConnection) + ) + } + + // Get rid of this? Not sure where to put PAT code. + public async getPat(client: CodeCatalystClient, username = client.identity.name): Promise { + const stored = await this.storage.getPat(username) + + if (stored) { + return stored + } + + const resp = await client.createAccessToken({ name: 'aws-toolkits-vscode-token' }) + await this.storage.storePat(username, resp.secret) + + return resp.secret + } + + public async getCredentialsForGit(client: CodeCatalystClient) { + getLogger().verbose(`codecatalyst (git): attempting to provide credentials`) + + const username = client.identity.name + + try { + return { + username, + password: await this.getPat(client, username), + } + } catch (err) { + getLogger().verbose(`codecatalyst (git): failed to get credentials for user "${username}": %s`, err) + } + } + + public async restore() { + await this.secondaryAuth.restoreConnection() + } + + private async accessDeniedExceptionHandler(showReauthPrompt: boolean = true) { + if (!this.isConnectionValid()) { + return + } + + await this.setScopeExpired(this.activeConnection!, true) + await setCodeCatalystConnectedContext(this.isConnectionValid()) + + // showReauthPrompt is true primarily when a user interaction triggered the ADE + if (showReauthPrompt) { + void this.showReauthenticationPrompt(this.activeConnection!) + } + } + + public async showReauthenticationPrompt(conn: SsoConnection): Promise { + await showReauthenticateMessage({ + message: localizedText.connectionExpired('CodeCatalyst'), + connect: localizedText.connect, + suppressId: 'codeCatalystConnectionExpired', + settings: ToolkitPromptSettings.instance, + reauthFunc: async () => { + await this.reauthenticate(conn) + }, + }) + } + + public async promptOnboarding(): Promise { + const message = `Using CodeCatalyst requires onboarding with a Space. Sign up with CodeCatalyst to get started.` + const openBrowser = 'Open Browser' + const resp = await vscode.window.showInformationMessage(message, { modal: true }, openBrowser) + if (resp === openBrowser) { + await openUrl(onboardingUrl) + } + + // Mark the current execution as cancelled regardless of the user response. We could poll here instead, waiting + // for the user to onboard. But that might take a while. + throw new ToolkitError('Not onboarded with CodeCatalyst', { code: 'NotOnboarded', cancelled: true }) + } + + /** + * Return a Builder ID connection that works with CodeCatalyst. + * + * This cannot create a Builder ID, but will return an existing Builder ID, + * upgrading the scopes if necessary. + */ + public async tryGetBuilderIdConnection(): Promise { + if (this.activeConnection && isBuilderIdConnection(this.activeConnection)) { + return this.activeConnection + } + + type ConnectionFlowEvent = Partial & { + readonly codecatalyst_connectionFlow: 'Create' | 'Switch' | 'Upgrade' // eslint-disable-line @typescript-eslint/naming-convention + } + + const existingBuilderId = (await this.auth.listConnections()).find(isBuilderIdConnection) + if (isValidCodeCatalystConnection(existingBuilderId)) { + // A Builder ID with the correct scopes already exists so we can use this immediately + await this.secondaryAuth.useNewConnection(existingBuilderId) + return this.activeConnection! + } + + const conn = (await this.auth.listConnections()).find(isBuilderIdConnection) + + if (conn === undefined) { + telemetry.record({ + codecatalyst_connectionFlow: 'Create', + } satisfies ConnectionFlowEvent as MetricShapes[MetricName]) + + const newConn = await createBuilderIdConnection(this.auth, defaultScopes) + if (this.auth.activeConnection?.id !== newConn.id) { + await this.secondaryAuth.useNewConnection(newConn) + } + + return newConn + } + + const upgrade = async () => { + telemetry.record({ + codecatalyst_connectionFlow: 'Upgrade', + } satisfies ConnectionFlowEvent as MetricShapes[MetricName]) + + return this.secondaryAuth.addScopes(conn, defaultScopes) + } + + if (this.auth.activeConnection?.id !== conn.id) { + telemetry.record({ + codecatalyst_connectionFlow: 'Switch', + } satisfies ConnectionFlowEvent as MetricShapes[MetricName]) + + if (isUpgradeableConnection(conn)) { + await upgrade() + } + + return (await this.secondaryAuth.useNewConnection(conn)) as SsoConnection + } + + if (isUpgradeableConnection(conn)) { + return upgrade() + } + + throw new ToolkitError('Not connected to CodeCatalyst', { code: 'NoConnectionBadState' }) + } + + public isConnected(): boolean { + return this.activeConnection !== undefined + } + + public isBuilderIdInUse(): boolean { + return this.isConnected() && isBuilderIdConnection(this.activeConnection) + } + + public isEnterpriseSsoInUse(): boolean { + return this.isConnected() && isIdcSsoConnection(this.activeConnection) + } + + @withTelemetryContext({ name: 'connectToAwsBuilderId', class: authClassName }) + public async connectToAwsBuilderId(): Promise { + let conn: SsoConnection + let isConnectionOnboarded: boolean + + try { + conn = await this.tryGetBuilderIdConnection() + + if (this.auth.getConnectionState(conn) === 'invalid') { + conn = await this.reauthenticate(conn) + } + + isConnectionOnboarded = await this.isConnectionOnboarded(conn, true) + } catch (e) { + throw ToolkitError.chain(e, 'Failed to connect to Builder ID', { + code: 'FailedToConnect', + }) + } + + if (!isConnectionOnboarded) { + await this.promptOnboarding() + } + + return (await this.secondaryAuth.useNewConnection(conn)) as SsoConnection + } + + @withTelemetryContext({ name: 'connectToEnterpriseSso', class: authClassName }) + public async connectToEnterpriseSso(startUrl: string, region: string): Promise { + let conn: SsoConnection | undefined + let isConnectionOnboarded: boolean + + try { + conn = (await this.auth.listConnections()).find( + (c): c is SsoConnection => isSsoConnection(c) && c.startUrl.toLowerCase() === startUrl.toLowerCase() + ) + + if (!conn) { + conn = await this.auth.createConnection(createSsoProfile(startUrl, region, defaultScopes)) + } else if (!isValidCodeCatalystConnection(conn)) { + conn = await this.secondaryAuth.addScopes(conn, defaultScopes) + } + + if (this.auth.getConnectionState(conn) === 'invalid') { + conn = await this.reauthenticate(conn) + } + + isConnectionOnboarded = await this.isConnectionOnboarded(conn, true) + } catch (e) { + throw ToolkitError.chain(e, 'Failed to connect to IAM Identity Center', { + code: 'FailedToConnect', + }) + } + + if (!isConnectionOnboarded) { + await this.promptOnboarding() + } + + return (await this.secondaryAuth.useNewConnection(conn)) as SsoConnection + } + + /** + * Try to ensure a specific connection is active. + */ + @withTelemetryContext({ name: 'tryConnectTo', class: authClassName }) + public async tryConnectTo(connection: { startUrl: string; region: string }) { + if (!this.isConnectionValid() || connection.startUrl !== this.activeConnection!.startUrl) { + if (connection.startUrl === builderIdStartUrl) { + await this.connectToAwsBuilderId() + } else { + await this.connectToEnterpriseSso(connection.startUrl, connection.region) + } + } + } + + @withTelemetryContext({ name: 'reauthenticate', class: authClassName }) + public async reauthenticate(conn: SsoConnection) { + try { + let connToReauth = conn + // Sanity check - connections with other scopes should have been forced out at this point. + if (!hasExactScopes(conn, defaultScopes)) { + const newConn = await setScopes(conn, defaultScopes) + connToReauth = await this.secondaryAuth.useNewConnection(newConn) + } + + return await this.auth.reauthenticate(connToReauth) + } catch (err) { + throw ToolkitError.chain(err, 'Unable to reauthenticate CodeCatalyst connection.') + } + } + + private getStates(): Record { + return globals.globalState.tryGet>('codecatalyst.connections', Object, {}) + } + + public tryGetConnectionState(conn: SsoConnection): ConnectionState | undefined { + return this.getStates()[conn.id] + } + public getConnectionState(conn: SsoConnection): ConnectionState { + return ( + this.tryGetConnectionState(conn) ?? { + onboarded: false, + scopeExpired: false, + } + ) + } + + private async setConnectionState(conn: SsoConnection, state: ConnectionState) { + await globals.globalState.update('codecatalyst.connections', { + ...this.getStates(), + [conn.id]: state, + }) + } + + private async updateConnectionState(conn: SsoConnection, state: Partial) { + const initial = this.getConnectionState(conn) + await this.setConnectionState(conn, { ...initial, ...state }) + } + + public async isConnectionOnboarded(conn: SsoConnection, recheck = false) { + const state = this.tryGetConnectionState(conn) + if (state !== undefined && !recheck) { + return state.onboarded + } + + try { + await createClient(conn) + await this.updateConnectionState(conn, { onboarded: true }) + + return true + } catch (e) { + if (isOnboardingException(e) && this.auth.getConnectionState(conn) === 'valid') { + await this.updateConnectionState(conn, { onboarded: false }) + + return false + } + + throw e + } + + function isOnboardingException(e: unknown) { + // `GetUserDetails` returns `AccessDeniedException` if the user has not onboarded + return isAwsError(e) && e.code === 'AccessDeniedException' && e.message.includes('GetUserDetails') + } + } + + static #instance: CodeCatalystAuthenticationProvider | undefined + + public static get instance(): CodeCatalystAuthenticationProvider | undefined { + return CodeCatalystAuthenticationProvider.#instance + } + + public static fromContext(ctx: Pick) { + return (this.#instance ??= new this(new CodeCatalystAuthStorage(ctx.secrets))) + } +} diff --git a/packages/core/src/codecatalyst/commands.ts b/packages/core/src/codecatalyst/commands.ts new file mode 100644 index 00000000000..509d9b40d01 --- /dev/null +++ b/packages/core/src/codecatalyst/commands.ts @@ -0,0 +1,371 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import globals from '../shared/extensionGlobals' + +import * as nls from 'vscode-nls' +const localize = nls.loadMessageBundle() + +import * as vscode from 'vscode' +import { selectCodeCatalystRepository, selectCodeCatalystResource } from './wizards/selectResource' +import { openCodeCatalystUrl } from './utils' +import { CodeCatalystAuthenticationProvider } from './auth' +import { Commands, VsCodeCommandArg, placeholder } from '../shared/vscode/commands2' +import { CodeCatalystClient, CodeCatalystResource, createClient } from '../shared/clients/codecatalystClient' +import { DevEnvironmentId, getConnectedDevEnv, openDevEnv } from './model' +import { showConfigureDevEnv } from './vue/configure/backend' +import { showCreateDevEnv } from './vue/create/backend' +import { CancellationError } from '../shared/utilities/timeoutUtils' +import { ToolkitError, errorCode } from '../shared/errors' +import { telemetry } from '../shared/telemetry/telemetry' +import { showConfirmationMessage } from '../shared/utilities/messages' +import { AccountStatus } from '../shared/telemetry/telemetryClient' +import { SsoConnection } from '../auth/connection' +import { isInDevEnv, isRemoteWorkspace } from '../shared/vscode/env' +import { commandPalette } from '../codewhisperer/commands/types' +import { CreateDevEnvironmentRequest, UpdateDevEnvironmentRequest } from '@aws-sdk/client-codecatalyst' + +/** "List CodeCatalyst Commands" command. */ +export async function listCommands(): Promise { + await vscode.commands.executeCommand('workbench.action.quickOpen', '> CodeCatalyst') +} + +/** "Clone CodeCatalyst Repository" command. */ +export async function cloneCodeCatalystRepo(client: CodeCatalystClient, url?: vscode.Uri): Promise { + let resource: { name: string; project: string; org: string } + if (!url) { + const r = await selectCodeCatalystRepository(client, false) + if (!r) { + throw new CancellationError('user') + } + resource = { name: r.name, project: r.project.name, org: r.org.name } + } else { + const [_, org, project, repo] = url.path.slice(1).split('/') + if (!org || !project || !repo) { + throw new Error(`Invalid CodeCatalyst URL: unable to parse repository`) + } + resource = { name: repo, project, org } + } + + const uri = await client.getRepoCloneUrl({ + spaceName: resource.org, + projectName: resource.project, + sourceRepositoryName: resource.name, + }) + await vscode.commands.executeCommand('git.clone', uri) +} + +/** + * Implements commands: + * - "Open CodeCatalyst Space" + * - "Open CodeCatalyst Project" + * - "Open CodeCatalyst Repository" + */ +export async function openCodeCatalystResource( + client: CodeCatalystClient, + kind: CodeCatalystResource['type'] +): Promise { + const resource = await selectCodeCatalystResource(client, kind) + + if (!resource) { + throw new CancellationError('user') + } + + openCodeCatalystUrl(resource) +} + +export async function stopDevEnv( + client: CodeCatalystClient, + devenv: DevEnvironmentId, + opts?: { readonly showPrompt?: boolean } +): Promise { + if (opts?.showPrompt) { + const confirmed = await showConfirmationMessage({ + prompt: localize( + 'aws.codecatalyst.stopDevEnv.confirm', + 'Stopping the Dev Environment will end all processes. Continue?' + ), + }) + + if (!confirmed) { + throw new CancellationError('user') + } + } + + await client.stopDevEnvironment({ + id: devenv.id, + projectName: devenv.project.name, + spaceName: devenv.org.name, + }) +} + +export async function deleteDevEnv(client: CodeCatalystClient, devenv: DevEnvironmentId): Promise { + await client.deleteDevEnvironment({ + id: devenv.id, + projectName: devenv.project.name, + spaceName: devenv.org.name, + }) +} + +export type DevEnvironmentSettings = Pick< + CreateDevEnvironmentRequest, + 'alias' | 'instanceType' | 'inactivityTimeoutMinutes' | 'persistentStorage' +> + +export type UpdateDevEnvironmentSettings = Pick< + UpdateDevEnvironmentRequest, + 'alias' | 'instanceType' | 'inactivityTimeoutMinutes' +> + +export async function updateDevEnv( + client: CodeCatalystClient, + devenv: DevEnvironmentId, + settings: UpdateDevEnvironmentSettings +) { + return client.updateDevEnvironment({ + ...settings, + id: devenv.id, + projectName: devenv.project.name, + spaceName: devenv.org.name, + }) +} + +function createClientInjector(authProvider: CodeCatalystAuthenticationProvider): ClientInjector { + return async (command, ...args) => { + telemetry.record({ userId: AccountStatus.NotSet }) + + await authProvider.restore() + const conn = authProvider.activeConnection + if (!conn) { + // TODO: In the future, it would be very nice to open a connection picker here. + throw new ToolkitError('Not connected to CodeCatalyst', { code: 'NoConnectionBadState' }) + } + const validatedConn = await validateConnection(conn, authProvider) + const client = await createClient(validatedConn) + telemetry.record({ userId: client.identity.id }) + + return command(client, ...args) + } +} + +/** + * Returns a connection that is ensured to be authenticated. + * + * Provides the user the ability to re-authenticate if needed, + * otherwise throwing an error. + */ +async function validateConnection( + conn: SsoConnection, + authProvider: CodeCatalystAuthenticationProvider +): Promise { + if (authProvider.auth.getConnectionState(conn) === 'valid') { + return conn + } + + // Have user try to log in + const loginMessage = localize('aws.auth.invalidConnection', 'Connection is invalid or expired, login again?') + const result = await vscode.window.showErrorMessage(loginMessage, 'Login') + + if (result !== 'Login') { + throw new ToolkitError('User cancelled login.', { cancelled: true, code: errorCode.invalidConnection }) + } + + conn = await authProvider.reauthenticate(conn) + + // Log in attempt failed + if (authProvider.auth.getConnectionState(conn) !== 'valid') { + throw new ToolkitError('Login failed.', { code: errorCode.invalidConnection }) + } + + return conn +} + +function createCommandDecorator(commands: CodeCatalystCommands): CommandDecorator { + return (command) => + (...args) => + commands.withClient(command, ...args) +} + +interface CodeCatalystCommand { + (client: CodeCatalystClient, ...args: T): U | Promise +} + +interface ClientInjector { + (command: CodeCatalystCommand, ...args: T): Promise +} + +interface CommandDecorator { + (command: CodeCatalystCommand): (...args: T) => Promise +} + +type Inject = T extends (...args: infer P) => infer R + ? P extends [U, ...infer L] + ? (...args: L) => R + : never + : never + +type WithClient = Parameters> + +class RemoteContextError extends ToolkitError { + constructor() { + super('Cannot connect from a remote context. Try again from a local VS Code instance.', { + code: 'ConnectedToRemote', + }) + } +} + +export const codecatalystConnectionsCmd = Commands.declare( + 'aws.codecatalyst.manageConnections', + () => () => + void vscode.commands.executeCommand( + 'aws.toolkit.auth.manageConnections', + placeholder, + 'codecatalystDeveloperTools', + 'codecatalyst' + ) +) + +export class CodeCatalystCommands { + public readonly withClient: ClientInjector + public readonly bindClient = createCommandDecorator(this) + + public constructor(private authProvider: CodeCatalystAuthenticationProvider) { + this.withClient = createClientInjector(authProvider) + } + + public listCommands() { + return listCommands() + } + + public cloneRepo(_?: VsCodeCommandArg, ...args: WithClient) { + return this.withClient(cloneCodeCatalystRepo, ...args) + } + + public createDevEnv(_?: VsCodeCommandArg): Promise { + if (isRemoteWorkspace() && isInDevEnv()) { + throw new RemoteContextError() + } + return this.withClient(showCreateDevEnv, globals.context, CodeCatalystCommands.declared) + } + + public openResource(...args: WithClient) { + return this.withClient(openCodeCatalystResource, ...args) + } + + public stopDevEnv(...args: WithClient) { + return this.withClient(stopDevEnv, ...args).then(() => { + void vscode.commands.executeCommand('workbench.action.remote.close') + }) + } + + public deleteDevEnv(...args: WithClient) { + return this.withClient(deleteDevEnv, ...args) + } + + public updateDevEnv(...args: WithClient) { + telemetry.record({ + codecatalyst_updateDevEnvironmentLocationType: 'remote', + }) + + return this.withClient(updateDevEnv, ...args) + } + + public openSpace() { + return this.openResource('org') + } + + public openProject() { + return this.openResource('project') + } + + public openRepository() { + return this.openResource('repo') + } + + public async openDevfile(uri: vscode.Uri) { + await vscode.window.showTextDocument(uri) + } + + public async openDevEnv( + _?: VsCodeCommandArg, + id?: DevEnvironmentId, + targetPath?: string, + connection?: { startUrl: string; region: string } + ): Promise { + if (isRemoteWorkspace() && isInDevEnv()) { + throw new RemoteContextError() + } + + const devenv = id ?? (await this.selectDevEnv()) + + // TODO(sijaden): add named timestamp markers for granular duration info + // + // right now this command may prompt the user if they came from the explorer or command palette + // need to be careful of mapping explosion so this granular data would either need + // to be flattened or we restrict the names to a pre-determined set + if (id === undefined) { + telemetry.record({ source: commandPalette }) + } + + if (connection !== undefined) { + await this.authProvider.tryConnectTo(connection) + } else if (!this.authProvider.isConnectionValid()) { + void codecatalystConnectionsCmd.execute() + return + } + + return this.withClient(openDevEnv, devenv, targetPath) + } + + public async openDevEnvSettings(): Promise { + const devenv = await this.withClient(getConnectedDevEnv) + + return this.withClient(showConfigureDevEnv, globals.context, devenv, CodeCatalystCommands.declared) + } + + private async selectDevEnv(): Promise { + const devenv = await this.withClient(selectCodeCatalystResource, 'devEnvironment' as const) + + if (!devenv) { + throw new CancellationError('user') + } + + return devenv + } + + public static fromContext(ctx: Pick) { + const auth = CodeCatalystAuthenticationProvider.fromContext(ctx) + + return new this(auth) + } + + public static readonly declared = { + openResource: Commands.from(this).declareOpenResource('aws.codecatalyst.openResource'), + listCommands: Commands.from(this).declareListCommands('aws.codecatalyst.listCommands'), + openSpace: Commands.from(this).declareOpenSpace('aws.codecatalyst.openOrg'), + openProject: Commands.from(this).declareOpenProject('aws.codecatalyst.openProject'), + openRepository: Commands.from(this).declareOpenRepository('aws.codecatalyst.openRepo'), + stopDevEnv: Commands.from(this).declareStopDevEnv('aws.codecatalyst.stopDevEnv'), + deleteDevEnv: Commands.from(this).declareDeleteDevEnv('aws.codecatalyst.deleteDevEnv'), + openDevEnvSettings: Commands.from(this).declareOpenDevEnvSettings('aws.codecatalyst.openDevEnvSettings'), + openDevfile: Commands.from(this).declareOpenDevfile('aws.codecatalyst.openDevfile'), + cloneRepo: Commands.from(this).declareCloneRepo({ + id: 'aws.codecatalyst.cloneRepo', + telemetryName: 'codecatalyst_localClone', + }), + createDevEnv: Commands.from(this).declareCreateDevEnv({ + id: 'aws.codecatalyst.createDevEnv', + telemetryName: 'codecatalyst_createDevEnvironment', + }), + updateDevEnv: Commands.from(this).declareUpdateDevEnv({ + id: 'aws.codecatalyst.updateDevEnv', + telemetryName: 'codecatalyst_updateDevEnvironmentSettings', + }), + openDevEnv: Commands.from(this).declareOpenDevEnv({ + id: 'aws.codecatalyst.openDevEnv', + telemetryName: 'codecatalyst_connect', + }), + } as const +} diff --git a/packages/core/src/codecatalyst/devEnv.ts b/packages/core/src/codecatalyst/devEnv.ts new file mode 100644 index 00000000000..962c772eb2f --- /dev/null +++ b/packages/core/src/codecatalyst/devEnv.ts @@ -0,0 +1,286 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DevEnvironment } from '../shared/clients/codecatalystClient' +import { DevEnvActivity, DevEnvClient } from '../shared/clients/devenvClient' +import globals from '../shared/extensionGlobals' +import * as vscode from 'vscode' +import { waitUntil } from '../shared/utilities/timeoutUtils' +import { getLogger } from '../shared/logger/logger' +import { CodeCatalystAuthenticationProvider } from './auth' +import { getThisDevEnv } from './model' +import { isInDevEnv } from '../shared/vscode/env' +import { shared } from '../shared/utilities/functionUtils' +import { DevSettings } from '../shared/settings' + +/** Starts the {@link DevEnvActivity} Hearbeat mechanism. */ +export class DevEnvActivityStarter { + private static _instance: DevEnvActivityStarter | undefined = undefined + + /** + * Trys to start the {@link DevEnvActivity} heartbeat. + * This mechanism keeps the Dev Env from timing out. + */ + public static init(authProvider: CodeCatalystAuthenticationProvider) { + if (!isInDevEnv()) { + getLogger().debug('codecatalyst: not in a devenv, skipping DevEnvActivity setup') + return + } + + DevEnvActivityStarter._instance ??= new DevEnvActivityStarter(authProvider) + DevEnvActivityStarter._instance.retryStartActivityHeartbeat().catch((e) => { + getLogger().error('retryStartActivityHeartbeat failed: %s', (e as Error).message) + }) + } + + private devEnvActivity: DevEnvActivity | undefined = undefined + private onDidChangeAuth: ReturnType + + protected constructor(private readonly authProvider: CodeCatalystAuthenticationProvider) { + this.onDidChangeAuth = authProvider.onDidChangeActiveConnection(() => { + this.tryStartActivityHeartbeat(true).catch((e) => { + getLogger().error('tryStartActivityHeartbeat failed: %s', (e as Error).message) + }) + }) + globals.context.subscriptions.push(this.onDidChangeAuth) + } + + /** + * Keeps executing {@link DevEnvActivityStarter.tryStartActivityHeartbeat} on an interval until it succeeds. + * After a certain amount of time this will stop retrying. + */ + private async retryStartActivityHeartbeat() { + return waitUntil( + async () => { + await this.tryStartActivityHeartbeat(false) + return !!this.devEnvActivity + }, + { interval: 20_000, timeout: 60_000 * 5 } + ) + } + + /** Trys to start the Activity Heartbeat mechanism */ + private tryStartActivityHeartbeat = shared(async (reauth: boolean) => { + if (!!this.devEnvActivity && !reauth) { + return + } + + const thisDevenv = (await getThisDevEnv(this.authProvider))?.unwrapOrElse((err) => { + getLogger().warn('codecatalyst: failed to get current devenv: %s', err) + return undefined + }) + + if (!thisDevenv && reauth) { + getLogger().warn('codecatalyst: failed to get devenv after reauthenticate attempt') + return + } else if (!thisDevenv) { + const connection = this.authProvider.activeConnection + if (connection) { + void vscode.window + .showErrorMessage('CodeCatalyst: Reauthenticate your connection.', 'Reauthenticate') + .then(async (res) => { + if (res !== 'Reauthenticate') { + return + } + await this.authProvider.reauthenticate(connection) + }) + } + getLogger().warn( + 'codecatalyst: starting DevEnvActivity heartbeat without auth (unknown inactivityTimeoutMinutes)' + ) + } + + const devenvTimeoutMs = DevSettings.instance.get('devenvTimeoutMs', 0) + if (devenvTimeoutMs) { + getLogger().warn('codecatalyst: using devenvTimeoutMs=%d', devenvTimeoutMs) + } + // If user is not authenticated, assume 15 minutes. + const inactivityTimeoutMin = + devenvTimeoutMs > 0 ? devenvTimeoutMs / 60000 : (thisDevenv?.summary.inactivityTimeoutMinutes ?? 15) + if (!shouldSendActivity(inactivityTimeoutMin)) { + getLogger().info( + `codecatalyst: disabling DevEnvActivity heartbeat: configured to never timeout (inactivityTimeoutMinutes=${inactivityTimeoutMin})` + ) + return + } + + const devEnvActivity = await DevEnvActivity.create(thisDevenv?.devenvClient ?? DevEnvClient.instance) + if (!devEnvActivity) { + getLogger().error('codecatalyst: failed to start DevEnvActivity heartbeat, devenv may timeout') + return + } + + if (this.devEnvActivity) { + // Special case: user authenticated, so reinitialize with the correct `inactivityTimeoutMinutes`. + this.devEnvActivity.dispose() + } + + // Everything is good, we can start the activity heartbeat now + devEnvActivity.setUpdateActivityOnIdeActivity(true) + + // Setup the "shutdown imminent" message. Skip this if we aren't authenticated, because we + // don't know the actual `inactivityTimeoutMinutes`. + if (thisDevenv) { + const inactivityMessage = new InactivityMessage() + await inactivityMessage.init(inactivityTimeoutMin, devEnvActivity) + globals.context.subscriptions.push(inactivityMessage) + this.onDidChangeAuth.dispose() // Don't need to wait for reauth now. + getLogger().debug('codecatalyst: setup InactivityMessage') + } + + globals.context.subscriptions.push(devEnvActivity) + this.devEnvActivity = devEnvActivity + }) +} + +/** + * Should we send activity heartbeat? + * + * If inactivityTimeoutMinutes=0 then it never expires, so Toolkit doesn't need to send heartbeat. + */ +export function shouldSendActivity(inactivityTimeoutMin: DevEnvironment['inactivityTimeoutMinutes']): boolean { + // This value is initialized when the dev env is first created. If it is updated, MDE restarts + // the dev env and this extension will grab the new value on startup. + // https://docs.aws.amazon.com/codecatalyst/latest/APIReference/API_UpdateDevEnvironment.html#codecatalyst-UpdateDevEnvironment-request-inactivityTimeoutMinutes + return inactivityTimeoutMin > 0 +} + +/** Shows a "Dev env will shutdown in x minutes due to inactivity" warning. */ +export class InactivityMessage implements vscode.Disposable { + #showMessageTimer: NodeJS.Timeout | undefined + /** Show a message this many minutes before auto-shutdown. */ + public readonly shutdownWarningThreshold = 5 + + /** + * Creates a timer which will show warning messsage(s) when the dev env is nearing auto-shutdown because of inactivity. + * + * @param maxInactivityMinutes Maximum inactivity allowed before auto-shutdown. + * @param devEnvActivity DevEnvActivity client + * @param oneMin Milliseconds in a "minute". Used in tests. + */ + async init( + maxInactivityMinutes: DevEnvironment['inactivityTimeoutMinutes'], + devEnvActivity: DevEnvActivity, + oneMin: number = 60_000 + ) { + // Send an initial update to the dev env on startup + await devEnvActivity.sendActivityUpdate() + + // Reset (redefine) the timer whenever user is active. + devEnvActivity.onActivityUpdate(async (lastActivity) => { + this.clear() + + const { millisToWait, minutesSinceActivity } = this.millisUntilNextWholeMinute(lastActivity, oneMin) + const minutesUntilShutdown = Math.round(maxInactivityMinutes - minutesSinceActivity) + const minutesUntilMessage = Math.round(Math.max(0, minutesUntilShutdown - this.shutdownWarningThreshold)) + const timerInterval = millisToWait + minutesUntilMessage * oneMin + + if (timerInterval <= 10 * oneMin || DevSettings.instance.get('devenvTimeoutMs', 0)) { + getLogger().debug( + 'InactivityMessage: minutesUntilMessage=%d timerInterval=%d', + minutesUntilMessage, + timerInterval + ) + } + + /** Wait until we are {@link InactivityMessage.shutdownWarningThreshold} minutes before shutdown. */ + this.#showMessageTimer = globals.clock.setTimeout(() => { + const userIsActive = () => { + devEnvActivity.sendActivityUpdate().catch((e) => { + getLogger().error('DevEnvActivity.sendActivityUpdate failed: %s', (e as Error).message) + }) + } + + const willRefreshOnStaleTimestamp = async () => await devEnvActivity.isLocalActivityStale() + + this.clear() + this.show( + Math.round(Math.max(0, minutesSinceActivity + minutesUntilMessage)), + Math.round(Math.max(0, minutesUntilShutdown - minutesUntilMessage)), + userIsActive, + willRefreshOnStaleTimestamp + ).catch((e) => { + getLogger().error('InactivityMessage.show failed: %s', (e as Error).message) + }) + }, timerInterval) + }) + } + + /** + * The latest activity timestamp may not always be the current time, it may be from the past. + * So the amount of time that has passed since that timestamp may not be a whole minute. + * + * This returns the amount of time we need to wait until the next whole minute, along with how many + * minutes would have passed assuming the caller waitied until the next minute. + * + * Eg: + * - 1 minute and 29 seconds have passed since the given timestamp. + * - returns { millisToWait: 31_000, minutesSinceActivity: 2} + */ + private millisUntilNextWholeMinute( + latestTimestamp: number, + oneMin: number + ): { millisToWait: number; minutesSinceActivity: number } { + const millisSinceLastActivity = Date.now() - latestTimestamp + const millisToNextMinute = millisSinceLastActivity % oneMin + + const millisToWait = millisToNextMinute !== 0 ? oneMin - millisToNextMinute : 0 + const minutesSinceActivity = (millisSinceLastActivity + millisToWait) / oneMin + + return { millisToWait, minutesSinceActivity } + } + + /** + * Show the warning message + * + * @param minutesUserWasInactive total minutes user was inactive. + * @param minutesUntilShutdown remaining minutes until shutdown. + * @param userIsActive Call this to signal that user is active. + * @param willRefreshOnStaleTimestamp sanity checks with the dev env api that the latest activity timestamp + * is the same as what this client has locally. If stale, the warning message + * will be refreshed asynchronously. Returns true if the message will be refreshed. + */ + async show( + minutesUserWasInactive: number, + minutesUntilShutdown: number, + userIsActive: () => void, + willRefreshOnStaleTimestamp: () => Promise + ) { + getLogger().debug( + 'InactivityMessage.show: minutesUserWasInactive=%d minutesUntilShutdown=%d', + minutesUserWasInactive, + minutesUntilShutdown + ) + + if (await willRefreshOnStaleTimestamp()) { + return + } + + const imHere = "I'm here!" + return vscode.window + .showWarningMessage( + `Your CodeCatalyst Dev Environment has been inactive for ${minutesUserWasInactive} minutes, and will stop soon.`, + { modal: true }, + imHere + ) + .then((res) => { + if (res === imHere) { + userIsActive() + } + }) + } + + /** Cancels or disposes the existing message and timer. */ + clear() { + if (this.#showMessageTimer) { + clearTimeout(this.#showMessageTimer) + this.#showMessageTimer = undefined + } + } + + dispose() { + this.clear() + } +} diff --git a/src/codecatalyst/devfile.ts b/packages/core/src/codecatalyst/devfile.ts similarity index 85% rename from src/codecatalyst/devfile.ts rename to packages/core/src/codecatalyst/devfile.ts index 42683f56c5e..f8a857e2555 100644 --- a/src/codecatalyst/devfile.ts +++ b/packages/core/src/codecatalyst/devfile.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -10,13 +10,13 @@ import * as vscode from 'vscode' import * as path from 'path' import { DevEnvClient } from '../shared/clients/devenvClient' import { DevfileRegistry, devfileGlobPattern } from '../shared/fs/devfileRegistry' -import { getLogger } from '../shared/logger' +import { getLogger } from '../shared/logger/logger' import { Commands } from '../shared/vscode/commands2' import { checkUnsavedChanges } from '../shared/utilities/workspaceUtils' import { ToolkitError } from '../shared/errors' async function updateDevfile(uri: vscode.Uri): Promise { - const client = new DevEnvClient() + const client = DevEnvClient.instance if (!client.isCodeCatalystDevEnv()) { throw new Error('Cannot update devfile outside a Dev Environment') } @@ -42,12 +42,12 @@ async function updateDevfile(uri: vscode.Uri): Promise { // TODO: accurate telemetry is hard to capture here } -export const updateDevfileCommand = Commands.register( +export const updateDevfileCommand = Commands.declare( { id: 'aws.codecatalyst.updateDevfile', telemetryName: 'codecatalyst_updateDevfile', }, - updateDevfile + () => (uri) => updateDevfile(uri) ) type Workspace = Pick @@ -60,17 +60,17 @@ export class DevfileCodeLensProvider implements vscode.CodeLensProvider { public constructor( registry: DevfileRegistry, - private readonly client = new DevEnvClient(), + private readonly client = DevEnvClient.instance, workspace: Workspace = vscode.workspace ) { this.disposables.push(this._onDidChangeCodeLenses) this.disposables.push( - workspace.onDidSaveTextDocument(async document => { - if (!registry.getRegisteredItem(document.fileName)) { + workspace.onDidSaveTextDocument(async (document) => { + if (!registry.getItem(document.fileName)) { return } - await this.handleUpdate(document).catch(err => { + await this.handleUpdate(document).catch((err) => { getLogger().debug(`codecatalyst: devfile codelens failure: ${err?.message}`) }) }) @@ -110,7 +110,10 @@ export class DevfileCodeLensProvider implements vscode.CodeLensProvider { export function registerDevfileWatcher(devenvClient: DevEnvClient): vscode.Disposable { const registry = new DevfileRegistry() const codelensProvider = new DevfileCodeLensProvider(registry, devenvClient) - registry.addWatchPattern(devfileGlobPattern) + registry.addWatchPatterns([devfileGlobPattern]) + registry.rebuild().catch((e) => { + getLogger().error('WatchedFiles.rebuild failed: %s', (e as Error).message) + }) const codelensDisposable = vscode.languages.registerCodeLensProvider( { diff --git a/packages/core/src/codecatalyst/explorer.ts b/packages/core/src/codecatalyst/explorer.ts new file mode 100644 index 00000000000..af3d6a20474 --- /dev/null +++ b/packages/core/src/codecatalyst/explorer.ts @@ -0,0 +1,228 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { DevEnvironment } from '../shared/clients/codecatalystClient' +import { addColor, getIcon } from '../shared/icons' +import { TreeNode } from '../shared/treeview/resourceTreeDataProvider' +import { Commands } from '../shared/vscode/commands2' +import { CodeCatalystAuthenticationProvider } from './auth' +import { CodeCatalystCommands, codecatalystConnectionsCmd } from './commands' +import { ConnectedDevEnv, getDevfileLocation, getThisDevEnv } from './model' +import * as codecatalyst from './model' +import { getLogger } from '../shared/logger/logger' +import { SsoConnection } from '../auth/connection' +import { openUrl } from '../shared/utilities/vsCodeUtils' + +export const learnMoreCommand = Commands.declare('aws.learnMore', () => async (docsUrl: vscode.Uri) => { + return openUrl(docsUrl) +}) + +export const reauth = Commands.declare( + '_aws.codecatalyst.reauthenticate', + () => async (conn: SsoConnection, authProvider: CodeCatalystAuthenticationProvider) => { + await authProvider.reauthenticate(conn) + } +) + +export const onboardCommand = Commands.declare( + '_aws.codecatalyst.onboard', + () => async (authProvider: CodeCatalystAuthenticationProvider) => { + void authProvider.promptOnboarding() + } +) + +async function getLocalCommands(auth: CodeCatalystAuthenticationProvider) { + const docsUrl = codecatalyst.docs.overview + const learnMoreNode = learnMoreCommand.build(docsUrl).asTreeNode({ + label: 'Learn more about CodeCatalyst', + iconPath: getIcon('vscode-question'), + }) + + // There is a connection, but it is expired, or CodeCatalyst scopes are expired. + if (auth.activeConnection && !auth.isConnectionValid()) { + return [ + reauth.build(auth.activeConnection, auth).asTreeNode({ + label: 'Re-authenticate to connect', + iconPath: addColor(getIcon('vscode-debug-disconnect'), 'notificationsErrorIcon.foreground'), + }), + learnMoreNode, + ] + } + + if (!auth.activeConnection) { + return [ + codecatalystConnectionsCmd.build().asTreeNode({ + label: 'Sign in to get started', + iconPath: getIcon('vscode-account'), + }), + learnMoreNode, + ] + } + + // We are connected but not onboarded, so show them button to onboard + if (!(await auth.isConnectionOnboarded(auth.activeConnection))) { + return [ + onboardCommand.build(auth).asTreeNode({ + label: 'Onboard CodeCatalyst to get started', + iconPath: getIcon('vscode-account'), + }), + learnMoreNode, + ] + } + + return [ + CodeCatalystCommands.declared.cloneRepo.build().asTreeNode({ + label: 'Clone Repository', + iconPath: getIcon('vscode-symbol-namespace'), + }), + CodeCatalystCommands.declared.openDevEnv.build().asTreeNode({ + label: 'Open Dev Environment', + iconPath: getIcon('vscode-vm-connect'), + }), + CodeCatalystCommands.declared.createDevEnv.build().asTreeNode({ + label: 'Create Dev Environment', + iconPath: getIcon('vscode-add'), + }), + CodeCatalystCommands.declared.listCommands.build().asTreeNode({ + label: 'Show CodeCatalyst Commands', + iconPath: getIcon('vscode-list-flat'), // TODO(sijaden): use better icon + }), + ] +} + +function getRemoteCommands(currentDevEnv: DevEnvironment, devfileLocation: vscode.Uri) { + return [ + CodeCatalystCommands.declared.stopDevEnv.build(currentDevEnv, { showPrompt: true }).asTreeNode({ + label: 'Stop Dev Environment', + iconPath: getIcon('vscode-stop-circle'), + }), + CodeCatalystCommands.declared.openDevEnvSettings.build().asTreeNode({ + label: 'Open Settings', + iconPath: getIcon('vscode-settings-gear'), + }), + CodeCatalystCommands.declared.openDevfile.build(devfileLocation).asTreeNode({ + label: 'Open Devfile', + iconPath: getIcon('vscode-symbol-namespace'), + description: vscode.workspace.asRelativePath(devfileLocation), + }), + ] +} + +export class CodeCatalystRootNode implements TreeNode { + public readonly id = 'codecatalyst' + public readonly resource = this.devenv + + private readonly onDidChangeEmitter = new vscode.EventEmitter() + + public readonly onDidChangeVisibility = this.onDidChangeEmitter.event + public readonly onDidChangeTreeItem = this.onDidChangeEmitter.event + public readonly onDidChangeChildren = this.onDidChangeEmitter.event + + private refreshEmitters: (() => void)[] = [] + + private devenv?: ConnectedDevEnv + private resolveDevEnv: Promise | undefined = undefined + + public constructor(private readonly authProvider: CodeCatalystAuthenticationProvider) { + this.addRefreshEmitter(() => this.onDidChangeEmitter.fire()) + + this.authProvider.onDidChange(() => { + for (const fire of this.refreshEmitters) { + fire() + } + }) + } + + public async getChildren(): Promise { + await this.initDevEnvLoad() + + if (!this.devenv) { + return getLocalCommands(this.authProvider) + } + + const devfileLocation = await getDevfileLocation(this.devenv.devenvClient) + + return getRemoteCommands(this.devenv.summary, devfileLocation) + } + + /** + * HACK: Since this is assumed to be an immediate child of the + * root, we return undefined. + * + * TODO: Look to have a base root class to extend so we do not + * need to implement this here. + * @returns + */ + getParent(): TreeNode | undefined { + return undefined + } + + public async getTreeItem() { + await this.initDevEnvLoad() + + await this.authProvider.restore() + + const item = new vscode.TreeItem('CodeCatalyst', vscode.TreeItemCollapsibleState.Collapsed) + item.contextValue = this.authProvider.isUsingSavedConnection + ? 'awsCodeCatalystNodeSaved' + : 'awsCodeCatalystNode' + + if (this.devenv !== undefined) { + item.description = 'Connected to Dev Environment' + item.iconPath = addColor(getIcon('vscode-pass'), 'testing.iconPassed') + } else { + item.description = this.getDescription() + } + + return item + } + + private getDescription(): string { + if (this.authProvider.activeConnection) { + if (this.authProvider.secondaryAuth.isConnectionExpired) { + return 'Expired Connection' + } + if (this.authProvider.isBuilderIdInUse()) { + return 'AWS Builder ID Connected' + } + if (this.authProvider.isEnterpriseSsoInUse()) { + return 'IAM Identity Center Connected' + } + } + return '' + } + + public addRefreshEmitter(emitter: () => void) { + this.refreshEmitters.push(emitter) + } + + /** + * CALL THIS BEFORE USING `this.devenv` + * Ensures that the node has attempted to load the `devenv` field by + * creating a promise that can be awaited if init has already begun and is being called elsewhere. + */ + private async initDevEnvLoad() { + if (this.resolveDevEnv) { + await this.resolveDevEnv + return + } + let resolve: ((val: boolean) => void) | undefined + if (this.resolveDevEnv === undefined) { + this.resolveDevEnv = new Promise((res) => { + resolve = res + }) + } + + this.devenv = (await getThisDevEnv(this.authProvider))?.unwrapOrElse((e) => { + const err = e as Error + getLogger().warn('codecatalyst: failed to get current Dev Enviroment: %s', err.message) + return undefined + }) + if (resolve !== undefined) { + resolve(true) + } + } +} diff --git a/packages/core/src/codecatalyst/model.ts b/packages/core/src/codecatalyst/model.ts new file mode 100644 index 00000000000..0451f9be95b --- /dev/null +++ b/packages/core/src/codecatalyst/model.ts @@ -0,0 +1,309 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import globals from '../shared/extensionGlobals' + +import * as vscode from 'vscode' +import * as path from 'path' +import { + createClient, + CodeCatalystClient, + DevEnvironment, + CodeCatalystRepo, + getCodeCatalystConfig, +} from '../shared/clients/codecatalystClient' +import { DevEnvClient } from '../shared/clients/devenvClient' +import { getLogger } from '../shared/logger/logger' +import { AsyncCollection, toCollection } from '../shared/utilities/asyncCollection' +import { getCodeCatalystSpaceName, getCodeCatalystProjectName, getCodeCatalystDevEnvId } from '../shared/vscode/env' +import { sshAgentSocketVariable, startSshAgent, startVscodeRemote } from '../shared/extensions/ssh' +import { isDevenvVscode } from './utils' +import { Timeout } from '../shared/utilities/timeoutUtils' +import { Commands } from '../shared/vscode/commands2' +import { areEqual } from '../shared/utilities/pathUtils' +import { fileExists } from '../shared/filesystemUtilities' +import { CodeCatalystAuthenticationProvider } from './auth' +import { ToolkitError } from '../shared/errors' +import { Result } from '../shared/utilities/result' +import { EnvProvider, VscodeRemoteConnection, createBoundProcess, ensureDependencies } from '../shared/remoteSession' +import { SshConfig, sshLogFileLocation } from '../shared/sshConfig' +import { fs } from '../shared/fs/fs' + +export type DevEnvironmentId = Pick +export const connectScriptPrefix = 'codecatalyst_connect' + +export const docs = { + main: vscode.Uri.parse('https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/codecatalyst-service'), + overview: vscode.Uri.parse( + 'https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/codecatalyst-overview.html' + ), + devenv: vscode.Uri.parse( + 'https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/codecatalyst-devenvironment.html' + ), + setup: vscode.Uri.parse('https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/codecatalyst-setup.html'), + troubleshoot: vscode.Uri.parse( + 'https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/codecatalyst-troubleshoot.html' + ), +} as const + +export function getCodeCatalystSsmEnv(region: string, ssmPath: string, devenv: DevEnvironmentId): NodeJS.ProcessEnv { + return Object.assign( + { + AWS_REGION: region, + AWS_SSM_CLI: ssmPath, + CODECATALYST_ENDPOINT: getCodeCatalystConfig().endpoint, + BEARER_TOKEN_LOCATION: bearerTokenCacheLocation(devenv.id), + LOG_FILE_LOCATION: sshLogFileLocation('codecatalyst', devenv.id), + SPACE_NAME: devenv.org.name, + PROJECT_NAME: devenv.project.name, + DEVENV_ID: devenv.id, + }, + process.env + ) +} + +export function createCodeCatalystEnvProvider( + client: CodeCatalystClient, + ssmPath: string, + devenv: DevEnvironment, + useSshAgent: boolean = true +): EnvProvider { + return async () => { + await cacheBearerToken(await client.getBearerToken(), devenv.id) + const vars = getCodeCatalystSsmEnv(client.regionCode, ssmPath, devenv) + + return useSshAgent ? { [sshAgentSocketVariable]: await startSshAgent(), ...vars } : vars + } +} + +export async function cacheBearerToken(bearerToken: string, devenvId: string): Promise { + await fs.writeFile(bearerTokenCacheLocation(devenvId), `${bearerToken}`, 'utf8') +} + +export function bearerTokenCacheLocation(devenvId: string): string { + return path.join(globals.context.globalStorageUri.fsPath, `codecatalyst.${devenvId}.token`) +} + +export interface ConnectedDevEnv { + readonly summary: DevEnvironment + readonly devenvClient: DevEnvClient +} + +export async function getConnectedDevEnv( + codeCatalystClient: CodeCatalystClient, + devenvClient = DevEnvClient.instance +): Promise { + const devEnvId = devenvClient.id + if (!devEnvId) { + throw new ToolkitError('Not connected to a dev environment', { code: 'NotConnectedToDevEnv' }) + } + + const projectName = getCodeCatalystProjectName() + const spaceName = getCodeCatalystSpaceName() + + if (!projectName || !spaceName) { + throw new Error('No project or space name found') + } + + const summary = await codeCatalystClient.getDevEnvironment({ + projectName: projectName, + spaceName: spaceName, + id: devEnvId, + }) + + return { summary, devenvClient: devenvClient } +} + +/** + * Gets the current devenv that Toolkit is running in, if any. + */ +export async function getThisDevEnv(authProvider: CodeCatalystAuthenticationProvider) { + if (!getCodeCatalystDevEnvId()) { + return + } + + try { + await authProvider.restore() + const conn = authProvider.activeConnection + if (conn !== undefined && authProvider.auth.getConnectionState(conn) === 'valid') { + const client = await createClient(conn) + return Result.ok(await getConnectedDevEnv(client)) + } + } catch (err) { + return Result.err(err) + } +} + +/** + * Everything needed to connect to a dev environment via VS Code or `ssh` + */ +interface DevEnvConnection extends VscodeRemoteConnection { + readonly devenv: DevEnvironment +} + +export async function prepareDevEnvConnection( + client: CodeCatalystClient, + { id, org, project }: DevEnvironmentId, + { topic, timeout }: { topic?: string; timeout?: Timeout } = {} +): Promise { + const { ssm, vsc, ssh } = (await ensureDependencies()).unwrap() + const hostNamePrefix = 'aws-devenv-' + const sshConfig = new SshConfig(ssh, hostNamePrefix, connectScriptPrefix) + const config = await sshConfig.ensureValid() + + if (config.isErr()) { + const err = config.err() + getLogger().error(`codecatalyst: failed to add ssh config section: ${err.message}`) + + throw err + } + + const runningDevEnv = await client.startDevEnvironmentWithProgress({ + id, + spaceName: org.name, + projectName: project.name, + }) + + const hostname = `${hostNamePrefix}${id}` + const logPrefix = topic ? `codecatalyst ${topic} (${id})` : `codecatalyst (${id})` + const logger = (data: string) => getLogger().verbose(`${logPrefix}: ${data}`) + const envProvider = createCodeCatalystEnvProvider(client, ssm, runningDevEnv) + const SessionProcess = createBoundProcess(envProvider).extend({ + timeout, + onStdout: logger, + onStderr: logger, + rejectOnErrorCode: true, + }) + + return { + hostname, + devenv: runningDevEnv, + envProvider, + sshPath: ssh, + vscPath: vsc, + SessionProcess, + } +} + +/** + * Starts the given devenv, waits, and connects a new vscode instance when the devenv is ready. + * + * @param client CodeCatalyst service client + * @param devenv DevEnvironment to open + * @param targetPath vscode workspace (default: "/projects/[repo]") + */ +export async function openDevEnv( + client: CodeCatalystClient, + devenv: DevEnvironmentId, + targetPath?: string +): Promise { + const env = await prepareDevEnvConnection(client, devenv, { topic: 'connect' }) + + if (!targetPath) { + const repo = env.devenv.repositories.length === 1 ? env.devenv.repositories[0].repositoryName : undefined + targetPath = repo ? `/projects/${repo}` : '/projects' + } + await startVscodeRemote(env.SessionProcess, env.hostname, targetPath, env.vscPath) +} + +// The "codecatalyst_connect" metric should really be splt into two parts: +// 1. the setup/launch from the local machine +// 2. toolkit initialization on the remote +// +// Recording metrics like this is a lot more involved so for now we'll +// assume that if the first step succeeds, the user probably succeeded +// in connecting to the devenv +export const codeCatalystConnectCommand = Commands.declare( + { + id: '_aws.codecatalyst.connect', + telemetryName: 'codecatalyst_connect', + }, + () => (client, devenv, targetPath) => openDevEnv(client, devenv, targetPath) +) + +export async function getDevfileLocation(client: DevEnvClient, root?: vscode.Uri) { + const rootDirectory = root ?? vscode.workspace.workspaceFolders?.[0].uri + if (!rootDirectory) { + throw new Error('No root directory or Dev Environment folder found') + } + + async function checkDefaultLocations(rootDirectory: vscode.Uri): Promise { + // Check the projects root location + const devfileRoot = vscode.Uri.joinPath(vscode.Uri.parse('/projects'), 'devfile.yaml') + if (await fileExists(devfileRoot.fsPath)) { + return devfileRoot + } + + // Check the location relative to the current directory + const projectRoot = vscode.Uri.joinPath(rootDirectory, 'devfile.yaml') + if (await fileExists(projectRoot.fsPath)) { + return projectRoot + } + + throw new Error('Devfile location was not found') + } + + // TODO(sijaden): should make this load greedily and continously poll + // latency is very high for some reason + const devfileLocation = await client.getStatus().then((r) => r.location) + if (!devfileLocation) { + return checkDefaultLocations(rootDirectory) + } + + if (areEqual(undefined, rootDirectory.fsPath, '/projects')) { + return vscode.Uri.joinPath(rootDirectory, devfileLocation) + } + + // we have /projects/repo, where MDE may or may not return [repo]/devfile.yaml + const repo = path.basename(rootDirectory.fsPath) + const splitDevfilePath = devfileLocation.split('/') + const devfilePath = vscode.Uri.joinPath(rootDirectory, 'devfile.yaml') + if (repo === splitDevfilePath[0] && (await fileExists(devfilePath.fsPath))) { + return devfilePath + } + + const baseLocation = vscode.Uri.joinPath(rootDirectory, devfileLocation) + if (await fileExists(baseLocation.fsPath)) { + return baseLocation + } + + return checkDefaultLocations(rootDirectory) +} + +/** + * Given a collection of CodeCatalyst repos, try to find a corresponding devenv, if any + */ +export function associateDevEnv( + client: CodeCatalystClient, + repos: AsyncCollection +): AsyncCollection { + return toCollection(async function* () { + const devenvs = await client + .listResources('devEnvironment') + .flatten() + .filter((env) => env.repositories.length > 0 && isDevenvVscode(env.ides)) + .toMap((env) => `${env.org.name}.${env.project.name}.${env.repositories[0].repositoryName}`) + + yield* repos.map((repo) => ({ + ...repo, + devEnv: devenvs.get(`${repo.org.name}.${repo.project.name}.${repo.name}`), + })) + }) +} + +export interface DevEnvMemento { + /** True if the extension is watching the status of the devenv to try and reconnect. */ + attemptingReconnect?: boolean + /** Unix time of the most recent connection. */ + previousConnectionTimestamp: number + /** Previous open vscode workspace directory. */ + previousVscodeWorkspace: string + /** CodeCatalyst Space (Org) Name */ + spaceName: string + /** CodeCatalyst Project name */ + projectName: string + /** CodeCatalyst Alias */ + alias: string | undefined +} diff --git a/packages/core/src/codecatalyst/reconnect.ts b/packages/core/src/codecatalyst/reconnect.ts new file mode 100644 index 00000000000..9e0ab491f38 --- /dev/null +++ b/packages/core/src/codecatalyst/reconnect.ts @@ -0,0 +1,296 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as nls from 'vscode-nls' +import * as vscode from 'vscode' +import { CodeCatalystClient, createClient, DevEnvironment } from '../shared/clients/codecatalystClient' +import { ExtContext } from '../shared/extensions' +import { getLogger } from '../shared/logger/logger' +import { sleep } from '../shared/utilities/timeoutUtils' +import { DevEnvironmentSettings } from './commands' +import { codeCatalystConnectCommand, DevEnvironmentId, DevEnvMemento } from './model' +import { showViewLogsMessage } from '../shared/utilities/messages' +import { CodeCatalystAuthenticationProvider } from './auth' +import { getCodeCatalystDevEnvId } from '../shared/vscode/env' +import globals from '../shared/extensionGlobals' +import { isDevenvVscode } from './utils' +import { telemetry } from '../shared/telemetry/telemetry' +import { SsoConnection } from '../auth/connection' + +const localize = nls.loadMessageBundle() + +const reconnectTimer = 5000 +const maxReconnectTime = 10 * 60 * 1000 + +export function watchRestartingDevEnvs(ctx: ExtContext, authProvider: CodeCatalystAuthenticationProvider) { + let restartHandled = false + authProvider.onDidChangeActiveConnection(async (conn) => { + if (restartHandled || conn === undefined || authProvider.auth.getConnectionState(conn) !== 'valid') { + return + } + getLogger().info(`codecatalyst: reconnect: onDidChangeActiveConnection: startUrl=${conn.startUrl}`) + + const envId = getCodeCatalystDevEnvId() + handleRestart(conn, ctx, envId) + restartHandled = true + }) +} + +function handleRestart(conn: SsoConnection, ctx: ExtContext, envId: string | undefined) { + if (envId !== undefined) { + const pendingReconnects = globals.globalState.get>('CODECATALYST_RECONNECT') ?? {} + if (envId in pendingReconnects) { + const devenv = pendingReconnects[envId] + const devenvName = getDevEnvName(devenv.alias, envId) + getLogger().info(`codecatalyst: ssh session reconnected to devenv: ${devenvName}`) + void vscode.window.showInformationMessage( + localize('AWS.codecatalyst.reconnect.success', 'Reconnected to Dev Environment: {0}', devenvName) + ) + delete pendingReconnects[envId] + globals.globalState.tryUpdate('CODECATALYST_RECONNECT', pendingReconnects) + } + } else { + getLogger().info('codecatalyst: attempting to poll dev environments') + + // Reconnect devenvs (if coming from a restart) + reconnectDevEnvs(conn).catch((err) => { + getLogger().error(`codecatalyst: error while resuming devenvs: ${err}`) + }) + } +} + +/** + * Attempt to poll for connection in all valid devenvs + * @param conn a connection that may be used for CodeCatalyst + * @param ctx the extension context + */ +async function reconnectDevEnvs(conn: SsoConnection): Promise { + const pendingDevEnvs = globals.globalState.tryGet>( + 'CODECATALYST_RECONNECT', + Object, + {} + ) + const validDevEnvs = filterInvalidDevEnvs(pendingDevEnvs) + if (Object.keys(validDevEnvs).length === 0) { + return + } + + const devenvNames = [] + for (const [id, devenv] of Object.entries(validDevEnvs)) { + devenvNames.push(getDevEnvName(devenv.alias, id)) + } + + const polledDevEnvs = devenvNames.join(', ') + const progressTitle = localize( + 'AWS.codecatalyst.reconnect.restarting', + 'Dev Environments restarting: {0}', + polledDevEnvs + ) + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + }, + async (progress, token) => { + progress.report({ message: progressTitle }) + const client = await createClient(conn) + + return pollDevEnvs(client, progress, token, validDevEnvs) + } + ) +} + +/** + * Filter out devenvs who are expired OR are already attempting to reconnect. + * @param devenvs All of the possible in devenvs to check + */ +function filterInvalidDevEnvs(devenvs: Record) { + for (const reconnectDevEnvId in devenvs) { + const devenvDetail = devenvs[reconnectDevEnvId] + if (isExpired(devenvDetail.previousConnectionTimestamp) || devenvDetail.attemptingReconnect) { + delete devenvs[reconnectDevEnvId] + } + } + return devenvs +} + +/** + * Ensure that all devenvs that are currently being looked at set to attempting to reconnect so that they are not looked at + * by any other instance of VSCode. + * @param devenvs + */ +function setWatchedDevEnvStatus(devenvs: Record, watchStatus: boolean) { + for (const [id, detail] of Object.entries(devenvs)) { + devenvs[id] = { ...detail, attemptingReconnect: watchStatus } + } + return globals.globalState.update('CODECATALYST_RECONNECT', devenvs) +} + +/** + * Continuously poll all the devenvs until they are either: + * 1. Available for re-opening, in which case re-open them automatically + * 2. In a terminating state or expired, in which case no longer watch the devenv + * 3. Failed to start, in which case notify the user + * @param client A connected client + * @param devenvs All VALID devenvs that are not being watched by any other VSCode instance + */ +async function pollDevEnvs( + client: CodeCatalystClient, + progress: vscode.Progress<{ message: string }>, + token: vscode.CancellationToken, + devenvs: Record +) { + // Ensure that all devenvs that you want to look at are attempting reconnection + // and won't be watched by any other VSCode instance + await setWatchedDevEnvStatus(devenvs, true) + + const shouldCloseRootInstance = Object.keys(devenvs).length === 1 + getLogger().info(`codecatalyst: reconnect: pollDevEnvs: ${Object.keys(devenvs).length}`) + + while (Object.keys(devenvs).length > 0) { + if (token.isCancellationRequested) { + await setWatchedDevEnvStatus(devenvs, false) + return + } + + for (const id in devenvs) { + const details = devenvs[id] + const devenvName = getDevEnvName(details.alias, id) + + try { + const metadata = await client.getDevEnvironment({ + id: id, + spaceName: details.spaceName, + projectName: details.projectName, + }) + + const ide = metadata.ides?.[0]?.name + getLogger().info(`codecatalyst: reconnect: ides=${ide} statusReason=${metadata.statusReason}`) + + if (metadata?.status === 'RUNNING') { + progress.report({ + message: `Dev Environment ${devenvName} is now running. Attempting to reconnect.`, + }) + + openReconnectedDevEnv(client, id, details, shouldCloseRootInstance).catch((e) => { + getLogger().error('openReconnectedDevEnv failed: %s', (e as Error).message) + }) + + // Don't watch this devenv, it is already being re-opened in SSH. + delete devenvs[id] + } else if (!isDevenvVscode(metadata.ides)) { + // Technically vscode _can_ connect to a ideRuntime=jetbrains devenv, but + // we refuse to anyway so that the experience is consistent since that devenv + // is not capable of connecting to a devenv that lacks their runtime/bootstrap files. + const ide = metadata.ides?.[0] + const toIde = ide ? ` to "${ide.name}"` : '' + progress.report({ message: `Dev Environment ${devenvName} was switched${toIde}` }) + // Don't watch devenv that is no longer connectable. + delete devenvs[id] + } else if (isTerminating(metadata)) { + progress.report({ message: `Dev Environment ${devenvName} is terminating` }) + // Don't watch devenv that is terminating. + delete devenvs[id] + } else if (isExpired(details.previousConnectionTimestamp)) { + progress.report({ message: `Dev Environment ${devenvName} has expired` }) + } + } catch { + await failDevEnv(id) + delete devenvs[id] + void showViewLogsMessage( + localize('AWS.codecatalyst.reconnect', 'Unable to reconnect to ${0}', devenvName) + ) + } + } + await sleep(reconnectTimer) + } +} + +function isTerminating(devenv: Pick): boolean { + if (!devenv.status) { + return false + } + return devenv.status === 'FAILED' || devenv.status === 'DELETING' || devenv.status === 'DELETED' +} + +function isExpired(previousConnectionTime: number): boolean { + return Date.now() - previousConnectionTime > maxReconnectTime +} + +/** + * When a devenv fails, remove it from globalState so we no longer watch it in the future + * @param devenvId the id of the deveng to fail + */ +function failDevEnv(devenvId: string) { + const curr = globals.globalState.tryGet>('CODECATALYST_RECONNECT', Object, {}) + delete curr[devenvId] + return globals.globalState.update('CODECATALYST_RECONNECT', curr) +} + +async function openReconnectedDevEnv( + client: CodeCatalystClient, + id: string, + devenv: DevEnvMemento, + closeRootInstance: boolean +): Promise { + const identifier: DevEnvironmentId = { + id, + org: { name: devenv.spaceName }, + project: { name: devenv.projectName }, + } + + await telemetry.runRoot(async () => { + telemetry.record({ source: 'Reconnect' }) + await codeCatalystConnectCommand.execute(client, identifier, devenv.previousVscodeWorkspace) + + // When we only have 1 devenv to watch we might as well close the local vscode instance + if (closeRootInstance) { + // A brief delay ensures that metrics are saved from the connect command + await sleep(5000).then(() => vscode.commands.executeCommand('workbench.action.closeWindow')) + } + }) +} + +function getDevEnvName(alias: string | undefined, id: string) { + return alias && alias !== '' ? alias : id +} + +export function isLongReconnect(oldSettings: DevEnvironmentSettings, newSettings: DevEnvironmentSettings): boolean { + return ( + newSettings.inactivityTimeoutMinutes !== undefined && + newSettings.instanceType !== undefined && + (oldSettings.inactivityTimeoutMinutes !== newSettings.inactivityTimeoutMinutes || + oldSettings.instanceType !== newSettings.instanceType) + ) +} + +export function saveReconnectionInformation(devenv: DevEnvironmentId & Pick): Thenable { + const pendingReconnects = globals.globalState.tryGet>( + 'CODECATALYST_RECONNECT', + Object, + {} + ) + const workspaceFolders = vscode.workspace.workspaceFolders + const currentWorkspace = + workspaceFolders !== undefined && workspaceFolders.length > 0 ? workspaceFolders[0].uri.fsPath : '/projects' + pendingReconnects[devenv.id] = { + previousVscodeWorkspace: currentWorkspace, + spaceName: devenv.org.name, + projectName: devenv.project.name, + attemptingReconnect: false, + previousConnectionTimestamp: Date.now(), + alias: devenv.alias, + } + return globals.globalState.update('CODECATALYST_RECONNECT', pendingReconnects) +} + +export function removeReconnectionInformation(devenv: DevEnvironmentId): Thenable { + const pendingReconnects = globals.globalState.tryGet>( + 'CODECATALYST_RECONNECT', + Object, + {} + ) + delete pendingReconnects[devenv.id] + return globals.globalState.update('CODECATALYST_RECONNECT', pendingReconnects) +} diff --git a/src/codecatalyst/repos/remoteSourceProvider.ts b/packages/core/src/codecatalyst/repos/remoteSourceProvider.ts similarity index 95% rename from src/codecatalyst/repos/remoteSourceProvider.ts rename to packages/core/src/codecatalyst/repos/remoteSourceProvider.ts index bc14d0e26fc..55be5dac223 100644 --- a/src/codecatalyst/repos/remoteSourceProvider.ts +++ b/packages/core/src/codecatalyst/repos/remoteSourceProvider.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -53,7 +53,7 @@ export class CodeCatalystRemoteSourceProvider implements RemoteSourceProvider { } public async getRemoteSources(query?: string): Promise { - return this.commands.withClient(async client => { + return this.commands.withClient(async (client) => { const intoRemote = async (repo: CodeCatalystRepo): Promise => { const resource = { name: repo.name, project: repo.project.name, org: repo.org.name } const url = await client.getRepoCloneUrl({ diff --git a/packages/core/src/codecatalyst/uriHandlers.ts b/packages/core/src/codecatalyst/uriHandlers.ts new file mode 100644 index 00000000000..7e4f9080519 --- /dev/null +++ b/packages/core/src/codecatalyst/uriHandlers.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 { SearchParams, UriHandler } from '../shared/vscode/uriHandler' +import { getCodeCatalystConfig } from '../shared/clients/codecatalystClient' +import { CodeCatalystCommands } from './commands' +import { defaultSsoRegion } from '../auth/connection' +import { getLogger } from '../shared/logger/logger' +import { builderIdStartUrl } from '../auth/sso/constants' + +type ConnectParams = { + devEnvironmentId: string + spaceName: string + projectName: string + sso?: { + startUrl: string + region: string + } +} + +export function register( + handler: UriHandler, + commands: Pick +) { + async function cloneHandler(params: ReturnType) { + if (params.url.authority.endsWith(getCodeCatalystConfig().gitHostname)) { + await commands.cloneRepo.execute(undefined, params.url) + } else { + await vscode.commands.executeCommand('git.clone', params.url.toString()) + } + } + + async function connectHandler(params: ConnectParams) { + await commands.openDevEnv.execute( + undefined, + { + id: params.devEnvironmentId, + org: { name: params.spaceName }, + project: { name: params.projectName }, + }, + undefined, + params.sso + ) + } + + return vscode.Disposable.from( + handler.onPath('/clone', cloneHandler, parseCloneParams), + handler.onPath('/connect/codecatalyst', connectHandler, parseConnectParams), + handler.onPath('/connect/caws', connectHandler, parseConnectParamsOld) // FIXME: remove this before GA + ) +} + +function parseCloneParams(query: SearchParams) { + return { url: query.getAsOrThrow('url', 'A URL must be provided', (v) => vscode.Uri.parse(v, true)) } +} + +export function parseConnectParams(query: SearchParams): ConnectParams { + const params = query.getFromKeysOrThrow('devEnvironmentId', 'spaceName', 'projectName') + + try { + const ssoParams = query.getFromKeysOrThrow('sso_start_url', 'sso_region') + return { ...params, sso: { startUrl: ssoParams.sso_start_url, region: ssoParams.sso_region } } + } catch { + getLogger().debug(`No IdC SSO params provided in CodeCatalyst URI, defaulting to Builder ID.`) + return { ...params, sso: { startUrl: builderIdStartUrl, region: defaultSsoRegion } } + } +} + +function parseConnectParamsOld(query: SearchParams): ConnectParams { + const params = query.getFromKeysOrThrow('devEnvironmentId', 'organizationName', 'projectName') + return { + devEnvironmentId: params.devEnvironmentId, + spaceName: params.organizationName, + projectName: params.projectName, + } +} diff --git a/packages/core/src/codecatalyst/utils.ts b/packages/core/src/codecatalyst/utils.ts new file mode 100644 index 00000000000..3f9cf6fe0bf --- /dev/null +++ b/packages/core/src/codecatalyst/utils.ts @@ -0,0 +1,60 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Ide } from '@aws-sdk/client-codecatalyst' +import * as vscode from 'vscode' +import { CodeCatalystResource, getCodeCatalystConfig } from '../shared/clients/codecatalystClient' +import { pushIf } from '../shared/utilities/collectionUtils' +import { getLogger } from '../shared/logger/logger' + +/** + * Builds a web URL from the given CodeCatalyst object. + */ +export function toCodeCatalystUrl(resource: CodeCatalystResource): string { + const prefix = `https://${getCodeCatalystConfig().hostname}/spaces` + + const format = (org: string, proj?: string, repo?: string, branch?: string) => { + const parts = [prefix, org] + pushIf(parts, !!proj, 'projects', proj) + pushIf(parts, !!repo, 'source-repositories', repo) + pushIf(parts, !!branch, 'branch', 'refs', 'heads', branch) + + return parts.concat('view').join('/') + } + + switch (resource.type) { + case 'org': + return format(resource.name) + case 'project': + return format(resource.org.name, resource.name) + case 'repo': + return format(resource.org.name, resource.project.name, resource.name) + case 'branch': + return format(resource.org.name, resource.project.name, resource.repo.name, resource.name) + case 'devEnvironment': + // There's currently no page to view an individual devenv. + // This may be changed to direct to the underlying repository instead + return [prefix, resource.org.name, 'projects', resource.project.name, 'dev-environments'].join('/') + } +} + +export function getHelpUrl(): string { + return `https://${getCodeCatalystConfig().hostname}/help` +} + +/** + * Builds a web URL from the given CodeCatalyst object. + */ +export function openCodeCatalystUrl(o: CodeCatalystResource) { + const url = toCodeCatalystUrl(o) + vscode.env.openExternal(vscode.Uri.parse(url)).then(undefined, (e) => { + getLogger().error('openExternal failed: %s', (e as Error).message) + }) +} + +/** Returns true if the dev env has a "vscode" IDE runtime. */ +export function isDevenvVscode(ides: Ide[] | undefined): boolean { + return ides !== undefined && ides.some((ide) => ide.name === 'VSCode') +} diff --git a/src/codecatalyst/vue/compute.vue b/packages/core/src/codecatalyst/vue/compute.vue similarity index 100% rename from src/codecatalyst/vue/compute.vue rename to packages/core/src/codecatalyst/vue/compute.vue diff --git a/packages/core/src/codecatalyst/vue/configure/backend.ts b/packages/core/src/codecatalyst/vue/configure/backend.ts new file mode 100644 index 00000000000..e5dd360cac7 --- /dev/null +++ b/packages/core/src/codecatalyst/vue/configure/backend.ts @@ -0,0 +1,210 @@ +/*! + * 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 { ConnectedDevEnv, DevEnvironmentId, getDevfileLocation } from '../../model' +import { CodeCatalystCommands, DevEnvironmentSettings } from '../../commands' +import { VueWebview } from '../../../webviews/main' +import { Prompter } from '../../../shared/ui/prompter' +import { isValidResponse } from '../../../shared/wizards/wizard' +import { sleep } from '../../../shared/utilities/timeoutUtils' +import { GetStatusResponse } from '../../../shared/clients/devenvClient' +import { openCodeCatalystUrl } from '../../utils' +import { assertHasProps } from '../../../shared/utilities/tsUtils' +import { + createAliasPrompter, + createInstancePrompter, + createTimeoutPrompter, + getAllInstanceDescriptions, + isValidSubscriptionType, +} from '../../wizards/devenvSettings' +import { updateDevfileCommand } from '../../devfile' +import { showViewLogsMessage } from '../../../shared/utilities/messages' +import { isLongReconnect, removeReconnectionInformation, saveReconnectionInformation } from '../../reconnect' +import { CodeCatalystClient, DevEnvironment } from '../../../shared/clients/codecatalystClient' + +const localize = nls.loadMessageBundle() + +export class CodeCatalystConfigureWebview extends VueWebview { + public static readonly sourcePath: string = 'src/codecatalyst/vue/configure/index.js' + public readonly id = 'configureCodeCatalyst' + + public readonly onDidChangeDevfile = new vscode.EventEmitter() + + public constructor( + private readonly client: CodeCatalystClient, + private readonly devenv: ConnectedDevEnv, + private readonly commands: typeof CodeCatalystCommands.declared + ) { + super(CodeCatalystConfigureWebview.sourcePath) + } + + public init() { + return this.devenv.summary + } + + public getAllInstanceDescriptions() { + return getAllInstanceDescriptions() + } + + public async getDevfileLocation() { + const location = await getDevfileLocation(this.devenv.devenvClient) + return vscode.workspace.asRelativePath(location) + } + + public async openDevfile() { + const location = await getDevfileLocation(this.devenv.devenvClient) + return this.commands.openDevfile.execute(location) + } + + public async updateDevfile(location: string) { + // TODO(sijaden): we should be able to store the absolute URI somewhere? + const rootDirectory = vscode.workspace.workspaceFolders?.[0].uri + if (!rootDirectory) { + throw new Error('No workspace folder found') + } + + await updateDevfileCommand.execute(vscode.Uri.joinPath(rootDirectory, location)) + } + + public async stopDevEnv(id: DevEnvironmentId) { + return this.commands.stopDevEnv.execute(id) + } + + public async deleteDevEnv(id: DevEnvironmentId) { + return this.commands.deleteDevEnv.execute(id) + } + + public async updateDevEnv( + id: Pick, + settings: DevEnvironmentSettings + ) { + if (isLongReconnect(this.devenv.summary, settings)) { + try { + await saveReconnectionInformation(id) + const resp = await this.commands.updateDevEnv.execute(id, settings) + if (resp === undefined) { + await removeReconnectionInformation(id) + } else { + reportClosingMessage() + } + + return resp + } catch (err) { + await removeReconnectionInformation(id) + throw err + } + } else { + return this.commands.updateDevEnv.execute(id, settings) + } + } + + public async showLogsMessage(title: string): Promise { + return showViewLogsMessage(title) + } + + public async editSetting( + settings: DevEnvironmentSettings, + key: keyof DevEnvironmentSettings + ): Promise { + const spaceName = this.devenv.summary.org.name + const subscriptionType = await this.client + .getSubscription({ spaceName }) + .then((resp) => (isValidSubscriptionType(resp.subscriptionType) ? resp.subscriptionType : 'FREE')) + + async function prompt(prompter: Prompter) { + prompter.recentItem = settings[key] + const response = await prompter.prompt() + + if (isValidResponse(response)) { + return { ...settings, [key]: response } + } else { + return settings + } + } + + switch (key) { + case 'alias': + return prompt(createAliasPrompter()) + case 'instanceType': + return prompt(createInstancePrompter(subscriptionType)) + case 'inactivityTimeoutMinutes': + return prompt(createTimeoutPrompter()) + case 'persistentStorage': + throw new Error('Persistent storage cannot be changed after creation') + } + } + + public openBranch(): void { + const repo = this.devenv.summary.repositories?.[0] + assertHasProps(repo, 'branchName') + openCodeCatalystUrl({ + type: 'branch', + name: repo.branchName, + repo: { name: repo.repositoryName }, + org: { name: this.devenv.summary.org.name }, + project: { name: this.devenv.summary.project.name }, + }) + } +} + +const Panel = VueWebview.compilePanel(CodeCatalystConfigureWebview) +let activePanel: InstanceType | undefined +let subscriptions: vscode.Disposable[] | undefined + +export async function showConfigureDevEnv( + client: CodeCatalystClient, + ctx: vscode.ExtensionContext, + devenv: ConnectedDevEnv, + commands: typeof CodeCatalystCommands.declared +): Promise { + activePanel ??= new Panel(ctx, client, devenv, commands) + const webview = await activePanel.show({ + title: localize('AWS.view.configureDevEnv.title', 'Dev Environment Settings'), + viewColumn: vscode.ViewColumn.Active, + }) + + if (!subscriptions) { + const poller = pollDevfile(devenv, activePanel.server) + subscriptions = [ + poller, + webview.onDidDispose(() => { + vscode.Disposable.from(...(subscriptions ?? [])).dispose() + activePanel = undefined + subscriptions = undefined + }), + ] + } +} + +function pollDevfile(devenv: ConnectedDevEnv, server: CodeCatalystConfigureWebview): vscode.Disposable { + let done = false + + void (async () => { + while (!done) { + const resp = await devenv.devenvClient.getStatus() + if (resp.status === 'CHANGED') { + server.onDidChangeDevfile.fire({ ...resp, actionId: 'devfile' }) + } + await sleep(2500) + } + })() + + return { dispose: () => (done = true) } +} + +function reportClosingMessage(): void { + void vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: 'Session ended. Session will restore when the Dev Environment is available again.', + }, + async (progress, token) => { + await sleep(2500) + await vscode.commands.executeCommand('workbench.action.remote.close') + } + ) +} diff --git a/packages/core/src/codecatalyst/vue/configure/index.ts b/packages/core/src/codecatalyst/vue/configure/index.ts new file mode 100644 index 00000000000..fbd8dd1fb18 --- /dev/null +++ b/packages/core/src/codecatalyst/vue/configure/index.ts @@ -0,0 +1,16 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { createApp } from 'vue' +import component from './root.vue' + +const create = () => createApp(component) +const app = create() +app.mount('#vue-app') + +window.addEventListener('remount', () => { + app.unmount() + create().mount('#vue-app') +}) diff --git a/src/codecatalyst/vue/configure/root.vue b/packages/core/src/codecatalyst/vue/configure/root.vue similarity index 100% rename from src/codecatalyst/vue/configure/root.vue rename to packages/core/src/codecatalyst/vue/configure/root.vue diff --git a/packages/core/src/codecatalyst/vue/create/backend.ts b/packages/core/src/codecatalyst/vue/create/backend.ts new file mode 100644 index 00000000000..102c6329653 --- /dev/null +++ b/packages/core/src/codecatalyst/vue/create/backend.ts @@ -0,0 +1,284 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as nls from 'vscode-nls' +const localize = nls.loadMessageBundle() + +import * as vscode from 'vscode' +import { CodeCatalystCommands, DevEnvironmentSettings } from '../../commands' +import { VueWebview } from '../../../webviews/main' +import { Prompter } from '../../../shared/ui/prompter' +import { isValidResponse } from '../../../shared/wizards/wizard' +import { + createAliasPrompter, + createInstancePrompter, + createStoragePrompter, + createTimeoutPrompter, + getAllInstanceDescriptions, + isValidSubscriptionType, +} from '../../wizards/devenvSettings' +import { showViewLogsMessage } from '../../../shared/utilities/messages' +import { + CodeCatalystBranch, + CodeCatalystOrg, + CodeCatalystProject, + CodeCatalystClient, + DevEnvironment, + isThirdPartyRepo, +} from '../../../shared/clients/codecatalystClient' +import { CancellationError } from '../../../shared/utilities/timeoutUtils' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { isNonNullable } from '../../../shared/utilities/tsUtils' +import { createOrgPrompter, createProjectPrompter } from '../../wizards/selectResource' +import { GetSourceRepositoryCloneUrlsRequest } from '@aws-sdk/client-codecatalyst' +import { QuickPickPrompter } from '../../../shared/ui/pickerPrompter' + +interface LinkedResponse { + readonly type: 'linked' + readonly selectedSpace: Pick + readonly selectedProject: CodeCatalystProject + readonly selectedBranch: CodeCatalystBranch + readonly newBranch: string +} + +interface EmptyResponse { + readonly type: 'none' + readonly selectedSpace: Pick + readonly selectedProject: CodeCatalystProject +} + +export type SourceResponse = LinkedResponse | EmptyResponse + +export class CodeCatalystCreateWebview extends VueWebview { + public static readonly sourcePath: string = 'src/codecatalyst/vue/create/index.js' + public readonly id = 'createCodeCatalyst' + + private projectPrompter?: QuickPickPrompter + private spacePrompter?: QuickPickPrompter + + public constructor( + private readonly client: CodeCatalystClient, + private readonly commands: typeof CodeCatalystCommands.declared, + private readonly onComplete: (devenv?: DevEnvironment) => void + ) { + super(CodeCatalystCreateWebview.sourcePath) + + // triggers pre-loading of Spaces + this.spacePrompter = createOrgPrompter(client) + } + + public close() { + this.dispose() + this.onComplete() + } + + /** + * Opens a quick pick that lists all Spaces of the user. + * + * @returns Space if it was selected, otherwise undefined due to user cancellation. + */ + public async quickPickSpace(): Promise { + this.spacePrompter = this.spacePrompter ?? createOrgPrompter(this.client) + const selectedSpace = await this.spacePrompter.prompt() + this.spacePrompter = undefined // We want the prompter to be re-created on subsequent calls + + if (!isValidResponse(selectedSpace)) { + return undefined + } + + // This will initiate preloading of the projects + this.projectPrompter = createProjectPrompter(this.client, selectedSpace?.name) + + return selectedSpace + } + + /** + * Opens a quick pick that lists all Projects from a given Space. + * + * @param spaceName Space to show Projects from + * @returns Project if it was selected, otherwise undefined due to user cancellation. + */ + public async quickPickProject(spaceName: CodeCatalystOrg['name']): Promise { + this.projectPrompter = this.projectPrompter ?? createProjectPrompter(this.client, spaceName) + const selectedProject = await this.projectPrompter.prompt() + this.projectPrompter = undefined // We want the prompter to be re-created on subsequent calls + + if (!isValidResponse(selectedProject)) { + return undefined + } + + return selectedProject + } + + public async getBranches(project: CodeCatalystProject) { + const repos = this.client + .listSourceRepositories({ + spaceName: project.org.name, + projectName: project.name, + }) + .flatten() + + const branches = repos.map((r) => + this.client + .listBranches({ + spaceName: r.org.name, + projectName: r.project.name, + sourceRepositoryName: r.name, + }) + .flatten() + .promise() + ) + + return branches.flatten().promise() + } + + public isThirdPartyRepo(codeCatalystRepo: GetSourceRepositoryCloneUrlsRequest): Promise { + return isThirdPartyRepo(this.client, codeCatalystRepo) + } + + public getAllInstanceDescriptions() { + return getAllInstanceDescriptions() + } + + public async showLogsMessage(title: string): Promise { + return showViewLogsMessage(title) + } + + public async editSetting( + settings: DevEnvironmentSettings, + key: keyof DevEnvironmentSettings, + org?: Pick + ): Promise { + const subscriptionType = isNonNullable(org) + ? await this.client + .getSubscription({ spaceName: org.name }) + .then((resp) => (isValidSubscriptionType(resp.subscriptionType) ? resp.subscriptionType : 'FREE')) + : 'FREE' + + async function prompt(prompter: Prompter) { + prompter.recentItem = settings[key] + const response = await prompter.prompt() + + if (isValidResponse(response)) { + return { ...settings, [key]: response } + } else { + return settings + } + } + + switch (key) { + case 'alias': + return prompt(createAliasPrompter()) + case 'instanceType': + return prompt(createInstancePrompter(subscriptionType)) + case 'inactivityTimeoutMinutes': + return prompt(createTimeoutPrompter()) + case 'persistentStorage': + return prompt(createStoragePrompter(subscriptionType)) + } + } + + public async submit(settings: DevEnvironmentSettings, source: SourceResponse) { + const devenv = await this.createDevEnvOfType(settings, source) + void this.commands.openDevEnv.execute(undefined, devenv) + } + + public async createDevEnvOfType(settings: DevEnvironmentSettings, source: SourceResponse) { + const devenv: DevEnvironment = await (() => { + switch (source.type) { + case 'none': + return this.createEmptyDevEnv(settings, source) + case 'linked': + return this.createLinkedDevEnv(settings, source) + } + })() + + telemetry.record({ + source: 'Webview', + codecatalyst_createDevEnvironmentRepoType: source.type, + }) + + this.onComplete(devenv) + return devenv + } + + private async createEmptyDevEnv(settings: DevEnvironmentSettings, source: EmptyResponse) { + return this.client.createDevEnvironment({ + ides: [{ name: 'VSCode' }], + projectName: source.selectedProject.name, + spaceName: source.selectedProject.org.name, + ...settings, + }) + } + + private async createLinkedDevEnv(settings: DevEnvironmentSettings, source: LinkedResponse) { + const isNewBranch = !!source.newBranch + if (isNewBranch) { + await this.client.createSourceBranch({ + name: source.newBranch, + spaceName: source.selectedProject.org.name, + projectName: source.selectedProject.name, + sourceRepositoryName: source.selectedBranch.repo.name, + headCommitId: source.selectedBranch.headCommitId, + }) + } + + const branchName = isNewBranch ? source.newBranch : source.selectedBranch.name.replace('refs/heads/', '') + return this.client.createDevEnvironment({ + ides: [{ name: 'VSCode' }], + projectName: source.selectedProject.name, + spaceName: source.selectedProject.org.name, + repositories: [ + { + repositoryName: source.selectedBranch.repo.name, + branchName, + }, + ], + ...settings, + }) + } +} + +// TODO(sijaden): de-dupe this basic init pattern for webviews +// the logic here is mainly to preserve the same panel in case the +// user re-runs a command, which is fairly common +const Panel = VueWebview.compilePanel(CodeCatalystCreateWebview) +let activePanel: InstanceType | undefined +let subscriptions: vscode.Disposable[] | undefined +let submitPromise: Promise | undefined + +export async function showCreateDevEnv( + client: CodeCatalystClient, + ctx: vscode.ExtensionContext, + commands: typeof CodeCatalystCommands.declared +): Promise { + submitPromise ??= new Promise((resolve, reject) => { + activePanel ??= new Panel(ctx, client, commands, (devenv) => { + if (devenv === undefined) { + reject(new CancellationError('user')) + } else { + resolve() + } + }) + }) + + const webview = await activePanel!.show({ + title: localize('AWS.view.createDevEnv.title', 'Create a CodeCatalyst Dev Environment'), + viewColumn: vscode.ViewColumn.Active, + }) + + if (!subscriptions) { + subscriptions = [ + webview.onDidDispose(() => { + vscode.Disposable.from(...(subscriptions ?? [])).dispose() + activePanel = undefined + subscriptions = undefined + submitPromise = undefined + }), + ] + } + + return submitPromise +} diff --git a/packages/core/src/codecatalyst/vue/create/index.ts b/packages/core/src/codecatalyst/vue/create/index.ts new file mode 100644 index 00000000000..fbd8dd1fb18 --- /dev/null +++ b/packages/core/src/codecatalyst/vue/create/index.ts @@ -0,0 +1,16 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { createApp } from 'vue' +import component from './root.vue' + +const create = () => createApp(component) +const app = create() +app.mount('#vue-app') + +window.addEventListener('remount', () => { + app.unmount() + create().mount('#vue-app') +}) diff --git a/src/codecatalyst/vue/create/root.vue b/packages/core/src/codecatalyst/vue/create/root.vue similarity index 100% rename from src/codecatalyst/vue/create/root.vue rename to packages/core/src/codecatalyst/vue/create/root.vue diff --git a/packages/core/src/codecatalyst/vue/create/source.vue b/packages/core/src/codecatalyst/vue/create/source.vue new file mode 100644 index 00000000000..c3e8e60d293 --- /dev/null +++ b/packages/core/src/codecatalyst/vue/create/source.vue @@ -0,0 +1,349 @@ + + + + + diff --git a/src/codecatalyst/vue/devfile.vue b/packages/core/src/codecatalyst/vue/devfile.vue similarity index 100% rename from src/codecatalyst/vue/devfile.vue rename to packages/core/src/codecatalyst/vue/devfile.vue diff --git a/src/codecatalyst/vue/summary.vue b/packages/core/src/codecatalyst/vue/summary.vue similarity index 99% rename from src/codecatalyst/vue/summary.vue rename to packages/core/src/codecatalyst/vue/summary.vue index 20887ce0e27..38ac2e1d18c 100644 --- a/src/codecatalyst/vue/summary.vue +++ b/packages/core/src/codecatalyst/vue/summary.vue @@ -157,7 +157,7 @@ export default defineComponent({ #stop-icon { color: var(--vscode-debugIcon-stopForeground); - padding: 4px; + margin-right: 5px; vertical-align: -0.2em; } diff --git a/src/codecatalyst/wizards/devenvSettings.ts b/packages/core/src/codecatalyst/wizards/devenvSettings.ts similarity index 85% rename from src/codecatalyst/wizards/devenvSettings.ts rename to packages/core/src/codecatalyst/wizards/devenvSettings.ts index 1150c0a73a2..676b65fb74c 100644 --- a/src/codecatalyst/wizards/devenvSettings.ts +++ b/packages/core/src/codecatalyst/wizards/devenvSettings.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -10,8 +10,8 @@ import { Prompter } from '../../shared/ui/prompter' import { toTitleCase } from '../../shared/utilities/textUtilities' const devenvOptions = settings['environment'] -export type InstanceType = keyof typeof devenvOptions['instanceType'] -export type SubscriptionType = typeof subscriptionTypes[number] +export type InstanceType = keyof (typeof devenvOptions)['instanceType'] +export type SubscriptionType = (typeof subscriptionTypes)[number] const subscriptionTypes = ['FREE', 'STANDARD'] as const @@ -19,7 +19,7 @@ export function isValidSubscriptionType(type = ''): type is SubscriptionType { return (subscriptionTypes as readonly string[]).includes(type) } -interface InstanceDescription { +export interface InstanceDescription { name: string specs: string } @@ -50,7 +50,9 @@ export function getInstanceDescription(type: InstanceType): InstanceDescription export function getAllInstanceDescriptions(): { [key: string]: InstanceDescription } { const desc: { [key: string]: InstanceDescription } = {} - entries(devenvOptions.instanceType).forEach(([k]) => (desc[k] = getInstanceDescription(k))) + for (const [k] of entries(devenvOptions.instanceType)) { + desc[k] = getInstanceDescription(k) + } return desc } @@ -72,14 +74,14 @@ export function createTimeoutPrompter(): Prompter { return createInputBox({ title: 'Timeout Length', placeholder: 'Timeout length in minutes', - validateInput: resp => (Number.isNaN(Number(resp)) ? 'Timeout must be a number' : undefined), - }).transform(r => Number(r)) + validateInput: (resp) => (Number.isNaN(Number(resp)) ? 'Timeout must be a number' : undefined), + }).transform(Number) } export function createAliasPrompter(): InputBoxPrompter { return createInputBox({ title: 'Edit Alias', - validateInput: value => { + validateInput: (value) => { if (value?.length > 128) { return 'Dev Environment alias cannot be longer than 128 characters' } @@ -89,7 +91,7 @@ export function createAliasPrompter(): InputBoxPrompter { export function createStoragePrompter(subscriptionType: SubscriptionType): QuickPickPrompter<{ sizeInGiB: number }> { const isSupported = (v: number) => subscriptionType !== 'FREE' || v === 16 - const items = settings.environment.persistentStorageSize.map(v => ({ + const items = settings.environment.persistentStorageSize.map((v) => ({ data: { sizeInGiB: v }, label: `${v} GB`, description: isSupported(v) ? '' : 'unavailable in current organization billing tier', diff --git a/src/codecatalyst/wizards/parameterDescriptions.json b/packages/core/src/codecatalyst/wizards/parameterDescriptions.json similarity index 100% rename from src/codecatalyst/wizards/parameterDescriptions.json rename to packages/core/src/codecatalyst/wizards/parameterDescriptions.json diff --git a/packages/core/src/codecatalyst/wizards/selectResource.ts b/packages/core/src/codecatalyst/wizards/selectResource.ts new file mode 100644 index 00000000000..54e3bb20951 --- /dev/null +++ b/packages/core/src/codecatalyst/wizards/selectResource.ts @@ -0,0 +1,238 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as codecatalyst from '../../shared/clients/codecatalystClient' +import { createCommonButtons, createRefreshButton } from '../../shared/ui/buttons' +import { + createQuickPick, + DataQuickPickItem, + ExtendedQuickPickOptions, + QuickPickPrompter, +} from '../../shared/ui/pickerPrompter' +import { AsyncCollection } from '../../shared/utilities/asyncCollection' +import { isValidResponse } from '../../shared/wizards/wizard' +import { associateDevEnv, docs } from '../model' +import { getHelpUrl, isDevenvVscode } from '../utils' +import { getLogger } from '../../shared/logger/logger' +import { getRelativeDate } from '../../shared/datetime' + +export function createRepoLabel(r: codecatalyst.CodeCatalystRepo): string { + return `${r.org.name} / ${r.project.name} / ${r.name}` +} + +/** + * Maps CodeCatalystFoo objects to `vscode.QuickPickItem` objects. + */ +export function asQuickpickItem(resource: T): DataQuickPickItem { + switch (resource.type) { + case 'project': + return { + label: `${resource.org.name} / ${resource.name}`, + description: resource.description, + data: resource, + } + case 'repo': + return { + label: createRepoLabel(resource), + description: resource.description, + data: resource, + } + case 'devEnvironment': + return { ...fromDevEnv(resource), data: resource } + case 'org': + return { label: resource.name, detail: resource.description, data: resource } + default: + return { label: resource.name, data: resource } + } +} + +function fromDevEnv(env: codecatalyst.DevEnvironment): Omit, 'data'> { + const labelParts = [] as string[] + + if (env.status === 'RUNNING') { + labelParts.push('$(pass)') + } else { + labelParts.push('$(circle-slash)') // TODO(sijaden): get actual 'stopped' icon + } + + labelParts.push(env.alias ? env.alias : env.id) + + const repo = env.repositories[0] + const branchName = repo?.branchName?.replace('refs/heads/', '') + const repoLabel = repo + ? branchName + ? `${repo.repositoryName}/${branchName}` + : repo.repositoryName + : '(no repository)' + + const statusLabel = env.status === 'RUNNING' ? 'RUNNING - IN USE' : env.status + const desc = `${statusLabel} ${getRelativeDate(env.lastUpdatedTime)}` + + return { + label: labelParts.join(' '), + description: desc, + detail: `${env.org.name}/${env.project.name}/${repoLabel}`, + } +} + +function createResourcePrompter( + resources: AsyncCollection, + helpUri: vscode.Uri, + presentation: Omit, 'buttons'> +): QuickPickPrompter { + const refresh = createRefreshButton() + const items = resources.map((p) => p.map(asQuickpickItem)) + const prompter = createQuickPick(items, { + buttons: [refresh, ...createCommonButtons(helpUri)], + ...presentation, + matchOnDetail: true, + }) + + refresh.onClick = () => { + prompter.clearAndLoadItems(items).catch((e) => { + getLogger().error('clearAndLoadItems failed: %s', (e as Error).message) + }) + } + + return prompter +} + +export function createOrgPrompter( + client: codecatalyst.CodeCatalystClient +): QuickPickPrompter { + const helpUri = docs.main + return createResourcePrompter(client.listSpaces(), helpUri, { + title: 'Select a CodeCatalyst Organization', + placeholder: 'Search for an Organization', + }) +} + +export function createProjectPrompter( + client: codecatalyst.CodeCatalystClient, + spaceName?: codecatalyst.CodeCatalystOrg['name'] +): QuickPickPrompter { + const helpUri = docs.main + const projects = spaceName ? client.listProjects({ spaceName }) : client.listResources('project') + + return createResourcePrompter(projects, helpUri, { + title: 'Select a CodeCatalyst Project', + placeholder: 'Search for a Project', + }) +} + +export function createRepoPrompter( + client: codecatalyst.CodeCatalystClient, + proj?: codecatalyst.CodeCatalystProject, + thirdParty?: boolean +): QuickPickPrompter { + const helpUri = docs.main + const repos = proj + ? client.listSourceRepositories({ spaceName: proj.org.name, projectName: proj.name }, thirdParty) + : client.listResources('repo', thirdParty) + + return createResourcePrompter(repos, helpUri, { + title: 'Select a CodeCatalyst Repository', + placeholder: 'Search for a CodeCatalyst Repository', + }) +} + +export function createDevEnvPrompter( + client: codecatalyst.CodeCatalystClient, + proj?: codecatalyst.CodeCatalystProject +): QuickPickPrompter { + const helpUri = docs.devenv + const envs = proj ? client.listDevEnvironments(proj) : client.listResources('devEnvironment') + const filtered = envs.map((arr) => arr.filter((env) => isDevenvVscode(env.ides))) + const isData = (obj: T | DataQuickPickItem['data']): obj is T => { + return typeof obj !== 'function' && isValidResponse(obj) + } + + return createResourcePrompter(filtered, helpUri, { + title: 'Select a CodeCatalyst Dev Environment', + placeholder: 'Search for a Dev Environment', + compare: (a, b) => { + if (isData(a.data) && isData(b.data)) { + return b.data.lastUpdatedTime.getTime() - a.data.lastUpdatedTime.getTime() + } + + return 0 + }, + }) +} + +type ResourceType = codecatalyst.CodeCatalystResource['type'] + +export async function selectCodeCatalystResource( + client: codecatalyst.CodeCatalystClient, + type: T & ResourceType +): Promise<(codecatalyst.CodeCatalystResource & { type: typeof type }) | undefined> { + const prompter = (() => { + switch (type as ResourceType) { + case 'org': + return createOrgPrompter(client) + case 'project': + return createProjectPrompter(client) + case 'repo': + return createRepoPrompter(client) + case 'branch': + throw new Error('Picking a branch is not supported') + case 'devEnvironment': + return createDevEnvPrompter(client) + } + })() + + const response = await prompter.prompt() + return isValidResponse(response) ? (response as codecatalyst.CodeCatalystResource & { type: T }) : undefined +} + +export async function selectCodeCatalystRepository( + client: codecatalyst.CodeCatalystClient, + includeThirdPartyRepos?: boolean +): Promise { + const prompter = createRepoPrompter(client, undefined, includeThirdPartyRepos) + const response = await prompter.prompt() + return isValidResponse(response) ? response : undefined +} + +/** + * Special-case of {@link createRepoPrompter} for creating a new devenv + */ +export async function selectRepoForDevEnv( + client: codecatalyst.CodeCatalystClient +): Promise { + const repos = associateDevEnv(client, client.listResources('repo').flatten()) + + const refresh = createRefreshButton() + const items = repos.map((repo) => [ + { + ...asQuickpickItem(repo), + invalidSelection: repo.devEnv !== undefined, + description: repo.devEnv ? `Repository already has a Dev Environment` : '', + }, + ]) + + const prompter = createQuickPick(items, { + buttons: [refresh, ...createCommonButtons(getHelpUrl())], + title: 'Select a CodeCatalyst Repository', + placeholder: 'Search for a Repository', + compare: (a, b) => { + if (a.invalidSelection === b.invalidSelection) { + return 0 + } + + return a.invalidSelection ? 1 : b.invalidSelection ? -1 : 0 + }, + }) + + refresh.onClick = () => { + prompter.clearAndLoadItems(items).catch((e) => { + getLogger().error('clearAndLoadItems failed: %s', (e as Error).message) + }) + } + + const response = await prompter.prompt() + return isValidResponse(response) ? response : undefined +} diff --git a/packages/core/src/codewhisperer/activation.ts b/packages/core/src/codewhisperer/activation.ts new file mode 100644 index 00000000000..e037657958d --- /dev/null +++ b/packages/core/src/codewhisperer/activation.ts @@ -0,0 +1,488 @@ +/*! + * 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 * as CodeWhispererConstants from './models/constants' +import { + CodeSuggestionsState, + CodeScansState, + SecurityTreeViewFilterState, + AggregatedCodeScanIssue, + CodeScanIssue, + CodeIssueGroupingStrategyState, +} from './models/model' +import { CodeWhispererSettings } from './util/codewhispererSettings' +import { ExtContext } from '../shared/extensions' +import { CodeWhispererTracker } from './tracker/codewhispererTracker' +import * as codewhispererClient from './client/codewhisperer' +import { getLogger } from '../shared/logger/logger' +import { + enableCodeSuggestions, + toggleCodeSuggestions, + showReferenceLog, + showLogs, + showSecurityScan, + showLearnMore, + showSsoSignIn, + showFreeTierLimit, + updateReferenceLog, + showIntroduction, + reconnect, + openSecurityIssuePanel, + selectCustomizationPrompt, + notifyNewCustomizationsCmd, + connectWithCustomization, + applySecurityFix, + signoutCodeWhisperer, + toggleCodeScans, + registerToolkitApiCallback, + showFileScan, + clearFilters, + generateFix, + explainIssue, + ignoreIssue, + rejectFix, + showSecurityIssueFilters, + regenerateFix, + ignoreAllIssues, + focusIssue, + showCodeIssueGroupingQuickPick, + selectRegionProfileCommand, +} from './commands/basicCommands' +import { ReferenceLogViewProvider } from './service/referenceLogViewProvider' +import { ReferenceHoverProvider } from './service/referenceHoverProvider' +import { ReferenceInlineProvider } from './service/referenceInlineProvider' +import { + disposeSecurityDiagnostic, + securityScanRender, + updateSecurityDiagnosticCollection, +} from './service/diagnosticsProvider' +import { SecurityPanelViewProvider, openEditorAtRange } from './views/securityPanelViewProvider' +import { Commands, registerCommandErrorHandler, registerDeclaredCommands } from '../shared/vscode/commands2' +import { refreshStatusBar } from './service/statusBar' +import { AuthUtil } from './util/authUtil' +import { ImportAdderProvider } from './service/importAdderProvider' +import { openUrl } from '../shared/utilities/vsCodeUtils' +import { notifyNewCustomizations, onProfileChangedListener } from './util/customizationUtil' +import { CodeWhispererCommandBackend, CodeWhispererCommandDeclarations } from './commands/gettingStartedPageCommands' +import { SecurityIssueHoverProvider } from './service/securityIssueHoverProvider' +import { SecurityIssueCodeActionProvider } from './service/securityIssueCodeActionProvider' +import { listCodeWhispererCommands } from './ui/statusBarMenu' +import { debounceStartSecurityScan } from './commands/startSecurityScan' +import { securityScanLanguageContext } from './util/securityScanLanguageContext' +import { registerWebviewErrorHandler } from '../webviews/server' +import { logAndShowError, logAndShowWebviewError } from '../shared/utilities/logAndShowUtils' +import { openSettings } from '../shared/settings' +import { telemetry } from '../shared/telemetry/telemetry' +import { FeatureConfigProvider } from '../shared/featureConfig' +import { SecurityIssueProvider } from './service/securityIssueProvider' +import { SecurityIssueTreeViewProvider } from './service/securityIssueTreeViewProvider' +import { setContext } from '../shared/vscode/setContext' +import { syncSecurityIssueWebview } from './views/securityIssue/securityIssueWebview' +import { detectCommentAboveLine } from '../shared/utilities/commentUtils' +import { activateEditTracking } from './nextEditPrediction/activation' +import { notifySelectDeveloperProfile } from './region/utils' + +let localize: nls.LocalizeFunc + +export async function activate(context: ExtContext): Promise { + localize = nls.loadMessageBundle() + + // Import old CodeWhisperer settings into Amazon Q + await CodeWhispererSettings.instance.importSettings() + + // initialize AuthUtil earlier to make sure it can listen to connection change events. + const auth = AuthUtil.instance + auth.initCodeWhispererHooks() + + // TODO: is this indirection useful? + registerDeclaredCommands( + context.extensionContext.subscriptions, + CodeWhispererCommandDeclarations.instance, + new CodeWhispererCommandBackend(context.extensionContext) + ) + + /** + * CodeWhisperer security panel + */ + const securityPanelViewProvider = new SecurityPanelViewProvider(context.extensionContext) + context.extensionContext.subscriptions.push( + vscode.window.registerWebviewViewProvider(SecurityPanelViewProvider.viewType, securityPanelViewProvider) + ) + + // TODO: this is already done in packages/core/src/extensionCommon.ts, why doesn't amazonq use that? + registerCommandErrorHandler((info, error) => { + const defaultMessage = localize('AWS.generic.message.error', 'Failed to run command: {0}', info.id) + void logAndShowError(localize, error, info.id, defaultMessage) + }) + + // TODO: this is already done in packages/core/src/extensionCommon.ts, why doesn't amazonq use that? + registerWebviewErrorHandler((error: unknown, webviewId: string, command: string) => { + return logAndShowWebviewError(localize, error, webviewId, command) + }) + + /** + * Service control + */ + const client = new codewhispererClient.DefaultCodeWhispererClient() + + // Service initialization + ReferenceInlineProvider.instance + ImportAdderProvider.instance + + context.extensionContext.subscriptions.push( + // register toolkit api callback + registerToolkitApiCallback.register(), + signoutCodeWhisperer.register(auth), + /** + * Configuration change + */ + vscode.workspace.onDidChangeConfiguration(async (configurationChangeEvent) => { + if (configurationChangeEvent.affectsConfiguration('amazonQ.showCodeWithReferences')) { + ReferenceLogViewProvider.instance.update() + if (auth.isEnterpriseSsoInUse()) { + await vscode.window + .showInformationMessage( + CodeWhispererConstants.ssoConfigAlertMessage, + CodeWhispererConstants.settingsLearnMore + ) + .then(async (resp) => { + if (resp === CodeWhispererConstants.settingsLearnMore) { + void openUrl(vscode.Uri.parse(CodeWhispererConstants.learnMoreUri)) + } + }) + } + } + + if (configurationChangeEvent.affectsConfiguration('editor.inlineSuggest.enabled')) { + await vscode.window + .showInformationMessage( + CodeWhispererConstants.reloadWindowPrompt, + CodeWhispererConstants.reloadWindow + ) + .then((selected) => { + if (selected === CodeWhispererConstants.reloadWindow) { + void vscode.commands.executeCommand('workbench.action.reloadWindow') + } + }) + } + + if (configurationChangeEvent.affectsConfiguration('amazonQ.ignoredSecurityIssues')) { + const ignoredIssues = CodeWhispererSettings.instance.getIgnoredSecurityIssues() + toggleIssuesVisibility((issue) => !ignoredIssues.includes(issue.title)) + } + }), + /** + * Open Configuration + */ + Commands.register('aws.amazonq.configure', async (id) => { + if (id === 'codewhisperer') { + await vscode.commands.executeCommand( + 'workbench.action.openSettings', + `@id:amazonQ.showCodeWithReferences` + ) + } else { + await openSettings('amazonQ') + } + }), + // TODO port this to lsp + // Commands.register('aws.amazonq.refreshAnnotation', async (forceProceed: boolean) => { + // telemetry.record({ + // traceId: TelemetryHelper.instance.traceId, + // }) + + // const editor = vscode.window.activeTextEditor + // if (editor) { + // if (forceProceed) { + // await container.lineAnnotationController.refresh(editor, 'codewhisperer', true) + // } else { + // await container.lineAnnotationController.refresh(editor, 'codewhisperer') + // } + // } + // }), + // show introduction + showIntroduction.register(), + // toggle code suggestions + toggleCodeSuggestions.register(CodeSuggestionsState.instance), + // toggle code scans + toggleCodeScans.register(CodeScansState.instance), + // enable code suggestions + enableCodeSuggestions.register(context), + // project scan + showSecurityScan.register(context, securityPanelViewProvider, client), + // on demand file scan + showFileScan.register(context, securityPanelViewProvider, client), + // show security issue webview panel + openSecurityIssuePanel.register(context), + // sign in with sso or AWS ID + showSsoSignIn.register(), + // show reconnect prompt + reconnect.register(), + // learn more about CodeWhisperer + showLearnMore.register(), + // show free tier limit + showFreeTierLimit.register(), + // update reference log instance + updateReferenceLog.register(), + // refresh codewhisperer status bar + refreshStatusBar.register(), + // generate code fix + generateFix.register(client, context), + // regenerate code fix + regenerateFix.register(), + // apply suggested fix + applySecurityFix.register(), + // reject suggested fix + rejectFix.register(context.extensionContext), + // ignore issues by title + ignoreAllIssues.register(), + // ignore single issue + ignoreIssue.register(), + // explain issue + explainIssue.register(), + // quick pick with codewhisperer options + listCodeWhispererCommands.register(), + // quick pick with security issues tree filters + showSecurityIssueFilters.register(), + // quick pick code issue grouping strategy + showCodeIssueGroupingQuickPick.register(), + // reset security issue filters + clearFilters.register(), + // handle security issues tree item clicked + focusIssue.register(), + // refresh the treeview on every change + SecurityTreeViewFilterState.instance.onDidChangeState((e) => { + SecurityIssueTreeViewProvider.instance.refresh() + }), + // refresh treeview when grouping strategy changes + CodeIssueGroupingStrategyState.instance.onDidChangeState((e) => { + SecurityIssueTreeViewProvider.instance.refresh() + }), + // show a no match state + SecurityIssueTreeViewProvider.instance.onDidChangeTreeData((e) => { + const noMatches = + Array.isArray(e) && + e.length === 0 && + SecurityIssueProvider.instance.issues.some((group) => group.issues.some((issue) => issue.visible)) + void setContext('aws.amazonq.security.noMatches', noMatches) + }), + // select customization + selectCustomizationPrompt.register(), + // notify new customizations + notifyNewCustomizationsCmd.register(), + selectRegionProfileCommand.register(), + + // direct CodeWhisperer connection setup with customization + connectWithCustomization.register(), + + vscode.languages.registerHoverProvider( + [...CodeWhispererConstants.platformLanguageIds], + ReferenceHoverProvider.instance + ), + vscode.window.registerWebviewViewProvider(ReferenceLogViewProvider.viewType, ReferenceLogViewProvider.instance), + showReferenceLog.register(), + showLogs.register(), + vscode.languages.registerCodeLensProvider( + [...CodeWhispererConstants.platformLanguageIds], + ReferenceInlineProvider.instance + ), + vscode.languages.registerCodeLensProvider( + [...CodeWhispererConstants.platformLanguageIds, { scheme: 'untitled' }], + ImportAdderProvider.instance + ), + vscode.languages.registerHoverProvider( + [...CodeWhispererConstants.securityScanLanguageIds], + SecurityIssueHoverProvider.instance + ), + vscode.languages.registerCodeActionsProvider( + [...CodeWhispererConstants.securityScanLanguageIds], + SecurityIssueCodeActionProvider.instance + ), + vscode.commands.registerCommand('aws.amazonq.openEditorAtRange', openEditorAtRange), + auth.regionProfileManager.onDidChangeRegionProfile(onProfileChangedListener) + ) + + // run the auth startup code with context for telemetry + await telemetry.function_call.run( + async () => { + await auth.restore() + await auth.clearExtraConnections() + + if (auth.isConnectionExpired()) { + auth.showReauthenticatePrompt().catch((e) => { + const defaulMsg = localize('AWS.generic.message.error', 'Failed to reauth:') + void logAndShowError(localize, e, 'showReauthenticatePrompt', defaulMsg) + }) + if (auth.isEnterpriseSsoInUse()) { + await auth.notifySessionConfiguration() + } + } + + if (auth.requireProfileSelection()) { + await notifySelectDeveloperProfile() + } + }, + { emit: false, functionId: { name: 'activateCwCore' } } + ) + + if (auth.isValidEnterpriseSsoInUse()) { + await notifyNewCustomizations() + } + if (auth.isBuilderIdInUse()) { + await CodeScansState.instance.setScansEnabled(false) + } + + /** + * CodeWhisperer auto scans + */ + setSubscriptionsForAutoScans() + + setSubscriptionsForCodeIssues() + + function shouldRunAutoScan(editor: vscode.TextEditor | undefined, isScansEnabled?: boolean) { + return ( + (isScansEnabled ?? CodeScansState.instance.isScansEnabled()) && + !CodeScansState.instance.isMonthlyQuotaExceeded() && + auth.isConnectionValid() && + !auth.isBuilderIdInUse() && + editor && + editor.document.uri.scheme === 'file' && + securityScanLanguageContext.isLanguageSupported(editor.document.languageId) + ) + } + + function setSubscriptionsForAutoScans() { + // Initial scan when the editor opens for the first time + const editor = vscode.window.activeTextEditor + if (editor && shouldRunAutoScan(editor) && editor.document.getText().length > 0) { + void debounceStartSecurityScan( + securityPanelViewProvider, + editor, + client, + context.extensionContext, + CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO, + false + ) + } + + context.extensionContext.subscriptions.push( + // Trigger scan if focus switches to a different file + vscode.window.onDidChangeActiveTextEditor((editor) => { + const codewhispererDiagnostics = editor + ? securityScanRender.securityDiagnosticCollection + ?.get(editor.document.uri) + ?.filter(({ source }) => source === CodeWhispererConstants.codewhispererDiagnosticSourceLabel) + : undefined + + if ( + editor && + shouldRunAutoScan(editor) && + editor.document.getText().length > 0 && + (!codewhispererDiagnostics || codewhispererDiagnostics?.length === 0) + ) { + void debounceStartSecurityScan( + securityPanelViewProvider, + editor, + client, + context.extensionContext, + CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO, + false + ) + } + }), + // Trigger scan if the file contents change + vscode.workspace.onDidChangeTextDocument(async (event) => { + const editor = vscode.window.activeTextEditor + if ( + editor && + shouldRunAutoScan(editor) && + event.document === editor.document && + event.contentChanges.length > 0 + ) { + void debounceStartSecurityScan( + securityPanelViewProvider, + editor, + client, + context.extensionContext, + CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO, + false + ) + } + }) + ) + + // Trigger scan if the toggle has just been enabled + CodeScansState.instance.onDidChangeState((isScansEnabled) => { + const editor = vscode.window.activeTextEditor + if (editor && shouldRunAutoScan(editor, isScansEnabled) && editor.document.getText().length > 0) { + void debounceStartSecurityScan( + securityPanelViewProvider, + editor, + client, + context.extensionContext, + CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO, + false + ) + } + }) + } + + void FeatureConfigProvider.instance.fetchFeatureConfigs().catch((error) => { + getLogger().error('Failed to fetch feature configs - %s', error) + }) + + await Commands.tryExecute('aws.amazonq.refreshConnectionCallback') + + function setSubscriptionsForCodeIssues() { + context.extensionContext.subscriptions.push( + vscode.workspace.onDidChangeTextDocument(async (e) => { + if (e.document.uri.scheme !== 'file') { + return + } + const diagnostics = securityScanRender.securityDiagnosticCollection?.get(e.document.uri) + if (!diagnostics || diagnostics.length === 0) { + return + } + disposeSecurityDiagnostic(e) + + SecurityIssueProvider.instance.handleDocumentChange(e) + SecurityIssueTreeViewProvider.instance.refresh() + await syncSecurityIssueWebview(context) + + toggleIssuesVisibility((issue, filePath) => + filePath !== e.document.uri.fsPath + ? issue.visible + : !detectCommentAboveLine( + e.document, + issue.startLine, + CodeWhispererConstants.amazonqIgnoreNextLine + ) + ) + }), + vscode.window.createTreeView(SecurityIssueTreeViewProvider.viewType, { + treeDataProvider: SecurityIssueTreeViewProvider.instance, + }) + ) + } + + activateEditTracking(context) +} + +export async function shutdown() { + await CodeWhispererTracker.getTracker().shutdown() +} + +function toggleIssuesVisibility(visibleCondition: (issue: CodeScanIssue, filePath: string) => boolean) { + const updatedIssues: AggregatedCodeScanIssue[] = SecurityIssueProvider.instance.issues.map((group) => ({ + ...group, + issues: group.issues.map((issue) => ({ ...issue, visible: visibleCondition(issue, group.filePath) })), + })) + for (const issue of updatedIssues) { + updateSecurityDiagnosticCollection(issue) + } + SecurityIssueProvider.instance.issues = updatedIssues + SecurityIssueTreeViewProvider.instance.refresh() +} diff --git a/packages/core/src/codewhisperer/client/codewhisperer.ts b/packages/core/src/codewhisperer/client/codewhisperer.ts new file mode 100644 index 00000000000..22ae0447d0a --- /dev/null +++ b/packages/core/src/codewhisperer/client/codewhisperer.ts @@ -0,0 +1,326 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AWSError, Credentials, Service } from 'aws-sdk' +import globals from '../../shared/extensionGlobals' +import * as CodeWhispererClient from './codewhispererclient' +import * as CodeWhispererUserClient from './codewhispereruserclient' +import { SendTelemetryEventRequest } from './codewhispereruserclient' +import { ServiceOptions } from '../../shared/awsClientBuilder' +import { hasVendedIamCredentials } from '../../auth/auth' +import { CodeWhispererSettings } from '../util/codewhispererSettings' +import { PromiseResult } from 'aws-sdk/lib/request' +import { AuthUtil } from '../util/authUtil' +import { isSsoConnection } from '../../auth/connection' +import apiConfig = require('./service-2.json') +import userApiConfig = require('./user-service-2.json') +import { session } from '../util/codeWhispererSession' +import { getLogger } from '../../shared/logger/logger' +import { getClientId, getOptOutPreference, getOperatingSystem } from '../../shared/telemetry/util' +import { extensionVersion, getServiceEnvVarConfig } from '../../shared/vscode/env' +import { DevSettings } from '../../shared/settings' +import { CodeWhispererConfig } from '../models/model' + +const keepAliveHeader = 'keep-alive-codewhisperer' + +export function getCodewhispererConfig(): CodeWhispererConfig { + const clientConfig = AuthUtil.instance.regionProfileManager.clientConfig + return { + ...DevSettings.instance.getServiceConfig('codewhispererService', clientConfig), + + // Environment variable overrides + ...getServiceEnvVarConfig('codewhisperer', Object.keys(clientConfig)), + } +} + +export type ProgrammingLanguage = Readonly< + CodeWhispererClient.ProgrammingLanguage | CodeWhispererUserClient.ProgrammingLanguage +> +export type FileContext = Readonly +export type ListRecommendationsRequest = Readonly< + CodeWhispererClient.ListRecommendationsRequest | CodeWhispererUserClient.GenerateCompletionsRequest +> +export type GenerateRecommendationsRequest = Readonly +export type RecommendationsList = CodeWhispererClient.RecommendationsList | CodeWhispererUserClient.Completions +export type ListRecommendationsResponse = + | CodeWhispererClient.ListRecommendationsResponse + | CodeWhispererUserClient.GenerateCompletionsResponse +export type GenerateRecommendationsResponse = CodeWhispererClient.GenerateRecommendationsResponse +export type Recommendation = CodeWhispererClient.Recommendation | CodeWhispererUserClient.Completion +export type Completion = CodeWhispererUserClient.Completion +export type Reference = CodeWhispererClient.Reference | CodeWhispererUserClient.Reference +export type References = CodeWhispererClient.References | CodeWhispererUserClient.References +export type CreateUploadUrlRequest = Readonly< + CodeWhispererClient.CreateUploadUrlRequest | CodeWhispererUserClient.CreateUploadUrlRequest +> +export type CreateCodeScanRequest = Readonly< + CodeWhispererClient.CreateCodeScanRequest | CodeWhispererUserClient.StartCodeAnalysisRequest +> +export type GetCodeScanRequest = Readonly< + CodeWhispererClient.GetCodeScanRequest | CodeWhispererUserClient.GetCodeAnalysisRequest +> +export type ListCodeScanFindingsRequest = Readonly< + CodeWhispererClient.ListCodeScanFindingsRequest | CodeWhispererUserClient.ListCodeAnalysisFindingsRequest +> +export type SupplementalContext = Readonly< + CodeWhispererClient.SupplementalContext | CodeWhispererUserClient.SupplementalContext +> +// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents +export type ArtifactType = Readonly +export type ArtifactMap = Readonly +export type ListCodeScanFindingsResponse = + | CodeWhispererClient.ListCodeScanFindingsResponse + | CodeWhispererUserClient.ListCodeAnalysisFindingsResponse +export type CreateUploadUrlResponse = + | CodeWhispererClient.CreateUploadUrlResponse + | CodeWhispererUserClient.CreateUploadUrlResponse +export type CreateCodeScanResponse = + | CodeWhispererClient.CreateCodeScanResponse + | CodeWhispererUserClient.StartCodeAnalysisResponse +export type Import = CodeWhispererUserClient.Import +export type Imports = CodeWhispererUserClient.Imports + +export class DefaultCodeWhispererClient { + private async createSdkClient(): Promise { + const isOptedOut = CodeWhispererSettings.instance.isOptoutEnabled() + const cwsprConfig = getCodewhispererConfig() + return (await globals.sdkClientBuilder.createAwsService( + Service, + { + apiConfig: apiConfig, + region: cwsprConfig.region, + credentials: await AuthUtil.instance.getCredentials(), + endpoint: cwsprConfig.endpoint, + onRequestSetup: [ + (req) => { + if (req.operation === 'listRecommendations') { + req.on('build', () => { + req.httpRequest.headers['x-amzn-codewhisperer-optout'] = `${isOptedOut}` + }) + } + // This logic is for backward compatability with legacy SDK v2 behavior for refreshing + // credentials. Once the Toolkit adds a file watcher for credentials it won't be needed. + + if (hasVendedIamCredentials()) { + req.on('retry', (resp) => { + if ( + resp.error?.code === 'AccessDeniedException' && + resp.error.message.match(/expired/i) + ) { + AuthUtil.instance.reauthenticate().catch((e) => { + getLogger().error('reauthenticate failed: %s', (e as Error).message) + }) + resp.error.retryable = true + } + }) + } + }, + ], + } as ServiceOptions, + undefined + )) as CodeWhispererClient + } + + async createUserSdkClient(maxRetries?: number): Promise { + const isOptedOut = CodeWhispererSettings.instance.isOptoutEnabled() + session.setFetchCredentialStart() + const bearerToken = await AuthUtil.instance.getBearerToken() + session.setSdkApiCallStart() + const cwsprConfig = getCodewhispererConfig() + return (await globals.sdkClientBuilder.createAwsService( + Service, + { + apiConfig: userApiConfig, + region: cwsprConfig.region, + endpoint: cwsprConfig.endpoint, + maxRetries: maxRetries, + credentials: new Credentials({ accessKeyId: 'xxx', secretAccessKey: 'xxx' }), + onRequestSetup: [ + (req) => { + req.on('build', ({ httpRequest }) => { + httpRequest.headers['Authorization'] = `Bearer ${bearerToken}` + }) + if (req.operation === 'generateCompletions') { + req.on('build', () => { + req.httpRequest.headers['x-amzn-codewhisperer-optout'] = `${isOptedOut}` + req.httpRequest.headers['Connection'] = keepAliveHeader + }) + } + }, + ], + } as ServiceOptions, + undefined + )) as CodeWhispererUserClient + } + + private isBearerTokenAuth(): boolean { + return isSsoConnection(AuthUtil.instance.conn) + } + + public async generateRecommendations( + request: GenerateRecommendationsRequest + ): Promise { + return (await this.createSdkClient()).generateRecommendations(request).promise() + } + + public async listRecommendations(request: ListRecommendationsRequest): Promise { + if (this.isBearerTokenAuth()) { + return await (await this.createUserSdkClient()).generateCompletions(request).promise() + } + return (await this.createSdkClient()).listRecommendations(request).promise() + } + + public async createUploadUrl( + request: CreateUploadUrlRequest + ): Promise> { + if (this.isBearerTokenAuth()) { + return (await this.createUserSdkClient()).createUploadUrl(request).promise() + } + return (await this.createSdkClient()).createCodeScanUploadUrl(request).promise() + } + + public async createCodeScan( + request: CreateCodeScanRequest + ): Promise> { + if (this.isBearerTokenAuth()) { + return (await this.createUserSdkClient()).startCodeAnalysis(request).promise() + } + return (await this.createSdkClient()).createCodeScan(request).promise() + } + + public async getCodeScan( + request: GetCodeScanRequest + ): Promise> { + if (this.isBearerTokenAuth()) { + return (await this.createUserSdkClient()).getCodeAnalysis(request).promise() + } + return (await this.createSdkClient()).getCodeScan(request).promise() + } + + public async listCodeScanFindings( + request: ListCodeScanFindingsRequest, + profileArn: string | undefined + ): Promise> { + if (this.isBearerTokenAuth()) { + const req = { + jobId: request.jobId, + nextToken: request.nextToken, + codeAnalysisFindingsSchema: 'codeanalysis/findings/1.0', + profileArn: profileArn, + } as CodeWhispererUserClient.ListCodeAnalysisFindingsRequest + return (await this.createUserSdkClient()).listCodeAnalysisFindings(req).promise() + } + return (await this.createSdkClient()) + .listCodeScanFindings(request as CodeWhispererClient.ListCodeScanFindingsRequest) + .promise() + } + + public async sendTelemetryEvent(request: SendTelemetryEventRequest) { + const requestWithCommonFields: SendTelemetryEventRequest = { + ...request, + optOutPreference: getOptOutPreference(), + userContext: { + ideCategory: 'VSCODE', + operatingSystem: getOperatingSystem(), + product: 'CodeWhisperer', // TODO: update this? + clientId: getClientId(globals.globalState), + ideVersion: extensionVersion, + pluginVersion: extensionVersion, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + } + if (!AuthUtil.instance.isValidEnterpriseSsoInUse() && !globals.telemetry.telemetryEnabled) { + return + } + const response = await (await this.createUserSdkClient()).sendTelemetryEvent(requestWithCommonFields).promise() + getLogger().debug(`codewhisperer: sendTelemetryEvent requestID: ${response.$response.requestId}`) + } + + /** + * @description Use this function to start the transformation job. + * @param request + * @returns transformationJobId - String id for the Job + */ + public async codeModernizerStartCodeTransformation( + request: CodeWhispererUserClient.StartTransformationRequest + ): Promise> { + return (await this.createUserSdkClient()).startTransformation(request).promise() + } + + /** + * @description Use this function to stop the transformation job. + * @param request + * @returns transformationJobId - String id for the Job + */ + public async codeModernizerStopCodeTransformation( + request: CodeWhispererUserClient.StopTransformationRequest + ): Promise> { + return (await this.createUserSdkClient()).stopTransformation(request).promise() + } + + /** + * @description Use this function to get the status of the code transformation. We should + * be polling this function periodically to get updated results. When this function + * returns PARTIALLY_COMPLETED or COMPLETED we know the transformation is done. + */ + public async codeModernizerGetCodeTransformation( + request: CodeWhispererUserClient.GetTransformationRequest + ): Promise> { + // instead of the default of 3 retries, use 8 retries for this API which is polled every 5 seconds + return (await this.createUserSdkClient(8)).getTransformation(request).promise() + } + + /** + * @description During client-side build, or after the job has been PAUSED we need to get user intervention. + * Once that user action has been handled we can resume the transformation job. + * @params transformationJobId - String id returned from StartCodeTransformationResponse + * @params userActionStatus - String to determine what action the user took, if any. + */ + public async codeModernizerResumeTransformation( + request: CodeWhispererUserClient.ResumeTransformationRequest + ): Promise> { + return (await this.createUserSdkClient(8)).resumeTransformation(request).promise() + } + + /** + * @description After starting a transformation use this function to display the LLM + * transformation plan to the user. + * @params transformationJobId - String id returned from StartCodeTransformationResponse + */ + public async codeModernizerGetCodeTransformationPlan( + request: CodeWhispererUserClient.GetTransformationPlanRequest + ): Promise> { + // instead of the default of 3 retries, use 8 retries for this API which is polled every 5 seconds + return (await this.createUserSdkClient(8)).getTransformationPlan(request).promise() + } + + public async startCodeFixJob( + request: CodeWhispererUserClient.StartCodeFixJobRequest + ): Promise> { + return (await this.createUserSdkClient()).startCodeFixJob(request).promise() + } + + public async getCodeFixJob( + request: CodeWhispererUserClient.GetCodeFixJobRequest + ): Promise> { + return (await this.createUserSdkClient()).getCodeFixJob(request).promise() + } + + public async startTestGeneration( + request: CodeWhispererUserClient.StartTestGenerationRequest + ): Promise> { + return (await this.createUserSdkClient()).startTestGeneration(request).promise() + } + + public async getTestGeneration( + request: CodeWhispererUserClient.GetTestGenerationRequest + ): Promise> { + return (await this.createUserSdkClient()).getTestGeneration(request).promise() + } +} + +export const codeWhispererClient = new DefaultCodeWhispererClient() + +export class CognitoCredentialsError extends Error {} diff --git a/packages/core/src/codewhisperer/client/service-2.json b/packages/core/src/codewhisperer/client/service-2.json new file mode 100644 index 00000000000..3e063c38a10 --- /dev/null +++ b/packages/core/src/codewhisperer/client/service-2.json @@ -0,0 +1,1283 @@ +{ + "version": "2.0", + "metadata": { + "apiVersion": "2022-06-15", + "endpointPrefix": "codewhisperer", + "jsonVersion": "1.0", + "protocol": "json", + "serviceFullName": "AWS CodeWhisperer", + "serviceId": "CodeWhisperer", + "signatureVersion": "v4", + "signingName": "codewhisperer", + "targetPrefix": "AWSCodeWhispererService", + "uid": "codewhisperer-2022-06-15" + }, + "operations": { + "CreateCodeScan": { + "name": "CreateCodeScan", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "CreateCodeScanRequest" + }, + "output": { + "shape": "CreateCodeScanResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "ConflictException" + }, + { + "shape": "ResourceNotFoundException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ], + "idempotent": true + }, + "CreateCodeScanUploadUrl": { + "name": "CreateCodeScanUploadUrl", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "CreateUploadUrlRequest" + }, + "output": { + "shape": "CreateUploadUrlResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ], + "idempotent": true + }, + "CreateProfile": { + "name": "CreateProfile", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "CreateProfileRequest" + }, + "output": { + "shape": "CreateProfileResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "ConflictException" + }, + { + "shape": "ResourceNotFoundException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "DeleteProfile": { + "name": "DeleteProfile", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "DeleteProfileRequest" + }, + "output": { + "shape": "DeleteProfileResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "ConflictException" + }, + { + "shape": "ResourceNotFoundException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "GenerateRecommendations": { + "name": "GenerateRecommendations", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GenerateRecommendationsRequest" + }, + "output": { + "shape": "GenerateRecommendationsResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "GetAccessToken": { + "name": "GetAccessToken", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GetAccessTokenRequest" + }, + "output": { + "shape": "GetAccessTokenResponse" + }, + "errors": [ + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "GetCodeScan": { + "name": "GetCodeScan", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GetCodeScanRequest" + }, + "output": { + "shape": "GetCodeScanResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "ResourceNotFoundException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "ListCodeScanFindings": { + "name": "ListCodeScanFindings", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "ListCodeScanFindingsRequest" + }, + "output": { + "shape": "ListCodeScanFindingsResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "ResourceNotFoundException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "ListProfiles": { + "name": "ListProfiles", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "ListProfilesRequest" + }, + "output": { + "shape": "ListProfilesResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "ListRecommendations": { + "name": "ListRecommendations", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "ListRecommendationsRequest" + }, + "output": { + "shape": "ListRecommendationsResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "ListTagsForResource": { + "name": "ListTagsForResource", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "ListTagsForResourceRequest" + }, + "output": { + "shape": "ListTagsForResourceResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "ResourceNotFoundException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "TagResource": { + "name": "TagResource", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "TagResourceRequest" + }, + "output": { + "shape": "TagResourceResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "ResourceNotFoundException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "UntagResource": { + "name": "UntagResource", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "UntagResourceRequest" + }, + "output": { + "shape": "UntagResourceResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "ResourceNotFoundException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "UpdateProfile": { + "name": "UpdateProfile", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "UpdateProfileRequest" + }, + "output": { + "shape": "UpdateProfileResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "ConflictException" + }, + { + "shape": "ResourceNotFoundException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + } + }, + "shapes": { + "AccessDeniedException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "ArtifactMap": { + "type": "map", + "key": { + "shape": "ArtifactType" + }, + "value": { + "shape": "UploadId" + }, + "max": 64, + "min": 1 + }, + "ArtifactType": { + "type": "string", + "enum": ["SourceCode", "BuiltJars"] + }, + "Base64EncodedPaginationToken": { + "type": "string", + "max": 2048, + "min": 1, + "pattern": "(?:[A-Za-z0-9\\+/]{4})*(?:[A-Za-z0-9\\+/]{2}\\=\\=|[A-Za-z0-9\\+/]{3}\\=)?" + }, + "CodeScanFindingsSchema": { + "type": "string", + "enum": ["codescan/findings/1.0"] + }, + "CodeScanStatus": { + "type": "string", + "enum": ["Completed", "Pending", "Failed"] + }, + "ConflictException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "CreateCodeScanRequest": { + "type": "structure", + "required": ["artifacts", "programmingLanguage"], + "members": { + "artifacts": { + "shape": "ArtifactMap" + }, + "programmingLanguage": { + "shape": "ProgrammingLanguage" + }, + "clientToken": { + "shape": "CreateCodeScanRequestClientTokenString", + "idempotencyToken": true + } + } + }, + "CreateCodeScanRequestClientTokenString": { + "type": "string", + "max": 256, + "min": 1 + }, + "CreateCodeScanResponse": { + "type": "structure", + "required": ["jobId", "status"], + "members": { + "jobId": { + "shape": "CreateCodeScanResponseJobIdString" + }, + "status": { + "shape": "CodeScanStatus" + }, + "errorMessage": { + "shape": "String" + } + } + }, + "CreateCodeScanResponseJobIdString": { + "type": "string", + "max": 256, + "min": 1 + }, + "CreateProfileRequest": { + "type": "structure", + "required": ["identitySource", "profileName", "referenceTrackerConfiguration"], + "members": { + "identitySource": { + "shape": "IdentitySource" + }, + "profileName": { + "shape": "ProfileName" + }, + "referenceTrackerConfiguration": { + "shape": "ReferenceTrackerConfiguration" + }, + "clientToken": { + "shape": "IdempotencyToken", + "idempotencyToken": true + }, + "kmsKeyArn": { + "shape": "ResourceArn" + }, + "tags": { + "shape": "TagList" + } + } + }, + "CreateProfileResponse": { + "type": "structure", + "required": ["profileArn"], + "members": { + "profileArn": { + "shape": "ResourceArn" + } + } + }, + "CreateUploadUrlRequest": { + "type": "structure", + "members": { + "contentMd5": { + "shape": "CreateUploadUrlRequestContentMd5String" + }, + "artifactType": { + "shape": "ArtifactType" + } + } + }, + "CreateUploadUrlRequestContentMd5String": { + "type": "string", + "max": 128, + "min": 1 + }, + "CreateUploadUrlResponse": { + "type": "structure", + "required": ["uploadId", "uploadUrl"], + "members": { + "uploadId": { + "shape": "UploadId" + }, + "uploadUrl": { + "shape": "PreSignedUrl" + }, + "kmsKeyArn": { + "shape": "ResourceArn" + } + } + }, + "DeleteProfileRequest": { + "type": "structure", + "required": ["profileArn"], + "members": { + "profileArn": { + "shape": "ResourceArn" + } + } + }, + "DeleteProfileResponse": { + "type": "structure", + "members": {} + }, + "FileContext": { + "type": "structure", + "required": ["leftFileContent", "rightFileContent", "filename", "programmingLanguage"], + "members": { + "leftFileContent": { + "shape": "FileContextLeftFileContentString" + }, + "rightFileContent": { + "shape": "FileContextRightFileContentString" + }, + "filename": { + "shape": "FileContextFilenameString" + }, + "fileUri": { + "shape": "FileContextFileUriString" + }, + "programmingLanguage": { + "shape": "ProgrammingLanguage" + } + } + }, + "FileContextFileUriString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "FileContextFilenameString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "FileContextLeftFileContentString": { + "type": "string", + "max": 10240, + "min": 0, + "sensitive": true + }, + "FileContextRightFileContentString": { + "type": "string", + "max": 10240, + "min": 0, + "sensitive": true + }, + "SupplementalContext": { + "type": "structure", + "required": ["filePath", "content"], + "members": { + "filePath": { + "shape": "SupplementalContextFilePathString" + }, + "content": { + "shape": "SupplementalContextContentString" + } + } + }, + "SupplementalContextFilePathString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "SupplementalContextContentString": { + "type": "string", + "max": 5120, + "min": 0, + "sensitive": true + }, + "SupplementalContextList": { + "type": "list", + "member": { + "shape": "SupplementalContext" + }, + "max": 10, + "min": 0 + }, + "GenerateRecommendationsRequest": { + "type": "structure", + "required": ["fileContext"], + "members": { + "fileContext": { + "shape": "FileContext" + }, + "supplementalContexts": { + "shape": "SupplementalContextList" + }, + "maxResults": { + "shape": "GenerateRecommendationsRequestMaxResultsInteger" + }, + "nextToken": { + "shape": "GenerateRecommendationsRequestNextTokenString" + }, + "referenceTrackerConfiguration": { + "shape": "ReferenceTrackerConfiguration" + } + } + }, + "GenerateRecommendationsRequestMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 10, + "min": 1 + }, + "GenerateRecommendationsRequestNextTokenString": { + "type": "string", + "max": 2048, + "min": 0, + "pattern": "(?:[A-Za-z0-9\\+/]{4})*(?:[A-Za-z0-9\\+/]{2}\\=\\=|[A-Za-z0-9\\+/]{3}\\=)?" + }, + "GenerateRecommendationsResponse": { + "type": "structure", + "members": { + "recommendations": { + "shape": "RecommendationsList" + }, + "nextToken": { + "shape": "String" + } + } + }, + "GetAccessTokenRequest": { + "type": "structure", + "required": ["identityToken"], + "members": { + "identityToken": { + "shape": "GetAccessTokenRequestIdentityTokenString" + } + } + }, + "GetAccessTokenRequestIdentityTokenString": { + "type": "string", + "max": 1024, + "min": 0, + "sensitive": true + }, + "GetAccessTokenResponse": { + "type": "structure", + "members": { + "accessToken": { + "shape": "SensitiveString" + } + } + }, + "GetCodeScanRequest": { + "type": "structure", + "required": ["jobId"], + "members": { + "jobId": { + "shape": "GetCodeScanRequestJobIdString" + } + } + }, + "GetCodeScanRequestJobIdString": { + "type": "string", + "max": 256, + "min": 1 + }, + "GetCodeScanResponse": { + "type": "structure", + "required": ["status"], + "members": { + "status": { + "shape": "CodeScanStatus" + }, + "errorMessage": { + "shape": "String" + } + } + }, + "IdempotencyToken": { + "type": "string", + "max": 256, + "min": 1 + }, + "IdentityDetails": { + "type": "structure", + "members": { + "ssoIdentityDetails": { + "shape": "SSOIdentityDetails" + } + }, + "union": true + }, + "IdentitySource": { + "type": "structure", + "members": { + "ssoIdentitySource": { + "shape": "SSOIdentitySource" + } + }, + "union": true + }, + "Import": { + "type": "structure", + "members": { + "statement": { + "shape": "ImportStatementString" + } + } + }, + "ImportStatementString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "Imports": { + "type": "list", + "member": { + "shape": "Import" + }, + "max": 10, + "min": 0 + }, + "InternalServerException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true, + "fault": true, + "retryable": { + "throttling": false + } + }, + "ListCodeScanFindingsRequest": { + "type": "structure", + "required": ["jobId", "codeScanFindingsSchema"], + "members": { + "jobId": { + "shape": "ListCodeScanFindingsRequestJobIdString" + }, + "nextToken": { + "shape": "PaginationToken" + }, + "codeScanFindingsSchema": { + "shape": "CodeScanFindingsSchema" + } + } + }, + "ListCodeScanFindingsRequestJobIdString": { + "type": "string", + "max": 256, + "min": 1 + }, + "ListCodeScanFindingsResponse": { + "type": "structure", + "required": ["codeScanFindings"], + "members": { + "nextToken": { + "shape": "PaginationToken" + }, + "codeScanFindings": { + "shape": "String" + } + } + }, + "ListProfilesRequest": { + "type": "structure", + "members": { + "maxResults": { + "shape": "ListProfilesRequestMaxResultsInteger" + }, + "nextToken": { + "shape": "Base64EncodedPaginationToken" + } + } + }, + "ListProfilesRequestMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 1 + }, + "ListProfilesResponse": { + "type": "structure", + "required": ["profiles"], + "members": { + "profiles": { + "shape": "ProfileList" + }, + "nextToken": { + "shape": "Base64EncodedPaginationToken" + } + } + }, + "ListRecommendationsRequest": { + "type": "structure", + "required": ["fileContext"], + "members": { + "fileContext": { + "shape": "FileContext" + }, + "maxResults": { + "shape": "ListRecommendationsRequestMaxResultsInteger" + }, + "supplementalContexts": { + "shape": "SupplementalContextList" + }, + "nextToken": { + "shape": "ListRecommendationsRequestNextTokenString" + }, + "referenceTrackerConfiguration": { + "shape": "ReferenceTrackerConfiguration" + } + } + }, + "ListRecommendationsRequestMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 10, + "min": 1 + }, + "ListRecommendationsRequestNextTokenString": { + "type": "string", + "max": 2048, + "min": 0, + "pattern": "(?:[A-Za-z0-9\\+/]{4})*(?:[A-Za-z0-9\\+/]{2}\\=\\=|[A-Za-z0-9\\+/]{3}\\=)?" + }, + "ListRecommendationsResponse": { + "type": "structure", + "members": { + "recommendations": { + "shape": "RecommendationsList" + }, + "nextToken": { + "shape": "String" + } + } + }, + "ListTagsForResourceRequest": { + "type": "structure", + "required": ["resourceName"], + "members": { + "resourceName": { + "shape": "ResourceArn" + } + } + }, + "ListTagsForResourceResponse": { + "type": "structure", + "members": { + "tags": { + "shape": "TagList" + } + } + }, + "PaginationToken": { + "type": "string", + "max": 2048, + "min": 1, + "pattern": "\\S+" + }, + "PreSignedUrl": { + "type": "string", + "max": 2048, + "min": 1 + }, + "Profile": { + "type": "structure", + "required": ["arn", "identityDetails", "profileName", "referenceTrackerConfiguration"], + "members": { + "arn": { + "shape": "ResourceArn" + }, + "identityDetails": { + "shape": "IdentityDetails" + }, + "profileName": { + "shape": "ProfileName" + }, + "referenceTrackerConfiguration": { + "shape": "ReferenceTrackerConfiguration" + }, + "kmsKeyArn": { + "shape": "ResourceArn" + } + } + }, + "ProfileList": { + "type": "list", + "member": { + "shape": "Profile" + } + }, + "ProfileName": { + "type": "string", + "max": 100, + "min": 1, + "pattern": "[a-zA-Z][a-zA-Z0-9_-]*" + }, + "ProgrammingLanguage": { + "type": "structure", + "required": ["languageName"], + "members": { + "languageName": { + "shape": "ProgrammingLanguageLanguageNameString" + } + } + }, + "ProgrammingLanguageLanguageNameString": { + "type": "string", + "max": 128, + "min": 1, + "pattern": "(python|javascript|java|csharp|typescript|c|cpp|go|kotlin|php|ruby|rust|scala|shell|sql)" + }, + "Recommendation": { + "type": "structure", + "required": ["content"], + "members": { + "content": { + "shape": "RecommendationContentString" + }, + "references": { + "shape": "References" + }, + "mostRelevantMissingImports": { + "shape": "Imports" + } + } + }, + "RecommendationContentString": { + "type": "string", + "max": 5120, + "min": 1, + "sensitive": true + }, + "RecommendationsList": { + "type": "list", + "member": { + "shape": "Recommendation" + }, + "max": 10, + "min": 0 + }, + "RecommendationsWithReferencesPreference": { + "type": "string", + "enum": ["BLOCK", "ALLOW"] + }, + "Reference": { + "type": "structure", + "members": { + "licenseName": { + "shape": "ReferenceLicenseNameString" + }, + "repository": { + "shape": "ReferenceRepositoryString" + }, + "url": { + "shape": "ReferenceUrlString" + }, + "recommendationContentSpan": { + "shape": "Span" + } + } + }, + "ReferenceLicenseNameString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "ReferenceRepositoryString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "ReferenceTrackerConfiguration": { + "type": "structure", + "required": ["recommendationsWithReferences"], + "members": { + "recommendationsWithReferences": { + "shape": "RecommendationsWithReferencesPreference" + } + } + }, + "ReferenceUrlString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "References": { + "type": "list", + "member": { + "shape": "Reference" + }, + "max": 10, + "min": 0 + }, + "ResourceArn": { + "type": "string", + "max": 1224, + "min": 0 + }, + "ResourceNotFoundException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "SSOIdentityDetails": { + "type": "structure", + "required": ["instanceArn", "oidcClientId"], + "members": { + "instanceArn": { + "shape": "ResourceArn" + }, + "oidcClientId": { + "shape": "String" + } + } + }, + "SSOIdentitySource": { + "type": "structure", + "required": ["instanceArn"], + "members": { + "instanceArn": { + "shape": "ResourceArn" + } + } + }, + "SensitiveString": { + "type": "string", + "sensitive": true + }, + "Span": { + "type": "structure", + "members": { + "start": { + "shape": "SpanStartInteger" + }, + "end": { + "shape": "SpanEndInteger" + } + } + }, + "SpanEndInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "SpanStartInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "String": { + "type": "string" + }, + "Tag": { + "type": "structure", + "required": ["key", "value"], + "members": { + "key": { + "shape": "TagKey" + }, + "value": { + "shape": "TagValue" + } + } + }, + "TagKey": { + "type": "string", + "max": 128, + "min": 1 + }, + "TagKeyList": { + "type": "list", + "member": { + "shape": "TagKey" + }, + "max": 200, + "min": 0 + }, + "TagList": { + "type": "list", + "member": { + "shape": "Tag" + }, + "max": 200, + "min": 0 + }, + "TagResourceRequest": { + "type": "structure", + "required": ["resourceName", "tags"], + "members": { + "resourceName": { + "shape": "ResourceArn" + }, + "tags": { + "shape": "TagList" + } + } + }, + "TagResourceResponse": { + "type": "structure", + "members": {} + }, + "TagValue": { + "type": "string", + "max": 256, + "min": 0 + }, + "ThrottlingException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true, + "retryable": { + "throttling": false + } + }, + "UntagResourceRequest": { + "type": "structure", + "required": ["resourceName", "tagKeys"], + "members": { + "resourceName": { + "shape": "ResourceArn" + }, + "tagKeys": { + "shape": "TagKeyList" + } + } + }, + "UntagResourceResponse": { + "type": "structure", + "members": {} + }, + "UpdateProfileRequest": { + "type": "structure", + "required": ["profileArn"], + "members": { + "profileArn": { + "shape": "ResourceArn" + }, + "profileName": { + "shape": "ProfileName" + }, + "referenceTrackerConfiguration": { + "shape": "ReferenceTrackerConfiguration" + }, + "kmsKeyArn": { + "shape": "ResourceArn" + } + } + }, + "UpdateProfileResponse": { + "type": "structure", + "required": ["profileArn"], + "members": { + "profileArn": { + "shape": "ResourceArn" + } + } + }, + "UploadId": { + "type": "string", + "max": 128, + "min": 1 + }, + "ValidationException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + } + } +} diff --git a/packages/core/src/codewhisperer/client/user-service-2.json b/packages/core/src/codewhisperer/client/user-service-2.json new file mode 100644 index 00000000000..619ce74aa5b --- /dev/null +++ b/packages/core/src/codewhisperer/client/user-service-2.json @@ -0,0 +1,4914 @@ +{ + "version": "2.0", + "metadata": { + "apiVersion": "2022-11-11", + "endpointPrefix": "amazoncodewhispererservice", + "jsonVersion": "1.0", + "protocol": "json", + "serviceFullName": "Amazon CodeWhisperer", + "serviceId": "CodeWhispererRuntime", + "signingName": "amazoncodewhispererservice", + "targetPrefix": "AmazonCodeWhispererService", + "uid": "codewhispererruntime-2022-11-11" + }, + "operations": { + "CreateArtifactUploadUrl": { + "name": "CreateArtifactUploadUrl", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "CreateUploadUrlRequest" }, + "output": { "shape": "CreateUploadUrlResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

Creates a pre-signed, S3 write URL for uploading a repository zip archive.

", + "idempotent": true + }, + "CreateSubscriptionToken": { + "name": "CreateSubscriptionToken", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "CreateSubscriptionTokenRequest" }, + "output": { "shape": "CreateSubscriptionTokenResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "idempotent": true + }, + "CreateTaskAssistConversation": { + "name": "CreateTaskAssistConversation", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "CreateTaskAssistConversationRequest" }, + "output": { "shape": "CreateTaskAssistConversationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to create task assist conversation.

" + }, + "CreateUploadUrl": { + "name": "CreateUploadUrl", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "CreateUploadUrlRequest" }, + "output": { "shape": "CreateUploadUrlResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ConflictException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

Creates a pre-signed, S3 write URL for uploading a repository zip archive.

", + "idempotent": true + }, + "CreateUserMemoryEntry": { + "name": "CreateUserMemoryEntry", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "CreateUserMemoryEntryInput" }, + "output": { "shape": "CreateUserMemoryEntryOutput" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to create a single user memory entry

", + "idempotent": true + }, + "CreateWorkspace": { + "name": "CreateWorkspace", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "CreateWorkspaceRequest" }, + "output": { "shape": "CreateWorkspaceResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ConflictException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

Create a workspace based on a workspace root

" + }, + "DeleteTaskAssistConversation": { + "name": "DeleteTaskAssistConversation", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "DeleteTaskAssistConversationRequest" }, + "output": { "shape": "DeleteTaskAssistConversationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to delete task assist conversation.

" + }, + "DeleteUserMemoryEntry": { + "name": "DeleteUserMemoryEntry", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "DeleteUserMemoryEntryInput" }, + "output": { "shape": "DeleteUserMemoryEntryOutput" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to delete a single user memory entry

", + "idempotent": true + }, + "DeleteWorkspace": { + "name": "DeleteWorkspace", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "DeleteWorkspaceRequest" }, + "output": { "shape": "DeleteWorkspaceResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

Delete a workspace based on a workspaceId

" + }, + "GenerateCompletions": { + "name": "GenerateCompletions", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "GenerateCompletionsRequest" }, + "output": { "shape": "GenerateCompletionsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

Generate completions based on the provided file context in a paginated response.

" + }, + "GetCodeAnalysis": { + "name": "GetCodeAnalysis", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "GetCodeAnalysisRequest" }, + "output": { "shape": "GetCodeAnalysisResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

Gets the metadata of a code analysis job.

" + }, + "GetCodeFixJob": { + "name": "GetCodeFixJob", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "GetCodeFixJobRequest" }, + "output": { "shape": "GetCodeFixJobResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ] + }, + "GetTaskAssistCodeGeneration": { + "name": "GetTaskAssistCodeGeneration", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "GetTaskAssistCodeGenerationRequest" }, + "output": { "shape": "GetTaskAssistCodeGenerationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ConflictException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to get status of task assist code generation.

" + }, + "GetTestGeneration": { + "name": "GetTestGeneration", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "GetTestGenerationRequest" }, + "output": { "shape": "GetTestGenerationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to get test generation job.

" + }, + "GetTransformation": { + "name": "GetTransformation", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "GetTransformationRequest" }, + "output": { "shape": "GetTransformationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to get code transformation status.

" + }, + "GetTransformationPlan": { + "name": "GetTransformationPlan", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "GetTransformationPlanRequest" }, + "output": { "shape": "GetTransformationPlanResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to get code transformation status.

" + }, + "GetUsageLimits": { + "name": "GetUsageLimits", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "GetUsageLimitsRequest" }, + "output": { "shape": "GetUsageLimitsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to get current usage limits

" + }, + "ListAvailableCustomizations": { + "name": "ListAvailableCustomizations", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "ListAvailableCustomizationsRequest" }, + "output": { "shape": "ListAvailableCustomizationsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ] + }, + "ListAvailableModels": { + "name": "ListAvailableModels", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "ListAvailableModelsRequest" }, + "output": { "shape": "ListAvailableModelsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ] + }, + "ListAvailableProfiles": { + "name": "ListAvailableProfiles", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "ListAvailableProfilesRequest" }, + "output": { "shape": "ListAvailableProfilesResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ] + }, + "ListCodeAnalysisFindings": { + "name": "ListCodeAnalysisFindings", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "ListCodeAnalysisFindingsRequest" }, + "output": { "shape": "ListCodeAnalysisFindingsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

Lists the findings from a particular code analysis job.

" + }, + "ListEvents": { + "name": "ListEvents", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "ListEventsRequest" }, + "output": { "shape": "ListEventsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

List events for agent activity

" + }, + "ListFeatureEvaluations": { + "name": "ListFeatureEvaluations", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "ListFeatureEvaluationsRequest" }, + "output": { "shape": "ListFeatureEvaluationsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

Return configruations for each feature that has been setup for A/B testing.

" + }, + "ListUserMemoryEntries": { + "name": "ListUserMemoryEntries", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "ListUserMemoryEntriesInput" }, + "output": { "shape": "ListUserMemoryEntriesOutput" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to list user memories

" + }, + "ListWorkspaceMetadata": { + "name": "ListWorkspaceMetadata", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "ListWorkspaceMetadataRequest" }, + "output": { "shape": "ListWorkspaceMetadataResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

List workspace metadata based on a workspace root

" + }, + "PushTelemetryEvent": { + "name": "PushTelemetryEvent", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "PushTelemetryEventRequest" }, + "output": { "shape": "PushTelemetryEventResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to push telemetry events to CloudWatch, DataHub and EventBridge.

", + "idempotent": true + }, + "ResumeTransformation": { + "name": "ResumeTransformation", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "ResumeTransformationRequest" }, + "output": { "shape": "ResumeTransformationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to resume transformation job.

" + }, + "SendTelemetryEvent": { + "name": "SendTelemetryEvent", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "SendTelemetryEventRequest" }, + "output": { "shape": "SendTelemetryEventResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to record telemetry events.

", + "idempotent": true + }, + "StartCodeAnalysis": { + "name": "StartCodeAnalysis", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "StartCodeAnalysisRequest" }, + "output": { "shape": "StartCodeAnalysisResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ConflictException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

Starts a code analysis job

", + "idempotent": true + }, + "StartCodeFixJob": { + "name": "StartCodeFixJob", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "StartCodeFixJobRequest" }, + "output": { "shape": "StartCodeFixJobResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ] + }, + "StartTaskAssistCodeGeneration": { + "name": "StartTaskAssistCodeGeneration", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "StartTaskAssistCodeGenerationRequest" }, + "output": { "shape": "StartTaskAssistCodeGenerationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ConflictException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to start task assist code generation.

" + }, + "StartTestGeneration": { + "name": "StartTestGeneration", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "StartTestGenerationRequest" }, + "output": { "shape": "StartTestGenerationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to start test generation.

", + "idempotent": true + }, + "StartTransformation": { + "name": "StartTransformation", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "StartTransformationRequest" }, + "output": { "shape": "StartTransformationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to start code translation.

" + }, + "StopTransformation": { + "name": "StopTransformation", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "StopTransformationRequest" }, + "output": { "shape": "StopTransformationResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" } + ], + "documentation": "

API to stop code transformation status.

" + }, + "UpdateUsageLimits": { + "name": "UpdateUsageLimits", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { "shape": "UpdateUsageLimitsRequest" }, + "output": { "shape": "UpdateUsageLimitsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerException" }, + { "shape": "ValidationException" }, + { "shape": "AccessDeniedException" }, + { "shape": "UpdateUsageLimitQuotaExceededException" } + ], + "documentation": "

API to update usage limits for enterprise customers

" + } + }, + "shapes": { + "AccessDeniedException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "reason": { "shape": "AccessDeniedExceptionReason" } + }, + "documentation": "

This exception is thrown when the user does not have sufficient access to perform this action.

", + "exception": true + }, + "AccessDeniedExceptionReason": { + "type": "string", + "documentation": "

Reason for AccessDeniedException

", + "enum": [ + "UNAUTHORIZED_CUSTOMIZATION_RESOURCE_ACCESS", + "UNAUTHORIZED_WORKSPACE_CONTEXT_FEATURE_ACCESS", + "TEMPORARILY_SUSPENDED", + "FEATURE_NOT_SUPPORTED" + ] + }, + "ActivationToken": { + "type": "string", + "max": 11, + "min": 11 + }, + "ActiveFunctionalityList": { + "type": "list", + "member": { "shape": "FunctionalityName" }, + "max": 10, + "min": 0 + }, + "AdditionalContentEntry": { + "type": "structure", + "required": ["name", "description"], + "members": { + "name": { + "shape": "AdditionalContentEntryNameString", + "documentation": "

The name/identifier for this context entry

" + }, + "description": { + "shape": "AdditionalContentEntryDescriptionString", + "documentation": "

A description of what this context entry represents

" + }, + "innerContext": { + "shape": "AdditionalContentEntryInnerContextString", + "documentation": "

The actual contextual content

" + } + }, + "documentation": "

Structure representing a single entry of additional contextual content

" + }, + "AdditionalContentEntryDescriptionString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "AdditionalContentEntryInnerContextString": { + "type": "string", + "max": 8192, + "min": 1, + "sensitive": true + }, + "AdditionalContentEntryNameString": { + "type": "string", + "max": 1024, + "min": 1, + "pattern": "[a-z]+(?:-[a-z0-9]+)*", + "sensitive": true + }, + "AdditionalContentList": { + "type": "list", + "member": { "shape": "AdditionalContentEntry" }, + "documentation": "

A list of additional content entries, limited to 20 items

", + "max": 20, + "min": 0 + }, + "AgentTaskType": { + "type": "string", + "documentation": "

Type of agent task

", + "enum": ["vibe", "spectask"] + }, + "AgenticChatEventStatus": { + "type": "string", + "enum": ["SUCCEEDED", "CANCELLED", "FAILED"] + }, + "AppStudioState": { + "type": "structure", + "required": ["namespace", "propertyName", "propertyContext"], + "members": { + "namespace": { + "shape": "AppStudioStateNamespaceString", + "documentation": "

The namespace of the context. Examples: 'ui.Button', 'ui.Table.DataSource', 'ui.Table.RowActions.Button', 'logic.invokeAWS', 'logic.JavaScript'

" + }, + "propertyName": { + "shape": "AppStudioStatePropertyNameString", + "documentation": "

The name of the property. Examples: 'visibility', 'disability', 'value', 'code'

" + }, + "propertyValue": { + "shape": "AppStudioStatePropertyValueString", + "documentation": "

The value of the property.

" + }, + "propertyContext": { + "shape": "AppStudioStatePropertyContextString", + "documentation": "

Context about how the property is used

" + } + }, + "documentation": "

Description of a user's context when they are calling Q Chat from AppStudio

" + }, + "AppStudioStateNamespaceString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "AppStudioStatePropertyContextString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "AppStudioStatePropertyNameString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "AppStudioStatePropertyValueString": { + "type": "string", + "max": 10240, + "min": 0, + "sensitive": true + }, + "ApplicationProperties": { + "type": "structure", + "required": ["tenantId", "applicationArn", "tenantUrl", "applicationType"], + "members": { + "tenantId": { "shape": "TenantId" }, + "applicationArn": { "shape": "ResourceArn" }, + "tenantUrl": { "shape": "Url" }, + "applicationType": { "shape": "FunctionalityName" } + } + }, + "ApplicationPropertiesList": { + "type": "list", + "member": { "shape": "ApplicationProperties" } + }, + "ArtifactId": { + "type": "string", + "max": 126, + "min": 1, + "pattern": "[a-zA-Z0-9-_]+" + }, + "ArtifactMap": { + "type": "map", + "key": { "shape": "ArtifactType" }, + "value": { "shape": "UploadId" }, + "max": 64, + "min": 1 + }, + "ArtifactType": { + "type": "string", + "enum": ["SourceCode", "BuiltJars"] + }, + "AssistantResponseMessage": { + "type": "structure", + "required": ["content"], + "members": { + "messageId": { "shape": "MessageId" }, + "content": { + "shape": "AssistantResponseMessageContentString", + "documentation": "

The content of the text message in markdown format.

" + }, + "supplementaryWebLinks": { + "shape": "SupplementaryWebLinks", + "documentation": "

Web References

" + }, + "references": { + "shape": "References", + "documentation": "

Code References

" + }, + "followupPrompt": { + "shape": "FollowupPrompt", + "documentation": "

Followup Prompt

" + }, + "toolUses": { + "shape": "ToolUses", + "documentation": "

ToolUse Request

" + }, + "cachePoint": { + "shape": "CachePoint", + "documentation": "

Indicates whether this message is a cache point

" + }, + "reasoningContent": { + "shape": "ReasoningContent", + "documentation": "

Model's internal reasoning process, either as readable text or redacted binary content

" + } + }, + "documentation": "

Markdown text message.

" + }, + "AssistantResponseMessageContentString": { + "type": "string", + "min": 0, + "sensitive": true + }, + "AttributesMap": { + "type": "map", + "key": { "shape": "AttributesMapKeyString" }, + "value": { "shape": "StringList" }, + "documentation": "

Attributes is a map of key-value pairs

" + }, + "AttributesMapKeyString": { + "type": "string", + "max": 128, + "min": 1 + }, + "Base64EncodedPaginationToken": { + "type": "string", + "max": 2048, + "min": 1, + "pattern": "(?:[A-Za-z0-9\\+/]{4})*(?:[A-Za-z0-9\\+/]{2}\\=\\=|[A-Za-z0-9\\+/]{3}\\=)?" + }, + "Blob": { "type": "blob" }, + "Boolean": { + "type": "boolean", + "box": true + }, + "ByUserAnalytics": { + "type": "structure", + "required": ["toggle"], + "members": { + "s3Uri": { "shape": "S3Uri" }, + "toggle": { "shape": "OptInFeatureToggle" } + } + }, + "CachePoint": { + "type": "structure", + "required": ["type"], + "members": { + "type": { "shape": "CachePointType" } + } + }, + "CachePointType": { + "type": "string", + "enum": ["default"] + }, + "ChangeLogGranularityType": { + "type": "string", + "enum": ["STANDARD", "BUSINESS"] + }, + "ChangeLogOptions": { + "type": "structure", + "required": ["granularity"], + "members": { + "granularity": { "shape": "ChangeLogGranularityType" } + } + }, + "ChatAddMessageEvent": { + "type": "structure", + "required": ["conversationId", "messageId"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "messageId": { "shape": "MessageId" }, + "customizationArn": { "shape": "CustomizationArn" }, + "userIntent": { "shape": "UserIntent" }, + "hasCodeSnippet": { "shape": "Boolean" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "activeEditorTotalCharacters": { "shape": "Integer" }, + "timeToFirstChunkMilliseconds": { "shape": "Double" }, + "timeBetweenChunks": { "shape": "timeBetweenChunks" }, + "fullResponselatency": { "shape": "Double" }, + "requestLength": { "shape": "Integer" }, + "responseLength": { "shape": "Integer" }, + "numberOfCodeBlocks": { "shape": "Integer" }, + "hasProjectLevelContext": { "shape": "Boolean" }, + "result": { "shape": "AgenticChatEventStatus" } + } + }, + "ChatHistory": { + "type": "list", + "member": { "shape": "ChatMessage" }, + "documentation": "

Indicates Participant in Chat conversation

", + "min": 0 + }, + "ChatInteractWithMessageEvent": { + "type": "structure", + "required": ["conversationId", "messageId"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "messageId": { "shape": "MessageId" }, + "customizationArn": { "shape": "CustomizationArn" }, + "interactionType": { "shape": "ChatMessageInteractionType" }, + "interactionTarget": { "shape": "ChatInteractWithMessageEventInteractionTargetString" }, + "acceptedCharacterCount": { "shape": "Integer" }, + "acceptedLineCount": { "shape": "Integer" }, + "acceptedSnippetHasReference": { "shape": "Boolean" }, + "hasProjectLevelContext": { "shape": "Boolean" }, + "userIntent": { "shape": "UserIntent" }, + "addedIdeDiagnostics": { "shape": "IdeDiagnosticList" }, + "removedIdeDiagnostics": { "shape": "IdeDiagnosticList" } + } + }, + "ChatInteractWithMessageEventInteractionTargetString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "ChatMessage": { + "type": "structure", + "members": { + "userInputMessage": { "shape": "UserInputMessage" }, + "assistantResponseMessage": { "shape": "AssistantResponseMessage" } + }, + "union": true + }, + "ChatMessageInteractionType": { + "type": "string", + "documentation": "

Chat Message Interaction Type

", + "enum": [ + "INSERT_AT_CURSOR", + "COPY_SNIPPET", + "COPY", + "CLICK_LINK", + "CLICK_BODY_LINK", + "CLICK_FOLLOW_UP", + "HOVER_REFERENCE", + "UPVOTE", + "DOWNVOTE", + "AGENTIC_CODE_ACCEPTED" + ] + }, + "ChatTriggerType": { + "type": "string", + "documentation": "

Trigger Reason for Chat

", + "enum": ["MANUAL", "DIAGNOSTIC", "INLINE_CHAT"] + }, + "ChatUserModificationEvent": { + "type": "structure", + "required": ["conversationId", "messageId", "modificationPercentage"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "customizationArn": { "shape": "CustomizationArn" }, + "messageId": { "shape": "MessageId" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "modificationPercentage": { "shape": "Double" }, + "hasProjectLevelContext": { "shape": "Boolean" } + } + }, + "ClientCacheConfig": { + "type": "structure", + "members": { + "useClientCachingOnly": { "shape": "Boolean" } + } + }, + "ClientId": { + "type": "string", + "max": 255, + "min": 1 + }, + "CodeAnalysisFindingsSchema": { + "type": "string", + "enum": ["codeanalysis/findings/1.0"] + }, + "CodeAnalysisScope": { + "type": "string", + "enum": ["FILE", "PROJECT", "AGENTIC"] + }, + "CodeAnalysisStatus": { + "type": "string", + "enum": ["Completed", "Pending", "Failed"] + }, + "CodeAnalysisUploadContext": { + "type": "structure", + "required": ["codeScanName"], + "members": { + "codeScanName": { "shape": "CodeScanName" } + } + }, + "CodeCoverageEvent": { + "type": "structure", + "required": ["programmingLanguage", "acceptedCharacterCount", "totalCharacterCount", "timestamp"], + "members": { + "customizationArn": { "shape": "CustomizationArn" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "acceptedCharacterCount": { "shape": "PrimitiveInteger" }, + "totalCharacterCount": { "shape": "PrimitiveInteger" }, + "timestamp": { "shape": "Timestamp" }, + "unmodifiedAcceptedCharacterCount": { "shape": "PrimitiveInteger" }, + "totalNewCodeCharacterCount": { "shape": "PrimitiveInteger" }, + "totalNewCodeLineCount": { "shape": "PrimitiveInteger" }, + "userWrittenCodeCharacterCount": { "shape": "CodeCoverageEventUserWrittenCodeCharacterCountInteger" }, + "userWrittenCodeLineCount": { "shape": "CodeCoverageEventUserWrittenCodeLineCountInteger" }, + "addedCharacterCount": { "shape": "CodeCoverageEventAddedCharacterCountInteger" } + } + }, + "CodeCoverageEventAddedCharacterCountInteger": { + "type": "integer", + "min": 0 + }, + "CodeCoverageEventUserWrittenCodeCharacterCountInteger": { + "type": "integer", + "min": 0 + }, + "CodeCoverageEventUserWrittenCodeLineCountInteger": { + "type": "integer", + "min": 0 + }, + "CodeDescription": { + "type": "structure", + "required": ["href"], + "members": { + "href": { + "shape": "CodeDescriptionHrefString", + "documentation": "

An URI to open with more information about the diagnostic error.

" + } + }, + "documentation": "

Structure to capture a description for an error code.

" + }, + "CodeDescriptionHrefString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "CodeFixAcceptanceEvent": { + "type": "structure", + "required": ["jobId"], + "members": { + "jobId": { "shape": "String" }, + "ruleId": { "shape": "String" }, + "detectorId": { "shape": "String" }, + "findingId": { "shape": "String" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "linesOfCodeAccepted": { "shape": "Integer" }, + "charsOfCodeAccepted": { "shape": "Integer" } + } + }, + "CodeFixGenerationEvent": { + "type": "structure", + "required": ["jobId"], + "members": { + "jobId": { "shape": "String" }, + "ruleId": { "shape": "String" }, + "detectorId": { "shape": "String" }, + "findingId": { "shape": "String" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "linesOfCodeGenerated": { "shape": "Integer" }, + "charsOfCodeGenerated": { "shape": "Integer" } + } + }, + "CodeFixJobStatus": { + "type": "string", + "enum": ["Succeeded", "InProgress", "Failed"] + }, + "CodeFixName": { + "type": "string", + "documentation": "

Code fix name

", + "max": 128, + "min": 1, + "pattern": "[a-zA-Z0-9-_$:.]*" + }, + "CodeFixUploadContext": { + "type": "structure", + "required": ["codeFixName"], + "members": { + "codeFixName": { "shape": "CodeFixName" } + } + }, + "CodeGenerationId": { + "type": "string", + "documentation": "

ID which represents a single code generation in a conversation

", + "max": 128, + "min": 1 + }, + "CodeGenerationStatus": { + "type": "structure", + "required": ["status", "currentStage"], + "members": { + "status": { "shape": "CodeGenerationWorkflowStatus" }, + "currentStage": { "shape": "CodeGenerationWorkflowStage" } + } + }, + "CodeGenerationStatusDetail": { + "type": "string", + "documentation": "

Detailed message about the code generation status

", + "sensitive": true + }, + "CodeGenerationWorkflowStage": { + "type": "string", + "enum": ["InitialCodeGeneration", "CodeRefinement"] + }, + "CodeGenerationWorkflowStatus": { + "type": "string", + "enum": ["InProgress", "Complete", "Failed"] + }, + "CodeScanEvent": { + "type": "structure", + "required": ["programmingLanguage", "codeScanJobId", "timestamp"], + "members": { + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "codeScanJobId": { "shape": "CodeScanJobId" }, + "timestamp": { "shape": "Timestamp" }, + "codeAnalysisScope": { "shape": "CodeAnalysisScope" } + }, + "documentation": "

Published when a security scan or code review starts

" + }, + "CodeScanFailedEvent": { + "type": "structure", + "required": ["programmingLanguage", "codeScanJobId", "timestamp"], + "members": { + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "codeScanJobId": { "shape": "CodeScanJobId" }, + "timestamp": { "shape": "Timestamp" }, + "codeAnalysisScope": { "shape": "CodeAnalysisScope" } + }, + "documentation": "

Published when a security scan or code review fails

" + }, + "CodeScanJobId": { + "type": "string", + "max": 128, + "min": 1 + }, + "CodeScanName": { + "type": "string", + "documentation": "

Code analysis scan name

", + "max": 128, + "min": 1 + }, + "CodeScanRemediationsEvent": { + "type": "structure", + "members": { + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "CodeScanRemediationsEventType": { "shape": "CodeScanRemediationsEventType" }, + "timestamp": { "shape": "Timestamp" }, + "detectorId": { "shape": "String" }, + "findingId": { "shape": "String" }, + "ruleId": { "shape": "String" }, + "component": { "shape": "String" }, + "reason": { "shape": "String" }, + "result": { "shape": "String" }, + "includesFix": { "shape": "Boolean" } + } + }, + "CodeScanRemediationsEventType": { + "type": "string", + "documentation": "

Code Scan Remediations Interaction Type

", + "enum": ["CODESCAN_ISSUE_HOVER", "CODESCAN_ISSUE_APPLY_FIX", "CODESCAN_ISSUE_VIEW_DETAILS"] + }, + "CodeScanSucceededEvent": { + "type": "structure", + "required": ["programmingLanguage", "codeScanJobId", "timestamp", "numberOfFindings"], + "members": { + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "codeScanJobId": { "shape": "CodeScanJobId" }, + "timestamp": { "shape": "Timestamp" }, + "numberOfFindings": { "shape": "PrimitiveInteger" }, + "codeAnalysisScope": { "shape": "CodeAnalysisScope" } + }, + "documentation": "

Published when a security scan or code review completes successfully

" + }, + "Completion": { + "type": "structure", + "required": ["content"], + "members": { + "content": { "shape": "CompletionContentString" }, + "references": { "shape": "References" }, + "mostRelevantMissingImports": { "shape": "Imports" } + } + }, + "CompletionContentString": { + "type": "string", + "max": 5120, + "min": 1, + "sensitive": true + }, + "CompletionType": { + "type": "string", + "enum": ["BLOCK", "LINE"] + }, + "Completions": { + "type": "list", + "member": { "shape": "Completion" }, + "max": 10, + "min": 0 + }, + "ConflictException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "reason": { "shape": "ConflictExceptionReason" } + }, + "documentation": "

This exception is thrown when the action to perform could not be completed because the resource is in a conflicting state.

", + "exception": true + }, + "ConflictExceptionReason": { + "type": "string", + "documentation": "

Reason for ConflictException

", + "enum": ["CUSTOMER_KMS_KEY_INVALID_KEY_POLICY", "CUSTOMER_KMS_KEY_DISABLED", "MISMATCHED_KMS_KEY"] + }, + "ConsoleState": { + "type": "structure", + "members": { + "region": { "shape": "String" }, + "consoleUrl": { "shape": "SensitiveString" }, + "serviceId": { "shape": "String" }, + "serviceConsolePage": { "shape": "String" }, + "serviceSubconsolePage": { "shape": "String" }, + "taskName": { "shape": "SensitiveString" } + }, + "documentation": "

Information about the state of the AWS management console page from which the user is calling

" + }, + "ContentChecksumType": { + "type": "string", + "enum": ["SHA_256"] + }, + "ContentType": { + "type": "string", + "documentation": "

The type of content

", + "enum": ["FILE", "PROMPT", "CODE", "WORKSPACE"] + }, + "ContextTruncationScheme": { + "type": "string", + "documentation": "

Workspace context truncation schemes based on usecase

", + "enum": ["ANALYSIS", "GUMBY"] + }, + "ConversationId": { + "type": "string", + "documentation": "

ID which represents a multi-turn conversation

", + "max": 128, + "min": 1 + }, + "ConversationState": { + "type": "structure", + "required": ["currentMessage", "chatTriggerType"], + "members": { + "conversationId": { + "shape": "ConversationId", + "documentation": "

Unique identifier for the chat conversation stream

" + }, + "workspaceId": { + "shape": "UUID", + "documentation": "

Unique identifier for remote workspace

" + }, + "history": { + "shape": "ChatHistory", + "documentation": "

Holds the history of chat messages.

" + }, + "currentMessage": { + "shape": "ChatMessage", + "documentation": "

Holds the current message being processed or displayed.

" + }, + "chatTriggerType": { + "shape": "ChatTriggerType", + "documentation": "

Trigger Reason for Chat

" + }, + "customizationArn": { "shape": "ResourceArn" }, + "agentContinuationId": { + "shape": "UUID", + "documentation": "

Unique identifier for the agent task execution

" + }, + "agentTaskType": { "shape": "AgentTaskType" } + }, + "documentation": "

Structure to represent the current state of a chat conversation.

" + }, + "CreateSubscriptionTokenRequest": { + "type": "structure", + "members": { + "clientToken": { + "shape": "IdempotencyToken", + "idempotencyToken": true + }, + "statusOnly": { "shape": "Boolean" } + } + }, + "CreateSubscriptionTokenResponse": { + "type": "structure", + "required": ["status"], + "members": { + "encodedVerificationUrl": { "shape": "EncodedVerificationUrl" }, + "token": { "shape": "ActivationToken" }, + "status": { "shape": "SubscriptionStatus" } + } + }, + "CreateTaskAssistConversationRequest": { + "type": "structure", + "members": { + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Structure to represent bootstrap conversation request.

" + }, + "CreateTaskAssistConversationResponse": { + "type": "structure", + "required": ["conversationId"], + "members": { + "conversationId": { "shape": "ConversationId" } + }, + "documentation": "

Structure to represent bootstrap conversation response.

" + }, + "CreateUploadUrlRequest": { + "type": "structure", + "members": { + "contentMd5": { "shape": "CreateUploadUrlRequestContentMd5String" }, + "contentChecksum": { "shape": "CreateUploadUrlRequestContentChecksumString" }, + "contentChecksumType": { "shape": "ContentChecksumType" }, + "contentLength": { "shape": "CreateUploadUrlRequestContentLengthLong" }, + "artifactType": { "shape": "ArtifactType" }, + "uploadIntent": { "shape": "UploadIntent" }, + "uploadContext": { "shape": "UploadContext" }, + "uploadId": { "shape": "UploadId" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "CreateUploadUrlRequestContentChecksumString": { + "type": "string", + "max": 512, + "min": 1, + "sensitive": true + }, + "CreateUploadUrlRequestContentLengthLong": { + "type": "long", + "box": true, + "min": 1 + }, + "CreateUploadUrlRequestContentMd5String": { + "type": "string", + "max": 128, + "min": 1, + "sensitive": true + }, + "CreateUploadUrlResponse": { + "type": "structure", + "required": ["uploadId", "uploadUrl"], + "members": { + "uploadId": { "shape": "UploadId" }, + "uploadUrl": { "shape": "PreSignedUrl" }, + "kmsKeyArn": { "shape": "ResourceArn" }, + "requestHeaders": { "shape": "RequestHeaders" } + } + }, + "CreateUserMemoryEntryInput": { + "type": "structure", + "required": ["memoryEntryString", "origin"], + "members": { + "memoryEntryString": { "shape": "CreateUserMemoryEntryInputMemoryEntryStringString" }, + "origin": { "shape": "Origin" }, + "profileArn": { + "shape": "CreateUserMemoryEntryInputProfileArnString", + "documentation": "

ProfileArn for the managing Q Profile

" + }, + "clientToken": { + "shape": "IdempotencyToken", + "idempotencyToken": true + } + } + }, + "CreateUserMemoryEntryInputMemoryEntryStringString": { + "type": "string", + "max": 500, + "min": 1, + "sensitive": true + }, + "CreateUserMemoryEntryInputProfileArnString": { + "type": "string", + "min": 1, + "pattern": "arn:aws:(codewhisperer|transform):[-.a-z0-9]{1,63}:\\d{12}:profile/([a-zA-Z0-9]){12}" + }, + "CreateUserMemoryEntryOutput": { + "type": "structure", + "required": ["memoryEntry"], + "members": { + "memoryEntry": { "shape": "MemoryEntry" } + } + }, + "CreateWorkspaceRequest": { + "type": "structure", + "required": ["workspaceRoot"], + "members": { + "workspaceRoot": { "shape": "CreateWorkspaceRequestWorkspaceRootString" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "CreateWorkspaceRequestWorkspaceRootString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "CreateWorkspaceResponse": { + "type": "structure", + "required": ["workspace"], + "members": { + "workspace": { "shape": "WorkspaceMetadata" } + } + }, + "CursorState": { + "type": "structure", + "members": { + "position": { + "shape": "Position", + "documentation": "

Represents a cursor position in a Text Document

" + }, + "range": { + "shape": "Range", + "documentation": "

Represents a text selection in a Text Document

" + } + }, + "documentation": "

Represents the state of the Cursor in an Editor

", + "union": true + }, + "Customization": { + "type": "structure", + "required": ["arn"], + "members": { + "arn": { "shape": "CustomizationArn" }, + "name": { "shape": "CustomizationName" }, + "description": { "shape": "Description" }, + "modelId": { "shape": "ModelId" } + } + }, + "CustomizationArn": { + "type": "string", + "max": 950, + "min": 0, + "pattern": "arn:[-.a-z0-9]{1,63}:codewhisperer:([-.a-z0-9]{0,63}:){2}([a-zA-Z0-9-_:/]){1,1023}" + }, + "CustomizationName": { + "type": "string", + "max": 100, + "min": 1, + "pattern": "[a-zA-Z][a-zA-Z0-9_-]*" + }, + "Customizations": { + "type": "list", + "member": { "shape": "Customization" } + }, + "DashboardAnalytics": { + "type": "structure", + "required": ["toggle"], + "members": { + "toggle": { "shape": "OptInFeatureToggle" } + } + }, + "DeleteTaskAssistConversationRequest": { + "type": "structure", + "required": ["conversationId"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Structure to represent bootstrap conversation request.

" + }, + "DeleteTaskAssistConversationResponse": { + "type": "structure", + "required": ["conversationId"], + "members": { + "conversationId": { "shape": "ConversationId" } + }, + "documentation": "

Structure to represent bootstrap conversation response.

" + }, + "DeleteUserMemoryEntryInput": { + "type": "structure", + "required": ["id"], + "members": { + "id": { "shape": "DeleteUserMemoryEntryInputIdString" }, + "profileArn": { + "shape": "DeleteUserMemoryEntryInputProfileArnString", + "documentation": "

ProfileArn for the managing Q Profile

" + } + } + }, + "DeleteUserMemoryEntryInputIdString": { + "type": "string", + "max": 36, + "min": 36, + "pattern": "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" + }, + "DeleteUserMemoryEntryInputProfileArnString": { + "type": "string", + "min": 1, + "pattern": "arn:aws:(codewhisperer|transform):[-.a-z0-9]{1,63}:\\d{12}:profile/([a-zA-Z0-9]){12}" + }, + "DeleteUserMemoryEntryOutput": { + "type": "structure", + "members": {} + }, + "DeleteWorkspaceRequest": { + "type": "structure", + "required": ["workspaceId"], + "members": { + "workspaceId": { "shape": "UUID" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "DeleteWorkspaceResponse": { + "type": "structure", + "members": {} + }, + "Description": { + "type": "string", + "max": 256, + "min": 0, + "pattern": "[\\sa-zA-Z0-9_-]*" + }, + "Diagnostic": { + "type": "structure", + "members": { + "textDocumentDiagnostic": { + "shape": "TextDocumentDiagnostic", + "documentation": "

Diagnostics originating from a TextDocument

" + }, + "runtimeDiagnostic": { + "shape": "RuntimeDiagnostic", + "documentation": "

Diagnostics originating from a Runtime

" + } + }, + "documentation": "

Represents a Diagnostic message

", + "union": true + }, + "DiagnosticLocation": { + "type": "structure", + "required": ["uri", "range"], + "members": { + "uri": { "shape": "DiagnosticLocationUriString" }, + "range": { "shape": "Range" } + }, + "documentation": "

Represents a location inside a resource, such as a line inside a text file.

" + }, + "DiagnosticLocationUriString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "DiagnosticRelatedInformation": { + "type": "structure", + "required": ["location", "message"], + "members": { + "location": { + "shape": "DiagnosticLocation", + "documentation": "

The location of this related diagnostic information.

" + }, + "message": { + "shape": "DiagnosticRelatedInformationMessageString", + "documentation": "

The message of this related diagnostic information.

" + } + }, + "documentation": "

Represents a related message and source code location for a diagnostic.

" + }, + "DiagnosticRelatedInformationList": { + "type": "list", + "member": { "shape": "DiagnosticRelatedInformation" }, + "documentation": "

List of DiagnosticRelatedInformation

", + "max": 1024, + "min": 0 + }, + "DiagnosticRelatedInformationMessageString": { + "type": "string", + "max": 1024, + "min": 0, + "sensitive": true + }, + "DiagnosticSeverity": { + "type": "string", + "documentation": "

Diagnostic Error types

", + "enum": ["ERROR", "WARNING", "INFORMATION", "HINT"] + }, + "DiagnosticTag": { + "type": "string", + "documentation": "

The diagnostic tags.

", + "enum": ["UNNECESSARY", "DEPRECATED"] + }, + "DiagnosticTagList": { + "type": "list", + "member": { "shape": "DiagnosticTag" }, + "documentation": "

List of DiagnosticTag

", + "max": 1024, + "min": 0 + }, + "Dimension": { + "type": "structure", + "members": { + "name": { "shape": "DimensionNameString" }, + "value": { "shape": "DimensionValueString" } + } + }, + "DimensionList": { + "type": "list", + "member": { "shape": "Dimension" }, + "max": 30, + "min": 0 + }, + "DimensionNameString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": "[-a-zA-Z0-9._]*" + }, + "DimensionValueString": { + "type": "string", + "max": 1024, + "min": 1, + "pattern": "[-a-zA-Z0-9._]*" + }, + "DocFolderLevel": { + "type": "string", + "documentation": "

Specifies the folder depth level where the document should be generated

", + "enum": ["SUB_FOLDER", "ENTIRE_WORKSPACE"] + }, + "DocGenerationEvent": { + "type": "structure", + "required": ["conversationId"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "numberOfAddChars": { "shape": "PrimitiveInteger" }, + "numberOfAddLines": { "shape": "PrimitiveInteger" }, + "numberOfAddFiles": { "shape": "PrimitiveInteger" }, + "userDecision": { "shape": "DocUserDecision" }, + "interactionType": { "shape": "DocInteractionType" }, + "userIdentity": { "shape": "String" }, + "numberOfNavigation": { "shape": "PrimitiveInteger" }, + "folderLevel": { "shape": "DocFolderLevel" } + }, + "documentation": "

Deprecated: use DocV2AcceptanceEvent for tracking acceptance and DocV2GenerationEvent for tracking generation

" + }, + "DocInteractionType": { + "type": "string", + "documentation": "

Tracks whether user chose to generate a new document, update an existing one, or edit document

", + "enum": ["GENERATE_README", "UPDATE_README", "EDIT_README"] + }, + "DocUserDecision": { + "type": "string", + "enum": ["ACCEPT", "REJECT"] + }, + "DocV2AcceptanceEvent": { + "type": "structure", + "required": [ + "conversationId", + "numberOfAddedChars", + "numberOfAddedLines", + "numberOfAddedFiles", + "userDecision", + "interactionType", + "numberOfNavigations", + "folderLevel" + ], + "members": { + "conversationId": { "shape": "ConversationId" }, + "numberOfAddedChars": { "shape": "DocV2AcceptanceEventNumberOfAddedCharsInteger" }, + "numberOfAddedLines": { "shape": "DocV2AcceptanceEventNumberOfAddedLinesInteger" }, + "numberOfAddedFiles": { "shape": "DocV2AcceptanceEventNumberOfAddedFilesInteger" }, + "userDecision": { "shape": "DocUserDecision" }, + "interactionType": { "shape": "DocInteractionType" }, + "numberOfNavigations": { "shape": "DocV2AcceptanceEventNumberOfNavigationsInteger" }, + "folderLevel": { "shape": "DocFolderLevel" } + }, + "documentation": "

Interaction event for /doc, emitted when user accepts or rejects the generated content

" + }, + "DocV2AcceptanceEventNumberOfAddedCharsInteger": { + "type": "integer", + "min": 0 + }, + "DocV2AcceptanceEventNumberOfAddedFilesInteger": { + "type": "integer", + "min": 0 + }, + "DocV2AcceptanceEventNumberOfAddedLinesInteger": { + "type": "integer", + "min": 0 + }, + "DocV2AcceptanceEventNumberOfNavigationsInteger": { + "type": "integer", + "min": 0 + }, + "DocV2GenerationEvent": { + "type": "structure", + "required": [ + "conversationId", + "numberOfGeneratedChars", + "numberOfGeneratedLines", + "numberOfGeneratedFiles" + ], + "members": { + "conversationId": { "shape": "ConversationId" }, + "numberOfGeneratedChars": { "shape": "DocV2GenerationEventNumberOfGeneratedCharsInteger" }, + "numberOfGeneratedLines": { "shape": "DocV2GenerationEventNumberOfGeneratedLinesInteger" }, + "numberOfGeneratedFiles": { "shape": "DocV2GenerationEventNumberOfGeneratedFilesInteger" }, + "interactionType": { "shape": "DocInteractionType" }, + "numberOfNavigations": { "shape": "DocV2GenerationEventNumberOfNavigationsInteger" }, + "folderLevel": { "shape": "DocFolderLevel" } + }, + "documentation": "

Generation event for /doc, emitted when user requests document generation

" + }, + "DocV2GenerationEventNumberOfGeneratedCharsInteger": { + "type": "integer", + "min": 0 + }, + "DocV2GenerationEventNumberOfGeneratedFilesInteger": { + "type": "integer", + "min": 0 + }, + "DocV2GenerationEventNumberOfGeneratedLinesInteger": { + "type": "integer", + "min": 0 + }, + "DocV2GenerationEventNumberOfNavigationsInteger": { + "type": "integer", + "min": 0 + }, + "Document": { + "type": "structure", + "members": {}, + "document": true + }, + "DocumentSymbol": { + "type": "structure", + "required": ["name", "type"], + "members": { + "name": { + "shape": "DocumentSymbolNameString", + "documentation": "

Name of the Document Symbol

" + }, + "type": { + "shape": "SymbolType", + "documentation": "

Symbol type - DECLARATION / USAGE

" + }, + "source": { + "shape": "DocumentSymbolSourceString", + "documentation": "

Symbol package / source for FullyQualified names

" + } + } + }, + "DocumentSymbolNameString": { + "type": "string", + "max": 256, + "min": 1 + }, + "DocumentSymbolSourceString": { + "type": "string", + "max": 256, + "min": 1 + }, + "DocumentSymbols": { + "type": "list", + "member": { "shape": "DocumentSymbol" }, + "max": 1000, + "min": 0 + }, + "DocumentationIntentContext": { + "type": "structure", + "required": ["type"], + "members": { + "scope": { "shape": "DocumentationIntentContextScopeString" }, + "type": { "shape": "DocumentationType" }, + "changeLogOptions": { "shape": "ChangeLogOptions" } + } + }, + "DocumentationIntentContextScopeString": { + "type": "string", + "max": 4096, + "min": 1, + "sensitive": true + }, + "DocumentationType": { + "type": "string", + "enum": ["README", "CHANGE_LOG"] + }, + "Double": { + "type": "double", + "box": true + }, + "Edit": { + "type": "structure", + "required": ["content"], + "members": { + "content": { "shape": "EditContentString" }, + "references": { "shape": "References" } + } + }, + "EditContentString": { + "type": "string", + "max": 5120, + "min": 1, + "sensitive": true + }, + "EditorState": { + "type": "structure", + "members": { + "document": { + "shape": "TextDocument", + "documentation": "

Represents currently edited file

" + }, + "cursorState": { + "shape": "CursorState", + "documentation": "

Position of the cursor

" + }, + "relevantDocuments": { + "shape": "RelevantDocumentList", + "documentation": "

Represents IDE provided relevant files

" + }, + "useRelevantDocuments": { + "shape": "Boolean", + "documentation": "

Whether service should use relevant document in prompt

" + }, + "workspaceFolders": { + "shape": "WorkspaceFolderList", + "documentation": "

Represents IDE provided list of workspace folders

" + } + }, + "documentation": "

Represents the state of an Editor

" + }, + "EncodedVerificationUrl": { + "type": "string", + "max": 8192, + "min": 1 + }, + "EnvState": { + "type": "structure", + "members": { + "operatingSystem": { + "shape": "EnvStateOperatingSystemString", + "documentation": "

The name of the operating system in use

" + }, + "currentWorkingDirectory": { + "shape": "EnvStateCurrentWorkingDirectoryString", + "documentation": "

The current working directory of the environment

" + }, + "environmentVariables": { + "shape": "EnvironmentVariables", + "documentation": "

The environment variables set in the current environment

" + }, + "timezoneOffset": { + "shape": "EnvStateTimezoneOffsetInteger", + "documentation": "

Local timezone offset of the client. For more information, see documentation https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset

" + } + }, + "documentation": "

State related to the user's environment

" + }, + "EnvStateCurrentWorkingDirectoryString": { + "type": "string", + "max": 256, + "min": 1, + "sensitive": true + }, + "EnvStateOperatingSystemString": { + "type": "string", + "max": 32, + "min": 1, + "pattern": "(macos|linux|windows)" + }, + "EnvStateTimezoneOffsetInteger": { + "type": "integer", + "box": true, + "max": 1440, + "min": -1440 + }, + "EnvironmentVariable": { + "type": "structure", + "members": { + "key": { + "shape": "EnvironmentVariableKeyString", + "documentation": "

The key of an environment variable

" + }, + "value": { + "shape": "EnvironmentVariableValueString", + "documentation": "

The value of an environment variable

" + } + }, + "documentation": "

An environment variable

" + }, + "EnvironmentVariableKeyString": { + "type": "string", + "max": 256, + "min": 1, + "sensitive": true + }, + "EnvironmentVariableValueString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "EnvironmentVariables": { + "type": "list", + "member": { "shape": "EnvironmentVariable" }, + "documentation": "

A list of environment variables

", + "max": 100, + "min": 0 + }, + "ErrorDetails": { + "type": "string", + "max": 2048, + "min": 0 + }, + "Event": { + "type": "structure", + "required": ["eventId", "generationId", "eventTimestamp", "eventType", "eventBlob"], + "members": { + "eventId": { "shape": "UUID" }, + "generationId": { "shape": "UUID" }, + "eventTimestamp": { "shape": "SyntheticTimestamp_date_time" }, + "eventType": { "shape": "EventType" }, + "eventBlob": { "shape": "EventBlob" } + } + }, + "EventBlob": { + "type": "blob", + "max": 400000, + "min": 1, + "sensitive": true + }, + "EventList": { + "type": "list", + "member": { "shape": "Event" }, + "max": 10, + "min": 1 + }, + "EventType": { + "type": "string", + "max": 100, + "min": 1 + }, + "ExternalIdentityDetails": { + "type": "structure", + "members": { + "issuerUrl": { "shape": "IssuerUrl" }, + "clientId": { "shape": "ClientId" }, + "scimEndpoint": { "shape": "String" } + } + }, + "FeatureDevCodeAcceptanceEvent": { + "type": "structure", + "required": ["conversationId", "linesOfCodeAccepted", "charactersOfCodeAccepted"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "linesOfCodeAccepted": { "shape": "FeatureDevCodeAcceptanceEventLinesOfCodeAcceptedInteger" }, + "charactersOfCodeAccepted": { "shape": "FeatureDevCodeAcceptanceEventCharactersOfCodeAcceptedInteger" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" } + } + }, + "FeatureDevCodeAcceptanceEventCharactersOfCodeAcceptedInteger": { + "type": "integer", + "min": 0 + }, + "FeatureDevCodeAcceptanceEventLinesOfCodeAcceptedInteger": { + "type": "integer", + "min": 0 + }, + "FeatureDevCodeGenerationEvent": { + "type": "structure", + "required": ["conversationId", "linesOfCodeGenerated", "charactersOfCodeGenerated"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "linesOfCodeGenerated": { "shape": "FeatureDevCodeGenerationEventLinesOfCodeGeneratedInteger" }, + "charactersOfCodeGenerated": { + "shape": "FeatureDevCodeGenerationEventCharactersOfCodeGeneratedInteger" + }, + "programmingLanguage": { "shape": "ProgrammingLanguage" } + } + }, + "FeatureDevCodeGenerationEventCharactersOfCodeGeneratedInteger": { + "type": "integer", + "min": 0 + }, + "FeatureDevCodeGenerationEventLinesOfCodeGeneratedInteger": { + "type": "integer", + "min": 0 + }, + "FeatureDevEvent": { + "type": "structure", + "required": ["conversationId"], + "members": { + "conversationId": { "shape": "ConversationId" } + } + }, + "FeatureEvaluation": { + "type": "structure", + "required": ["feature", "variation", "value"], + "members": { + "feature": { "shape": "FeatureName" }, + "variation": { "shape": "FeatureVariation" }, + "value": { "shape": "FeatureValue" } + } + }, + "FeatureEvaluationsList": { + "type": "list", + "member": { "shape": "FeatureEvaluation" }, + "max": 50, + "min": 0 + }, + "FeatureName": { + "type": "string", + "max": 128, + "min": 1, + "pattern": "[-a-zA-Z0-9._]*" + }, + "FeatureValue": { + "type": "structure", + "members": { + "boolValue": { "shape": "Boolean" }, + "doubleValue": { "shape": "Double" }, + "longValue": { "shape": "Long" }, + "stringValue": { "shape": "FeatureValueStringType" } + }, + "union": true + }, + "FeatureValueStringType": { + "type": "string", + "max": 512, + "min": 0 + }, + "FeatureVariation": { + "type": "string", + "max": 128, + "min": 1, + "pattern": "[-a-zA-Z0-9._]*" + }, + "FileContext": { + "type": "structure", + "required": ["leftFileContent", "rightFileContent", "filename", "programmingLanguage"], + "members": { + "leftFileContent": { "shape": "FileContextLeftFileContentString" }, + "rightFileContent": { "shape": "FileContextRightFileContentString" }, + "filename": { "shape": "FileContextFilenameString" }, + "fileUri": { "shape": "FileContextFileUriString" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" } + } + }, + "FileContextFileUriString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "FileContextFilenameString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "FileContextLeftFileContentString": { + "type": "string", + "max": 10240, + "min": 0, + "sensitive": true + }, + "FileContextRightFileContentString": { + "type": "string", + "max": 10240, + "min": 0, + "sensitive": true + }, + "FollowupPrompt": { + "type": "structure", + "required": ["content"], + "members": { + "content": { + "shape": "FollowupPromptContentString", + "documentation": "

The content of the text message in markdown format.

" + }, + "userIntent": { + "shape": "UserIntent", + "documentation": "

User Intent

" + } + }, + "documentation": "

Followup Prompt for the Assistant Response

" + }, + "FollowupPromptContentString": { + "type": "string", + "max": 4096, + "min": 0, + "sensitive": true + }, + "FunctionalityName": { + "type": "string", + "enum": [ + "COMPLETIONS", + "ANALYSIS", + "CONVERSATIONS", + "TASK_ASSIST", + "TRANSFORMATIONS", + "CHAT_CUSTOMIZATION", + "TRANSFORMATIONS_WEBAPP", + "FEATURE_DEVELOPMENT" + ], + "max": 64, + "min": 1 + }, + "GenerateCompletionsRequest": { + "type": "structure", + "required": ["fileContext"], + "members": { + "fileContext": { "shape": "FileContext" }, + "editorState": { "shape": "EditorState" }, + "maxResults": { "shape": "GenerateCompletionsRequestMaxResultsInteger" }, + "predictionTypes": { "shape": "PredictionTypes" }, + "nextToken": { "shape": "GenerateCompletionsRequestNextTokenString" }, + "referenceTrackerConfiguration": { "shape": "ReferenceTrackerConfiguration" }, + "supplementalContexts": { "shape": "SupplementalContextList" }, + "customizationArn": { "shape": "CustomizationArn" }, + "optOutPreference": { "shape": "OptOutPreference" }, + "userContext": { "shape": "UserContext" }, + "profileArn": { "shape": "ProfileArn" }, + "workspaceId": { "shape": "UUID" }, + "modelId": { "shape": "ModelId" } + } + }, + "GenerateCompletionsRequestMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 10, + "min": 1 + }, + "GenerateCompletionsRequestNextTokenString": { + "type": "string", + "max": 2048, + "min": 0, + "pattern": "(?:[A-Za-z0-9\\+/]{4})*(?:[A-Za-z0-9\\+/]{2}\\=\\=|[A-Za-z0-9\\+/]{3}\\=)?", + "sensitive": true + }, + "GenerateCompletionsResponse": { + "type": "structure", + "members": { + "predictions": { "shape": "Predictions" }, + "completions": { "shape": "Completions" }, + "nextToken": { "shape": "SensitiveString" }, + "modelId": { "shape": "ModelId" } + } + }, + "GetCodeAnalysisRequest": { + "type": "structure", + "required": ["jobId"], + "members": { + "jobId": { "shape": "GetCodeAnalysisRequestJobIdString" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "GetCodeAnalysisRequestJobIdString": { + "type": "string", + "max": 256, + "min": 1 + }, + "GetCodeAnalysisResponse": { + "type": "structure", + "required": ["status"], + "members": { + "status": { "shape": "CodeAnalysisStatus" }, + "errorMessage": { "shape": "SensitiveString" } + } + }, + "GetCodeFixJobRequest": { + "type": "structure", + "required": ["jobId"], + "members": { + "jobId": { "shape": "GetCodeFixJobRequestJobIdString" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "GetCodeFixJobRequestJobIdString": { + "type": "string", + "max": 256, + "min": 1, + "pattern": ".*[A-Za-z0-9-:]+.*" + }, + "GetCodeFixJobResponse": { + "type": "structure", + "members": { + "jobStatus": { "shape": "CodeFixJobStatus" }, + "suggestedFix": { "shape": "SuggestedFix" } + } + }, + "GetTaskAssistCodeGenerationRequest": { + "type": "structure", + "required": ["conversationId", "codeGenerationId"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "codeGenerationId": { "shape": "CodeGenerationId" }, + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Request for getting task assist code generation.

" + }, + "GetTaskAssistCodeGenerationResponse": { + "type": "structure", + "required": ["conversationId", "codeGenerationStatus"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "codeGenerationStatus": { "shape": "CodeGenerationStatus" }, + "codeGenerationStatusDetail": { "shape": "CodeGenerationStatusDetail" }, + "codeGenerationRemainingIterationCount": { "shape": "Integer" }, + "codeGenerationTotalIterationCount": { "shape": "Integer" } + }, + "documentation": "

Response for getting task assist code generation status.

" + }, + "GetTestGenerationRequest": { + "type": "structure", + "required": ["testGenerationJobGroupName", "testGenerationJobId"], + "members": { + "testGenerationJobGroupName": { "shape": "TestGenerationJobGroupName" }, + "testGenerationJobId": { "shape": "UUID" }, + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Structure to represent get test generation request.

" + }, + "GetTestGenerationResponse": { + "type": "structure", + "members": { + "testGenerationJob": { "shape": "TestGenerationJob" } + }, + "documentation": "

Structure to represent get test generation response.

" + }, + "GetTransformationPlanRequest": { + "type": "structure", + "required": ["transformationJobId"], + "members": { + "transformationJobId": { "shape": "TransformationJobId" }, + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Structure to represent get code transformation plan request.

" + }, + "GetTransformationPlanResponse": { + "type": "structure", + "required": ["transformationPlan"], + "members": { + "transformationPlan": { "shape": "TransformationPlan" } + }, + "documentation": "

Structure to represent get code transformation plan response.

" + }, + "GetTransformationRequest": { + "type": "structure", + "required": ["transformationJobId"], + "members": { + "transformationJobId": { "shape": "TransformationJobId" }, + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Structure to represent get code transformation request.

" + }, + "GetTransformationResponse": { + "type": "structure", + "required": ["transformationJob"], + "members": { + "transformationJob": { "shape": "TransformationJob" } + }, + "documentation": "

Structure to represent get code transformation response.

" + }, + "GetUsageLimitsRequest": { + "type": "structure", + "members": { + "profileArn": { + "shape": "ProfileArn", + "documentation": "

The ARN of the Q Developer profile. Required for enterprise customers, optional for Builder ID users.

" + } + } + }, + "GetUsageLimitsResponse": { + "type": "structure", + "required": ["limits", "daysUntilReset"], + "members": { + "limits": { "shape": "UsageLimits" }, + "daysUntilReset": { + "shape": "Integer", + "documentation": "

Number of days remaining until the usage metrics reset

" + } + } + }, + "GitState": { + "type": "structure", + "members": { + "status": { + "shape": "GitStateStatusString", + "documentation": "

The output of the command git status --porcelain=v1 -b

" + } + }, + "documentation": "

State related to the Git VSC

" + }, + "GitStateStatusString": { + "type": "string", + "max": 4096, + "min": 0, + "sensitive": true + }, + "IdeCategory": { + "type": "string", + "enum": ["JETBRAINS", "VSCODE", "CLI", "JUPYTER_MD", "JUPYTER_SM", "ECLIPSE", "VISUAL_STUDIO"], + "max": 64, + "min": 1 + }, + "IdeDiagnostic": { + "type": "structure", + "required": ["ideDiagnosticType"], + "members": { + "range": { + "shape": "Range", + "documentation": "

The range at which the message applies.

" + }, + "source": { + "shape": "IdeDiagnosticSourceString", + "documentation": "

A human-readable string describing the source of the diagnostic

" + }, + "severity": { + "shape": "DiagnosticSeverity", + "documentation": "

Diagnostic Error type

" + }, + "ideDiagnosticType": { + "shape": "IdeDiagnosticType", + "documentation": "

Type of the diagnostic

" + } + }, + "documentation": "

Structure to represent metadata about a Diagnostic from user local IDE

" + }, + "IdeDiagnosticList": { + "type": "list", + "member": { "shape": "IdeDiagnostic" }, + "documentation": "

List of IDE Diagnostics

", + "max": 1024, + "min": 0 + }, + "IdeDiagnosticSourceString": { + "type": "string", + "max": 1024, + "min": 0, + "sensitive": true + }, + "IdeDiagnosticType": { + "type": "string", + "enum": ["SYNTAX_ERROR", "TYPE_ERROR", "REFERENCE_ERROR", "BEST_PRACTICE", "SECURITY", "OTHER"] + }, + "IdempotencyToken": { + "type": "string", + "max": 256, + "min": 1 + }, + "IdentityDetails": { + "type": "structure", + "members": { + "ssoIdentityDetails": { "shape": "SSOIdentityDetails" }, + "externalIdentityDetails": { "shape": "ExternalIdentityDetails" } + }, + "union": true + }, + "ImageBlock": { + "type": "structure", + "required": ["format", "source"], + "members": { + "format": { "shape": "ImageFormat" }, + "source": { "shape": "ImageSource" } + }, + "documentation": "

Represents the image source itself and the format of the image.

" + }, + "ImageBlocks": { + "type": "list", + "member": { "shape": "ImageBlock" }, + "max": 10, + "min": 0 + }, + "ImageFormat": { + "type": "string", + "enum": ["png", "jpeg", "gif", "webp"] + }, + "ImageSource": { + "type": "structure", + "members": { + "bytes": { "shape": "ImageSourceBytesBlob" } + }, + "documentation": "

Image bytes

", + "sensitive": true, + "union": true + }, + "ImageSourceBytesBlob": { + "type": "blob", + "max": 10000000, + "min": 1 + }, + "Import": { + "type": "structure", + "members": { + "statement": { "shape": "ImportStatementString" } + } + }, + "ImportStatementString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "Imports": { + "type": "list", + "member": { "shape": "Import" }, + "max": 10, + "min": 0 + }, + "InlineChatEvent": { + "type": "structure", + "required": ["requestId", "timestamp"], + "members": { + "requestId": { "shape": "UUID" }, + "timestamp": { "shape": "Timestamp" }, + "inputLength": { "shape": "PrimitiveInteger" }, + "numSelectedLines": { "shape": "PrimitiveInteger" }, + "numSuggestionAddChars": { "shape": "PrimitiveInteger" }, + "numSuggestionAddLines": { "shape": "PrimitiveInteger" }, + "numSuggestionDelChars": { "shape": "PrimitiveInteger" }, + "numSuggestionDelLines": { "shape": "PrimitiveInteger" }, + "codeIntent": { "shape": "Boolean" }, + "userDecision": { "shape": "InlineChatUserDecision" }, + "responseStartLatency": { "shape": "Double" }, + "responseEndLatency": { "shape": "Double" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" } + } + }, + "InlineChatUserDecision": { + "type": "string", + "enum": ["ACCEPT", "REJECT", "DISMISS"] + }, + "InputType": { + "type": "string", + "documentation": "

Types of input that can be processed by the model

", + "enum": ["IMAGE", "TEXT"] + }, + "Integer": { + "type": "integer", + "box": true + }, + "Intent": { + "type": "string", + "enum": ["DEV", "DOC"] + }, + "IntentContext": { + "type": "structure", + "members": { + "documentation": { "shape": "DocumentationIntentContext" } + }, + "union": true + }, + "InternalServerException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "reason": { "shape": "InternalServerExceptionReason" } + }, + "documentation": "

This exception is thrown when an unexpected error occurred during the processing of a request.

", + "exception": true, + "fault": true, + "retryable": { "throttling": false } + }, + "InternalServerExceptionReason": { + "type": "string", + "documentation": "

Reason for InternalServerException

", + "enum": ["MODEL_TEMPORARILY_UNAVAILABLE"] + }, + "IssuerUrl": { + "type": "string", + "max": 255, + "min": 1 + }, + "LineRangeList": { + "type": "list", + "member": { "shape": "Range" } + }, + "ListAvailableCustomizationsRequest": { + "type": "structure", + "members": { + "maxResults": { "shape": "ListAvailableCustomizationsRequestMaxResultsInteger" }, + "nextToken": { "shape": "Base64EncodedPaginationToken" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "ListAvailableCustomizationsRequestMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 1 + }, + "ListAvailableCustomizationsResponse": { + "type": "structure", + "required": ["customizations"], + "members": { + "customizations": { "shape": "Customizations" }, + "nextToken": { "shape": "Base64EncodedPaginationToken" } + } + }, + "ListAvailableModelsRequest": { + "type": "structure", + "required": ["origin"], + "members": { + "origin": { + "shape": "Origin", + "documentation": "

The origin context for which to list available models

" + }, + "maxResults": { + "shape": "ListAvailableModelsRequestMaxResultsInteger", + "documentation": "

Maximum number of models to return in a single response

" + }, + "nextToken": { + "shape": "Base64EncodedPaginationToken", + "documentation": "

Token for retrieving the next page of results

" + }, + "profileArn": { + "shape": "ProfileArn", + "documentation": "

ARN of the profile to use for model filtering

" + }, + "modelProvider": { + "shape": "ModelProvider", + "documentation": "

Provider of AI models

" + } + } + }, + "ListAvailableModelsRequestMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 1 + }, + "ListAvailableModelsResponse": { + "type": "structure", + "required": ["models"], + "members": { + "models": { + "shape": "Models", + "documentation": "

List of available models

" + }, + "nextToken": { + "shape": "Base64EncodedPaginationToken", + "documentation": "

Token for retrieving the next page of results

" + } + } + }, + "ListAvailableProfilesRequest": { + "type": "structure", + "members": { + "maxResults": { "shape": "ListAvailableProfilesRequestMaxResultsInteger" }, + "nextToken": { "shape": "Base64EncodedPaginationToken" } + } + }, + "ListAvailableProfilesRequestMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 10, + "min": 1 + }, + "ListAvailableProfilesResponse": { + "type": "structure", + "required": ["profiles"], + "members": { + "profiles": { "shape": "ProfileList" }, + "nextToken": { "shape": "Base64EncodedPaginationToken" } + } + }, + "ListCodeAnalysisFindingsRequest": { + "type": "structure", + "required": ["jobId", "codeAnalysisFindingsSchema"], + "members": { + "jobId": { "shape": "ListCodeAnalysisFindingsRequestJobIdString" }, + "nextToken": { "shape": "PaginationToken" }, + "codeAnalysisFindingsSchema": { "shape": "CodeAnalysisFindingsSchema" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "ListCodeAnalysisFindingsRequestJobIdString": { + "type": "string", + "max": 256, + "min": 1 + }, + "ListCodeAnalysisFindingsResponse": { + "type": "structure", + "required": ["codeAnalysisFindings"], + "members": { + "nextToken": { "shape": "PaginationToken" }, + "codeAnalysisFindings": { "shape": "SensitiveString" } + } + }, + "ListEventsRequest": { + "type": "structure", + "required": ["conversationId"], + "members": { + "conversationId": { "shape": "UUID" }, + "maxResults": { "shape": "ListEventsRequestMaxResultsInteger" }, + "nextToken": { "shape": "NextToken" } + } + }, + "ListEventsRequestMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 50, + "min": 1 + }, + "ListEventsResponse": { + "type": "structure", + "required": ["conversationId", "events"], + "members": { + "conversationId": { "shape": "UUID" }, + "events": { "shape": "EventList" }, + "nextToken": { "shape": "NextToken" } + } + }, + "ListFeatureEvaluationsRequest": { + "type": "structure", + "required": ["userContext"], + "members": { + "userContext": { "shape": "UserContext" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "ListFeatureEvaluationsResponse": { + "type": "structure", + "required": ["featureEvaluations"], + "members": { + "featureEvaluations": { "shape": "FeatureEvaluationsList" } + } + }, + "ListUserMemoryEntriesInput": { + "type": "structure", + "members": { + "maxResults": { "shape": "ListUserMemoryEntriesInputMaxResultsInteger" }, + "profileArn": { + "shape": "ListUserMemoryEntriesInputProfileArnString", + "documentation": "

ProfileArn for the managing Q Profile

" + }, + "nextToken": { "shape": "ListUserMemoryEntriesInputNextTokenString" } + } + }, + "ListUserMemoryEntriesInputMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 1 + }, + "ListUserMemoryEntriesInputNextTokenString": { + "type": "string", + "min": 1, + "pattern": "[A-Za-z0-9_-]+" + }, + "ListUserMemoryEntriesInputProfileArnString": { + "type": "string", + "min": 1, + "pattern": "arn:aws:(codewhisperer|transform):[-.a-z0-9]{1,63}:\\d{12}:profile/([a-zA-Z0-9]){12}" + }, + "ListUserMemoryEntriesOutput": { + "type": "structure", + "required": ["memoryEntries"], + "members": { + "memoryEntries": { "shape": "MemoryEntryList" }, + "nextToken": { "shape": "ListUserMemoryEntriesOutputNextTokenString" } + } + }, + "ListUserMemoryEntriesOutputNextTokenString": { + "type": "string", + "min": 1, + "pattern": "[A-Za-z0-9_-]+" + }, + "ListWorkspaceMetadataRequest": { + "type": "structure", + "members": { + "workspaceRoot": { "shape": "ListWorkspaceMetadataRequestWorkspaceRootString" }, + "nextToken": { "shape": "String" }, + "maxResults": { "shape": "Integer" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "ListWorkspaceMetadataRequestWorkspaceRootString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "ListWorkspaceMetadataResponse": { + "type": "structure", + "required": ["workspaces"], + "members": { + "workspaces": { "shape": "WorkspaceList" }, + "nextToken": { "shape": "String" } + } + }, + "Long": { + "type": "long", + "box": true + }, + "MemoryEntry": { + "type": "structure", + "required": ["id", "memoryEntryString", "metadata"], + "members": { + "id": { + "shape": "MemoryEntryIdString", + "documentation": "

A unique identifier for a single memory entry

" + }, + "memoryEntryString": { "shape": "MemoryEntryMemoryEntryStringString" }, + "metadata": { "shape": "MemoryEntryMetadata" } + }, + "documentation": "

MemoryEntry corresponds to a single user memory

" + }, + "MemoryEntryIdString": { + "type": "string", + "max": 36, + "min": 36, + "pattern": "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" + }, + "MemoryEntryList": { + "type": "list", + "member": { "shape": "MemoryEntry" }, + "documentation": "

List of user memories

" + }, + "MemoryEntryMemoryEntryStringString": { + "type": "string", + "max": 500, + "min": 1, + "sensitive": true + }, + "MemoryEntryMetadata": { + "type": "structure", + "required": ["origin", "createdAt", "updatedAt"], + "members": { + "origin": { "shape": "Origin" }, + "attributes": { "shape": "AttributesMap" }, + "createdAt": { "shape": "Timestamp" }, + "updatedAt": { "shape": "Timestamp" }, + "memoryStatus": { "shape": "MemoryStatus" } + }, + "documentation": "

Metadata for a single memory entry

" + }, + "MemoryStatus": { + "type": "string", + "documentation": "

Status of user memory

", + "enum": ["DECRYPTION_FAILURE", "VALID"] + }, + "MessageId": { + "type": "string", + "documentation": "

Unique identifier for the chat message

", + "max": 128, + "min": 0 + }, + "MetricData": { + "type": "structure", + "required": ["metricName", "metricValue", "timestamp", "product"], + "members": { + "metricName": { "shape": "MetricDataMetricNameString" }, + "metricValue": { "shape": "Double" }, + "timestamp": { "shape": "Timestamp" }, + "product": { "shape": "MetricDataProductString" }, + "dimensions": { "shape": "DimensionList" } + } + }, + "MetricDataMetricNameString": { + "type": "string", + "max": 1024, + "min": 1, + "pattern": "[-a-zA-Z0-9._]*" + }, + "MetricDataProductString": { + "type": "string", + "max": 128, + "min": 1, + "pattern": "[-a-zA-Z0-9._]*" + }, + "Model": { + "type": "structure", + "required": ["modelId"], + "members": { + "modelId": { + "shape": "ModelId", + "documentation": "

Unique identifier for the model

" + }, + "modelName": { + "shape": "ModelName", + "documentation": "

User-facing display name

" + }, + "description": { + "shape": "ModelDescription", + "documentation": "

Description of the model

" + }, + "rateMultiplier": { + "shape": "ModelRateMultiplierDouble", + "documentation": "

Rate multiplier of the model

" + }, + "rateUnit": { + "shape": "ModelRateUnitString", + "documentation": "

Unit for the rate multiplier

" + }, + "tokenLimits": { + "shape": "TokenLimits", + "documentation": "

Limits on token usage for this model

" + }, + "supportedInputTypes": { + "shape": "SupportedInputTypesList", + "documentation": "

List of input types supported by this model

" + }, + "supportsPromptCache": { + "shape": "Boolean", + "documentation": "

Whether the model supports prompt caching

" + } + } + }, + "ModelDescription": { + "type": "string", + "max": 256, + "min": 0, + "pattern": "[\\sa-zA-Z0-9_.-]*" + }, + "ModelId": { + "type": "string", + "documentation": "

Unique identifier for the model

", + "max": 1024, + "min": 1, + "pattern": "[a-zA-Z0-9_:.-]+" + }, + "ModelName": { + "type": "string", + "documentation": "

Identifier for the model Name

", + "max": 1024, + "min": 1, + "pattern": "[a-zA-Z0-9-_. ]+" + }, + "ModelProvider": { + "type": "string", + "documentation": "

Provider of AI models

", + "enum": ["DEFAULT"] + }, + "ModelRateMultiplierDouble": { + "type": "double", + "box": true, + "max": 100.0, + "min": 0 + }, + "ModelRateUnitString": { + "type": "string", + "max": 100, + "min": 0 + }, + "Models": { + "type": "list", + "member": { "shape": "Model" } + }, + "NextToken": { + "type": "string", + "max": 1000, + "min": 0 + }, + "Notifications": { + "type": "list", + "member": { "shape": "NotificationsFeature" }, + "max": 10, + "min": 0 + }, + "NotificationsFeature": { + "type": "structure", + "required": ["feature", "toggle"], + "members": { + "feature": { "shape": "FeatureName" }, + "toggle": { "shape": "OptInFeatureToggle" } + } + }, + "OperatingSystem": { + "type": "string", + "enum": ["MAC", "WINDOWS", "LINUX"], + "max": 64, + "min": 1 + }, + "OptInFeatureToggle": { + "type": "string", + "enum": ["ON", "OFF"] + }, + "OptInFeatures": { + "type": "structure", + "members": { + "promptLogging": { "shape": "PromptLogging" }, + "byUserAnalytics": { "shape": "ByUserAnalytics" }, + "dashboardAnalytics": { "shape": "DashboardAnalytics" }, + "notifications": { "shape": "Notifications" }, + "workspaceContext": { "shape": "WorkspaceContext" } + } + }, + "OptOutPreference": { + "type": "string", + "enum": ["OPTIN", "OPTOUT"] + }, + "Origin": { + "type": "string", + "documentation": "

Enum to represent the origin application conversing with Sidekick.

", + "enum": [ + "CHATBOT", + "CONSOLE", + "DOCUMENTATION", + "MARKETING", + "MOBILE", + "SERVICE_INTERNAL", + "UNIFIED_SEARCH", + "UNKNOWN", + "MD", + "IDE", + "SAGE_MAKER", + "CLI", + "AI_EDITOR", + "OPENSEARCH_DASHBOARD", + "GITLAB", + "Q_DEV_BEXT", + "MD_IDE", + "MD_CE", + "SM_AI_STUDIO_IDE", + "INLINE_CHAT" + ] + }, + "PackageInfo": { + "type": "structure", + "members": { + "executionCommand": { "shape": "SensitiveString" }, + "buildCommand": { "shape": "SensitiveString" }, + "buildOrder": { "shape": "PackageInfoBuildOrderInteger" }, + "testFramework": { "shape": "String" }, + "packageSummary": { "shape": "PackageInfoPackageSummaryString" }, + "packagePlan": { "shape": "PackageInfoPackagePlanString" }, + "targetFileInfoList": { "shape": "TargetFileInfoList" } + } + }, + "PackageInfoBuildOrderInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "PackageInfoList": { + "type": "list", + "member": { "shape": "PackageInfo" } + }, + "PackageInfoPackagePlanString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "PackageInfoPackageSummaryString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "PaginationToken": { + "type": "string", + "max": 2048, + "min": 1, + "pattern": "\\S+" + }, + "Position": { + "type": "structure", + "required": ["line", "character"], + "members": { + "line": { + "shape": "Integer", + "documentation": "

Line position in a document.

" + }, + "character": { + "shape": "Integer", + "documentation": "

Character offset on a line in a document (zero-based)

" + } + }, + "documentation": "

Indicates Cursor postion in a Text Document

" + }, + "PreSignedUrl": { + "type": "string", + "max": 2048, + "min": 1, + "sensitive": true + }, + "Prediction": { + "type": "structure", + "members": { + "completion": { "shape": "Completion" }, + "edit": { "shape": "Edit" } + }, + "union": true + }, + "PredictionType": { + "type": "string", + "enum": ["COMPLETIONS", "EDITS"] + }, + "PredictionTypes": { + "type": "list", + "member": { "shape": "PredictionType" } + }, + "Predictions": { + "type": "list", + "member": { "shape": "Prediction" }, + "max": 10, + "min": 0 + }, + "PreviousEditorStateMetadata": { + "type": "structure", + "required": ["timeOffset"], + "members": { + "timeOffset": { "shape": "Integer" } + } + }, + "PrimitiveInteger": { "type": "integer" }, + "Profile": { + "type": "structure", + "required": ["arn", "profileName"], + "members": { + "arn": { "shape": "ProfileArn" }, + "identityDetails": { "shape": "IdentityDetails" }, + "profileName": { "shape": "ProfileName" }, + "description": { "shape": "ProfileDescription" }, + "referenceTrackerConfiguration": { "shape": "ReferenceTrackerConfiguration" }, + "kmsKeyArn": { "shape": "ResourceArn" }, + "activeFunctionalities": { "shape": "ActiveFunctionalityList" }, + "status": { "shape": "ProfileStatus" }, + "errorDetails": { "shape": "ErrorDetails" }, + "resourcePolicy": { "shape": "ResourcePolicy" }, + "profileType": { "shape": "ProfileType" }, + "optInFeatures": { "shape": "OptInFeatures" }, + "permissionUpdateRequired": { "shape": "Boolean" }, + "applicationProperties": { "shape": "ApplicationPropertiesList" } + } + }, + "ProfileArn": { + "type": "string", + "max": 950, + "min": 0, + "pattern": "arn:aws:(codewhisperer|transform):[-.a-z0-9]{1,63}:\\d{12}:profile/([a-zA-Z0-9]){12}" + }, + "ProfileDescription": { + "type": "string", + "max": 256, + "min": 1, + "pattern": "[\\sa-zA-Z0-9_-]*" + }, + "ProfileList": { + "type": "list", + "member": { "shape": "Profile" } + }, + "ProfileName": { + "type": "string", + "max": 100, + "min": 1, + "pattern": "[a-zA-Z][a-zA-Z0-9_-]*" + }, + "ProfileStatus": { + "type": "string", + "enum": ["ACTIVE", "CREATING", "CREATE_FAILED", "UPDATING", "UPDATE_FAILED", "DELETING", "DELETE_FAILED"] + }, + "ProfileType": { + "type": "string", + "enum": ["Q_DEVELOPER", "CODEWHISPERER"] + }, + "ProgrammingLanguage": { + "type": "structure", + "required": ["languageName"], + "members": { + "languageName": { "shape": "ProgrammingLanguageLanguageNameString" } + }, + "documentation": "

Programming Languages supported by CodeWhisperer

" + }, + "ProgrammingLanguageLanguageNameString": { + "type": "string", + "max": 128, + "min": 1, + "pattern": "(python|javascript|java|csharp|typescript|c|cpp|go|kotlin|php|ruby|rust|scala|shell|sql|json|yaml|vue|tf|tsx|jsx|plaintext|systemverilog|dart|lua|swift|hcl|powershell|r|abap)" + }, + "ProgressUpdates": { + "type": "list", + "member": { "shape": "TransformationProgressUpdate" } + }, + "PromptLogging": { + "type": "structure", + "required": ["s3Uri", "toggle"], + "members": { + "s3Uri": { "shape": "S3Uri" }, + "toggle": { "shape": "OptInFeatureToggle" } + } + }, + "PushTelemetryEventRequest": { + "type": "structure", + "required": ["eventType", "event"], + "members": { + "clientToken": { + "shape": "IdempotencyToken", + "idempotencyToken": true + }, + "eventType": { "shape": "String" }, + "event": { "shape": "Document" } + } + }, + "PushTelemetryEventResponse": { + "type": "structure", + "members": {} + }, + "Range": { + "type": "structure", + "required": ["start", "end"], + "members": { + "start": { + "shape": "Position", + "documentation": "

The range's start position.

" + }, + "end": { + "shape": "Position", + "documentation": "

The range's end position.

" + } + }, + "documentation": "

Indicates Range / Span in a Text Document

" + }, + "ReasoningContent": { + "type": "structure", + "members": { + "reasoningText": { "shape": "ReasoningText" }, + "redactedContent": { + "shape": "Blob", + "documentation": "

Reasoning content that was encrypted by the model provider

" + } + }, + "documentation": "

The entire reasoning content that the model used to return the output

", + "sensitive": true, + "union": true + }, + "ReasoningText": { + "type": "structure", + "required": ["text"], + "members": { + "text": { "shape": "SensitiveString" }, + "signature": { + "shape": "SensitiveString", + "documentation": "

A token that verifies that the reasoning text was generated by the model

" + } + }, + "sensitive": true + }, + "RecommendationsWithReferencesPreference": { + "type": "string", + "documentation": "

Recommendations with references setting for CodeWhisperer

", + "enum": ["BLOCK", "ALLOW"] + }, + "Reference": { + "type": "structure", + "members": { + "licenseName": { + "shape": "ReferenceLicenseNameString", + "documentation": "

License name

" + }, + "repository": { + "shape": "ReferenceRepositoryString", + "documentation": "

Code Repsitory for the associated reference

" + }, + "url": { + "shape": "ReferenceUrlString", + "documentation": "

Respository URL

" + }, + "recommendationContentSpan": { + "shape": "Span", + "documentation": "

Span / Range for the Reference

" + } + }, + "documentation": "

Code Reference / Repository details

" + }, + "ReferenceLicenseNameString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "ReferenceRepositoryString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "ReferenceTrackerConfiguration": { + "type": "structure", + "required": ["recommendationsWithReferences"], + "members": { + "recommendationsWithReferences": { "shape": "RecommendationsWithReferencesPreference" } + } + }, + "ReferenceUrlString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "References": { + "type": "list", + "member": { "shape": "Reference" }, + "max": 10, + "min": 0 + }, + "RelevantDocumentList": { + "type": "list", + "member": { "shape": "RelevantTextDocument" }, + "max": 100, + "min": 0 + }, + "RelevantTextDocument": { + "type": "structure", + "required": ["relativeFilePath"], + "members": { + "relativeFilePath": { + "shape": "RelevantTextDocumentRelativeFilePathString", + "documentation": "

Filepath relative to the root of the workspace

" + }, + "programmingLanguage": { + "shape": "ProgrammingLanguage", + "documentation": "

The text document's language identifier.

" + }, + "text": { + "shape": "RelevantTextDocumentTextString", + "documentation": "

Content of the text document

" + }, + "documentSymbols": { + "shape": "DocumentSymbols", + "documentation": "

DocumentSymbols parsed from a text document

" + }, + "type": { + "shape": "ContentType", + "documentation": "

The type of content(file, prompt, symbol, or workspace)

" + } + }, + "documentation": "

Represents an IDE retrieved relevant Text Document / File

" + }, + "RelevantTextDocumentRelativeFilePathString": { + "type": "string", + "max": 4096, + "min": 1, + "sensitive": true + }, + "RelevantTextDocumentTextString": { + "type": "string", + "max": 40960, + "min": 0, + "sensitive": true + }, + "RequestHeaderKey": { + "type": "string", + "max": 64, + "min": 1 + }, + "RequestHeaderValue": { + "type": "string", + "max": 256, + "min": 1 + }, + "RequestHeaders": { + "type": "map", + "key": { "shape": "RequestHeaderKey" }, + "value": { "shape": "RequestHeaderValue" }, + "max": 16, + "min": 1, + "sensitive": true + }, + "ResourceArn": { + "type": "string", + "max": 1224, + "min": 0, + "pattern": "arn:([-.a-z0-9]{1,63}:){2}([-.a-z0-9]{0,63}:){2}([a-zA-Z0-9-_:/]){1,1023}" + }, + "ResourceNotFoundException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" } + }, + "documentation": "

This exception is thrown when describing a resource that does not exist.

", + "exception": true + }, + "ResourcePolicy": { + "type": "structure", + "required": ["effect"], + "members": { + "effect": { "shape": "ResourcePolicyEffect" } + } + }, + "ResourcePolicyEffect": { + "type": "string", + "enum": ["ALLOW", "DENY"] + }, + "ResumeTransformationRequest": { + "type": "structure", + "required": ["transformationJobId"], + "members": { + "transformationJobId": { "shape": "TransformationJobId" }, + "userActionStatus": { "shape": "TransformationUserActionStatus" }, + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Structure to represent stop code transformation request.

" + }, + "ResumeTransformationResponse": { + "type": "structure", + "required": ["transformationStatus"], + "members": { + "transformationStatus": { "shape": "TransformationStatus" } + }, + "documentation": "

Structure to represent stop code transformation response.

" + }, + "RuntimeDiagnostic": { + "type": "structure", + "required": ["source", "severity", "message"], + "members": { + "source": { + "shape": "RuntimeDiagnosticSourceString", + "documentation": "

A human-readable string describing the source of the diagnostic

" + }, + "severity": { + "shape": "DiagnosticSeverity", + "documentation": "

Diagnostic Error type

" + }, + "message": { + "shape": "RuntimeDiagnosticMessageString", + "documentation": "

The diagnostic's message.

" + } + }, + "documentation": "

Structure to represent metadata about a Runtime Diagnostics

" + }, + "RuntimeDiagnosticMessageString": { + "type": "string", + "max": 1024, + "min": 0, + "sensitive": true + }, + "RuntimeDiagnosticSourceString": { + "type": "string", + "max": 1024, + "min": 0, + "sensitive": true + }, + "S3Uri": { + "type": "string", + "max": 1024, + "min": 1, + "pattern": "s3://((?!xn--)[a-z0-9](?![^/]*[.]{2})[a-z0-9-.]{1,61}[a-z0-9](?This exception is thrown when request was denied due to caller exceeding their usage limits

", + "exception": true + }, + "ServiceQuotaExceededExceptionReason": { + "type": "string", + "documentation": "

Reason for ServiceQuotaExceededException

", + "enum": ["CONVERSATION_LIMIT_EXCEEDED", "MONTHLY_REQUEST_COUNT", "OVERAGE_REQUEST_LIMIT_EXCEEDED"] + }, + "ShellHistory": { + "type": "list", + "member": { "shape": "ShellHistoryEntry" }, + "documentation": "

A list of shell history entries

", + "max": 20, + "min": 0 + }, + "ShellHistoryEntry": { + "type": "structure", + "required": ["command"], + "members": { + "command": { + "shape": "ShellHistoryEntryCommandString", + "documentation": "

The shell command that was run

" + }, + "directory": { + "shape": "ShellHistoryEntryDirectoryString", + "documentation": "

The directory the command was ran in

" + }, + "exitCode": { + "shape": "Integer", + "documentation": "

The exit code of the command after it finished

" + }, + "stdout": { + "shape": "ShellHistoryEntryStdoutString", + "documentation": "

The stdout from the command

" + }, + "stderr": { + "shape": "ShellHistoryEntryStderrString", + "documentation": "

The stderr from the command

" + } + }, + "documentation": "

An single entry in the shell history

" + }, + "ShellHistoryEntryCommandString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "ShellHistoryEntryDirectoryString": { + "type": "string", + "max": 256, + "min": 1, + "sensitive": true + }, + "ShellHistoryEntryStderrString": { + "type": "string", + "max": 4096, + "min": 0, + "sensitive": true + }, + "ShellHistoryEntryStdoutString": { + "type": "string", + "max": 4096, + "min": 0, + "sensitive": true + }, + "ShellState": { + "type": "structure", + "required": ["shellName"], + "members": { + "shellName": { + "shape": "ShellStateShellNameString", + "documentation": "

The name of the current shell

" + }, + "shellHistory": { + "shape": "ShellHistory", + "documentation": "

The history previous shell commands for the current shell

" + } + }, + "documentation": "

Represents the state of a shell

" + }, + "ShellStateShellNameString": { + "type": "string", + "max": 32, + "min": 1, + "pattern": "(zsh|bash|fish|pwsh|nu)" + }, + "Span": { + "type": "structure", + "members": { + "start": { "shape": "SpanStartInteger" }, + "end": { "shape": "SpanEndInteger" } + }, + "documentation": "

Represents span in a text.

" + }, + "SpanEndInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "SpanStartInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "StartCodeAnalysisRequest": { + "type": "structure", + "required": ["artifacts", "programmingLanguage"], + "members": { + "artifacts": { "shape": "ArtifactMap" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "clientToken": { + "shape": "StartCodeAnalysisRequestClientTokenString", + "idempotencyToken": true + }, + "scope": { "shape": "CodeAnalysisScope" }, + "codeScanName": { "shape": "CodeScanName" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "StartCodeAnalysisRequestClientTokenString": { + "type": "string", + "max": 256, + "min": 1 + }, + "StartCodeAnalysisResponse": { + "type": "structure", + "required": ["jobId", "status"], + "members": { + "jobId": { "shape": "StartCodeAnalysisResponseJobIdString" }, + "status": { "shape": "CodeAnalysisStatus" }, + "errorMessage": { "shape": "SensitiveString" } + } + }, + "StartCodeAnalysisResponseJobIdString": { + "type": "string", + "max": 256, + "min": 1 + }, + "StartCodeFixJobRequest": { + "type": "structure", + "required": ["snippetRange", "uploadId"], + "members": { + "snippetRange": { "shape": "Range" }, + "uploadId": { "shape": "UploadId" }, + "description": { "shape": "StartCodeFixJobRequestDescriptionString" }, + "ruleId": { "shape": "StartCodeFixJobRequestRuleIdString" }, + "codeFixName": { "shape": "CodeFixName" }, + "referenceTrackerConfiguration": { "shape": "ReferenceTrackerConfiguration" }, + "profileArn": { "shape": "ProfileArn" } + } + }, + "StartCodeFixJobRequestDescriptionString": { + "type": "string", + "max": 5000, + "min": 1, + "sensitive": true + }, + "StartCodeFixJobRequestRuleIdString": { + "type": "string", + "max": 256, + "min": 1, + "pattern": ".*[A-Za-z0-9-]+.*" + }, + "StartCodeFixJobResponse": { + "type": "structure", + "members": { + "jobId": { "shape": "StartCodeFixJobResponseJobIdString" }, + "status": { "shape": "CodeFixJobStatus" } + } + }, + "StartCodeFixJobResponseJobIdString": { + "type": "string", + "max": 256, + "min": 1, + "pattern": ".*[A-Za-z0-9-:]+.*" + }, + "StartTaskAssistCodeGenerationRequest": { + "type": "structure", + "required": ["conversationState", "workspaceState"], + "members": { + "conversationState": { "shape": "ConversationState" }, + "workspaceState": { "shape": "WorkspaceState" }, + "taskAssistPlan": { "shape": "TaskAssistPlan" }, + "codeGenerationId": { "shape": "CodeGenerationId" }, + "currentCodeGenerationId": { "shape": "CodeGenerationId" }, + "intent": { "shape": "Intent" }, + "intentContext": { "shape": "IntentContext" }, + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Structure to represent start code generation request.

" + }, + "StartTaskAssistCodeGenerationResponse": { + "type": "structure", + "required": ["conversationId", "codeGenerationId"], + "members": { + "conversationId": { "shape": "ConversationId" }, + "codeGenerationId": { "shape": "CodeGenerationId" } + }, + "documentation": "

Structure to represent start code generation response.

" + }, + "StartTestGenerationRequest": { + "type": "structure", + "required": ["uploadId", "targetCodeList", "userInput"], + "members": { + "uploadId": { "shape": "UploadId" }, + "targetCodeList": { "shape": "TargetCodeList" }, + "userInput": { + "shape": "StartTestGenerationRequestUserInputString", + "documentation": "

The content of user input.

" + }, + "testGenerationJobGroupName": { "shape": "TestGenerationJobGroupName" }, + "clientToken": { + "shape": "StartTestGenerationRequestClientTokenString", + "idempotencyToken": true + }, + "profileArn": { "shape": "ProfileArn" }, + "referenceTrackerConfiguration": { "shape": "ReferenceTrackerConfiguration" } + }, + "documentation": "

Structure to represent test generation request.

" + }, + "StartTestGenerationRequestClientTokenString": { + "type": "string", + "max": 256, + "min": 1 + }, + "StartTestGenerationRequestUserInputString": { + "type": "string", + "max": 4096, + "min": 0, + "sensitive": true + }, + "StartTestGenerationResponse": { + "type": "structure", + "members": { + "testGenerationJob": { "shape": "TestGenerationJob" } + }, + "documentation": "

Structure to represent code transformation response.

" + }, + "StartTransformationRequest": { + "type": "structure", + "required": ["workspaceState", "transformationSpec"], + "members": { + "workspaceState": { "shape": "WorkspaceState" }, + "transformationSpec": { "shape": "TransformationSpec" }, + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Structure to represent code transformation request.

" + }, + "StartTransformationResponse": { + "type": "structure", + "required": ["transformationJobId"], + "members": { + "transformationJobId": { "shape": "TransformationJobId" } + }, + "documentation": "

Structure to represent code transformation response.

" + }, + "StepId": { + "type": "string", + "max": 126, + "min": 1 + }, + "StopTransformationRequest": { + "type": "structure", + "required": ["transformationJobId"], + "members": { + "transformationJobId": { "shape": "TransformationJobId" }, + "profileArn": { "shape": "ProfileArn" } + }, + "documentation": "

Structure to represent stop code transformation request.

" + }, + "StopTransformationResponse": { + "type": "structure", + "required": ["transformationStatus"], + "members": { + "transformationStatus": { "shape": "TransformationStatus" } + }, + "documentation": "

Structure to represent stop code transformation response.

" + }, + "String": { "type": "string" }, + "StringList": { + "type": "list", + "member": { "shape": "StringListMemberString" }, + "documentation": "

A list of strings

", + "max": 50, + "min": 0 + }, + "StringListMemberString": { + "type": "string", + "max": 256, + "min": 1, + "sensitive": true + }, + "SubscriptionStatus": { + "type": "string", + "enum": ["INACTIVE", "ACTIVE"] + }, + "SuggestedFix": { + "type": "structure", + "members": { + "codeDiff": { "shape": "SuggestedFixCodeDiffString" }, + "description": { "shape": "SuggestedFixDescriptionString" }, + "references": { "shape": "References" } + } + }, + "SuggestedFixCodeDiffString": { + "type": "string", + "max": 200000, + "min": 0, + "sensitive": true + }, + "SuggestedFixDescriptionString": { + "type": "string", + "max": 2000, + "min": 1, + "sensitive": true + }, + "SuggestionState": { + "type": "string", + "enum": ["ACCEPT", "REJECT", "DISCARD", "EMPTY", "MERGE"] + }, + "SuggestionType": { + "type": "string", + "enum": ["COMPLETIONS", "EDITS"] + }, + "SupplementalContext": { + "type": "structure", + "required": ["filePath", "content"], + "members": { + "filePath": { "shape": "SupplementalContextFilePathString" }, + "content": { "shape": "SupplementalContextContentString" }, + "type": { "shape": "SupplementalContextType" }, + "metadata": { "shape": "SupplementalContextMetadata" } + } + }, + "SupplementalContextContentString": { + "type": "string", + "max": 10240, + "min": 1, + "sensitive": true + }, + "SupplementalContextFilePathString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "SupplementalContextList": { + "type": "list", + "member": { "shape": "SupplementalContext" }, + "max": 20, + "min": 0 + }, + "SupplementalContextMetadata": { + "type": "structure", + "members": { + "previousEditorStateMetadata": { "shape": "PreviousEditorStateMetadata" } + }, + "union": true + }, + "SupplementalContextType": { + "type": "string", + "enum": ["PreviousEditorState", "WorkspaceContext"] + }, + "SupplementaryWebLink": { + "type": "structure", + "required": ["url", "title"], + "members": { + "url": { + "shape": "SupplementaryWebLinkUrlString", + "documentation": "

URL of the web reference link.

" + }, + "title": { + "shape": "SupplementaryWebLinkTitleString", + "documentation": "

Title of the web reference link.

" + }, + "snippet": { + "shape": "SupplementaryWebLinkSnippetString", + "documentation": "

Relevant text snippet from the link.

" + } + }, + "documentation": "

Represents an additional reference link retured with the Chat message

" + }, + "SupplementaryWebLinkSnippetString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "SupplementaryWebLinkTitleString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "SupplementaryWebLinkUrlString": { + "type": "string", + "max": 2048, + "min": 1, + "sensitive": true + }, + "SupplementaryWebLinks": { + "type": "list", + "member": { "shape": "SupplementaryWebLink" }, + "max": 10, + "min": 0 + }, + "SupportedInputTypesList": { + "type": "list", + "member": { "shape": "InputType" }, + "documentation": "

List of supported input types for the model

" + }, + "SymbolType": { + "type": "string", + "enum": ["DECLARATION", "USAGE"] + }, + "SyntheticTimestamp_date_time": { + "type": "timestamp", + "timestampFormat": "iso8601" + }, + "TargetCode": { + "type": "structure", + "required": ["relativeTargetPath"], + "members": { + "relativeTargetPath": { + "shape": "TargetCodeRelativeTargetPathString", + "documentation": "

The file path relative to the root of the workspace, could be a single file or a folder.

" + }, + "targetLineRangeList": { "shape": "LineRangeList" } + } + }, + "TargetCodeList": { + "type": "list", + "member": { "shape": "TargetCode" }, + "min": 1 + }, + "TargetCodeRelativeTargetPathString": { + "type": "string", + "max": 4096, + "min": 1, + "sensitive": true + }, + "TargetFileInfo": { + "type": "structure", + "members": { + "filePath": { "shape": "SensitiveString" }, + "testFilePath": { "shape": "SensitiveString" }, + "testCoverage": { "shape": "TargetFileInfoTestCoverageInteger" }, + "fileSummary": { "shape": "TargetFileInfoFileSummaryString" }, + "filePlan": { "shape": "TargetFileInfoFilePlanString" }, + "codeReferences": { "shape": "References" }, + "numberOfTestMethods": { "shape": "TargetFileInfoNumberOfTestMethodsInteger" } + } + }, + "TargetFileInfoFilePlanString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "TargetFileInfoFileSummaryString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "TargetFileInfoList": { + "type": "list", + "member": { "shape": "TargetFileInfo" } + }, + "TargetFileInfoNumberOfTestMethodsInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "TargetFileInfoTestCoverageInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 0 + }, + "TaskAssistPlan": { + "type": "list", + "member": { "shape": "TaskAssistPlanStep" }, + "min": 0 + }, + "TaskAssistPlanStep": { + "type": "structure", + "required": ["filePath", "description"], + "members": { + "filePath": { + "shape": "TaskAssistPlanStepFilePathString", + "documentation": "

File path on which the step is working on.

" + }, + "description": { + "shape": "TaskAssistPlanStepDescriptionString", + "documentation": "

Description on the step.

" + }, + "startLine": { + "shape": "TaskAssistPlanStepStartLineInteger", + "documentation": "

Start line number of the related changes.

" + }, + "endLine": { + "shape": "TaskAssistPlanStepEndLineInteger", + "documentation": "

End line number of the related changes.

" + }, + "action": { + "shape": "TaskAssistPlanStepAction", + "documentation": "

Type of the action.

" + } + }, + "documentation": "

Structured plan step for a task assist plan.

" + }, + "TaskAssistPlanStepAction": { + "type": "string", + "documentation": "

Action for task assist plan step

", + "enum": ["MODIFY", "CREATE", "DELETE", "UNKNOWN"] + }, + "TaskAssistPlanStepDescriptionString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "TaskAssistPlanStepEndLineInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "TaskAssistPlanStepFilePathString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "TaskAssistPlanStepStartLineInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "TaskAssistPlanningUploadContext": { + "type": "structure", + "required": ["conversationId"], + "members": { + "conversationId": { "shape": "ConversationId" } + } + }, + "TelemetryEvent": { + "type": "structure", + "members": { + "userTriggerDecisionEvent": { "shape": "UserTriggerDecisionEvent" }, + "codeCoverageEvent": { "shape": "CodeCoverageEvent" }, + "userModificationEvent": { "shape": "UserModificationEvent" }, + "codeScanEvent": { "shape": "CodeScanEvent" }, + "codeScanSucceededEvent": { "shape": "CodeScanSucceededEvent" }, + "codeScanFailedEvent": { "shape": "CodeScanFailedEvent" }, + "codeScanRemediationsEvent": { "shape": "CodeScanRemediationsEvent" }, + "codeFixGenerationEvent": { "shape": "CodeFixGenerationEvent" }, + "codeFixAcceptanceEvent": { "shape": "CodeFixAcceptanceEvent" }, + "metricData": { "shape": "MetricData" }, + "chatAddMessageEvent": { "shape": "ChatAddMessageEvent" }, + "chatInteractWithMessageEvent": { "shape": "ChatInteractWithMessageEvent" }, + "chatUserModificationEvent": { "shape": "ChatUserModificationEvent" }, + "terminalUserInteractionEvent": { "shape": "TerminalUserInteractionEvent" }, + "featureDevEvent": { "shape": "FeatureDevEvent" }, + "featureDevCodeGenerationEvent": { "shape": "FeatureDevCodeGenerationEvent" }, + "featureDevCodeAcceptanceEvent": { "shape": "FeatureDevCodeAcceptanceEvent" }, + "inlineChatEvent": { "shape": "InlineChatEvent" }, + "transformEvent": { "shape": "TransformEvent" }, + "docGenerationEvent": { "shape": "DocGenerationEvent" }, + "docV2GenerationEvent": { "shape": "DocV2GenerationEvent" }, + "docV2AcceptanceEvent": { "shape": "DocV2AcceptanceEvent" }, + "testGenerationEvent": { "shape": "TestGenerationEvent" } + }, + "union": true + }, + "TenantId": { + "type": "string", + "max": 1024, + "min": 1 + }, + "TerminalUserInteractionEvent": { + "type": "structure", + "members": { + "terminalUserInteractionEventType": { "shape": "TerminalUserInteractionEventType" }, + "terminal": { "shape": "String" }, + "terminalVersion": { "shape": "String" }, + "shell": { "shape": "String" }, + "shellVersion": { "shape": "String" }, + "duration": { "shape": "Integer" }, + "timeToSuggestion": { "shape": "Integer" }, + "isCompletionAccepted": { "shape": "Boolean" }, + "cliToolCommand": { "shape": "String" } + } + }, + "TerminalUserInteractionEventType": { + "type": "string", + "documentation": "

CodeWhisperer terminal Interaction Type

", + "enum": ["CODEWHISPERER_TERMINAL_TRANSLATION_ACTION", "CODEWHISPERER_TERMINAL_COMPLETION_INSERTED"] + }, + "TestGenerationEvent": { + "type": "structure", + "required": ["jobId", "groupName"], + "members": { + "jobId": { "shape": "UUID" }, + "groupName": { "shape": "TestGenerationJobGroupName" }, + "timestamp": { "shape": "Timestamp" }, + "ideCategory": { "shape": "IdeCategory" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "numberOfUnitTestCasesGenerated": { "shape": "Integer" }, + "numberOfUnitTestCasesAccepted": { "shape": "Integer" }, + "linesOfCodeGenerated": { "shape": "Integer" }, + "linesOfCodeAccepted": { "shape": "Integer" }, + "charsOfCodeGenerated": { "shape": "Integer" }, + "charsOfCodeAccepted": { "shape": "Integer" } + } + }, + "TestGenerationJob": { + "type": "structure", + "required": ["testGenerationJobId", "testGenerationJobGroupName", "status", "creationTime"], + "members": { + "testGenerationJobId": { "shape": "UUID" }, + "testGenerationJobGroupName": { "shape": "TestGenerationJobGroupName" }, + "status": { "shape": "TestGenerationJobStatus" }, + "shortAnswer": { "shape": "SensitiveString" }, + "creationTime": { "shape": "Timestamp" }, + "progressRate": { "shape": "TestGenerationJobProgressRateInteger" }, + "jobStatusReason": { "shape": "String" }, + "jobSummary": { "shape": "TestGenerationJobJobSummaryString" }, + "jobPlan": { "shape": "TestGenerationJobJobPlanString" }, + "packageInfoList": { "shape": "PackageInfoList" } + }, + "documentation": "

Represents a test generation job

" + }, + "TestGenerationJobGroupName": { + "type": "string", + "documentation": "

Test generation job group name

", + "max": 128, + "min": 1, + "pattern": "[a-zA-Z0-9-_]+" + }, + "TestGenerationJobJobPlanString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "TestGenerationJobJobSummaryString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "TestGenerationJobProgressRateInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 0 + }, + "TestGenerationJobStatus": { + "type": "string", + "enum": ["IN_PROGRESS", "FAILED", "COMPLETED"] + }, + "TextDocument": { + "type": "structure", + "required": ["relativeFilePath"], + "members": { + "relativeFilePath": { + "shape": "TextDocumentRelativeFilePathString", + "documentation": "

Filepath relative to the root of the workspace

" + }, + "programmingLanguage": { + "shape": "ProgrammingLanguage", + "documentation": "

The text document's language identifier.

" + }, + "text": { + "shape": "TextDocumentTextString", + "documentation": "

Content of the text document

" + }, + "documentSymbols": { + "shape": "DocumentSymbols", + "documentation": "

DocumentSymbols parsed from a text document

" + } + }, + "documentation": "

Represents a Text Document / File

" + }, + "TextDocumentDiagnostic": { + "type": "structure", + "required": ["document", "range", "source", "severity", "message"], + "members": { + "document": { + "shape": "TextDocument", + "documentation": "

Represents a Text Document associated with Diagnostic

" + }, + "range": { + "shape": "Range", + "documentation": "

The range at which the message applies.

" + }, + "source": { + "shape": "SensitiveString", + "documentation": "

A human-readable string describing the source of the diagnostic

" + }, + "severity": { + "shape": "DiagnosticSeverity", + "documentation": "

Diagnostic Error type

" + }, + "message": { + "shape": "TextDocumentDiagnosticMessageString", + "documentation": "

The diagnostic's message.

" + }, + "code": { + "shape": "TextDocumentDiagnosticCodeString", + "documentation": "

The diagnostic's code, which might appear in the user interface.

" + }, + "codeDescription": { + "shape": "CodeDescription", + "documentation": "

An optional property to describe the error code.

" + }, + "tags": { + "shape": "DiagnosticTagList", + "documentation": "

Additional metadata about the diagnostic.

" + }, + "relatedInformation": { + "shape": "DiagnosticRelatedInformationList", + "documentation": "

an array of related diagnostic information, e.g. when symbol-names within a scope collide all definitions can be marked via this property.

" + }, + "data": { + "shape": "TextDocumentDiagnosticDataString", + "documentation": "

A data entry field that is preserved between a textDocument/publishDiagnostics notification and textDocument/codeAction request.

" + } + }, + "documentation": "

Structure to represent metadata about a TextDocument Diagnostic

" + }, + "TextDocumentDiagnosticCodeString": { + "type": "string", + "max": 1024, + "min": 0, + "sensitive": true + }, + "TextDocumentDiagnosticDataString": { + "type": "string", + "max": 4096, + "min": 0, + "sensitive": true + }, + "TextDocumentDiagnosticMessageString": { + "type": "string", + "max": 1024, + "min": 0, + "sensitive": true + }, + "TextDocumentRelativeFilePathString": { + "type": "string", + "max": 4096, + "min": 1, + "sensitive": true + }, + "TextDocumentTextString": { + "type": "string", + "max": 40000, + "min": 0, + "sensitive": true + }, + "ThrottlingException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "reason": { "shape": "ThrottlingExceptionReason" } + }, + "documentation": "

This exception is thrown when request was denied due to request throttling.

", + "exception": true, + "retryable": { "throttling": true } + }, + "ThrottlingExceptionReason": { + "type": "string", + "documentation": "

Reason for ThrottlingException

", + "enum": ["DAILY_REQUEST_COUNT", "MONTHLY_REQUEST_COUNT", "INSUFFICIENT_MODEL_CAPACITY"] + }, + "Timestamp": { "type": "timestamp" }, + "TokenLimits": { + "type": "structure", + "members": { + "maxInputTokens": { + "shape": "TokenLimitsMaxInputTokensInteger", + "documentation": "

Maximum number of input tokens the model can process

" + }, + "maxOutputTokens": { + "shape": "TokenLimitsMaxOutputTokensInteger", + "documentation": "

Maximum number of output tokens the model can produce

" + } + } + }, + "TokenLimitsMaxInputTokensInteger": { + "type": "integer", + "box": true, + "min": 1 + }, + "TokenLimitsMaxOutputTokensInteger": { + "type": "integer", + "box": true, + "min": 1 + }, + "Tool": { + "type": "structure", + "members": { + "toolSpecification": { "shape": "ToolSpecification" }, + "cachePoint": { "shape": "CachePoint" } + }, + "documentation": "

Information about a tool that can be used.

", + "union": true + }, + "ToolDescription": { + "type": "string", + "documentation": "

The description for the tool.

", + "max": 10240, + "min": 1, + "sensitive": true + }, + "ToolInputSchema": { + "type": "structure", + "members": { + "json": { "shape": "SensitiveDocument" } + }, + "documentation": "

The input schema for the tool in JSON format.

" + }, + "ToolName": { + "type": "string", + "documentation": "

The name for the tool.

", + "max": 64, + "min": 0, + "pattern": "[a-zA-Z0-9_-]+", + "sensitive": true + }, + "ToolResult": { + "type": "structure", + "required": ["toolUseId", "content"], + "members": { + "toolUseId": { "shape": "ToolUseId" }, + "content": { + "shape": "ToolResultContent", + "documentation": "

Content of the tool result.

" + }, + "status": { "shape": "ToolResultStatus" } + }, + "documentation": "

A tool result that contains the results for a tool request that was previously made.

" + }, + "ToolResultContent": { + "type": "list", + "member": { "shape": "ToolResultContentBlock" } + }, + "ToolResultContentBlock": { + "type": "structure", + "members": { + "text": { + "shape": "ToolResultContentBlockTextString", + "documentation": "

A tool result that is text.

" + }, + "json": { + "shape": "SensitiveDocument", + "documentation": "

A tool result that is JSON format data.

" + } + }, + "union": true + }, + "ToolResultContentBlockTextString": { + "type": "string", + "max": 10000000, + "min": 0, + "sensitive": true + }, + "ToolResultStatus": { + "type": "string", + "documentation": "

Status of the tools result.

", + "enum": ["success", "error"] + }, + "ToolResults": { + "type": "list", + "member": { "shape": "ToolResult" } + }, + "ToolSpecification": { + "type": "structure", + "required": ["inputSchema", "name"], + "members": { + "inputSchema": { "shape": "ToolInputSchema" }, + "name": { "shape": "ToolName" }, + "description": { "shape": "ToolDescription" } + }, + "documentation": "

The specification for the tool.

" + }, + "ToolUse": { + "type": "structure", + "required": ["toolUseId", "name", "input"], + "members": { + "toolUseId": { "shape": "ToolUseId" }, + "name": { "shape": "ToolName" }, + "input": { + "shape": "SensitiveDocument", + "documentation": "

The input to pass to the tool.

" + } + }, + "documentation": "

Contains information about a tool that the model is requesting be run. The model uses the result from the tool to generate a response.

" + }, + "ToolUseId": { + "type": "string", + "documentation": "

The ID for the tool request.

", + "max": 64, + "min": 0, + "pattern": "[a-zA-Z0-9_-]+" + }, + "ToolUses": { + "type": "list", + "member": { "shape": "ToolUse" } + }, + "Tools": { + "type": "list", + "member": { "shape": "Tool" } + }, + "TransformEvent": { + "type": "structure", + "required": ["jobId"], + "members": { + "jobId": { "shape": "TransformationJobId" }, + "timestamp": { "shape": "Timestamp" }, + "ideCategory": { "shape": "IdeCategory" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "linesOfCodeChanged": { "shape": "Integer" }, + "charsOfCodeChanged": { "shape": "Integer" }, + "linesOfCodeSubmitted": { "shape": "Integer" } + } + }, + "TransformationDotNetRuntimeEnv": { + "type": "string", + "enum": ["NET_5_0", "NET_6_0", "NET_7_0", "NET_8_0", "NET_9_0", "NET_STANDARD_2_0"] + }, + "TransformationDownloadArtifact": { + "type": "structure", + "members": { + "downloadArtifactType": { "shape": "TransformationDownloadArtifactType" }, + "downloadArtifactId": { "shape": "ArtifactId" } + } + }, + "TransformationDownloadArtifactType": { + "type": "string", + "enum": ["ClientInstructions", "Logs", "GeneratedCode"] + }, + "TransformationDownloadArtifacts": { + "type": "list", + "member": { "shape": "TransformationDownloadArtifact" }, + "max": 10, + "min": 0 + }, + "TransformationJavaRuntimeEnv": { + "type": "string", + "enum": ["JVM_8", "JVM_11", "JVM_17", "JVM_21"] + }, + "TransformationJob": { + "type": "structure", + "members": { + "jobId": { "shape": "TransformationJobId" }, + "transformationSpec": { "shape": "TransformationSpec" }, + "status": { "shape": "TransformationStatus" }, + "reason": { "shape": "String" }, + "creationTime": { "shape": "Timestamp" }, + "startExecutionTime": { "shape": "Timestamp" }, + "endExecutionTime": { "shape": "Timestamp" } + }, + "documentation": "

Represent a Transformation Job

" + }, + "TransformationJobId": { + "type": "string", + "documentation": "

Identifier for the Transformation Job

", + "max": 128, + "min": 1 + }, + "TransformationLanguage": { + "type": "string", + "enum": ["JAVA_8", "JAVA_11", "JAVA_17", "JAVA_21", "C_SHARP", "COBOL", "PL_I", "JCL"] + }, + "TransformationLanguages": { + "type": "list", + "member": { "shape": "TransformationLanguage" } + }, + "TransformationMainframeRuntimeEnv": { + "type": "string", + "enum": ["MAINFRAME"] + }, + "TransformationOperatingSystemFamily": { + "type": "string", + "enum": ["WINDOWS", "LINUX"] + }, + "TransformationPlan": { + "type": "structure", + "required": ["transformationSteps"], + "members": { + "transformationSteps": { "shape": "TransformationSteps" } + } + }, + "TransformationPlatformConfig": { + "type": "structure", + "members": { + "operatingSystemFamily": { "shape": "TransformationOperatingSystemFamily" } + } + }, + "TransformationProgressUpdate": { + "type": "structure", + "required": ["name", "status"], + "members": { + "name": { "shape": "String" }, + "status": { "shape": "TransformationProgressUpdateStatus" }, + "description": { "shape": "String" }, + "startTime": { "shape": "Timestamp" }, + "endTime": { "shape": "Timestamp" }, + "downloadArtifacts": { "shape": "TransformationDownloadArtifacts" } + } + }, + "TransformationProgressUpdateStatus": { + "type": "string", + "enum": ["IN_PROGRESS", "COMPLETED", "FAILED", "PAUSED", "AWAITING_CLIENT_ACTION", "SKIPPED"] + }, + "TransformationProjectArtifactDescriptor": { + "type": "structure", + "members": { + "sourceCodeArtifact": { "shape": "TransformationSourceCodeArtifactDescriptor" } + }, + "union": true + }, + "TransformationProjectState": { + "type": "structure", + "members": { + "language": { "shape": "TransformationLanguage" }, + "runtimeEnv": { "shape": "TransformationRuntimeEnv" }, + "platformConfig": { "shape": "TransformationPlatformConfig" }, + "projectArtifact": { "shape": "TransformationProjectArtifactDescriptor" } + } + }, + "TransformationRuntimeEnv": { + "type": "structure", + "members": { + "java": { "shape": "TransformationJavaRuntimeEnv" }, + "dotNet": { "shape": "TransformationDotNetRuntimeEnv" }, + "mainframe": { "shape": "TransformationMainframeRuntimeEnv" } + }, + "union": true + }, + "TransformationSourceCodeArtifactDescriptor": { + "type": "structure", + "members": { + "languages": { "shape": "TransformationLanguages" }, + "runtimeEnv": { "shape": "TransformationRuntimeEnv" } + } + }, + "TransformationSpec": { + "type": "structure", + "members": { + "transformationType": { "shape": "TransformationType" }, + "source": { "shape": "TransformationProjectState" }, + "target": { "shape": "TransformationProjectState" } + } + }, + "TransformationStatus": { + "type": "string", + "enum": [ + "CREATED", + "ACCEPTED", + "REJECTED", + "STARTED", + "PREPARING", + "PREPARED", + "PLANNING", + "PLANNED", + "TRANSFORMING", + "TRANSFORMED", + "FAILED", + "COMPLETED", + "PARTIALLY_COMPLETED", + "STOPPING", + "STOPPED", + "PAUSED", + "RESUMED" + ] + }, + "TransformationStep": { + "type": "structure", + "required": ["id", "name", "description", "status"], + "members": { + "id": { "shape": "StepId" }, + "name": { "shape": "String" }, + "description": { "shape": "String" }, + "status": { "shape": "TransformationStepStatus" }, + "progressUpdates": { "shape": "ProgressUpdates" }, + "startTime": { "shape": "Timestamp" }, + "endTime": { "shape": "Timestamp" } + } + }, + "TransformationStepStatus": { + "type": "string", + "enum": ["CREATED", "COMPLETED", "PARTIALLY_COMPLETED", "STOPPED", "FAILED", "PAUSED", "SKIPPED"] + }, + "TransformationSteps": { + "type": "list", + "member": { "shape": "TransformationStep" } + }, + "TransformationType": { + "type": "string", + "enum": ["LANGUAGE_UPGRADE", "DOCUMENT_GENERATION"] + }, + "TransformationUploadArtifactType": { + "type": "string", + "enum": ["Dependencies", "ClientBuildResult"] + }, + "TransformationUploadContext": { + "type": "structure", + "required": ["jobId", "uploadArtifactType"], + "members": { + "jobId": { "shape": "TransformationJobId" }, + "uploadArtifactType": { "shape": "TransformationUploadArtifactType" } + } + }, + "TransformationUserActionStatus": { + "type": "string", + "enum": ["COMPLETED", "REJECTED"] + }, + "UUID": { + "type": "string", + "max": 36, + "min": 36 + }, + "UpdateUsageLimitQuotaExceededException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" } + }, + "documentation": "

Exception thrown when the number of usage limit update requests exceeds the monthly quota (default 3 requests per month)

", + "exception": true + }, + "UpdateUsageLimitsRequest": { + "type": "structure", + "required": ["accountId", "featureType", "requestedLimit"], + "members": { + "accountId": { "shape": "String" }, + "accountlessUserId": { "shape": "String" }, + "featureType": { "shape": "UsageLimitType" }, + "requestedLimit": { "shape": "Long" }, + "justification": { "shape": "String" } + } + }, + "UpdateUsageLimitsResponse": { + "type": "structure", + "required": ["status"], + "members": { + "status": { "shape": "UsageLimitUpdateRequestStatus" }, + "approvedLimit": { "shape": "Long" }, + "remainingRequestsThisMonth": { "shape": "Integer" } + } + }, + "UploadContext": { + "type": "structure", + "members": { + "taskAssistPlanningUploadContext": { "shape": "TaskAssistPlanningUploadContext" }, + "transformationUploadContext": { "shape": "TransformationUploadContext" }, + "codeAnalysisUploadContext": { "shape": "CodeAnalysisUploadContext" }, + "codeFixUploadContext": { "shape": "CodeFixUploadContext" }, + "workspaceContextUploadContext": { "shape": "WorkspaceContextUploadContext" } + }, + "union": true + }, + "UploadId": { + "type": "string", + "documentation": "

Upload ID returned by CreateUploadUrl API

", + "max": 128, + "min": 1 + }, + "UploadIntent": { + "type": "string", + "documentation": "

Upload Intent

", + "enum": [ + "TRANSFORMATION", + "TASK_ASSIST_PLANNING", + "AUTOMATIC_FILE_SECURITY_SCAN", + "FULL_PROJECT_SECURITY_SCAN", + "UNIT_TESTS_GENERATION", + "CODE_FIX_GENERATION", + "WORKSPACE_CONTEXT", + "AGENTIC_CODE_REVIEW" + ] + }, + "Url": { + "type": "string", + "max": 1024, + "min": 1 + }, + "UsageLimitList": { + "type": "structure", + "required": ["type", "currentUsageLimit", "totalUsageLimit"], + "members": { + "type": { "shape": "UsageLimitType" }, + "currentUsageLimit": { "shape": "Long" }, + "totalUsageLimit": { "shape": "Long" }, + "percentUsed": { "shape": "Double" } + } + }, + "UsageLimitType": { + "type": "string", + "enum": ["CODE_COMPLETIONS", "AGENTIC_REQUEST", "AI_EDITOR", "TRANSFORM"] + }, + "UsageLimitUpdateRequestStatus": { + "type": "string", + "enum": ["APPROVED", "PENDING_REVIEW", "REJECTED"] + }, + "UsageLimits": { + "type": "list", + "member": { "shape": "UsageLimitList" }, + "max": 10, + "min": 0 + }, + "UserContext": { + "type": "structure", + "required": ["ideCategory", "operatingSystem", "product"], + "members": { + "ideCategory": { "shape": "IdeCategory" }, + "operatingSystem": { "shape": "OperatingSystem" }, + "product": { "shape": "UserContextProductString" }, + "clientId": { "shape": "UUID" }, + "ideVersion": { "shape": "String" }, + "pluginVersion": { "shape": "UserContextPluginVersionString" }, + "lspVersion": { "shape": "UserContextLspVersionString" } + } + }, + "UserContextLspVersionString": { + "type": "string", + "max": 50, + "min": 0 + }, + "UserContextPluginVersionString": { + "type": "string", + "max": 50, + "min": 0 + }, + "UserContextProductString": { + "type": "string", + "max": 128, + "min": 1, + "pattern": "[-a-zA-Z0-9._]*" + }, + "UserInputMessage": { + "type": "structure", + "required": ["content"], + "members": { + "content": { + "shape": "UserInputMessageContentString", + "documentation": "

The content of the chat message.

" + }, + "userInputMessageContext": { + "shape": "UserInputMessageContext", + "documentation": "

Chat message context associated with the Chat Message.

" + }, + "userIntent": { + "shape": "UserIntent", + "documentation": "

User Intent.

" + }, + "origin": { + "shape": "Origin", + "documentation": "

User Input Origin.

" + }, + "images": { + "shape": "ImageBlocks", + "documentation": "

Images associated with the Chat Message.

" + }, + "modelId": { + "shape": "ModelId", + "documentation": "

Unique identifier for the model used in this conversation

" + }, + "cachePoint": { + "shape": "CachePoint", + "documentation": "

Indicates whether to add a cache point after the current message

" + }, + "clientCacheConfig": { + "shape": "ClientCacheConfig", + "documentation": "

Client cache config

" + } + }, + "documentation": "

Structure to represent a chat input message from User.

" + }, + "UserInputMessageContentString": { + "type": "string", + "max": 10000000, + "min": 0, + "sensitive": true + }, + "UserInputMessageContext": { + "type": "structure", + "members": { + "editorState": { + "shape": "EditorState", + "documentation": "

Editor state chat message context.

" + }, + "shellState": { + "shape": "ShellState", + "documentation": "

Shell state chat message context.

" + }, + "gitState": { + "shape": "GitState", + "documentation": "

Git state chat message context.

" + }, + "envState": { + "shape": "EnvState", + "documentation": "

Environment state chat message context.

" + }, + "appStudioContext": { + "shape": "AppStudioState", + "documentation": "

The state of a user's AppStudio UI when sending a message.

" + }, + "diagnostic": { + "shape": "Diagnostic", + "documentation": "

Diagnostic chat message context.

" + }, + "consoleState": { + "shape": "ConsoleState", + "documentation": "

Contextual information about the environment from which the user is calling.

" + }, + "userSettings": { + "shape": "UserSettings", + "documentation": "

Settings information, e.g., whether the user has enabled cross-region API calls.

" + }, + "additionalContext": { + "shape": "AdditionalContentList", + "documentation": "

List of additional contextual content entries that can be included with the message.

" + }, + "toolResults": { + "shape": "ToolResults", + "documentation": "

ToolResults for the requested ToolUses.

" + }, + "tools": { + "shape": "Tools", + "documentation": "

Tools that can be used.

" + } + }, + "documentation": "

Additional Chat message context associated with the Chat Message

" + }, + "UserIntent": { + "type": "string", + "documentation": "

User Intent

", + "enum": [ + "SUGGEST_ALTERNATE_IMPLEMENTATION", + "APPLY_COMMON_BEST_PRACTICES", + "IMPROVE_CODE", + "SHOW_EXAMPLES", + "CITE_SOURCES", + "EXPLAIN_LINE_BY_LINE", + "EXPLAIN_CODE_SELECTION", + "GENERATE_CLOUDFORMATION_TEMPLATE", + "GENERATE_UNIT_TESTS", + "CODE_GENERATION" + ] + }, + "UserModificationEvent": { + "type": "structure", + "required": [ + "sessionId", + "requestId", + "programmingLanguage", + "modificationPercentage", + "timestamp", + "acceptedCharacterCount", + "unmodifiedAcceptedCharacterCount" + ], + "members": { + "sessionId": { "shape": "UUID" }, + "requestId": { "shape": "UUID" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "modificationPercentage": { "shape": "Double" }, + "customizationArn": { "shape": "CustomizationArn" }, + "timestamp": { "shape": "Timestamp" }, + "acceptedCharacterCount": { "shape": "PrimitiveInteger" }, + "unmodifiedAcceptedCharacterCount": { "shape": "PrimitiveInteger" }, + "addedCharacterCount": { "shape": "UserModificationEventAddedCharacterCountInteger" }, + "unmodifiedAddedCharacterCount": { + "shape": "UserModificationEventUnmodifiedAddedCharacterCountInteger" + } + } + }, + "UserModificationEventAddedCharacterCountInteger": { + "type": "integer", + "min": 0 + }, + "UserModificationEventUnmodifiedAddedCharacterCountInteger": { + "type": "integer", + "min": 0 + }, + "UserSettings": { + "type": "structure", + "members": { + "hasConsentedToCrossRegionCalls": { "shape": "Boolean" } + }, + "documentation": "

Settings information passed by the Q widget

" + }, + "UserTriggerDecisionEvent": { + "type": "structure", + "required": [ + "sessionId", + "requestId", + "programmingLanguage", + "completionType", + "suggestionState", + "recommendationLatencyMilliseconds", + "timestamp" + ], + "members": { + "sessionId": { "shape": "UUID" }, + "requestId": { "shape": "UUID" }, + "customizationArn": { "shape": "CustomizationArn" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" }, + "completionType": { "shape": "CompletionType" }, + "suggestionState": { "shape": "SuggestionState" }, + "recommendationLatencyMilliseconds": { "shape": "Double" }, + "timestamp": { "shape": "Timestamp" }, + "triggerToResponseLatencyMilliseconds": { "shape": "Double" }, + "suggestionReferenceCount": { "shape": "PrimitiveInteger" }, + "generatedLine": { "shape": "PrimitiveInteger" }, + "numberOfRecommendations": { "shape": "PrimitiveInteger" }, + "perceivedLatencyMilliseconds": { "shape": "Double" }, + "acceptedCharacterCount": { "shape": "PrimitiveInteger" }, + "addedIdeDiagnostics": { "shape": "IdeDiagnosticList" }, + "removedIdeDiagnostics": { "shape": "IdeDiagnosticList" }, + "addedCharacterCount": { "shape": "UserTriggerDecisionEventAddedCharacterCountInteger" }, + "deletedCharacterCount": { "shape": "UserTriggerDecisionEventDeletedCharacterCountInteger" }, + "streakLength": { "shape": "UserTriggerDecisionEventStreakLengthInteger" }, + "suggestionType": { "shape": "SuggestionType" } + } + }, + "UserTriggerDecisionEventAddedCharacterCountInteger": { + "type": "integer", + "min": 0 + }, + "UserTriggerDecisionEventDeletedCharacterCountInteger": { + "type": "integer", + "min": 0 + }, + "UserTriggerDecisionEventStreakLengthInteger": { + "type": "integer", + "min": -1 + }, + "ValidationException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "reason": { "shape": "ValidationExceptionReason" } + }, + "documentation": "

This exception is thrown when the input fails to satisfy the constraints specified by the service.

", + "exception": true + }, + "ValidationExceptionReason": { + "type": "string", + "documentation": "

Reason for ValidationException

", + "enum": [ + "INVALID_CONVERSATION_ID", + "CONTENT_LENGTH_EXCEEDS_THRESHOLD", + "INVALID_KMS_GRANT", + "INVALID_MODEL_ID" + ] + }, + "WorkspaceContext": { + "type": "structure", + "required": ["toggle"], + "members": { + "toggle": { "shape": "OptInFeatureToggle" } + } + }, + "WorkspaceContextUploadContext": { + "type": "structure", + "required": ["workspaceId", "relativePath", "programmingLanguage"], + "members": { + "workspaceId": { "shape": "UUID" }, + "relativePath": { "shape": "SensitiveString" }, + "programmingLanguage": { "shape": "ProgrammingLanguage" } + } + }, + "WorkspaceFolderList": { + "type": "list", + "member": { "shape": "WorkspaceFolderListMemberString" }, + "max": 100, + "min": 0 + }, + "WorkspaceFolderListMemberString": { + "type": "string", + "max": 4096, + "min": 1, + "sensitive": true + }, + "WorkspaceList": { + "type": "list", + "member": { "shape": "WorkspaceMetadata" } + }, + "WorkspaceMetadata": { + "type": "structure", + "required": ["workspaceId", "workspaceStatus"], + "members": { + "workspaceId": { "shape": "UUID" }, + "workspaceStatus": { "shape": "WorkspaceStatus" }, + "environmentAddress": { "shape": "SensitiveString" }, + "environmentId": { "shape": "SensitiveString" } + } + }, + "WorkspaceState": { + "type": "structure", + "required": ["uploadId", "programmingLanguage"], + "members": { + "uploadId": { + "shape": "UploadId", + "documentation": "

Upload ID representing an Upload using a PreSigned URL

" + }, + "programmingLanguage": { + "shape": "ProgrammingLanguage", + "documentation": "

Primary programming language of the Workspace

" + }, + "contextTruncationScheme": { + "shape": "ContextTruncationScheme", + "documentation": "

Workspace context truncation schemes based on usecase

" + } + }, + "documentation": "

Represents a Workspace state uploaded to S3 for Async Code Actions

" + }, + "WorkspaceStatus": { + "type": "string", + "enum": ["CREATED", "PENDING", "READY", "CONNECTED", "DELETING"] + }, + "timeBetweenChunks": { + "type": "list", + "member": { "shape": "Double" }, + "max": 100, + "min": 0 + } + } +} diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts new file mode 100644 index 00000000000..ec1b5ae6e63 --- /dev/null +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -0,0 +1,855 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { CodewhispererCodeScanIssueApplyFix, Component, telemetry } from '../../shared/telemetry/telemetry' +import { ExtContext, VSCODE_EXTENSION_ID } from '../../shared/extensions' +import { Commands, VsCodeCommandArg, placeholder } from '../../shared/vscode/commands2' +import * as CodeWhispererConstants from '../models/constants' +import { DefaultCodeWhispererClient } from '../client/codewhisperer' +import { confirmStopSecurityScan, startSecurityScan } from './startSecurityScan' +import { SecurityPanelViewProvider } from '../views/securityPanelViewProvider' +import { + CodeScanIssue, + CodeScansState, + codeScanState, + CodeSuggestionsState, + onDemandFileScanState, + SecurityIssueFilters, + SecurityTreeViewFilterState, + severities, + vsCodeState, +} from '../models/model' +import { connectToEnterpriseSso, getStartUrl } from '../util/getStartUrl' +import { showCodeWhispererConnectionPrompt } from '../util/showSsoPrompt' +import { ReferenceLogViewProvider } from '../service/referenceLogViewProvider' +import { AuthUtil } from '../util/authUtil' +import { getLogger } from '../../shared/logger/logger' +import { isExtensionActive, isExtensionInstalled, localize, openUrl } from '../../shared/utilities/vsCodeUtils' +import { + getPersistedCustomizations, + notifyNewCustomizations, + selectCustomization, + showCustomizationPrompt, +} from '../util/customizationUtil' +import { + closeSecurityIssueWebview, + isSecurityIssueWebviewOpen, + showSecurityIssueWebview, + updateSecurityIssueWebview, +} from '../views/securityIssue/securityIssueWebview' +import { Mutable } from '../../shared/utilities/tsUtils' +import { CodeWhispererSource } from './types' +import { TelemetryHelper } from '../util/telemetryHelper' +import { Auth } from '../../auth/auth' +import { AwsConnection } from '../../auth/connection' +import { once } from '../../shared/utilities/functionUtils' +import { focusAmazonQPanel } from '../../codewhispererChat/commands/registerCommands' +import { removeDiagnostic } from '../service/diagnosticsProvider' +import { SsoAccessTokenProvider } from '../../auth/sso/ssoAccessTokenProvider' +import { ToolkitError, getTelemetryReason, getTelemetryReasonDesc } from '../../shared/errors' +import { isRemoteWorkspace } from '../../shared/vscode/env' +import { isBuilderIdConnection } from '../../auth/connection' +import globals from '../../shared/extensionGlobals' +import { getVscodeCliPath } from '../../shared/utilities/pathFind' +import { tryRun } from '../../shared/utilities/pathFind' +import { IssueItem, SecurityIssueTreeViewProvider } from '../service/securityIssueTreeViewProvider' +import { SecurityIssueProvider } from '../service/securityIssueProvider' +import { CodeWhispererSettings } from '../util/codewhispererSettings' +import { closeDiff, getPatchedCode } from '../../shared/utilities/diffUtils' +import { insertCommentAboveLine } from '../../shared/utilities/commentUtils' +import path from 'path' +import { UserWrittenCodeTracker } from '../tracker/userWrittenCodeTracker' +import { parsePatch } from 'diff' +import { createCodeIssueGroupingStrategyPrompter } from '../ui/prompters' +import { cancel, confirm } from '../../shared/localizedText' +import { showQuickPick } from '../../shared/ui/pickerPrompter' +import { i18n } from '../../shared/i18n-helper' + +const MessageTimeOut = 5_000 + +export const toggleCodeSuggestions = Commands.declare( + { id: 'aws.amazonq.toggleCodeSuggestion', compositeKey: { 1: 'source' } }, + (suggestionState: CodeSuggestionsState) => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { + await telemetry.aws_modifySetting.run(async (span) => { + span.record({ + settingId: CodeWhispererConstants.autoSuggestionConfig.settingId, + }) + + const isSuggestionsEnabled = await suggestionState.toggleSuggestions() + + span.record({ + settingState: isSuggestionsEnabled + ? CodeWhispererConstants.autoSuggestionConfig.activated + : CodeWhispererConstants.autoSuggestionConfig.deactivated, + }) + vsCodeState.isFreeTierLimitReached = false + + void vscode.window.setStatusBarMessage( + isSuggestionsEnabled + ? 'Amazon Q: Auto-Suggestions are currently running.' + : 'Amazon Q: Auto-Suggestions are currently paused.', + MessageTimeOut + ) + }) + } +) + +export const enableCodeSuggestions = Commands.declare( + 'aws.amazonq.enableCodeSuggestions', + (context: ExtContext) => + async (isAuto: boolean = true) => { + await CodeSuggestionsState.instance.setSuggestionsEnabled(isAuto) + vsCodeState.isFreeTierLimitReached = false + await vscode.commands.executeCommand('aws.amazonq.refreshStatusBar') + } +) + +export const toggleCodeScans = Commands.declare( + { id: 'aws.codeWhisperer.toggleCodeScan', compositeKey: { 1: 'source' } }, + (scansState: CodeScansState) => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { + await telemetry.aws_modifySetting.run(async (span) => { + if (isBuilderIdConnection(AuthUtil.instance.conn)) { + throw new Error(`Auto-scans are not supported with the Amazon Builder ID connection.`) + } + span.record({ + settingId: CodeWhispererConstants.autoScansConfig.settingId, + }) + + const isScansEnabled = await scansState.toggleScans() + span.record({ + settingState: isScansEnabled + ? CodeWhispererConstants.autoScansConfig.activated + : CodeWhispererConstants.autoScansConfig.deactivated, + }) + + await vscode.commands.executeCommand('aws.amazonq.refreshStatusBar') + void vscode.window.setStatusBarMessage( + isScansEnabled + ? 'Amazon Q: Auto-Scans are currently running.' + : 'Amazon Q: Auto-Scans are currently paused.', + MessageTimeOut + ) + }) + } +) + +export const showReferenceLog = Commands.declare( + { id: 'aws.amazonq.openReferencePanel', compositeKey: { 1: 'source' } }, + () => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { + if (_ !== placeholder) { + source = 'ellipsesMenu' + } + await vscode.commands.executeCommand(`${ReferenceLogViewProvider.viewType}.focus`) + } +) + +export const showLogs = Commands.declare( + { id: 'aws.amazonq.showLogs', compositeKey: { 1: 'source' } }, + () => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { + if (_ !== placeholder) { + source = 'ellipsesMenu' + } + + // Show warning message without buttons - just informational + void vscode.window.showWarningMessage( + 'Log files may contain sensitive information such as account IDs, resource names, and other data. Be careful when sharing these logs.' + ) + + // Get the log directory path + const logFolderPath = globals.context.logUri?.fsPath + const path = require('path') + const logFilePath = path.join(logFolderPath, 'Amazon Q Logs.log') + if (logFilePath) { + // Open the log directory in the OS file explorer directly + await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(logFilePath)) + } else { + // Fallback: show error if log path is not available + void vscode.window.showErrorMessage('Log location not available.') + } + } +) + +export const showIntroduction = Commands.declare('aws.amazonq.introduction', () => async () => { + void openUrl(vscode.Uri.parse(CodeWhispererConstants.learnMoreUriGeneral)) +}) + +export const showSecurityScan = Commands.declare( + { id: 'aws.amazonq.security.scan', compositeKey: { 1: 'source' } }, + (context: ExtContext, securityPanelViewProvider: SecurityPanelViewProvider, client: DefaultCodeWhispererClient) => + async (_: VsCodeCommandArg, source: CodeWhispererSource, initiatedByChat: boolean, scanUuid?: string) => { + if (AuthUtil.instance.isConnectionExpired()) { + await AuthUtil.instance.notifyReauthenticate() + } + if (codeScanState.isNotStarted()) { + // User intends to start as "Start Security Scan" is shown in the explorer tree + codeScanState.setToRunning() + void startSecurityScan( + securityPanelViewProvider, + undefined, + client, + context.extensionContext, + CodeWhispererConstants.CodeAnalysisScope.PROJECT, + initiatedByChat, + undefined, + scanUuid + ) + } else if (codeScanState.isRunning()) { + // User intends to stop as "Stop Security Scan" is shown in the explorer tree + // Cancel only when the code scan state is "Running" + await confirmStopSecurityScan( + codeScanState, + initiatedByChat, + CodeWhispererConstants.CodeAnalysisScope.PROJECT, + undefined + ) + } + vsCodeState.isFreeTierLimitReached = false + } +) + +export const showFileScan = Commands.declare( + { id: 'aws.amazonq.security.filescan', compositeKey: { 1: 'source' } }, + (context: ExtContext, securityPanelViewProvider: SecurityPanelViewProvider, client: DefaultCodeWhispererClient) => + async (_: VsCodeCommandArg, source: CodeWhispererSource, scanUuid?: string) => { + if (AuthUtil.instance.isConnectionExpired()) { + await AuthUtil.instance.notifyReauthenticate() + } + const editor = vscode.window.activeTextEditor + if (onDemandFileScanState.isNotStarted()) { + onDemandFileScanState.setToRunning() + void startSecurityScan( + securityPanelViewProvider, + editor, + client, + context.extensionContext, + CodeWhispererConstants.CodeAnalysisScope.FILE_ON_DEMAND, + true, + undefined, + scanUuid + ) + } else if (onDemandFileScanState.isRunning()) { + // TODO: Pending with progress bar implementation in the Q chat Panel + // User intends to stop the scan from Q chat panel. + // Cancel only when the file scan state is "Running" + await confirmStopSecurityScan( + onDemandFileScanState, + true, + CodeWhispererConstants.CodeAnalysisScope.FILE_ON_DEMAND, + editor?.document.fileName + ) + } + vsCodeState.isFreeTierLimitReached = false + } +) + +export const selectCustomizationPrompt = Commands.declare( + { id: 'aws.amazonq.selectCustomization', compositeKey: { 1: 'source' } }, + () => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { + if (isBuilderIdConnection(AuthUtil.instance.conn)) { + throw new Error(`Select Customizations are not supported with the Amazon Builder ID connection.`) + } + telemetry.ui_click.emit({ elementId: 'cw_selectCustomization_Cta' }) + void showCustomizationPrompt().then() + } +) + +export const selectRegionProfileCommand = Commands.declare( + { id: 'aws.amazonq.selectRegionProfile', compositeKey: { 1: 'source' } }, + () => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { + const quickPickItems = AuthUtil.instance.regionProfileManager.generateQuickPickItem() + + await showQuickPick(quickPickItems, { + title: localize('AWS.amazonq.profile.quickPick.title', 'Select a Profile'), + placeholder: localize( + 'AWS.amazonq.profile.quickPick.placeholder', + 'You can choose from the following profiles:' + ), + recentlyUsed: i18n('AWS.codewhisperer.customization.selected'), + }) + } +) + +export const reconnect = Commands.declare( + { id: 'aws.amazonq.reconnect', compositeKey: { 1: 'source' } }, + () => async (_: VsCodeCommandArg, source: CodeWhispererSource) => await AuthUtil.instance.reauthenticate() +) + +/** @deprecated in favor of the `Add Connection` page */ +export const showSsoSignIn = Commands.declare('aws.amazonq.sso', () => async () => { + telemetry.ui_click.emit({ elementId: 'cw_signUp_Cta' }) + await showCodeWhispererConnectionPrompt() +}) + +// Shortcut command to directly connect to Identity Center or prompt start URL entry +// It can optionally set a customization too based on given values to match on + +// This command is only declared and registered in Amazon Q if Q exists +export const connectWithCustomization = Commands.declare( + { id: 'aws.codeWhisperer.connect', compositeKey: { 0: 'source' } }, + /** + * This command supports the following arguments: + * @param source - an identifier for who used this command. This value is not explicitly used in the function, but is used elsewhere. + * startUrl and region. If both arguments are provided they will be used, otherwise + * the command prompts for them interactively. + * customizationArn: select customization by ARN. If provided, `customizationNamePrefix` is ignored. + * customizationNamePrefix: select customization by prefix, if `customizationArn` is `undefined`. + */ + () => + async ( + source: string, + startUrl?: string, + region?: string, + customizationArn?: string, + customizationNamePrefix?: string + ) => { + SsoAccessTokenProvider.authSource = source + if (startUrl && region) { + await connectToEnterpriseSso(startUrl, region) + } else { + await getStartUrl() + } + + // No customization match information given, exit early. + if (!customizationArn && !customizationNamePrefix) { + return + } + + let persistedCustomizations = getPersistedCustomizations() + + // Check if any customizations have already been persisted. + // If not, call `notifyNewCustomizations` to handle it then recheck. + if (persistedCustomizations.length === 0) { + await notifyNewCustomizations() + persistedCustomizations = getPersistedCustomizations() + } + + // If given an ARN, assume a specific customization is desired and find an entry that matches it. Ignores the prefix logic. + // Otherwise if only a prefix is given, find an entry that matches it. + // Backwards compatible with previous implementation. + const match = customizationArn + ? persistedCustomizations.find((c) => c.arn === customizationArn) + : persistedCustomizations.find((c) => c.name?.startsWith(customizationNamePrefix as string)) + + // If no match is found, nothing to do :) + if (!match) { + getLogger().error( + `No customization match found: arn=${customizationArn} prefix=${customizationNamePrefix}` + ) + return + } + // Since we selected based on a match, we'll reuse the persisted values. + await selectCustomization(match) + } +) + +export const showLearnMore = Commands.declare( + { id: 'aws.amazonq._learnMore', compositeKey: { 0: 'source' } }, + () => async (source: CodeWhispererSource) => { + telemetry.ui_click.emit({ elementId: 'cw_learnMore_Cta' }) + void openUrl(vscode.Uri.parse(CodeWhispererConstants.learnMoreUriGeneral)) + } +) + +// TODO: Use a different URI +export const showFreeTierLimit = Commands.declare( + { id: 'aws.amazonq.freeTierLimit', compositeKey: { 1: 'source' } }, + () => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { + void openUrl(vscode.Uri.parse(CodeWhispererConstants.learnMoreUri)) + } +) + +export const updateReferenceLog = Commands.declare( + { + id: 'aws.amazonq.updateReferenceLog', + logging: false, + }, + () => () => { + ReferenceLogViewProvider.instance.update() + } +) + +export const openSecurityIssuePanel = Commands.declare( + 'aws.amazonq.openSecurityIssuePanel', + (context: ExtContext) => async (issue: CodeScanIssue | IssueItem, filePath: string) => { + const targetIssue: CodeScanIssue = issue instanceof IssueItem ? issue.issue : issue + const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath + await showSecurityIssueWebview(context.extensionContext, targetIssue, targetFilePath) + telemetry.codewhisperer_codeScanIssueViewDetails.emit({ + findingId: targetIssue.findingId, + detectorId: targetIssue.detectorId, + ruleId: targetIssue.ruleId, + credentialStartUrl: AuthUtil.instance.startUrl, + autoDetected: targetIssue.autoDetected, + }) + TelemetryHelper.instance.sendCodeScanRemediationsEvent( + undefined, + 'CODESCAN_ISSUE_VIEW_DETAILS', + targetIssue.detectorId, + targetIssue.findingId, + targetIssue.ruleId, + undefined, + undefined, + undefined, + !!targetIssue.suggestedFixes.length + ) + } +) + +export const notifyNewCustomizationsCmd = Commands.declare( + { id: 'aws.amazonq.notifyNewCustomizations', logging: false }, + () => () => { + notifyNewCustomizations().catch((e) => { + getLogger().error('notifyNewCustomizations failed: %s', (e as Error).message) + }) + } +) + +function focusQAfterDelay() { + // this command won't work without a small delay after install + globals.clock.setTimeout(() => { + focusAmazonQPanel.execute(placeholder, 'startDelay').catch((e) => { + getLogger().error('focusAmazonQPanel failed: %s', e) + }) + }, 1000) +} + +/** + * Actually install Amazon Q. + * Sometimes reload VS Code window after installation is necessary + * to properly activate extension. In that case, VS Code will prompt user to reload. + */ +export const installAmazonQExtension = Commands.declare( + { id: 'aws.toolkit.installAmazonQExtension', logging: true }, + () => async () => { + if (isRemoteWorkspace()) { + /** + * due to a bug in https://github.com/microsoft/vscode/pull/206381/files#diff-efa08c29460835c0ffa740d751c34078033fd6cb6c7b031500fb31f524655de2R360 + * installExtension will fail on remote environments when the amazon q extension is already installed locally. + * Until thats fixed we need to manually install the amazon q extension using the cli + */ + const vscPath = await getVscodeCliPath() + if (!vscPath) { + throw new ToolkitError('installAmazonQ: Unable to find VSCode CLI path', { + code: 'CodeCLINotFound', + }) + } + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + cancellable: false, + }, + (progress, token) => { + progress.report({ message: 'Installing Amazon Q' }) + return tryRun(vscPath, ['--install-extension', VSCODE_EXTENSION_ID.amazonq]) + } + ) + return + } + await vscode.commands.executeCommand('workbench.extensions.installExtension', VSCODE_EXTENSION_ID.amazonq) + + // jump to Amazon Q extension view after install. + focusQAfterDelay() + } +) + +export const applySecurityFix = Commands.declare( + 'aws.amazonq.applySecurityFix', + () => async (issue: CodeScanIssue | IssueItem, filePath: string, source: Component) => { + const targetIssue: CodeScanIssue = issue instanceof IssueItem ? issue.issue : issue + const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath + const targetSource: Component = issue instanceof IssueItem ? 'tree' : source + const [suggestedFix] = targetIssue.suggestedFixes + if (!suggestedFix || !targetFilePath || !suggestedFix.code) { + return + } + + const applyFixTelemetryEntry: Mutable = { + detectorId: targetIssue.detectorId, + findingId: targetIssue.findingId, + ruleId: targetIssue.ruleId, + component: targetSource, + result: 'Succeeded', + credentialStartUrl: AuthUtil.instance.startUrl, + codeFixAction: 'applyFix', + autoDetected: targetIssue.autoDetected, + codewhispererCodeScanJobId: targetIssue.scanJobId, + } + let languageId = undefined + try { + UserWrittenCodeTracker.instance.onQStartsMakingEdits() + const document = await vscode.workspace.openTextDocument(targetFilePath) + languageId = document.languageId + const updatedContent = await getPatchedCode(targetFilePath, suggestedFix.code) + if (!updatedContent) { + void vscode.window.showErrorMessage(CodeWhispererConstants.codeFixAppliedFailedMessage) + throw Error('Failed to get updated content from applying diff patch') + } + + const edit = new vscode.WorkspaceEdit() + const diffs = parsePatch(suggestedFix.code) + for (const diff of diffs) { + for (const hunk of [...diff.hunks].reverse()) { + const startLine = document.lineAt(hunk.oldStart - 1) + const endLine = document.lineAt(hunk.oldStart - 1 + hunk.oldLines - 1) + const range = new vscode.Range(startLine.range.start, endLine.range.end) + + const newText = updatedContent + .split('\n') + .slice(hunk.newStart - 1, hunk.newStart - 1 + hunk.newLines) + .join('\n') + + edit.replace(document.uri, range, newText) + } + } + const isApplied = await vscode.workspace.applyEdit(edit) + if (isApplied) { + void document.save().then((didSave) => { + if (!didSave) { + getLogger().error('Apply fix command failed to save the document.') + } + }) + } else { + throw Error('Failed to apply edit to the workspace.') + } + // add accepted references to reference log, if any + const fileName = path.basename(targetFilePath) + const time = new Date().toLocaleString() + // TODO: this is duplicated in controller.ts for test. Fix this later. + if (suggestedFix.references) { + for (const reference of suggestedFix.references) { + getLogger().debug('Processing reference: %O', reference) + // Log values for debugging + getLogger().debug('suggested fix code: %s', suggestedFix.code) + getLogger().debug('updated content: %s', updatedContent) + getLogger().debug( + 'start: %d, end: %d', + reference.recommendationContentSpan?.start, + reference.recommendationContentSpan?.end + ) + // given a start and end index, figure out which line number they belong to when splitting a string on /n characters + const getLineNumber = (content: string, index: number): number => { + const lines = content.slice(0, index).split('\n') + return lines.length + } + const startLine = getLineNumber(updatedContent, reference.recommendationContentSpan!.start!) + const endLine = getLineNumber(updatedContent, reference.recommendationContentSpan!.end!) + getLogger().debug('startLine: %d, endLine: %d', startLine, endLine) + const code = updatedContent.slice( + reference.recommendationContentSpan?.start, + reference.recommendationContentSpan?.end + ) + getLogger().debug('Extracted code slice: %s', code) + const referenceLog = + `[${time}] Accepted recommendation ` + + CodeWhispererConstants.referenceLogText( + `
${code}
`, + reference.licenseName!, + reference.repository!, + fileName, + startLine === endLine ? `(line at ${startLine})` : `(lines from ${startLine} to ${endLine})` + ) + + '
' + getLogger().debug('Adding reference log: %s', referenceLog) + ReferenceLogViewProvider.instance.addReferenceLog(referenceLog) + } + } + + removeDiagnostic(document.uri, targetIssue) + SecurityIssueProvider.instance.removeIssue(document.uri, targetIssue) + SecurityIssueTreeViewProvider.instance.refresh() + + await closeSecurityIssueWebview(targetIssue.findingId) + await closeDiff(targetFilePath) + await vscode.window.showTextDocument(document, { viewColumn: vscode.ViewColumn.One }) + const linesLength = suggestedFix.code.split('\n').length + const charsLength = suggestedFix.code.length + if (targetIssue.fixJobId) { + TelemetryHelper.instance.sendCodeFixAcceptanceEvent( + targetIssue.fixJobId, + languageId, + targetIssue.ruleId, + targetIssue.detectorId, + linesLength, + charsLength + ) + } + } catch (err) { + getLogger().error(`Apply fix command failed. ${err}`) + applyFixTelemetryEntry.result = 'Failed' + applyFixTelemetryEntry.reason = getTelemetryReason(err) + applyFixTelemetryEntry.reasonDesc = getTelemetryReasonDesc(err) + } finally { + telemetry.codewhisperer_codeScanIssueApplyFix.emit(applyFixTelemetryEntry) + TelemetryHelper.instance.sendCodeScanRemediationsEvent( + languageId, + 'CODESCAN_ISSUE_APPLY_FIX', + targetIssue.detectorId, + targetIssue.findingId, + targetIssue.ruleId, + source, + applyFixTelemetryEntry.reasonDesc, + applyFixTelemetryEntry.result, + !!targetIssue.suggestedFixes.length + ) + UserWrittenCodeTracker.instance.onQFinishesEdits() + } + } +) + +export const signoutCodeWhisperer = Commands.declare( + { id: 'aws.amazonq.signout', compositeKey: { 1: 'source' } }, + (auth: AuthUtil) => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { + await auth.secondaryAuth.deleteConnection() + SecurityIssueTreeViewProvider.instance.refresh() + return focusAmazonQPanel.execute(placeholder, source).catch((e) => { + getLogger().error('focusAmazonQPanel failed: %s', e) + return undefined + }) + } +) + +let _toolkitApi: any = undefined + +const registerToolkitApiCallbackOnce = once(() => { + getLogger().info(`toolkitApi: Registering callbacks of toolkit api`) + const auth = Auth.instance + + auth.onDidChangeConnectionState(async (e) => { + if (_toolkitApi && 'declareConnection' in _toolkitApi) { + const id = e.id + const conn = await auth.getConnection({ id }) + if (conn?.type === 'sso') { + getLogger().info(`toolkitApi: declare connection ${id}`) + _toolkitApi.declareConnection( + { + ssoRegion: conn.ssoRegion, + startUrl: conn.startUrl, + }, + 'Amazon Q' + ) + } + } + }) + auth.onDidDeleteConnection(async (event) => { + if (_toolkitApi && 'undeclareConnection' in _toolkitApi && event.storedProfile?.type === 'sso') { + const startUrl = event.storedProfile.startUrl + getLogger().info(`toolkitApi: undeclare connection ${event.connId} with starturl: ${startUrl}`) + _toolkitApi.undeclareConnection({ startUrl }) + } + }) +}) + +export const registerToolkitApiCallback = Commands.declare( + { id: 'aws.amazonq.refreshConnectionCallback' }, + () => async (toolkitApi?: any) => { + // Early return if already registered to avoid duplicate work + if (_toolkitApi) { + getLogger().debug('Toolkit API callback already registered, skipping') + return + } + + // While the Q/CW exposes an API for the Toolkit to register callbacks on auth changes, + // we need to do it manually here because the Toolkit would have been unable to call + // this API if the Q/CW extension started afterwards (and this code block is running). + if (isExtensionInstalled(VSCODE_EXTENSION_ID.awstoolkit)) { + getLogger().info(`Trying to register toolkit callback. Toolkit is installed, + toolkit activated = ${isExtensionActive(VSCODE_EXTENSION_ID.awstoolkit)}`) + if (toolkitApi) { + // when this command is executed by AWS Toolkit activation + _toolkitApi = toolkitApi.getApi(VSCODE_EXTENSION_ID.amazonq) + } else if (isExtensionActive(VSCODE_EXTENSION_ID.awstoolkit)) { + // when this command is executed by Amazon Q activation + const toolkitExt = vscode.extensions.getExtension(VSCODE_EXTENSION_ID.awstoolkit) + _toolkitApi = toolkitExt?.exports?.getApi(VSCODE_EXTENSION_ID.amazonq) + } + if (_toolkitApi) { + registerToolkitApiCallbackOnce() + // Declare current conn immediately + const currentConn = AuthUtil.instance.conn + if (currentConn?.type === 'sso') { + _toolkitApi.declareConnection( + { + type: currentConn.type, + ssoRegion: currentConn.ssoRegion, + startUrl: currentConn.startUrl, + id: currentConn.id, + } as AwsConnection, + 'Amazon Q' + ) + } + } + } + } +) + +export const clearFilters = Commands.declare( + { id: 'aws.amazonq.securityIssuesTreeFilter.clearFilters' }, + () => async () => { + await SecurityTreeViewFilterState.instance.resetFilters() + } +) + +export const generateFix = Commands.declare( + { id: 'aws.amazonq.security.generateFix' }, + (client: DefaultCodeWhispererClient, context: ExtContext) => + async ( + issueItem: IssueItem, + filePath: string, + source: Component, + refresh: boolean = false, + shouldOpenSecurityIssuePanel: boolean = true + ) => { + await vscode.commands.executeCommand('aws.amazonq.generateFix', issueItem.issue, issueItem.filePath) + } +) + +export const rejectFix = Commands.declare( + { id: 'aws.amazonq.security.rejectFix' }, + (context: vscode.ExtensionContext) => async (issue: CodeScanIssue | IssueItem | undefined, filePath: string) => { + const targetIssue: CodeScanIssue | undefined = issue instanceof IssueItem ? issue.issue : issue + const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath + if (!targetIssue) { + return + } + const updatedIssue: CodeScanIssue = { ...targetIssue, suggestedFixes: [] } + await updateSecurityIssueWebview({ + issue: updatedIssue, + context, + filePath: targetFilePath, + shouldRefreshView: false, + }) + + SecurityIssueProvider.instance.updateIssue(updatedIssue, targetFilePath) + SecurityIssueTreeViewProvider.instance.refresh() + await closeDiff(targetFilePath) + + return updatedIssue + } +) + +export const regenerateFix = Commands.declare( + { id: 'aws.amazonq.security.regenerateFix' }, + () => async (issue: CodeScanIssue | IssueItem | undefined, filePath: string, source: Component) => {} +) + +export const explainIssue = Commands.declare( + { id: 'aws.amazonq.security.explain' }, + () => async (issueItem: IssueItem) => { + await vscode.commands.executeCommand('aws.amazonq.explainIssue', issueItem.issue, issueItem.filePath) + } +) + +export const ignoreAllIssues = Commands.declare( + { id: 'aws.amazonq.security.ignoreAll' }, + () => async (issue: CodeScanIssue | IssueItem, source: Component) => { + const targetIssue: CodeScanIssue = issue instanceof IssueItem ? issue.issue : issue + const targetSource: Component = issue instanceof IssueItem ? 'tree' : source + const resp = await vscode.window.showWarningMessage( + CodeWhispererConstants.ignoreAllIssuesMessage(targetIssue.title), + confirm, + cancel + ) + if (resp === confirm) { + await telemetry.codewhisperer_codeScanIssueIgnore.run(async () => { + const ignoredIssues = CodeWhispererSettings.instance.getIgnoredSecurityIssues() + if (!ignoredIssues.includes(targetIssue.title)) { + await CodeWhispererSettings.instance.addToIgnoredSecurityIssuesList(targetIssue.title) + } + await closeSecurityIssueWebview(targetIssue.findingId) + + telemetry.record({ + component: targetSource, + credentialStartUrl: AuthUtil.instance.startUrl, + detectorId: targetIssue.detectorId, + findingId: targetIssue.findingId, + ruleId: targetIssue.ruleId, + variant: 'all', + autoDetected: targetIssue.autoDetected, + }) + }) + } + } +) + +export const ignoreIssue = Commands.declare( + { id: 'aws.amazonq.security.ignore' }, + () => async (issue: CodeScanIssue | IssueItem, filePath: string, source: Component) => { + await telemetry.codewhisperer_codeScanIssueIgnore.run(async () => { + const targetIssue: CodeScanIssue = issue instanceof IssueItem ? issue.issue : issue + const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath + const targetSource: Component = issue instanceof IssueItem ? 'tree' : source + const document = await vscode.workspace.openTextDocument(targetFilePath) + + const documentIsVisible = vscode.window.visibleTextEditors.some((editor) => editor.document === document) + if (!documentIsVisible) { + await vscode.window.showTextDocument(document, { + selection: new vscode.Range(targetIssue.startLine, 0, targetIssue.endLine, 0), + preserveFocus: true, + preview: true, + viewColumn: vscode.ViewColumn.One, + }) + } + insertCommentAboveLine(document, targetIssue.startLine, CodeWhispererConstants.amazonqIgnoreNextLine) + await closeSecurityIssueWebview(targetIssue.findingId) + + telemetry.record({ + component: targetSource, + credentialStartUrl: AuthUtil.instance.startUrl, + detectorId: targetIssue.detectorId, + findingId: targetIssue.findingId, + ruleId: targetIssue.ruleId, + autoDetected: targetIssue.autoDetected, + }) + }) + } +) + +export const showSecurityIssueFilters = Commands.declare({ id: 'aws.amazonq.security.showFilters' }, () => async () => { + const filterState = SecurityTreeViewFilterState.instance.getState() + const quickPickItems: vscode.QuickPickItem[] = severities.map((severity) => ({ + label: severity, + picked: filterState.severity[severity], + })) + const result = await vscode.window.showQuickPick(quickPickItems, { + title: localize('aws.commands.amazonq.filterIssues', 'Filter Issues'), + placeHolder: localize('aws.amazonq.security.showFilters.placeholder', 'Select code issues to show'), + canPickMany: true, + }) + if (result) { + await SecurityTreeViewFilterState.instance.setState({ + ...filterState, + severity: severities.reduce( + (p, c) => ({ ...p, [c]: result.map(({ label }) => label).includes(c) }), + {} + ) as SecurityIssueFilters['severity'], + }) + } +}) + +export const showCodeIssueGroupingQuickPick = Commands.declare( + { id: 'aws.amazonq.codescan.showGroupingStrategy' }, + () => async () => { + const prompter = createCodeIssueGroupingStrategyPrompter() + await prompter.prompt() + } +) + +export const focusIssue = Commands.declare( + { id: 'aws.amazonq.security.focusIssue' }, + () => async (issue: CodeScanIssue, filePath: string) => { + const document = await vscode.workspace.openTextDocument(filePath) + void vscode.window.showTextDocument(document, { + selection: new vscode.Range(issue.startLine, 0, issue.endLine, 0), + preserveFocus: true, + preview: true, + viewColumn: vscode.ViewColumn.One, + }) + + if (isSecurityIssueWebviewOpen()) { + void vscode.commands.executeCommand('aws.amazonq.openSecurityIssuePanel', issue, filePath) + } + } +) diff --git a/packages/core/src/codewhisperer/commands/gettingStartedPageCommands.ts b/packages/core/src/codewhisperer/commands/gettingStartedPageCommands.ts new file mode 100644 index 00000000000..ab7c8e00afc --- /dev/null +++ b/packages/core/src/codewhisperer/commands/gettingStartedPageCommands.ts @@ -0,0 +1,42 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { CommandDeclarations, Commands, VsCodeCommandArg } from '../../shared/vscode/commands2' +import { showCodeWhispererWebview } from '../vue/backend' +import { telemetry } from '../../shared/telemetry/telemetry' +import { AmazonQPromptSettings } from '../../shared/settings' +import { CodeWhispererSource } from './types' +/** + * The methods with backend logic for the Codewhisperer Getting Started Page commands. + */ +export class CodeWhispererCommandBackend { + constructor(private readonly extContext: vscode.ExtensionContext) {} + public async showGettingStartedPage(_: VsCodeCommandArg, source: CodeWhispererSource) { + if (_ !== undefined) { + source = 'vscodeComponent' + } + + const prompts = AmazonQPromptSettings.instance + // To check the condition If the user has already seen the welcome message + if (!prompts.isPromptEnabled('codeWhispererNewWelcomeMessage')) { + telemetry.ui_click.emit({ elementId: 'codewhisperer_Learn_ButtonClick', passive: true }) + } + return showCodeWhispererWebview(this.extContext, source) + } +} +/** + * Declared commands related to CodeWhisperer in the toolkit. + */ +export class CodeWhispererCommandDeclarations implements CommandDeclarations { + static #instance: CodeWhispererCommandDeclarations + + static get instance(): CodeWhispererCommandDeclarations { + return (this.#instance ??= new CodeWhispererCommandDeclarations()) + } + public readonly declared = { + showGettingStartedPage: + Commands.from(CodeWhispererCommandBackend).declareShowGettingStartedPage('aws.amazonq.gettingStarted'), + } as const +} diff --git a/packages/core/src/codewhisperer/commands/invokeRecommendation.ts b/packages/core/src/codewhisperer/commands/invokeRecommendation.ts new file mode 100644 index 00000000000..37fcb965774 --- /dev/null +++ b/packages/core/src/codewhisperer/commands/invokeRecommendation.ts @@ -0,0 +1,45 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { vsCodeState, ConfigurationEntry } from '../models/model' +import { resetIntelliSenseState } from '../util/globalStateUtil' +import { DefaultCodeWhispererClient } from '../client/codewhisperer' +import { RecommendationHandler } from '../service/recommendationHandler' +import { session } from '../util/codeWhispererSession' +import { RecommendationService } from '../service/recommendationService' + +/** + * This function is for manual trigger CodeWhisperer + */ + +export async function invokeRecommendation( + editor: vscode.TextEditor, + client: DefaultCodeWhispererClient, + config: ConfigurationEntry +) { + if (!editor || !config.isManualTriggerEnabled) { + return + } + + /** + * Skip when output channel gains focus and invoke + */ + if (editor.document.languageId === 'Log') { + return + } + /** + * When using intelliSense, if invocation position changed, reject previous active recommendations + */ + if (vsCodeState.isIntelliSenseActive && editor.selection.active !== session.startPos) { + resetIntelliSenseState( + config.isManualTriggerEnabled, + config.isAutomatedTriggerEnabled, + RecommendationHandler.instance.isValidResponse() + ) + } + + await RecommendationService.instance.generateRecommendation(client, editor, 'OnDemand', config, undefined) +} diff --git a/packages/core/src/codewhisperer/commands/onAcceptance.ts b/packages/core/src/codewhisperer/commands/onAcceptance.ts new file mode 100644 index 00000000000..e13c197cefd --- /dev/null +++ b/packages/core/src/codewhisperer/commands/onAcceptance.ts @@ -0,0 +1,85 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { vsCodeState, OnRecommendationAcceptanceEntry } from '../models/model' +import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { CodeWhispererTracker } from '../tracker/codewhispererTracker' +import { CodeWhispererCodeCoverageTracker } from '../tracker/codewhispererCodeCoverageTracker' +import { getLogger } from '../../shared/logger/logger' +import { handleExtraBrackets } from '../util/closingBracketUtil' +import { RecommendationHandler } from '../service/recommendationHandler' +import { ReferenceLogViewProvider } from '../service/referenceLogViewProvider' +import { ReferenceHoverProvider } from '../service/referenceHoverProvider' +import path from 'path' + +/** + * This function is called when user accepts a intelliSense suggestion or an inline suggestion + */ +export async function onAcceptance(acceptanceEntry: OnRecommendationAcceptanceEntry) { + RecommendationHandler.instance.cancelPaginatedRequest() + /** + * Format document + */ + if (acceptanceEntry.editor) { + const languageContext = runtimeLanguageContext.getLanguageContext( + acceptanceEntry.editor.document.languageId, + path.extname(acceptanceEntry.editor.document.fileName) + ) + const start = acceptanceEntry.range.start + const end = acceptanceEntry.range.end + + // codewhisperer will be doing editing while formatting. + // formatting should not trigger consoals auto trigger + vsCodeState.isCodeWhispererEditing = true + /** + * Mitigation to right context handling mainly for auto closing bracket use case + */ + try { + await handleExtraBrackets(acceptanceEntry.editor, end, start) + } catch (error) { + getLogger().error(`${error} in handleAutoClosingBrackets`) + } + // move cursor to end of suggestion before doing code format + // after formatting, the end position will still be editor.selection.active + acceptanceEntry.editor.selection = new vscode.Selection(end, end) + + vsCodeState.isCodeWhispererEditing = false + CodeWhispererTracker.getTracker().enqueue({ + time: new Date(), + fileUrl: acceptanceEntry.editor.document.uri, + originalString: acceptanceEntry.editor.document.getText(new vscode.Range(start, end)), + startPosition: start, + endPosition: end, + requestId: acceptanceEntry.requestId, + sessionId: acceptanceEntry.sessionId, + index: acceptanceEntry.acceptIndex, + triggerType: acceptanceEntry.triggerType, + completionType: acceptanceEntry.completionType, + language: languageContext.language, + }) + const insertedCoderange = new vscode.Range(start, end) + CodeWhispererCodeCoverageTracker.getTracker(languageContext.language)?.countAcceptedTokens( + insertedCoderange, + acceptanceEntry.editor.document.getText(insertedCoderange), + acceptanceEntry.editor.document.fileName + ) + if (acceptanceEntry.references !== undefined) { + const referenceLog = ReferenceLogViewProvider.getReferenceLog( + acceptanceEntry.recommendation, + acceptanceEntry.references, + acceptanceEntry.editor + ) + ReferenceLogViewProvider.instance.addReferenceLog(referenceLog) + ReferenceHoverProvider.instance.addCodeReferences( + acceptanceEntry.recommendation, + acceptanceEntry.references + ) + } + } + + // at the end of recommendation acceptance, report user decisions and clear recommendations. + RecommendationHandler.instance.reportUserDecisions(acceptanceEntry.acceptIndex) +} diff --git a/packages/core/src/codewhisperer/commands/onInlineAcceptance.ts b/packages/core/src/codewhisperer/commands/onInlineAcceptance.ts new file mode 100644 index 00000000000..d193af056f7 --- /dev/null +++ b/packages/core/src/codewhisperer/commands/onInlineAcceptance.ts @@ -0,0 +1,145 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as CodeWhispererConstants from '../models/constants' +import { vsCodeState, OnRecommendationAcceptanceEntry } from '../models/model' +import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { CodeWhispererTracker } from '../tracker/codewhispererTracker' +import { CodeWhispererCodeCoverageTracker } from '../tracker/codewhispererCodeCoverageTracker' +import { getLogger } from '../../shared/logger/logger' +import { RecommendationHandler } from '../service/recommendationHandler' +import { sleep } from '../../shared/utilities/timeoutUtils' +import { handleExtraBrackets } from '../util/closingBracketUtil' +import { Commands } from '../../shared/vscode/commands2' +import { isInlineCompletionEnabled } from '../util/commonUtil' +import { onAcceptance } from './onAcceptance' +import * as codewhispererClient from '../client/codewhisperer' +import { + CodewhispererCompletionType, + CodewhispererLanguage, + CodewhispererTriggerType, +} from '../../shared/telemetry/telemetry.gen' +import { ReferenceLogViewProvider } from '../service/referenceLogViewProvider' +import { ReferenceHoverProvider } from '../service/referenceHoverProvider' +import { ImportAdderProvider } from '../service/importAdderProvider' +import { session } from '../util/codeWhispererSession' +import path from 'path' +import { RecommendationService } from '../service/recommendationService' +import { Container } from '../service/serviceContainer' +import { telemetry } from '../../shared/telemetry/telemetry' +import { TelemetryHelper } from '../util/telemetryHelper' +import { UserWrittenCodeTracker } from '../tracker/userWrittenCodeTracker' + +export const acceptSuggestion = Commands.declare( + 'aws.amazonq.accept', + (context: vscode.ExtensionContext) => + async ( + range: vscode.Range, + effectiveRange: vscode.Range, + acceptIndex: number, + recommendation: string, + requestId: string, + sessionId: string, + triggerType: CodewhispererTriggerType, + completionType: CodewhispererCompletionType, + language: CodewhispererLanguage, + references: codewhispererClient.References + ) => { + telemetry.record({ + traceId: TelemetryHelper.instance.traceId, + }) + + RecommendationService.instance.incrementAcceptedCount() + const editor = vscode.window.activeTextEditor + await Container.instance.lineAnnotationController.refresh(editor, 'codewhisperer') + const onAcceptanceFunc = isInlineCompletionEnabled() ? onInlineAcceptance : onAcceptance + await onAcceptanceFunc({ + editor, + range, + effectiveRange, + acceptIndex, + recommendation, + requestId, + sessionId, + triggerType, + completionType, + language, + references, + }) + } +) +/** + * This function is called when user accepts a intelliSense suggestion or an inline suggestion + */ +export async function onInlineAcceptance(acceptanceEntry: OnRecommendationAcceptanceEntry) { + RecommendationHandler.instance.cancelPaginatedRequest() + RecommendationHandler.instance.disposeInlineCompletion() + + if (acceptanceEntry.editor) { + await sleep(CodeWhispererConstants.vsCodeCursorUpdateDelay) + const languageContext = runtimeLanguageContext.getLanguageContext( + acceptanceEntry.editor.document.languageId, + path.extname(acceptanceEntry.editor.document.fileName) + ) + const start = acceptanceEntry.range.start + const end = acceptanceEntry.editor.selection.active + + vsCodeState.isCodeWhispererEditing = true + /** + * Mitigation to right context handling mainly for auto closing bracket use case + */ + try { + // Do not handle extra bracket if there is a right context merge + if (acceptanceEntry.recommendation === session.recommendations[acceptanceEntry.acceptIndex].content) { + await handleExtraBrackets(acceptanceEntry.editor, end, acceptanceEntry.effectiveRange.start) + } + await ImportAdderProvider.instance.onAcceptRecommendation( + acceptanceEntry.editor, + session.recommendations[acceptanceEntry.acceptIndex], + start.line + ) + } catch (error) { + getLogger().error(`${error} in handling extra brackets or imports`) + } finally { + vsCodeState.isCodeWhispererEditing = false + } + + CodeWhispererTracker.getTracker().enqueue({ + time: new Date(), + fileUrl: acceptanceEntry.editor.document.uri, + originalString: acceptanceEntry.editor.document.getText(new vscode.Range(start, end)), + startPosition: start, + endPosition: end, + requestId: acceptanceEntry.requestId, + sessionId: acceptanceEntry.sessionId, + index: acceptanceEntry.acceptIndex, + triggerType: acceptanceEntry.triggerType, + completionType: acceptanceEntry.completionType, + language: languageContext.language, + }) + const insertedCoderange = new vscode.Range(start, end) + CodeWhispererCodeCoverageTracker.getTracker(languageContext.language)?.countAcceptedTokens( + insertedCoderange, + acceptanceEntry.editor.document.getText(insertedCoderange), + acceptanceEntry.editor.document.fileName + ) + UserWrittenCodeTracker.instance.onQFinishesEdits() + if (acceptanceEntry.references !== undefined) { + const referenceLog = ReferenceLogViewProvider.getReferenceLog( + acceptanceEntry.recommendation, + acceptanceEntry.references, + acceptanceEntry.editor + ) + ReferenceLogViewProvider.instance.addReferenceLog(referenceLog) + ReferenceHoverProvider.instance.addCodeReferences( + acceptanceEntry.recommendation, + acceptanceEntry.references + ) + } + + RecommendationHandler.instance.reportUserDecisions(acceptanceEntry.acceptIndex) + } +} diff --git a/packages/core/src/codewhisperer/commands/startCodeFixGeneration.ts b/packages/core/src/codewhisperer/commands/startCodeFixGeneration.ts new file mode 100644 index 00000000000..f0118eef46a --- /dev/null +++ b/packages/core/src/codewhisperer/commands/startCodeFixGeneration.ts @@ -0,0 +1,131 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { fs } from '../../shared/fs/fs' +import { getLogger } from '../../shared/logger/logger' +import { + createCodeFixJob, + getCodeFixJob, + getPresignedUrlAndUpload, + pollCodeFixJobStatus, + throwIfCancelled, +} from '../service/codeFixHandler' +import { ArtifactMap, DefaultCodeWhispererClient } from '../client/codewhisperer' +import { codeFixState, CodeScanIssue } from '../models/model' +import { CreateCodeFixError } from '../models/errors' +import AdmZip from 'adm-zip' +import path from 'path' +import { TelemetryHelper } from '../util/telemetryHelper' +import { tempDirPath } from '../../shared/filesystemUtilities' +import { CodeWhispererSettings } from '../util/codewhispererSettings' +import { AuthUtil } from '../util/authUtil' +import { saveDocumentIfDirty } from '../../shared/utilities/textDocumentUtilities' + +export async function startCodeFixGeneration( + client: DefaultCodeWhispererClient, + issue: CodeScanIssue, + filePath: string, + codeFixName: string +) { + const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile + /** + * Step 0: Initial code fix telemetry + */ + // TODO: Telemetry + let jobId + let linesOfFixGenerated + let charsOfFixGenerated + try { + getLogger().verbose( + `Starting code fix generation for lines ${issue.startLine + 1} through ${issue.endLine} of file ${filePath}` + ) + + /** + * Step 1: Generate zip + */ + throwIfCancelled() + + // Save the file if it has unsaved changes to ensure the latest content is included in the zip + await saveDocumentIfDirty(filePath) + const admZip = new AdmZip() + admZip.addLocalFile(filePath) + + const zipFilePath = path.join(tempDirPath, 'codefix.zip') + admZip.writeZip(zipFilePath) + + /** + * Step 2: Get presigned Url, upload and clean up + */ + let artifactMap: ArtifactMap = {} + try { + artifactMap = await getPresignedUrlAndUpload(client, zipFilePath, codeFixName, profile) + } finally { + await fs.delete(zipFilePath) + } + + /** + * Step 3: Create code fix job + */ + throwIfCancelled() + const codeFixJob = await createCodeFixJob( + client, + artifactMap.SourceCode, + { + start: { line: issue.startLine + 1, character: 0 }, + end: { line: issue.endLine, character: 0 }, + }, + issue.recommendation.text, + { + recommendationsWithReferences: CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled() + ? 'ALLOW' + : 'BLOCK', + }, + codeFixName, + issue.ruleId, + profile + ) + if (codeFixJob.status === 'Failed') { + throw new CreateCodeFixError() + } + jobId = codeFixJob.jobId + issue.fixJobId = codeFixJob.jobId + getLogger().verbose(`Created code fix job.`) + + /** + * Step 4: Polling mechanism on code fix job status + */ + throwIfCancelled() + const jobStatus = await pollCodeFixJobStatus(client, String(codeFixJob.jobId), profile) + if (jobStatus === 'Failed') { + getLogger().verbose(`Code fix generation failed.`) + throw new CreateCodeFixError() + } + + /** + * Step 5: Process and render code fix results + */ + throwIfCancelled() + getLogger().verbose(`Code fix job succeeded and start processing result.`) + + const { suggestedFix } = await getCodeFixJob(client, String(codeFixJob.jobId), profile) + // eslint-disable-next-line aws-toolkits/no-json-stringify-in-log + getLogger().verbose(`Suggested fix: ${JSON.stringify(suggestedFix)}`) + return { suggestedFix, jobId } + } catch (err) { + getLogger().error('Code fix generation failed: %s', err) + throw err + } finally { + codeFixState.setToNotStarted() + if (jobId) { + TelemetryHelper.instance.sendCodeFixGenerationEvent( + jobId, + issue.language, + issue.ruleId, + issue.detectorId, + linesOfFixGenerated, + charsOfFixGenerated + ) + } + } +} diff --git a/packages/core/src/codewhisperer/commands/startSecurityScan.ts b/packages/core/src/codewhisperer/commands/startSecurityScan.ts new file mode 100644 index 00000000000..ba9f4f9d926 --- /dev/null +++ b/packages/core/src/codewhisperer/commands/startSecurityScan.ts @@ -0,0 +1,553 @@ +/*! + * 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 { ArtifactMap, DefaultCodeWhispererClient } from '../client/codewhisperer' +import { initSecurityScanRender } from '../service/diagnosticsProvider' +import { SecurityPanelViewProvider } from '../views/securityPanelViewProvider' +import { getLogger } from '../../shared/logger/logger' +import { makeLogger } from '../../shared/logger/activation' +import * as CodeWhispererConstants from '../models/constants' +import { + getPresignedUrlAndUpload, + createScanJob, + pollScanJobStatus, + listScanResults, + throwIfCancelled, + getLoggerForScope, +} from '../service/securityScanHandler' +import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { + AggregatedCodeScanIssue, + CodeScansState, + CodeScanState, + codeScanState, + CodeScanStoppedError, + CodeScanTelemetryEntry, + onDemandFileScanState, + OnDemandFileScanState, +} from '../models/model' +import { cancel, ok } from '../../shared/localizedText' +import { telemetry } from '../../shared/telemetry/telemetry' +import { ToolkitError, getTelemetryReasonDesc, isAwsError } from '../../shared/errors' +import { AuthUtil } from '../util/authUtil' +import path from 'path' +import { ZipMetadata, ZipUtil } from '../util/zipUtil' +import { debounce } from 'lodash' +import { once } from '../../shared/utilities/functionUtils' +import { randomUUID } from '../../shared/crypto' +import { CodeAnalysisScope, ProjectSizeExceededErrorMessage, SecurityScanStep } from '../models/constants' +import { + CodeScanJobFailedError, + CreateCodeScanFailedError, + MaximumFileScanReachedError, + MaximumProjectScanReachedError, + SecurityScanError, +} from '../models/errors' +import { SecurityIssueTreeViewProvider } from '../service/securityIssueTreeViewProvider' +import { ChatSessionManager } from '../../amazonqScan/chat/storages/chatSession' +import { TelemetryHelper } from '../util/telemetryHelper' + +const localize = nls.loadMessageBundle() +export const stopScanButton = localize('aws.codewhisperer.stopscan', 'Stop Scan') + +/** + * Creates the vscode OutputChannel and Toolkit logger used by Security Scan, exactly once. + * + * To avoid cluttering the Output channels list, do not call this until it's actually needed. + * + * @returns Logger and OutputChannel created on the first invocation. + */ +const getLogOutputChan = once(() => { + const codeScanOutpuChan = vscode.window.createOutputChannel('Amazon Q Security Scan Logs') + const codeScanLogger = makeLogger({ + logLevel: 'info', + outputChannels: [codeScanOutpuChan], + }) + return [codeScanLogger, codeScanOutpuChan] as const +}) + +export function startSecurityScanWithProgress( + securityPanelViewProvider: SecurityPanelViewProvider, + editor: vscode.TextEditor | undefined, + client: DefaultCodeWhispererClient, + context: vscode.ExtensionContext, + scope: CodeWhispererConstants.CodeAnalysisScope, + initiatedByChat: boolean +) { + return vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: + scope === CodeWhispererConstants.CodeAnalysisScope.PROJECT + ? CodeWhispererConstants.runningSecurityScan + : CodeWhispererConstants.runningFileScan, + cancellable: false, + }, + async () => { + await startSecurityScan(securityPanelViewProvider, editor, client, context, scope, initiatedByChat) + } + ) +} + +export const debounceStartSecurityScan = debounce( + startSecurityScan, + CodeWhispererConstants.autoScanDebounceDelaySeconds * 1000 +) + +export async function startSecurityScan( + securityPanelViewProvider: SecurityPanelViewProvider, + editor: vscode.TextEditor | undefined, + client: DefaultCodeWhispererClient, + context: vscode.ExtensionContext, + scope: CodeWhispererConstants.CodeAnalysisScope, + initiatedByChat: boolean, + zipUtil: ZipUtil = new ZipUtil(), + scanUuid?: string +) { + if (scope === CodeAnalysisScope.AGENTIC) { + throw new CreateCodeScanFailedError('Cannot use Agentic scope') + } + const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile + const logger = getLoggerForScope(scope) + /** + * Step 0: Initial Code Scan telemetry + */ + const codeScanStartTime = performance.now() + if (scope === CodeAnalysisScope.FILE_AUTO) { + CodeScansState.instance.setLatestScanTime(codeScanStartTime) + } + let serviceInvocationStartTime = 0 + const codeScanTelemetryEntry: CodeScanTelemetryEntry = { + codewhispererLanguage: editor + ? runtimeLanguageContext.getLanguageContext( + editor.document.languageId, + path.extname(editor.document.fileName) + ).language + : 'plaintext', + codewhispererCodeScanSrcPayloadBytes: 0, + codewhispererCodeScanSrcZipFileBytes: 0, + codewhispererCodeScanLines: 0, + duration: 0, + contextTruncationDuration: 0, + artifactsUploadDuration: 0, + codeScanServiceInvocationsDuration: 0, + result: 'Succeeded', + codewhispererCodeScanTotalIssues: 0, + codewhispererCodeScanIssuesWithFixes: 0, + credentialStartUrl: AuthUtil.instance.startUrl, + codewhispererCodeScanScope: scope, + source: initiatedByChat ? 'chat' : 'menu', + } + const fileName = editor?.document.fileName + const scanState = scope === CodeAnalysisScope.PROJECT ? codeScanState : onDemandFileScanState + try { + logger.verbose(`Starting security scan `) + /** + * Step 1: Generate zip + */ + throwIfCancelled(scope, codeScanStartTime) + if (initiatedByChat) { + scanState.getChatControllers()?.scanProgress.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + step: SecurityScanStep.GENERATE_ZIP, + scope, + fileName, + scanUuid, + }) + } + const zipMetadata = await zipUtil.generateZip(editor?.document.uri, scope) + const projectPaths = zipUtil.getProjectPaths() + + const contextTruncationStartTime = performance.now() + codeScanTelemetryEntry.contextTruncationDuration = performance.now() - contextTruncationStartTime + logger.verbose(`Complete project context processing.`) + codeScanTelemetryEntry.codewhispererCodeScanSrcPayloadBytes = zipMetadata.srcPayloadSizeInBytes + codeScanTelemetryEntry.codewhispererCodeScanBuildPayloadBytes = zipMetadata.buildPayloadSizeInBytes + codeScanTelemetryEntry.codewhispererCodeScanSrcZipFileBytes = zipMetadata.zipFileSizeInBytes + codeScanTelemetryEntry.codewhispererCodeScanLines = zipMetadata.lines + if (zipMetadata.language) { + codeScanTelemetryEntry.codewhispererLanguage = zipMetadata.language + } + + /** + * Step 2: Get presigned Url, upload and clean up + */ + + throwIfCancelled(scope, codeScanStartTime) + if (initiatedByChat) { + scanState.getChatControllers()?.scanProgress.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + step: SecurityScanStep.UPLOAD_TO_S3, + scope, + fileName, + scanUuid, + }) + } + let artifactMap: ArtifactMap = {} + const uploadStartTime = performance.now() + const scanName = randomUUID() + try { + artifactMap = await getPresignedUrlAndUpload(client, zipMetadata, scope, scanName, profile) + } finally { + await zipUtil.removeTmpFiles(zipMetadata, scope) + codeScanTelemetryEntry.artifactsUploadDuration = performance.now() - uploadStartTime + } + + /** + * Step 3: Create scan job + */ + throwIfCancelled(scope, codeScanStartTime) + if (initiatedByChat) { + scanState.getChatControllers()?.scanProgress.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + step: SecurityScanStep.CREATE_SCAN_JOB, + scope, + fileName, + scanUuid, + }) + } + serviceInvocationStartTime = performance.now() + const scanJob = await createScanJob( + client, + artifactMap, + codeScanTelemetryEntry.codewhispererLanguage, + scope, + scanName, + profile + ) + if (scanJob.status === 'Failed') { + logger.verbose(`${scanJob.errorMessage}`) + const errorMessage = scanJob.errorMessage ?? 'CreateCodeScanFailed' + throw new CreateCodeScanFailedError(errorMessage) + } + logger.verbose(`Created security scan job.`) + codeScanTelemetryEntry.codewhispererCodeScanJobId = scanJob.jobId + + /** + * Step 4: Polling mechanism on scan job status + */ + throwIfCancelled(scope, codeScanStartTime) + if (initiatedByChat) { + scanState.getChatControllers()?.scanProgress.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + step: SecurityScanStep.POLL_SCAN_STATUS, + scope, + fileName, + scanUuid, + }) + } + // pass profile + const jobStatus = await pollScanJobStatus(client, scanJob.jobId, scope, codeScanStartTime, profile) + if (jobStatus === 'Failed') { + logger.verbose(`Security scan failed.`) + throw new CodeScanJobFailedError() + } + + /** + * Step 5: Process and render scan results + */ + throwIfCancelled(scope, codeScanStartTime) + if (initiatedByChat) { + scanState.getChatControllers()?.scanProgress.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + step: SecurityScanStep.PROCESS_SCAN_RESULTS, + scope, + fileName, + scanUuid, + }) + } + logger.verbose(`Security scan job succeeded and start processing result.`) + const securityRecommendationCollection = await listScanResults( + client, + scanJob.jobId, + CodeWhispererConstants.codeScanFindingsSchema, + projectPaths, + scope, + editor, + profile + ) + for (const issue of securityRecommendationCollection + .flatMap(({ issues }) => issues) + .filter(({ visible, autoDetected }) => visible && !autoDetected)) { + telemetry.codewhisperer_codeScanIssueDetected.emit({ + autoDetected: issue.autoDetected, + codewhispererCodeScanJobId: issue.scanJobId, + detectorId: issue.detectorId, + findingId: issue.findingId, + includesFix: issue.suggestedFixes.length > 0, + ruleId: issue.ruleId, + result: 'Succeeded', + }) + } + const { total, withFixes } = securityRecommendationCollection.reduce( + (accumulator, current) => ({ + total: accumulator.total + current.issues.length, + withFixes: accumulator.withFixes + current.issues.filter((i) => i.suggestedFixes.length > 0).length, + }), + { total: 0, withFixes: 0 } + ) + codeScanTelemetryEntry.codewhispererCodeScanTotalIssues = total + codeScanTelemetryEntry.codewhispererCodeScanIssuesWithFixes = withFixes + throwIfCancelled(scope, codeScanStartTime) + logger.verbose(`Security scan totally found ${total} issues. ${withFixes} of them have fixes.`) + /** + * initiatedByChat is true for PROJECT and FILE_ON_DEMAND scopes, + * initiatedByChat is false for PROJECT and FILE_AUTO scopes + */ + if (initiatedByChat) { + showScanResultsInChat( + securityPanelViewProvider, + securityRecommendationCollection, + editor, + context, + scope, + zipMetadata, + total, + scanUuid + ) + } else { + showSecurityScanResults(securityRecommendationCollection, editor, context, scope, zipMetadata, total) + } + TelemetryHelper.instance.sendCodeScanSucceededEvent( + codeScanTelemetryEntry.codewhispererLanguage, + scanJob.jobId, + total, + scope + ) + + logger.verbose(`Security scan completed.`) + } catch (error) { + getLogger().error('Security scan failed. %O', error) + if (error instanceof CodeScanStoppedError) { + codeScanState.getChatControllers()?.scanCancelled.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + scanUuid, + }) + codeScanTelemetryEntry.result = 'Cancelled' + } else if (isAwsError(error) && error.code === 'ThrottlingException') { + codeScanTelemetryEntry.result = 'Failed' + if ( + scope === CodeAnalysisScope.PROJECT && + error.message.includes(CodeWhispererConstants.scansLimitReachedErrorMessage) + ) { + const maximumProjectScanReachedError = new MaximumProjectScanReachedError() + getLogger().error(maximumProjectScanReachedError.customerFacingMessage) + errorPromptHelper(maximumProjectScanReachedError, scope, initiatedByChat, fileName, scanUuid) + // TODO: Should we set a graphical state? + // We shouldn't set vsCodeState.isFreeTierLimitReached here because it will hide CW and Q chat options. + } else if (scope === CodeAnalysisScope.PROJECT) { + getLogger().error(error.message) + errorPromptHelper( + new SecurityScanError( + error.code, + (error as any).statusCode?.toString() ?? '', + 'Too many requests, please wait before trying again.' + ), + scope, + initiatedByChat, + fileName, + scanUuid + ) + } else { + const maximumFileScanReachedError = new MaximumFileScanReachedError() + getLogger().error(maximumFileScanReachedError.customerFacingMessage) + errorPromptHelper(maximumFileScanReachedError, scope, initiatedByChat, fileName, scanUuid) + CodeScansState.instance.setMonthlyQuotaExceeded() + } + } else { + codeScanTelemetryEntry.result = 'Failed' + errorPromptHelper( + new SecurityScanError( + (error as any).code ?? 'unknown error', + (error as any).statusCode?.toString() ?? '', + 'Encountered an unexpected error when processing the request, please try again' + ), + scope, + initiatedByChat, + fileName + ) + } + codeScanTelemetryEntry.reasonDesc = + (error as ToolkitError)?.code === 'ContentLengthError' + ? 'Payload size limit reached' + : getTelemetryReasonDesc(error) + codeScanTelemetryEntry.reason = (error as ToolkitError)?.code ?? 'DefaultError' + if (codeScanTelemetryEntry.codewhispererCodeScanJobId) { + TelemetryHelper.instance.sendCodeScanFailedEvent( + codeScanTelemetryEntry.codewhispererLanguage, + codeScanTelemetryEntry.codewhispererCodeScanJobId, + scope + ) + } + } finally { + const scanState = scope === CodeAnalysisScope.PROJECT ? codeScanState : onDemandFileScanState + scanState.setToNotStarted() + scanState.getChatControllers()?.scanStopped.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + scanUuid, + }) + codeScanTelemetryEntry.duration = performance.now() - codeScanStartTime + codeScanTelemetryEntry.codeScanServiceInvocationsDuration = performance.now() - serviceInvocationStartTime + await emitCodeScanTelemetry(codeScanTelemetryEntry) + } +} + +export function showSecurityScanResults( + securityRecommendationCollection: AggregatedCodeScanIssue[], + editor: vscode.TextEditor | undefined, + context: vscode.ExtensionContext, + scope: CodeWhispererConstants.CodeAnalysisScope, + zipMetadata: ZipMetadata, + totalIssues: number +) { + initSecurityScanRender(securityRecommendationCollection, editor, scope) + + if (scope === CodeWhispererConstants.CodeAnalysisScope.PROJECT) { + populateCodeScanLogStream(zipMetadata.scannedFiles) + } +} + +export function showScanResultsInChat( + securityPanelViewProvider: SecurityPanelViewProvider, + securityRecommendationCollection: AggregatedCodeScanIssue[], + editor: vscode.TextEditor | undefined, + context: vscode.ExtensionContext, + scope: CodeWhispererConstants.CodeAnalysisScope, + zipMetadata: ZipMetadata, + totalIssues: number, + scanUuid: string | undefined +) { + const tabID = ChatSessionManager.Instance.getSession().tabID + const eventData = { + message: 'Show Findings in the Chat panel', + totalIssues, + securityRecommendationCollection, + fileName: scope === CodeAnalysisScope.FILE_ON_DEMAND ? [...zipMetadata.scannedFiles][0] : undefined, + tabID, + scope, + scanUuid, + } + switch (scope) { + case CodeAnalysisScope.PROJECT: + codeScanState.getChatControllers()?.showSecurityScan.fire(eventData) + break + case CodeAnalysisScope.FILE_ON_DEMAND: + onDemandFileScanState.getChatControllers()?.showSecurityScan.fire(eventData) + break + default: + break + } + + initSecurityScanRender(securityRecommendationCollection, editor, scope) + if (totalIssues > 0) { + SecurityIssueTreeViewProvider.focus() + } + + populateCodeScanLogStream(zipMetadata.scannedFiles) + if (scope === CodeAnalysisScope.PROJECT) { + showScanCompletedNotification(totalIssues, zipMetadata.scannedFiles) + } +} + +export async function emitCodeScanTelemetry(codeScanTelemetryEntry: CodeScanTelemetryEntry) { + codeScanTelemetryEntry.codewhispererCodeScanProjectBytes = 0 + telemetry.codewhisperer_securityScan.emit({ + ...codeScanTelemetryEntry, + passive: codeScanTelemetryEntry.codewhispererCodeScanScope === CodeAnalysisScope.FILE_AUTO, + }) +} + +export function errorPromptHelper( + error: SecurityScanError, + scope: CodeAnalysisScope, + initiatedByChat: boolean, + fileName?: string, + scanUuid?: string +) { + if (scope === CodeAnalysisScope.FILE_AUTO) { + return + } + if (initiatedByChat) { + const state = scope === CodeAnalysisScope.PROJECT ? codeScanState : onDemandFileScanState + state.getChatControllers()?.errorThrown.fire({ + error, + tabID: ChatSessionManager.Instance.getSession().tabID, + scope, + fileName, + scanUuid, + }) + } + if (error.code !== 'NoSourceFilesError') { + // Skip showing warning messages during tests to avoid interfering with test dialogs + if (process.env.NODE_ENV !== 'test') { + void vscode.window.showWarningMessage(getErrorMessage(error), ok) + } + } +} + +function getErrorMessage(error: any): string { + switch (error.code) { + case 'ContentLengthError': + return ProjectSizeExceededErrorMessage + case 'MaximumProjectScanReachedError': + case 'MaximumFileScanReachedError': + return CodeWhispererConstants.monthlyLimitReachedNotification + default: + return error.customerFacingMessage + } +} + +function populateCodeScanLogStream(scannedFiles: Set) { + const [codeScanLogger, codeScanOutpuChan] = getLogOutputChan() + const numScannedFiles = scannedFiles.size + // Clear log + codeScanOutpuChan.clear() + if (numScannedFiles === 1) { + codeScanLogger.info(`${numScannedFiles} file was scanned during the last Security Scan.`) + } else { + codeScanLogger.info(`${numScannedFiles} files were scanned during the last Security Scan.`) + } + // Log all the scanned files to codeScanLogger + for (const file of scannedFiles) { + const uri = vscode.Uri.file(file) + codeScanLogger.info(`File scanned: ${uri.fsPath}`) + } +} + +export async function confirmStopSecurityScan( + state: CodeScanState | OnDemandFileScanState, + initiatedByChat: boolean, + scope: CodeWhispererConstants.CodeAnalysisScope, + fileName: string | undefined, + scanUuid?: string +) { + // Confirm if user wants to stop security scan + const resp = await vscode.window.showWarningMessage(CodeWhispererConstants.stopScanMessage, stopScanButton, cancel) + if (resp === stopScanButton && state.isRunning()) { + getLogger().verbose('User requested to stop security scan. Stopping security scan.') + state.setToCancelling() + if (initiatedByChat) { + const scanState = scope === CodeAnalysisScope.PROJECT ? codeScanState : onDemandFileScanState + const scopeText = scope === CodeAnalysisScope.PROJECT ? 'Project' : 'File' + scanState.getChatControllers()?.errorThrown.fire({ + error: scopeText + CodeWhispererConstants.stopScanMessageInChat, + tabID: ChatSessionManager.Instance.getSession().tabID, + scope, + fileName, + }) + } + } +} + +function showScanCompletedNotification(total: number, scannedFiles: Set) { + const items = [CodeWhispererConstants.showScannedFilesMessage] + void vscode.window.showInformationMessage(`Code Review Completed`, ...items).then((value) => { + if (total > 0 && value === CodeWhispererConstants.showScannedFilesMessage) { + SecurityIssueTreeViewProvider.focus() + } + }) +} diff --git a/packages/core/src/codewhisperer/commands/startTransformByQ.ts b/packages/core/src/codewhisperer/commands/startTransformByQ.ts new file mode 100644 index 00000000000..410465a55c1 --- /dev/null +++ b/packages/core/src/codewhisperer/commands/startTransformByQ.ts @@ -0,0 +1,867 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as fs from 'fs' // eslint-disable-line no-restricted-imports +import path from 'path' +import { getLogger } from '../../shared/logger/logger' +import * as CodeWhispererConstants from '../models/constants' +import * as localizedText from '../../shared/localizedText' +import { + transformByQState, + StepProgress, + JDKVersion, + jobPlanProgress, + FolderInfo, + ZipManifest, + TransformationType, + TransformationCandidateProject, + RegionProfile, + sessionJobHistory, +} from '../models/model' +import { + createZipManifest, + downloadAndExtractResultArchive, + downloadHilResultArchive, + findDownloadArtifactStep, + getArtifactsFromProgressUpdate, + getTransformationSteps, + pollTransformationJob, + resumeTransformationJob, + startJob, + stopJob, + throwIfCancelled, + updateJobHistory, + uploadPayload, + zipCode, +} from '../service/transformByQ/transformApiHandler' +import { + getJavaProjects, + getOpenProjects, + validateOpenProjects, +} from '../service/transformByQ/transformProjectValidationHandler' +import { + prepareProjectDependencies, + runMavenDependencyUpdateCommands, +} from '../service/transformByQ/transformMavenHandler' +import { telemetry } from '../../shared/telemetry/telemetry' +import { CodeTransformTelemetryState } from '../../amazonqGumby/telemetry/codeTransformTelemetryState' +import { calculateTotalLatency } from '../../amazonqGumby/telemetry/codeTransformTelemetry' +import { MetadataResult } from '../../shared/telemetry/telemetryClient' +import { submitFeedback } from '../../feedback/vue/submitFeedback' +import { placeholder } from '../../shared/vscode/commands2' +import { + AlternateDependencyVersionsNotFoundError, + JobStartError, + ModuleUploadError, + PollJobError, + TransformationPreBuildError, +} from '../../amazonqGumby/errors' +import { ChatSessionManager } from '../../amazonqGumby/chat/storages/chatSession' +import { + getCodeIssueSnippetFromPom, + getDependenciesFolderInfo, + getJsonValuesFromManifestFile, + highlightPomIssueInProject, + parseVersionsListFromPomFile, + writeAndShowBuildLogs, +} from '../service/transformByQ/transformFileHandler' +import { sleep } from '../../shared/utilities/timeoutUtils' +import DependencyVersions from '../../amazonqGumby/models/dependencies' +import { dependencyNoAvailableVersions } from '../../amazonqGumby/models/constants' +import { HumanInTheLoopManager } from '../service/transformByQ/humanInTheLoopManager' +import { setContext } from '../../shared/vscode/setContext' +import globals from '../../shared/extensionGlobals' +import { convertDateToTimestamp } from '../../shared/datetime' +import { findStringInDirectory } from '../../shared/utilities/workspaceUtils' +import { makeTemporaryToolkitFolder } from '../../shared/filesystemUtilities' +import { AuthUtil } from '../util/authUtil' +import { + cleanupTempJobFiles, + createMetadataFile, + JobMetadata, + writeToHistoryFile, +} from '../service/transformByQ/transformationHistoryHandler' + +export function getFeedbackCommentData() { + const jobId = transformByQState.getJobId() + const s = `Q CodeTransformation jobId: ${jobId ? jobId : 'none'}` + return s +} + +export async function processLanguageUpgradeTransformFormInput( + pathToProject: string, + fromJDKVersion: JDKVersion, + toJDKVersion: JDKVersion +) { + transformByQState.setTransformationType(TransformationType.LANGUAGE_UPGRADE) + transformByQState.setProjectName(path.basename(pathToProject)) + transformByQState.setProjectPath(pathToProject) + transformByQState.setSourceJDKVersion(fromJDKVersion) + transformByQState.setTargetJDKVersion(toJDKVersion) +} + +export async function processSQLConversionTransformFormInput(pathToProject: string, schema: string) { + transformByQState.setTransformationType(TransformationType.SQL_CONVERSION) + transformByQState.setProjectName(path.basename(pathToProject)) + transformByQState.setProjectPath(pathToProject) + transformByQState.setSchema(schema) + // use dummy values of JDK8 & JDK17 so that startJob API can be called + transformByQState.setSourceJDKVersion(JDKVersion.JDK8) + transformByQState.setTargetJDKVersion(JDKVersion.JDK17) +} + +export async function compileProject() { + try { + const dependenciesFolder: FolderInfo = await getDependenciesFolderInfo() + transformByQState.setDependencyFolderInfo(dependenciesFolder) + const projectPath = transformByQState.getProjectPath() + await prepareProjectDependencies(dependenciesFolder.path, projectPath) + } catch (err) { + // open build-logs.txt file to show user error logs + await writeAndShowBuildLogs(true) + throw err + } +} + +export function startInterval() { + const intervalId = setInterval(() => { + void vscode.commands.executeCommand( + 'aws.amazonq.showPlanProgressInHub', + CodeTransformTelemetryState.instance.getStartTime() + ) + updateJobHistory() + }, CodeWhispererConstants.transformationJobPollingIntervalSeconds * 1000) + transformByQState.setIntervalId(intervalId) +} + +export async function startTransformByQ() { + // Set the default state variables for our store and the UI + const transformStartTime = globals.clock.Date.now() + await setTransformationToRunningState() + + try { + const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile + // Set webview UI to poll for progress + startInterval() + + // step 1: CreateUploadUrl and upload code + const uploadId = await preTransformationUploadCode() + + // step 2: StartJob and store the returned jobId in TransformByQState + const jobId = await startTransformationJob(uploadId, transformStartTime, profile) + + // step 3 (intermediate step): show transformation-plan.md file + await pollTransformationStatusUntilPlanReady(jobId) + + // step 4: poll until artifacts are ready to download + await humanInTheLoopRetryLogic(jobId, profile) + } catch (error: any) { + await transformationJobErrorHandler(error) + } finally { + await postTransformationJob() + await cleanupTransformationJob() + } +} + +/** + * The whileLoop condition WaitingUserInput is set inside pollTransformationStatusUntilComplete + * when we see a `PAUSED` state. If this is the case once completeHumanInTheLoopWork the + * WaitingUserInput should still be set until pollTransformationStatusUntilComplete is called again. + * We only don't want to continue calling pollTransformationStatusUntilComplete if there is no HIL + * state ever engaged or we have reached our max amount of HIL retries. + */ +export async function humanInTheLoopRetryLogic(jobId: string, profile: RegionProfile | undefined) { + let status = '' + try { + status = await pollTransformationStatusUntilComplete(jobId, profile) + if (status === 'PAUSED') { + const hilStatusFailure = await initiateHumanInTheLoopPrompt(jobId) + if (hilStatusFailure) { + // resume polling + void humanInTheLoopRetryLogic(jobId, profile) + } + } else { + await finalizeTransformByQ(status) + } + } catch (error) { + status = 'FAILED' + await finalizeTransformByQ(status) + throw error + } +} + +export async function finalizeTransformByQ(status: string) { + try { + // Set the result state variables for our store and the UI + await finalizeTransformationJob(status) + } catch (error: any) { + await transformationJobErrorHandler(error) + } +} + +export async function preTransformationUploadCode() { + await vscode.commands.executeCommand('aws.amazonq.transformationHub.focus') + + void vscode.window.showInformationMessage(CodeWhispererConstants.jobStartedNotification, { + title: localizedText.ok, + }) + + let uploadId = '' + throwIfCancelled() + try { + await telemetry.codeTransform_uploadProject.run(async () => { + telemetry.record({ codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId() }) + + const transformZipManifest = new ZipManifest() + // if the user chose to skip unit tests, add the custom build command here + transformZipManifest.customBuildCommand = transformByQState.getCustomBuildCommand() + const zipCodeResult = await zipCode({ + // dependenciesFolder will be undefined for SQL conversions since we don't compileProject + dependenciesFolder: transformByQState.getDependencyFolderInfo(), + projectPath: transformByQState.getProjectPath(), + zipManifest: transformZipManifest, + }) + + const payloadFilePath = zipCodeResult.tempFilePath + const zipSize = zipCodeResult.fileSize + + telemetry.record({ + codeTransformTotalByteSize: zipSize, + }) + + transformByQState.setPayloadFilePath(payloadFilePath) + uploadId = await uploadPayload(payloadFilePath, AuthUtil.instance.regionProfileManager.activeRegionProfile) + telemetry.record({ codeTransformJobId: uploadId }) // uploadId is re-used as jobId + }) + } catch (err) { + const errorMessage = (err as Error).message + transformByQState.setJobFailureErrorNotification( + `${CodeWhispererConstants.failedToUploadProjectNotification} ${errorMessage}` + ) + transformByQState.setJobFailureErrorChatMessage( + `${CodeWhispererConstants.failedToUploadProjectChatMessage} ${errorMessage}` + ) + + transformByQState.getChatControllers()?.errorThrown.fire({ + error: new ModuleUploadError(), + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + getLogger().error(errorMessage) + throw err + } + + throwIfCancelled() + await sleep(2000) // sleep before starting job to prevent ThrottlingException + + return uploadId +} + +export async function initiateHumanInTheLoopPrompt(jobId: string) { + try { + const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile + const humanInTheLoopManager = HumanInTheLoopManager.instance + // 1) We need to call GetTransformationPlan to get artifactId + const transformationSteps = await getTransformationSteps(jobId, profile) + const { transformationStep, progressUpdate } = findDownloadArtifactStep(transformationSteps) + + if (!transformationStep || !progressUpdate) { + throw new Error('Transformation step or progress update is undefined') + } + + const { artifactId, artifactType } = getArtifactsFromProgressUpdate(progressUpdate) + + // Early exit safeguard incase artifactId or artifactType are undefined + if (!artifactId || !artifactType) { + throw new Error('artifactId or artifactType is undefined') + } + + // 2) We need to call DownloadResultArchive to get the manifest and pom.xml + const { pomFileVirtualFileReference, manifestFileVirtualFileReference } = await downloadHilResultArchive( + jobId, + artifactId, + humanInTheLoopManager.getTmpDownloadsDir() + ) + humanInTheLoopManager.setPomFileVirtualFileReference(pomFileVirtualFileReference) + const manifestFileValues = await getJsonValuesFromManifestFile(manifestFileVirtualFileReference) + humanInTheLoopManager.setManifestFileValues(manifestFileValues) + + // 3) We need to replace version in pom.xml + const newPomFileVirtualFileReference = await humanInTheLoopManager.createPomFileCopy( + humanInTheLoopManager.getTmpDependencyListDir(), + pomFileVirtualFileReference + ) + humanInTheLoopManager.setNewPomFileVirtualFileReference(newPomFileVirtualFileReference) + await humanInTheLoopManager.replacePomFileVersion( + newPomFileVirtualFileReference, + manifestFileValues.sourcePomVersion + ) + + const codeSnippet = await getCodeIssueSnippetFromPom(newPomFileVirtualFileReference) + // Let the user know we've entered the loop in the chat + transformByQState.getChatControllers()?.humanInTheLoopStartIntervention.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + codeSnippet, + }) + + // 4) We need to run maven commands on that pom.xml to get available versions + const compileFolderInfo = humanInTheLoopManager.getCompileDependencyListFolderInfo() + runMavenDependencyUpdateCommands(compileFolderInfo) + const xmlString = await humanInTheLoopManager.getDependencyListXmlOutput() + const { latestVersion, majorVersions, minorVersions, status } = await parseVersionsListFromPomFile(xmlString) + + if (status === dependencyNoAvailableVersions) { + // let user know and early exit for human in the loop happened because no upgrade versions available + const error = new AlternateDependencyVersionsNotFoundError() + + transformByQState.getChatControllers()?.errorThrown.fire({ + error, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + + throw error + } + + const dependencies = new DependencyVersions( + latestVersion, + majorVersions, + minorVersions, + manifestFileValues.sourcePomVersion + ) + + // 5) We need to wait for user input + // This is asynchronous, so we have to wait to be called to complete this loop + transformByQState.getChatControllers()?.humanInTheLoopPromptUserForDependency.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + dependencies, + }) + } catch (err: any) { + try { + // Regardless of the error, + // Continue transformation flow + await terminateHILEarly(jobId) + } finally { + transformByQState.getChatControllers()?.errorThrown.fire({ + error: err, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + } + CodeTransformTelemetryState.instance.setCodeTransformMetaDataField({ + errorMessage: err.message, + }) + await HumanInTheLoopManager.instance.cleanUpArtifacts() + return true + } finally { + await sleep(1000) + telemetry.codeTransform_humanInTheLoop.emit({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + codeTransformJobId: jobId, + codeTransformMetadata: CodeTransformTelemetryState.instance.getCodeTransformMetaDataString(), + result: MetadataResult.Fail, + // TODO: make a generic reason field for telemetry logging so we don't log sensitive PII data + reason: 'Runtime error occurred', + }) + } + return false +} + +export async function openHilPomFile() { + const humanInTheLoopManager = HumanInTheLoopManager.instance + await highlightPomIssueInProject( + humanInTheLoopManager.getNewPomFileVirtualFileReference(), + HumanInTheLoopManager.instance.diagnosticCollection, + humanInTheLoopManager.getManifestFileValues().sourcePomVersion + ) +} + +export async function terminateHILEarly(jobID: string) { + // Call resume with "REJECTED" state which will put our service + // back into the normal flow and will not trigger HIL again for this step + await resumeTransformationJob(jobID, 'REJECTED') +} + +export async function finishHumanInTheLoop(selectedDependency?: string) { + let successfulFeedbackLoop = true + const jobId = transformByQState.getJobId() + let hilResult: MetadataResult = MetadataResult.Pass + const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile + try { + if (!selectedDependency) { + throw new Error('No dependency selected') + } + const humanInTheLoopManager = HumanInTheLoopManager.instance + const manifestFileValues = humanInTheLoopManager.getManifestFileValues() + const getUserInputValue = selectedDependency + + CodeTransformTelemetryState.instance.setCodeTransformMetaDataField({ + dependencyVersionSelected: selectedDependency, + }) + // 6) We need to add user input to that pom.xml, + // original pom.xml is intact somewhere, and run maven compile + const userInputPomFileVirtualFileReference = await humanInTheLoopManager.createPomFileCopy( + humanInTheLoopManager.getUserDependencyUpdateDir(), + humanInTheLoopManager.getPomFileVirtualFileReference() + ) + await humanInTheLoopManager.replacePomFileVersion(userInputPomFileVirtualFileReference, getUserInputValue) + + // 7) We need to take that output of maven and use CreateUploadUrl + const uploadFolderInfo = humanInTheLoopManager.getUploadFolderInfo() + await prepareProjectDependencies(uploadFolderInfo.path, uploadFolderInfo.path) + // zipCode side effects deletes the uploadFolderInfo right away + const uploadResult = await zipCode({ + dependenciesFolder: uploadFolderInfo, + zipManifest: createZipManifest({ + hilZipParams: { + pomGroupId: manifestFileValues.pomGroupId, + pomArtifactId: manifestFileValues.pomArtifactId, + targetPomVersion: getUserInputValue, + }, + }), + }) + + await uploadPayload(uploadResult.tempFilePath, profile, { + transformationUploadContext: { + jobId, + uploadArtifactType: 'Dependencies', + }, + }) + + // inform user in chat + transformByQState.getChatControllers()?.humanInTheLoopSelectionUploaded.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + + // 8) Once code has been uploaded we will restart the job + await resumeTransformationJob(jobId, 'COMPLETED') + + void humanInTheLoopRetryLogic(jobId, profile) + } catch (err: any) { + successfulFeedbackLoop = false + CodeTransformTelemetryState.instance.setCodeTransformMetaDataField({ + errorMessage: err.message, + }) + hilResult = MetadataResult.Fail + + // If anything went wrong in HIL state, we should restart the job + // with the rejected state + await terminateHILEarly(jobId) + void humanInTheLoopRetryLogic(jobId, profile) + } finally { + telemetry.codeTransform_humanInTheLoop.emit({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + codeTransformJobId: jobId, + codeTransformMetadata: CodeTransformTelemetryState.instance.getCodeTransformMetaDataString(), + result: hilResult, + reason: hilResult === MetadataResult.Fail ? 'Runtime error occurred' : undefined, + }) + await HumanInTheLoopManager.instance.cleanUpArtifacts() + } + + return successfulFeedbackLoop +} + +export async function startTransformationJob( + uploadId: string, + transformStartTime: number, + profile: RegionProfile | undefined +) { + let jobId = '' + try { + await telemetry.codeTransform_jobStart.run(async () => { + telemetry.record({ codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId() }) + + jobId = await startJob(uploadId, profile) + getLogger().info(`CodeTransformation: jobId: ${jobId}`) + + telemetry.record({ + codeTransformJobId: jobId, + codeTransformRunTimeLatency: calculateTotalLatency(transformStartTime), + }) + }) + + // create local history folder(s) and store metadata + const metadata: JobMetadata = { + jobId: jobId, + projectName: transformByQState.getProjectName(), + transformationType: transformByQState.getTransformationType() ?? TransformationType.LANGUAGE_UPGRADE, + sourceJDKVersion: transformByQState.getSourceJDKVersion() ?? JDKVersion.JDK8, + targetJDKVersion: transformByQState.getTargetJDKVersion() ?? JDKVersion.JDK17, + customDependencyVersionFilePath: transformByQState.getCustomDependencyVersionFilePath(), + customBuildCommand: transformByQState.getCustomBuildCommand(), + targetJavaHome: transformByQState.getTargetJavaHome() ?? '', + projectPath: transformByQState.getProjectPath(), + startTime: transformByQState.getStartTime(), + } + + const jobHistoryPath = await createMetadataFile(transformByQState.getPayloadFilePath(), metadata) + transformByQState.setJobHistoryPath(jobHistoryPath) + } catch (error) { + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToStartJobNotification}`, error) + const errorMessage = (error as Error).message.toLowerCase() + if (errorMessage.includes('too many active running jobs')) { + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.tooManyJobsNotification) + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.tooManyJobsChatMessage) + } else if (errorMessage.includes('lines of code limit breached')) { + transformByQState.setJobFailureErrorNotification( + CodeWhispererConstants.linesOfCodeLimitBreachedNotification + ) + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.linesOfCodeLimitBreachedChatMessage) + } else { + transformByQState.setJobFailureErrorNotification( + `${CodeWhispererConstants.failedToStartJobNotification} ${errorMessage}` + ) + transformByQState.setJobFailureErrorChatMessage( + `${CodeWhispererConstants.failedToStartJobChatMessage} ${errorMessage}` + ) + } + throw new JobStartError() + } + + await sleep(5000) // sleep before polling job status to prevent ThrottlingException + throwIfCancelled() + + return jobId +} + +export async function pollTransformationStatusUntilPlanReady(jobId: string, profile?: RegionProfile) { + try { + await pollTransformationJob(jobId, CodeWhispererConstants.validStatesForPlanGenerated, profile) + } catch (error) { + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToCompleteJobNotification}`, error) + + if (!transformByQState.getJobFailureErrorNotification()) { + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.failedToCompleteJobNotification) + } + if (!transformByQState.getJobFailureErrorChatMessage()) { + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.failedToCompleteJobChatMessage) + } + + // try to download pre-build error logs if available + let pathToLog = '' + try { + const tempToolkitFolder = await makeTemporaryToolkitFolder() + const tempBuildLogsDir = path.join(tempToolkitFolder, 'q-transformation-build-logs') + await downloadAndExtractResultArchive(jobId, tempBuildLogsDir) + pathToLog = path.join(tempBuildLogsDir, 'buildCommandOutput.log') + transformByQState.setPreBuildLogFilePath(pathToLog) + } catch (e) { + transformByQState.setPreBuildLogFilePath('') + getLogger().error( + 'CodeTransformation: failed to download any possible build error logs: ' + (e as Error).message + ) + throw e + } + + if (fs.existsSync(pathToLog) && !transformByQState.isCancelled()) { + throw new TransformationPreBuildError() + } else { + // not strictly needed to reset path here and above; doing it just to represent unavailable logs + transformByQState.setPreBuildLogFilePath('') + throw new PollJobError() + } + } + if (transformByQState.getTransformationType() === TransformationType.SQL_CONVERSION) { + // for now, no plan shown with SQL conversions. later, we may add one + return + } + jobPlanProgress['generatePlan'] = StepProgress.Succeeded + throwIfCancelled() +} + +export async function pollTransformationStatusUntilComplete(jobId: string, profile: RegionProfile | undefined) { + let status = '' + try { + status = await pollTransformationJob(jobId, CodeWhispererConstants.validStatesForCheckingDownloadUrl, profile) + } catch (error) { + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToCompleteJobNotification}`, error) + if (!transformByQState.getJobFailureErrorNotification()) { + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.failedToCompleteJobNotification) + } + if (!transformByQState.getJobFailureErrorChatMessage()) { + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.failedToCompleteJobChatMessage) + } + throw new PollJobError() + } + + return status +} + +export async function finalizeTransformationJob(status: string) { + if (!(status === 'COMPLETED' || status === 'PARTIALLY_COMPLETED')) { + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToCompleteJobNotification}`) + jobPlanProgress['transformCode'] = StepProgress.Failed + if (!transformByQState.getJobFailureErrorNotification()) { + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.failedToCompleteJobNotification) + } + if (!transformByQState.getJobFailureErrorChatMessage()) { + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.failedToCompleteJobChatMessage) + } + throw new Error('Job was not successful nor partially successful') + } + transformByQState.setToSucceeded() + if (status === 'PARTIALLY_COMPLETED') { + transformByQState.setToPartiallySucceeded() + } + await vscode.commands.executeCommand('aws.amazonq.transformationHub.reviewChanges.reveal') + jobPlanProgress['transformCode'] = StepProgress.Succeeded +} + +export async function getValidLanguageUpgradeCandidateProjects() { + const openProjects = await getOpenProjects() + const javaMavenProjects = await validateOpenProjects(openProjects) + getLogger().info(`CodeTransformation: found ${javaMavenProjects.length} projects eligible for language upgrade`) + return javaMavenProjects +} + +export async function getValidSQLConversionCandidateProjects() { + const embeddedSQLProjects: TransformationCandidateProject[] = [] + await telemetry.codeTransform_validateProject.run(async () => { + telemetry.record({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + }) + const openProjects = await getOpenProjects() + const javaProjects = await getJavaProjects(openProjects) + let resultLog = '' + for (const project of javaProjects) { + // as long as at least one of these strings is found, project contains embedded SQL statements + const searchStrings = ['oracle.jdbc.', 'jdbc:oracle:', 'jdbc:odbc:'] + for (const str of searchStrings) { + const spawnResult = await findStringInDirectory(str, project.path) + // just for telemetry purposes + if (spawnResult.error || spawnResult.stderr) { + resultLog += `search error: ${JSON.stringify(spawnResult)}--` + } else { + resultLog += `search complete (exit code: ${spawnResult.exitCode})--` + } + getLogger().info(`CodeTransformation: searching for ${str} in ${project.path}, result = ${resultLog}`) + if (spawnResult.exitCode === 0) { + embeddedSQLProjects.push(project) + break + } + } + } + getLogger().info( + `CodeTransformation: found ${embeddedSQLProjects.length} projects with embedded SQL statements` + ) + telemetry.record({ + codeTransformMetadata: resultLog, + }) + }) + return embeddedSQLProjects +} + +export async function setTransformationToRunningState() { + await setContextVariables() + await vscode.commands.executeCommand('aws.amazonq.transformationHub.reviewChanges.reset') + transformByQState.setToRunning() + jobPlanProgress['uploadCode'] = StepProgress.Pending + jobPlanProgress['buildCode'] = StepProgress.Pending + jobPlanProgress['generatePlan'] = StepProgress.Pending + jobPlanProgress['transformCode'] = StepProgress.Pending + transformByQState.resetPlanSteps() + transformByQState.resetSessionJobHistory() + transformByQState.setJobId('') // so that details for last job are not overwritten when running one job after another + transformByQState.setPolledJobStatus('') // so that previous job's status does not display at very beginning of this job + transformByQState.setHasSeenTransforming(false) + + CodeTransformTelemetryState.instance.setStartTime() + transformByQState.setStartTime( + convertDateToTimestamp(new Date(CodeTransformTelemetryState.instance.getStartTime())) + ) + + await vscode.commands.executeCommand('workbench.view.extension.aws-codewhisperer-transformation-hub') +} + +export async function postTransformationJob() { + updateJobHistory() + if (jobPlanProgress['uploadCode'] !== StepProgress.Succeeded) { + jobPlanProgress['uploadCode'] = StepProgress.Failed + } + if (jobPlanProgress['buildCode'] !== StepProgress.Succeeded) { + jobPlanProgress['buildCode'] = StepProgress.Failed + } + if (jobPlanProgress['generatePlan'] !== StepProgress.Succeeded) { + jobPlanProgress['generatePlan'] = StepProgress.Failed + } + if (jobPlanProgress['transformCode'] !== StepProgress.Succeeded) { + jobPlanProgress['transformCode'] = StepProgress.Failed + } + + let chatMessage = transformByQState.getJobFailureErrorChatMessage() + if (transformByQState.isSucceeded()) { + chatMessage = CodeWhispererConstants.jobCompletedChatMessage + } else if (transformByQState.isPartiallySucceeded()) { + chatMessage = CodeWhispererConstants.jobPartiallyCompletedChatMessage + } + + if (transformByQState.getSourceJDKVersion() !== transformByQState.getTargetJDKVersion()) { + chatMessage += CodeWhispererConstants.upgradeLibrariesMessage + } + + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: chatMessage, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + const durationInMs = calculateTotalLatency(CodeTransformTelemetryState.instance.getStartTime()) + const resultStatusMessage = transformByQState.getStatus() + + telemetry.codeTransform_totalRunTime.emit({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + codeTransformJobId: transformByQState.getJobId(), + codeTransformResultStatusMessage: resultStatusMessage, + codeTransformRunTimeLatency: durationInMs, + reason: transformByQState.getPolledJobStatus(), + result: + transformByQState.isSucceeded() || transformByQState.isPartiallySucceeded() + ? MetadataResult.Pass + : MetadataResult.Fail, + }) + + let notificationMessage = '' + + if (transformByQState.isSucceeded()) { + notificationMessage = CodeWhispererConstants.jobCompletedNotification + if (transformByQState.getSourceJDKVersion() !== transformByQState.getTargetJDKVersion()) { + notificationMessage += CodeWhispererConstants.upgradeLibrariesMessage + } + void vscode.window.showInformationMessage(notificationMessage, { + title: localizedText.ok, + }) + } else if (transformByQState.isPartiallySucceeded()) { + notificationMessage = CodeWhispererConstants.jobPartiallyCompletedNotification + if (transformByQState.getSourceJDKVersion() !== transformByQState.getTargetJDKVersion()) { + notificationMessage += CodeWhispererConstants.upgradeLibrariesMessage + } + void vscode.window + .showInformationMessage(notificationMessage, CodeWhispererConstants.amazonQFeedbackText) + .then((choice) => { + if (choice === CodeWhispererConstants.amazonQFeedbackText) { + void submitFeedback( + placeholder, + CodeWhispererConstants.amazonQFeedbackKey, + getFeedbackCommentData() + ) + } + }) + } + + await cleanupTempJobFiles( + transformByQState.getJobHistoryPath(), + transformByQState.getPolledJobStatus(), + transformByQState.getPayloadFilePath() + ) + + // attempt download for user + // TODO: refactor as explained here https://github.com/aws/aws-toolkit-vscode/pull/6519/files#r1946873107 + if (transformByQState.isSucceeded() || transformByQState.isPartiallySucceeded()) { + await vscode.commands.executeCommand('aws.amazonq.transformationHub.reviewChanges.startReview') + } + + // store job details and diff path locally (history) + // TODO: ideally when job is cancelled, should be stored as CANCELLED instead of FAILED (remove this if statement after bug is fixed) + if (!transformByQState.isCancelled()) { + const latest = sessionJobHistory[transformByQState.getJobId()] + await writeToHistoryFile( + latest.startTime, + latest.projectName, + latest.status, + latest.duration, + transformByQState.getJobId(), + transformByQState.getJobHistoryPath(), + latest.transformationType, + latest.sourceJDKVersion, + latest.targetJDKVersion, + latest.customDependencyVersionsFilePath, + latest.customBuildCommand + ) + } +} + +export async function transformationJobErrorHandler(error: any) { + if (!transformByQState.isCancelled()) { + // means some other error occurred; cancellation already handled by now with stopTransformByQ + transformByQState.setToFailed() + transformByQState.setPolledJobStatus('FAILED') + // jobFailureErrorNotification should always be defined here + transformByQState.setJobFailureErrorChatMessage( + transformByQState.getJobFailureErrorChatMessage() ?? CodeWhispererConstants.failedToCompleteJobChatMessage + ) + } else { + transformByQState.setToCancelled() + transformByQState.setPolledJobStatus('CANCELLED') + } + getLogger().error(`CodeTransformation: ${error.message}`) + + transformByQState.getChatControllers()?.errorThrown.fire({ + error, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) +} + +export async function cleanupTransformationJob() { + clearInterval(transformByQState.getIntervalId()) + transformByQState.setJobDefaults() + await setContext('gumby.isStopButtonAvailable', false) + await vscode.commands.executeCommand( + 'aws.amazonq.showPlanProgressInHub', + CodeTransformTelemetryState.instance.getStartTime() + ) + CodeTransformTelemetryState.instance.resetCodeTransformMetaDataField() +} + +export async function stopTransformByQ(jobId: string) { + await telemetry.codeTransform_jobIsCancelledByUser.run(async () => { + telemetry.record({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + codeTransformJobId: jobId, + }) + if (transformByQState.isRunning()) { + getLogger().info('CodeTransformation: User requested to stop transformation. Stopping transformation.') + transformByQState.setToCancelled() + transformByQState.setPolledJobStatus('CANCELLED') + await setContext('gumby.isStopButtonAvailable', false) + try { + await stopJob(jobId) + void vscode.window + .showErrorMessage( + CodeWhispererConstants.jobCancelledNotification, + CodeWhispererConstants.amazonQFeedbackText + ) + .then((choice) => { + if (choice === CodeWhispererConstants.amazonQFeedbackText) { + void submitFeedback( + placeholder, + CodeWhispererConstants.amazonQFeedbackKey, + getFeedbackCommentData() + ) + } + }) + } catch (err) { + void vscode.window + .showErrorMessage( + CodeWhispererConstants.errorStoppingJobNotification, + CodeWhispererConstants.amazonQFeedbackText + ) + .then((choice) => { + if (choice === CodeWhispererConstants.amazonQFeedbackText) { + void submitFeedback( + placeholder, + CodeWhispererConstants.amazonQFeedbackKey, + getFeedbackCommentData() + ) + } + }) + getLogger().error(`CodeTransformation: Error stopping transformation ${err}`) + } + } + }) +} + +async function setContextVariables() { + await setContext('gumby.wasQCodeTransformationUsed', true) + await setContext('gumby.isStopButtonAvailable', true) + await setContext('gumby.isPlanAvailable', false) + await setContext('gumby.isSummaryAvailable', false) +} diff --git a/packages/core/src/codewhisperer/commands/types.ts b/packages/core/src/codewhisperer/commands/types.ts new file mode 100644 index 00000000000..cec28829507 --- /dev/null +++ b/packages/core/src/codewhisperer/commands/types.ts @@ -0,0 +1,40 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ExtStartUpSources } from '../../shared/telemetry/util' +import { CompositeKey, Commands, vscodeComponent } from '../../shared/vscode/commands2' + +/** Indicates a CodeWhisperer command was executed through a tree node */ +export const cwTreeNodeSource = 'codewhispererTreeNode' +/** Indicates a CodeWhisperer command was executed through a quick pick item */ +export const cwQuickPickSource = 'codewhispererQuickPick' +/** Indicates a CodeWhisperer command was executed through the Amazon Q chat pane */ +export const amazonQChatSource = 'amazonQChat' +/** Indicates a CodeWhisperer command was executed during the first start of the extension */ +export const firstStartUpSource = ExtStartUpSources.firstStartUp +/** Indicates a CodeWhisperer command was executed as a result of selecting an ellipses menu item */ +export const cwEllipsesMenu = 'ellipsesMenu' +/** Indicates a CodeWhisperer command was executed from the command palette */ +export const commandPalette = 'commandPalette' +/** Indicates a CodeWhisperer command was executed as a result of a toast message interaction */ +export const toastMessage = 'toastMessage' + +/** + * Indicates what caused the CodeWhisperer command to be executed, since a command can be executed from different "sources" + * + * This source is mainly used for telemetry purposes, setting the `source` field in the command execution metric. + * + * **This is typically used in conjunction with {@link CompositeKey} and {@link Commands} even though + * the value may not be explicitly used.** + */ +export type CodeWhispererSource = + | typeof cwQuickPickSource + | typeof cwTreeNodeSource + | typeof vscodeComponent + | typeof amazonQChatSource + | typeof firstStartUpSource + | typeof cwEllipsesMenu + | typeof commandPalette + | typeof toastMessage diff --git a/packages/core/src/codewhisperer/index.ts b/packages/core/src/codewhisperer/index.ts new file mode 100644 index 00000000000..066e5ca2fcb --- /dev/null +++ b/packages/core/src/codewhisperer/index.ts @@ -0,0 +1,108 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export { activate, shutdown } from './activation' +export * from './util/authUtil' +export * from './models/model' +export * from './models/constants' +export * from './commands/basicCommands' +export * from './commands/types' +export type { + TransformationProgressUpdate, + TransformationStep, + FeatureEvaluation, + ListFeatureEvaluationsResponse, + GenerateCompletionsRequest, + Completion, + SendTelemetryEventResponse, + TelemetryEvent, + InlineChatEvent, + Customization, +} from './client/codewhispereruserclient.d.ts' +export type { default as CodeWhispererUserClient } from './client/codewhispereruserclient.d.ts' +export { SecurityPanelViewProvider } from './views/securityPanelViewProvider' +export { isInlineCompletionEnabled } from './util/commonUtil' +export { + DefaultCodeWhispererClient, + Recommendation, + ListCodeScanFindingsResponse, + ListRecommendationsResponse, + RecommendationsList, + FileContext, + ListRecommendationsRequest, + GenerateRecommendationsRequest, + codeWhispererClient, +} from './client/codewhisperer' +export { listCodeWhispererCommands, listCodeWhispererCommandsId } from './ui/statusBarMenu' +export { InlineCompletionService } from './service/inlineCompletionService' +export { refreshStatusBar, CodeWhispererStatusBarManager } from './service/statusBar' +export { SecurityIssueHoverProvider } from './service/securityIssueHoverProvider' +export { SecurityIssueCodeActionProvider } from './service/securityIssueCodeActionProvider' +export { + SecurityIssueTreeViewProvider, + SecurityViewTreeItem, + FileItem, + IssueItem, + SeverityItem, +} from './service/securityIssueTreeViewProvider' +export { onAcceptance } from './commands/onAcceptance' +export { CodeWhispererTracker } from './tracker/codewhispererTracker' +export { CodeWhispererUserGroupSettings } from './util/userGroupUtil' +export { session } from './util/codeWhispererSession' +export { onInlineAcceptance } from './commands/onInlineAcceptance' +export { stopTransformByQ } from './commands/startTransformByQ' +export { featureDefinitions, FeatureConfigProvider } from '../shared/featureConfig' +export { ReferenceInlineProvider } from './service/referenceInlineProvider' +export { ReferenceHoverProvider } from './service/referenceHoverProvider' +export { CWInlineCompletionItemProvider } from './service/inlineCompletionItemProvider' +export { ClassifierTrigger } from './service/classifierTrigger' +export { ReferenceLogViewProvider } from './service/referenceLogViewProvider' +export { RecommendationService } from './service/recommendationService' +export { ImportAdderProvider } from './service/importAdderProvider' +export { LicenseUtil } from './util/licenseUtil' +export { SecurityIssueProvider } from './service/securityIssueProvider' +export { listScanResults, mapToAggregatedList, pollScanJobStatus } from './service/securityScanHandler' +export { TelemetryHelper } from './util/telemetryHelper' +export { LineSelection, LineTracker } from './tracker/lineTracker' +export { BM25Okapi } from './util/supplementalContext/rankBm25' +export { runtimeLanguageContext, RuntimeLanguageContext } from './util/runtimeLanguageContext' +export * as startSecurityScan from './commands/startSecurityScan' +export * from './util/supplementalContext/utgUtils' +export * from './util/supplementalContext/crossFileContextUtil' +export * from './util/editorContext' +export { acceptSuggestion } from './commands/onInlineAcceptance' +export * from './util/showSsoPrompt' +export * from './util/securityScanLanguageContext' +export * from './util/importAdderUtil' +export * from './util/globalStateUtil' +export * from './util/zipUtil' +export * from './util/diagnosticsUtil' +export * from './util/commonUtil' +export * from './util/closingBracketUtil' +export * from './util/supplementalContext/codeParsingUtil' +export * from './util/supplementalContext/supplementalContextUtil' +export * from './util/codewhispererSettings' +export * as supplementalContextUtil from './util/supplementalContext/supplementalContextUtil' +export * from './service/diagnosticsProvider' +export * as diagnosticsProvider from './service/diagnosticsProvider' +export * from './ui/codeWhispererNodes' +export { SecurityScanError, SecurityScanTimedOutError } from '../codewhisperer/models/errors' +export * as CodeWhispererConstants from '../codewhisperer/models/constants' +export { + getSelectedCustomization, + setSelectedCustomization, + baseCustomization, + onProfileChangedListener, + CustomizationProvider, +} from './util/customizationUtil' +export { Container } from './service/serviceContainer' +export * from './util/gitUtil' +export * from './ui/prompters' +export { UserWrittenCodeTracker } from './tracker/userWrittenCodeTracker' +export { RegionProfileManager, defaultServiceConfig } from './region/regionProfileManager' +export { DocumentChangedSource, KeyStrokeHandler, DefaultDocumentChangedType } from './service/keyStrokeHandler' +export { RecommendationHandler } from './service/recommendationHandler' +export { CodeWhispererCodeCoverageTracker } from './tracker/codewhispererCodeCoverageTracker' +export { invokeRecommendation } from './commands/invokeRecommendation' diff --git a/packages/core/src/codewhisperer/indexNode.ts b/packages/core/src/codewhisperer/indexNode.ts new file mode 100644 index 00000000000..1d053989510 --- /dev/null +++ b/packages/core/src/codewhisperer/indexNode.ts @@ -0,0 +1,22 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * This module is a superset of index.ts. Index.ts is for "common" code. + * + * The only exports that should be set in this module are those that only run + * in Node.js environments. + */ +export * from './index' + +export { HumanInTheLoopManager } from './service/transformByQ/humanInTheLoopManager' +export * from './service/transformByQ/transformApiHandler' +export { + DiffModel, + AddedChangeNode, + ModifiedChangeNode, +} from './service/transformByQ/transformationResultsViewProvider' +export { parseVersionsListFromPomFile } from './service/transformByQ/transformFileHandler' +export { validateOpenProjects, getOpenProjects } from './service/transformByQ/transformProjectValidationHandler' diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts new file mode 100644 index 00000000000..81736d478da --- /dev/null +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -0,0 +1,949 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Automated and manual trigger + */ +export const invocationTimeIntervalThreshold = 2 // seconds + +export const promiseTimeoutLimit = 15 // seconds + +export const invocationKeyThreshold = 15 + +export const idleTimerPollPeriod = 25 // milliseconds + +export const showRecommendationTimerPollPeriod = 25 + +export const specialCharactersList = ['{', '[', '(', ':', '\t', '\n'] + +export const AWSTemplateKeyWords = ['AWSTemplateFormatVersion', 'Resources', 'AWS::', 'Description'] + +export const AWSTemplateCaseInsensitiveKeyWords = ['cloudformation', 'cfn', 'template', 'description'] + +export const JsonConfigFileNamingConvention = new Set([ + 'app.json', + 'appsettings.json', + 'bower.json', + 'composer.json', + 'db.json', + 'manifest.json', + 'package.json', + 'schema.json', + 'settings.json', + 'tsconfig.json', + 'vcpkg.json', +]) + +export const normalTextChangeRegex = /[A-Za-z0-9]/g + +export const autoSuggestionConfig = { + settingId: 'codewhisperer_autoSuggestionActivation', + activated: 'Activated', + deactivated: 'Deactivated', +} + +export const autoScansConfig = { + settingId: 'codewhisperer_autoScansActivation', + activated: 'Activated', + deactivated: 'Deactivated', +} + +/** + * EditorCon context + */ +export const charactersLimit = 10240 + +export const filenameCharsLimit = 1024 + +export const naturalLanguage = 'en-US' + +export const maxRecommendations = 1 + +export const space = ' ' + +export const lineBreak = '\n' + +export const lineBreakWin = '\r\n' + +export const supplementalContextTimeoutInMs = 100 + +export const supplementalContextMaxTotalLength = 20480 + +export const editorStateMaxLength = 40000 + +/** + * Ux of recommendations + */ +export const labelLength = 20 + +export const completionDetail = 'Amazon Q' + +/** + * CodeWhisperer in configuration + */ +export const codewhisperer = 'Amazon Q' + +// use vscode languageId here / Supported languages +// TODO: Dropped Cloud9 support - do we need Cloud9-commented entries here? +export const platformLanguageIds = [ + 'java', + 'python', + 'javascript', + 'javascriptreact', + 'typescript', + 'typescriptreact', + 'csharp', + 'c', + 'cpp', + 'c_cpp', // Cloud9 reports C++ files with this language-id. + 'go', + 'kotlin', + 'php', + 'ruby', + 'rust', + 'scala', + 'shellscript', + 'sh', // Cloud9 reports bash files with this language-id + 'sql', + 'golang', // Cloud9 reports Go files with this language-id + 'json', + 'yaml', + 'tf', + 'hcl', + 'terraform', + 'terragrunt', + 'packer', + 'plaintext', + 'jsonc', + 'systemverilog', + 'verilog', + 'powershell', + 'dart', + 'lua', + 'r', + 'swift', + 'vue', +] as const + +export type PlatformLanguageId = (typeof platformLanguageIds)[number] + +/** + * Prompt + */ +export const pendingResponse = 'Waiting for Amazon Q...' + +export const runningSecurityScan = 'Reviewing project for code issues...' + +export const runningFileScan = 'Reviewing current file for code issues...' + +export const noSuggestions = 'No suggestions from Amazon Q' + +export const noInlineSuggestionsMsg = 'No suggestions from Amazon Q' + +export const licenseFilter = 'Amazon Q suggestions were filtered due to reference settings' + +/** + * the interval of the background thread invocation, which is triggered by the timer + */ +export const defaultCheckPeriodMillis = 1000 * 60 * 5 +/** + * Key bindings JSON file path + */ +export const keyBindingPathMac = 'Library/Application Support/Code/User/keybindings.json' + +export const keyBindingPathLinux = '.config/Code/User/keybindings.json' + +export const keyBindingPathWin = 'Code/User/keybindings.json' + +/** + * Length of left context preview in output channel + */ +export const contextPreviewLen = 20 + +/** + * Unsupported language cache + */ +export const unsupportedLanguagesCacheTTL = 10 * 60 * 60 * 1000 + +export const unsupportedLanguagesKey = 'CODEWHISPERER_UNSUPPORTED_LANGUAGES_KEY' + +export const serviceActiveKey = 'CODEWHISPERER_SERVICE_ACTIVE' + +export const inlinehintKey = 'CODEWHISPERER_HINT_DISPLAYED' + +export type AnnotationChangeSource = 'codewhisperer' | 'selection' | 'editor' | 'content' + +export const learnMoreUriGeneral = 'https://aws.amazon.com/q/developer/' + +export const learnMoreUri = 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/q-in-IDE-setup.html' + +export const customLearnMoreUri = 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/customizations.html' + +export const securityScanLearnMoreUri = 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/security-scans.html' + +export const identityPoolID = 'us-east-1:70717e99-906f-4add-908c-bd9074a2f5b9' + +/** + * Delay for making requests once the user stops typing. Without a delay, inline suggestions request is triggered every keystroke. + */ +export const inlineCompletionsDebounceDelay = 200 + +// add 200ms more delay on top of inline default 30-50ms +export const inlineSuggestionShowDelay = 200 + +export const referenceLog = 'Code Reference Log' + +export const suggestionDetailReferenceText = (licenses: string) => + `Reference code under ${licenses}. View full details in Code Reference Log.` + +export const hoverInlayText = (licenseName: string | undefined, repository: string | undefined) => + `Reference code under the ${licenseName} license from repository ${repository}` + +export const referenceLogText = ( + code: string, + license: string, + repository: string, + filePath: string, + lineInfo: string +) => + `with code ${code} provided with reference under ${license} from repository ${repository}. Added to ${filePath} ${lineInfo}.` + +export const referenceLogPromptText = `Don\'t want suggestions that include code with references? Uncheck this option in + Amazon Q: Settings` + +export const referenceLogPromptTextEnterpriseSSO = + 'Your organization controls whether suggestions include code with references. To update these settings, please contact your admin.' +/** + * Security Scan + */ +export const codeScanJavaPayloadSizeLimitBytes = Math.pow(2, 20) // 1 MB + +export const codeScanCsharpPayloadSizeLimitBytes = Math.pow(2, 20) // 1 MB + +export const codeScanRubyPayloadSizeLimitBytes = 200 * Math.pow(2, 10) // 200 KB + +export const codeScanGoPayloadSizeLimitBytes = 200 * Math.pow(2, 10) // 200 KB + +export const codeScanPythonPayloadSizeLimitBytes = 200 * Math.pow(2, 10) // 200 KB + +export const codeScanCFPayloadSizeLimitBytes = 200 * Math.pow(2, 10) // 200 KB + +export const codeScanTerraformPayloadSizeLimitBytes = 200 * Math.pow(2, 10) // 200 KB + +export const codeScanJavascriptPayloadSizeLimitBytes = 200 * Math.pow(2, 10) // 200 KB + +export const fileScanPayloadSizeLimitBytes = 200 * Math.pow(2, 10) // 200 KB + +export const fileScanUploadIntent = 'AUTOMATIC_FILE_SECURITY_SCAN' + +export const projectScanPayloadSizeLimitBytes = 1 * Math.pow(2, 30) // 1GB + +export const projectScanUploadIntent = 'FULL_PROJECT_SECURITY_SCAN' + +export const codeScanTruncDirPrefix = 'codewhisperer_scan' + +export const TestGenerationTruncDirPrefix = 'Q_TestGeneration' + +export const codeScanZipExt = '.zip' + +export const contextTruncationTimeoutSeconds = 10 + +export const standardScanTimeoutMs = 900_000 // 15 minutes + +export const expressScanTimeoutMs = 60_000 + +export const codeFixJobTimeoutMs = 120_000 + +export const projectSizeCalculateTimeoutSeconds = 10 + +export const codeScanJobPollingIntervalSeconds = 1 + +export const codeFixJobPollingIntervalMs = 5_000 + +export const fileScanPollingDelaySeconds = 10 + +export const projectScanPollingDelaySeconds = 30 + +export const codeFixJobPollingDelayMs = 10_000 + +export const testGenPollingDelaySeconds = 10 + +export const testGenJobPollingIntervalMilliseconds = 1000 + +export const testGenJobTimeoutMilliseconds = 60 * 10 * 1000 // 10 minutes + +export const testGenUploadIntent = 'UNIT_TESTS_GENERATION' + +export const codeFixUploadIntent = 'CODE_FIX_GENERATION' + +export const artifactTypeSource = 'SourceCode' + +export const codeScanFindingsSchema = 'codescan/findings/1.0' + +export const autoScanDebounceDelaySeconds = 30 + +export const codewhispererDiagnosticSourceLabel = 'Amazon Q ' + +// use vscode languageId here / Supported languages +export const securityScanLanguageIds = [ + 'java', + 'python', + 'javascript', + 'javascriptreact', + 'typescript', + 'typescriptreact', + 'csharp', + 'go', + 'ruby', + // Cloud9 reports Go files with this language-id + // TODO: Dropped Cloud9 support - is this still needed? + 'golang', + 'json', + 'yaml', + 'tf', + 'hcl', + 'terraform', + 'terragrunt', + 'packer', + 'plaintext', + 'jsonc', + 'c', + 'cpp', + 'php', + 'xml', + 'toml', + 'pip-requirements', + 'java-properties', + 'go.mod', + 'go.sum', + 'kotlin', + 'scala', + 'sh', + 'shell', + 'shellscript', + 'brazilPackageConfig', +] as const + +export type SecurityScanLanguageId = (typeof securityScanLanguageIds)[number] + +export const sasRuleId = 'sbom-software-assurance-services' + +// wait time for editor to update editor.selection.active (in milliseconds) +export const vsCodeCursorUpdateDelay = 10 + +export const reloadWindow = 'Reload Now' + +export const reloadWindowPrompt = + 'Inline suggestion settings changed. The current window needs to be reloaded for Amazon Q to use these changes.' + +export const ssoConfigAlertMessage = `This setting is controlled by your organization\’s admin and has been reset to the value they\’ve specified.` + +export const ssoConfigAlertMessageShareData = `This setting doesn\’t apply, since you are in Professional tier` + +export const settingsLearnMore = 'Learn More about Amazon Q Settings' + +export const freeTierLimitReached = 'You have reached the monthly fair use limit of code recommendations.' + +export const freeTierLimitReachedCodeScan = 'You have reached the monthly quota of code reviews.' + +export const scansLimitReachedErrorMessage = + 'Maximum com.amazon.aws.codewhisperer.StartCodeAnalysis reached for this month.' + +export const utgLimitReached = + 'Maximum com.amazon.aws.codewhisperer.runtime.StartTestGeneration reached for this month.' + +export const DefaultCodeScanErrorMessage = + 'Amazon Q encountered an error while reviewing for code issues. Try again later.' + +export const defaultTestGenErrorMessage = 'Amazon Q encountered an error while generating tests. Try again later.' + +export const defaultCodeFixErrorMessage = 'Amazon Q encountered an error while generating code fixes. Try again later.' + +export const FileSizeExceededErrorMessage = `Amazon Q: The selected file exceeds the input artifact limit. Try again with a smaller file. For more information about review limits, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/security-scans.html#quotas).` + +export const ProjectSizeExceededErrorMessage = `Amazon Q: The selected workspace exceeds the input artifact limit. Try again with a smaller workspace. For more information about review limits, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/security-scans.html#quotas).` + +export const monthlyLimitReachedNotification = + "You've reached the monthly quota for Amazon Q Developer's agent capabilities. You can try again next month. For more information on usage limits, see the Amazon Q Developer pricing page." + +export const noSourceFilesErrorMessage = 'Amazon Q: workspace does not contain valid files to review' + +export const noActiveFileErrorMessage = 'Amazon Q: Open valid file to run a file review' + +export const UploadArtifactToS3ErrorMessage = `Amazon Q is unable to upload your workspace artifacts to Amazon S3 for security reviews. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/security_iam_manage-access-with-policies.html#data-perimeters).` + +export const throttlingLearnMore = `Learn More` + +export const throttlingMessage = `Maximum recommendation count reached for this month` + +export const fileScansThrottlingMessage = `Maximum file reviews count reached for this month` + +export const projectScansThrottlingMessage = `Maximum workspace review count reached for this month` + +export const connectionChangeMessage = `Keep using Amazon Q with ` + +// TODO: align this text with service side +export const invalidCustomizationMessage = `You are not authorized to access` + +export const failedToConnectAwsBuilderId = `Failed to connect to AWS Builder ID` + +export const failedToConnectIamIdentityCenter = `Failed to connect to IAM Identity Center` + +export const stopScanMessage = + 'Stop security review? This review will be counted as one complete review towards your monthly security review limits.' + +// TODO: Change the Text according to the UX +export const stopScanMessageInChat = 'Review is stopped. Retry reviews by selecting below options' + +export const showScannedFilesMessage = 'View Code Issues' + +export const ignoreAllIssuesMessage = (issueTitle: string) => { + return `Are you sure you want to ignore all "${issueTitle}" issues? Amazon Q will not show these issues for future reviews. You can manage a list of your ignored issues in the Amazon Q extension settings.` +} + +export const updateInlineLockKey = 'CODEWHISPERER_INLINE_UPDATE_LOCK_KEY' + +export const newCustomizationMessage = 'You have access to new Amazon Q customizations.' + +// Start of QCT Strings + +export const uploadZipSizeLimitInBytes = 2000000000 // 2GB + +export const maxBufferSize = 1024 * 1024 * 8 // this is 8MB; the default max buffer size for stdout for spawnSync is 1MB + +export const transformationJobPollingIntervalSeconds = 5 + +export const defaultLanguage = 'Java' + +export const contentChecksumType = 'SHA_256' + +export const uploadIntent = 'TRANSFORMATION' + +export const transformationType = 'LANGUAGE_UPGRADE' + +// initial build succeeded +export const validStatesForBuildSucceeded = [ + 'PREPARED', + 'PLANNING', + 'PLANNED', + 'TRANSFORMING', + 'TRANSFORMED', + 'PARTIALLY_COMPLETED', + 'COMPLETED', +] + +// plan must be available +export const validStatesForPlanGenerated = [ + 'PLANNED', + 'TRANSFORMING', + 'TRANSFORMED', + 'PARTIALLY_COMPLETED', + 'COMPLETED', +] + +export const failureStates = ['FAILED', 'STOPPING', 'STOPPED', 'REJECTED'] + +export const pausedStates = ['PAUSED'] + +// if status is COMPLETED or PARTIALLY_COMPLETED we can download artifacts +export const validStatesForCheckingDownloadUrl = [ + 'COMPLETED', + 'PARTIALLY_COMPLETED', + 'FAILED', + 'STOPPING', + 'STOPPED', + 'REJECTED', +] + +export const amazonQFeedbackKey = 'Amazon Q' + +export const amazonQFeedbackText = 'Submit feedback' + +export const codeTransformTroubleshootStartJobFailed = + 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/transform-java.html#quotas-java-transformation-ide' + +export const codeTransformTroubleshootProjectSize = + 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/troubleshooting-code-transformation.html#reduce-project-size' + +export const codeTransformTroubleshootMvnFailure = + 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/troubleshooting-code-transformation.html#maven-commands-failing' + +export const codeTransformTroubleshootConfigureProxy = + 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/troubleshooting-code-transformation.html#configure-proxy' + +export const codeTransformTroubleshootDownloadExpired = + 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/troubleshooting-code-transformation.html#download-24-hrs' + +export const codeTransformTroubleshootAllowS3Access = + 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/troubleshooting-code-transformation.html#allowlist-s3-bucket' + +export const codeTransformTroubleshootUploadError = + 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/troubleshooting-code-transformation.html#project-upload-fail' + +export const codeTransformTroubleshootDownloadError = + 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/troubleshooting-code-transformation.html#download-code-fail' + +export const codeTransformPrereqDoc = + 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#prerequisites' + +export const codeTransformBillingText = (linesOfCode: number) => + `

${linesOfCode} lines of code were submitted for transformation. If you reach the quota for lines of code included in your subscription, you will be charged $${codeTransformBillingRate} for each additional line of code. You might be charged up to $${( + linesOfCode * codeTransformBillingRate + ).toFixed( + 2 + )} for this transformation. To avoid being charged, stop the transformation job before it completes. For more information on pricing and quotas, see [Amazon Q Developer pricing](${linkToBillingInfo}).

` + +export const codeTransformBillingRate = 0.003 + +export const codeTransformLocThreshold = 100000 + +export const jobStartedChatMessage = + 'I am starting to transform your code. It can take 10 to 30 minutes to upgrade your code, depending on the size of your project. To monitor progress, go to the Transformation Hub. If I run into any issues, I might pause the transformation to get input from you on how to proceed.' + +export const chooseTransformationObjective = `I can help you with the following tasks:\n- Upgrade your Java 8, Java 11, and Java 17 codebases to Java 17 or Java 21.\n- Upgrade Java 17 or Java 21 code with up-to-date libraries and other dependencies.\n- Convert embedded SQL code for Oracle to PostgreSQL database migrations in AWS DMS. [Learn more](https://docs.aws.amazon.com/dms/latest/userguide/schema-conversion-embedded-sql.html).\n\nWhat would you like to do? You can enter "language upgrade" or "sql conversion".` + +export const chooseTransformationObjectivePlaceholder = 'Enter "language upgrade" or "sql conversion"' + +export const userPatchDescriptionChatMessage = (version: string) => ` +If you'd like to update and test your code with fewer changes at a time, I can divide the transformation results into separate diff patches. If applicable to your application, I can split up the diffs up into the following groups of upgrades. Here are the upgrades included in each diff: + +• Minimal Compatible Library Upgrade to Java ${version}: Dependencies to the minimum compatible versions in Java ${version}, including Springboot, JUnit, and PowerMockito. + +• Popular Enterprise Specifications Application Frameworks: Popular enterprise and application frameworks like Jakarta EE, Hibernate, and Micronaut 3. + +• HTTP Client Utilities Web Frameworks: HTTP client libraries, Apache Commons utilities, and Struts frameworks. + +• Testing Tools Frameworks: Testing tools like ArchUnit, Mockito, and TestContainers and build tools like Jenkins and Maven Wrapper. + +• Miscellaneous Processing Documentation: Upgrades ORMs, XML processing, and Swagger to SpringDoc/OpenAPI. + +• Deprecated API replacement, dependency upgrades, and formatting: Replaces deprecated APIs, makes additional dependency version upgrades, and formats code changes. +` + +export const uploadingCodeStepMessage = 'Upload your code' + +export const buildCodeStepMessage = 'Analyze uploaded code in secure environment' + +export const generatePlanStepMessage = 'Generate transformation plan' + +export const transformStepMessage = 'Transform your code' + +export const filesUploadedMessage = + 'Files have been uploaded to Amazon Q, transformation job has been accepted and is preparing to start.' + +export const planningMessage = 'Amazon Q is analyzing your code in order to generate a transformation plan.' + +export const transformingMessage = 'Amazon Q is transforming your code.' + +export const stoppingJobMessage = 'Stopping the transformation...' + +export const buildingCodeMessage = + 'Amazon Q is building your code using Java JAVA_VERSION_HERE in a secure build environment.' + +export const scanningProjectMessage = + 'Amazon Q is reviewing the project files and getting ready to start the job. To start the job, Amazon Q needs to upload the project artifacts. Once that is done, Amazon Q can start the transformation job. The estimated time for this operation ranges from a few seconds to several minutes.' + +export const failedStepMessage = 'The step failed, fetching additional details...' + +export const jobCompletedMessage = 'The transformation completed.' + +export const noChangesMadeMessage = "I didn't make any changes for this transformation." + +export const noOngoingJobMessage = 'No ongoing job.' + +export const noJobHistoryMessage = 'No job history' + +export const jobStartedNotification = + 'Amazon Q is transforming your code. It can take 10 to 30 minutes to upgrade your code, depending on the size of your project. To monitor progress, go to the Transformation Hub.' + +export const openTransformationHubButtonText = 'Open Transformation Hub' + +export const startTransformationButtonText = 'Start a new transformation' + +export const viewSummaryButtonText = 'View summary' + +export const stopTransformationButtonText = 'Stop transformation' + +export const checkingForProjectsChatMessage = 'Checking for eligible projects...' + +export const buildStartedChatMessage = + 'I am building your project. This can take up to 10 minutes, depending on the size of your project.' + +export const buildSucceededChatMessage = 'I was able to build your project and will start transforming your code soon.' + +export const buildSucceededNotification = + 'Amazon Q was able to build your project and will start transforming your code soon.' + +export const absolutePathDetectedMessage = (numPaths: number, buildFile: string, listOfPaths: string) => + `I detected ${numPaths} potential absolute file path(s) in your ${buildFile} file: **${listOfPaths}**. Absolute file paths might cause issues when I build your code. Any errors will show up in the build log.` + +export const selectSQLMetadataFileHelpMessage = + 'Okay, I can convert the embedded SQL code for your Oracle to PostgreSQL transformation. To get started, upload the zipped metadata file from your schema conversion in AWS Data Migration Service (DMS). To retrieve the metadata file:\n1. Open your database migration project in the AWS DMS console.\n2. Open the schema conversion and choose **Convert the embedded SQL in your application**.\n3. Once you complete the conversion, close the project and go to the S3 bucket where your project is stored.\n4. Open the folder and find the project folder ("sct-project").\n5. Download the object inside the project folder. This will be a zip file.\n\nFor more info, refer to the [documentation](https://docs.aws.amazon.com/dms/latest/userguide/schema-conversion-embedded-sql.html).' + +export const invalidMetadataFileUnsupportedSourceDB = + 'I can only convert SQL for migrations from an Oracle source database. The provided .sct file indicates another source database for this migration.' + +export const invalidMetadataFileUnsupportedTargetDB = + 'I can only convert SQL for migrations to Aurora PostgreSQL or Amazon RDS for PostgreSQL target databases. The provided .sct file indicates another target database for this migration.' + +export const invalidCustomVersionsFileMessage = (errorMessage: string) => + `The dependency upgrade file provided is malformed: ${errorMessage}. Check that it is configured properly and try again. For an example of the required dependency upgrade file format, see the [documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/code-transformation.html#dependency-upgrade-file).` + +export const invalidMetadataFileErrorParsing = + "It looks like the .sct file you provided isn't valid. Make sure that you've uploaded the .zip file you retrieved from your schema conversion in AWS DMS." + +export const invalidMetadataFileNoSctFile = + "An .sct file is required for transformation. Make sure that you've uploaded the .zip file you retrieved from your schema conversion in AWS DMS." + +export const invalidFromToJdkChatMessage = + "I can't transform a project from Java 21 to Java 17, but I can upgrade Java 21 code with up to date libraries and other dependencies. Try again with a supported language upgrade." + +export const sqlMetadataFileReceived = + 'I found the following source database, target database, and host based on the schema conversion metadata you provided:' + +export const failedToStartJobChatMessage = + "Sorry, I couldn't begin the transformation. Please try starting the transformation again." + +export const failedToStartJobNotification = + 'Amazon Q could not begin the transformation. Please try starting the transformation again.' + +export const tooManyJobsChatMessage = `Sorry, I couldn't begin the transformation. You have too many active transformations running. Please try again after your other transformations have completed. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootStartJobFailed}).` + +export const tooManyJobsNotification = `Amazon Q could not begin the transformation. You have too many active transformations running. Please try again after your other transformations have completed. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootStartJobFailed}).` + +export const linesOfCodeLimitBreachedChatMessage = `Sorry, I couldn't begin the transformation. You have exceeded the lines of code limit. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootStartJobFailed}).` + +export const linesOfCodeLimitBreachedNotification = `Amazon Q could not begin the transformation. You have exceeded the lines of code limit. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootStartJobFailed}).` + +export const failedToUploadProjectChatMessage = + "Sorry, I couldn't upload your project. Please try starting the transformation again." + +export const failedToUploadProjectNotification = + 'Amazon Q could not upload your project. Please try starting the transformation again.' + +export const failedToGetPlanChatMessage = + "Sorry, I couldn't create the transformation plan to upgrade your project. Please try starting the transformation again." + +export const failedToGetPlanNotification = + 'Amazon Q could not create the transformation plan to upgrade your project. Please try starting the transformation again.' + +export const failedToCompleteJobChatMessage = + "Sorry, I couldn't complete the transformation. Please try starting the transformation again." + +export const failedToCompleteJobNotification = + 'Amazon Q could not complete the transformation. Please try starting the transformation again.' + +export const failedToCompleteJobGenericChatMessage = "Sorry, I couldn't complete the transformation." + +export const failedToCompleteJobGenericNotification = 'Amazon Q could not complete the transformation.' + +export const genericErrorMessage = + 'Sorry, I am experiencing technical issues at the moment. Please try again in a few minutes.' + +export const jobCancelledChatMessage = + 'If you want to start another transformation, choose **Start a new transformation**.' + +export const jobCancelledNotification = 'You cancelled the transformation.' + +export const continueWithoutHilMessage = 'I will continue transforming your code without upgrading this dependency.' + +export const continueWithoutConfigFileMessage = + 'Ok, I will continue the transformation without additional dependency upgrade information.' + +export const receivedValidConfigFileMessage = + 'The dependency upgrade file looks good. I will use this information to upgrade the dependencies you specified.' + +export const chooseConfigFileMessageJdkUpgrade = + 'Would you like to provide a dependency upgrade file? You can specify first party dependencies and their versions in a YAML file, and I will upgrade them during the JDK upgrade transformation. For an example dependency upgrade file, see the [documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/code-transformation.html#dependency-upgrade-file).' + +export const chooseConfigFileMessageLibraryUpgrade = + 'Would you like to provide a dependency upgrade file? You can specify third party dependencies and their versions in a YAML file, and I will only upgrade these dependencies during the library upgrade transformation. For an example dependency upgrade file, see the [documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/code-transformation.html#dependency-upgrade-file).' + +export const enterJavaHomePlaceholder = 'Enter the path to your Java installation' + +export const openNewTabPlaceholder = 'Open a new tab to chat with Q' + +export const jobCompletedChatMessage = + 'I completed your transformation. You can review the diff to see my proposed changes and accept or reject them. The transformation summary has details about the changes I am proposing. ' + +export const jobCompletedNotification = + 'Amazon Q transformed your code. You can review the diff to see my proposed changes and accept or reject them. The transformation summary has details about the changes. ' + +export const upgradeLibrariesMessage = + 'After successfully transforming to Java 17 or 21, an additional transformation is required to upgrade your libraries and dependencies. Choose the same source code version and target code version (for example, 17 to 17) to do this.' + +export const jobPartiallyCompletedChatMessage = `I transformed part of your code. You can review the diff to see my proposed changes and accept or reject them. The transformation summary has details about the files I updated and the errors that prevented a complete transformation. ` + +export const jobPartiallyCompletedNotification = `Amazon Q transformed part of your code. You can review the diff to see my proposed changes and accept or reject them. The transformation summary has details about the files I updated and the errors that prevented a complete transformation. ` + +export const noPomXmlFoundChatMessage = `I couldn\'t find a project that I can upgrade. I couldn\'t find a pom.xml file in any of your open projects, nor could I find any embedded SQL statements. Currently, I can upgrade Java 8, 11, or 17 projects built on Maven, or Oracle SQL to PostgreSQL statements in Java projects. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).` + +export const noJavaHomeFoundChatMessage = `Sorry, I couldn\'t locate your Java installation. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).` + +export const dependencyVersionsErrorMessage = + 'I could not find any other versions of this dependency in your local Maven repository. Try transforming the dependency to make it compatible with your target Java version, and then try transforming this module again.' + +export const errorUploadingWithExpiredUrl = `The upload error may have been caused by the expiration of the S3 pre-signed URL that was used to upload code artifacts to Q Code Transformation. The S3 pre-signed URL expires in 30 minutes. This could be caused by any delays introduced by intermediate services in your network infrastructure. Please investigate your network configuration and consider allowlisting 'amazonq-code-transformation-us-east-1-c6160f047e0.s3.amazonaws.com' to skip any reviewing that might delay the upload. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootAllowS3Access}).` + +export const socketConnectionFailed = + 'Please check your network connectivity or firewall configuration, and then try again.' + +export const selfSignedCertificateError = `This might have been caused by your IDE not trusting the certificate of your HTTP proxy. Ensure all certificates for your proxy client have been configured in your IDE, and then retry transformation. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootConfigureProxy}).` + +export const errorStoppingJobChatMessage = "Sorry, I couldn't stop the transformation." + +export const errorStoppingJobNotification = 'Amazon Q could not stop the transformation.' + +export const errorDownloadingDiffChatMessage = `Sorry, I couldn\'t download the diff with your upgraded code. Please try downloading it again. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootDownloadError}).` + +export const errorDownloadingDiffNotification = `Amazon Q could not download the diff with your upgraded code. Please try downloading it again. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootDownloadError}).` + +export const errorDownloadingExpiredDiff = `Your transformation is not available anymore. Your code and transformation summary are deleted 24 hours after the transformation completes. Please try starting the transformation again. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootDownloadExpired}).` + +export const errorDeserializingDiffChatMessage = `Sorry, I couldn\'t parse the diff with your upgraded code. Please try starting the transformation again. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootDownloadError}).` + +export const errorDeserializingDiffNotification = `Amazon Q could not parse the diff with your upgraded code. Please try starting the transformation again. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootDownloadError}).` + +export const viewProposedChangesChatMessage = + 'Download complete. You can view a summary of the transformation and accept or reject the proposed changes in the Transformation Hub.' + +export const viewProposedChangesNotification = + 'Download complete. You can view a summary of the transformation and accept or reject the proposed changes in the Transformation Hub.' + +export const changesAppliedChatMessageOneDiff = 'I applied the changes to your project.' + +export const changesAppliedNotificationOneDiff = 'Amazon Q applied the changes to your project' + +export const noOpenProjectsFoundChatMessage = `I couldn\'t find a project that I can upgrade. Currently, I support Java 8, Java 11, Java 17, and Java 21 projects built on Maven. Make sure your project is open in the IDE. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).` + +export const noOpenFileFoundChatMessage = `Sorry, there isn't a source file open right now that I can generate a test for. Make sure you open a source file so I can generate tests.` + +export const invalidFileTypeChatMessage = `Sorry, your current active window is not a source code file. Make sure you select a source file as your primary context.` + +export const noOpenProjectsFoundChatTestGenMessage = `Sorry, I couldn\'t find a project to generate tests` + +export const unitTestGenerationCancelMessage = 'Unit test generation cancelled.' + +export const tooManyRequestErrorMessage = 'Too many requests. Please wait before retrying.' + +export const noJavaProjectsFoundChatMessage = `I couldn\'t find a project that I can upgrade. Currently, I support Java 8, Java 11, Java 17, and Java 21 projects built on Maven. Make sure your project is open in the IDE. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).` + +export const linkToDocsHome = 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html' + +export const linkToBillingInfo = 'https://aws.amazon.com/q/developer/pricing/' + +export const dependencyFolderName = 'transformation_dependencies_temp_' + +export const cleanTestCompileErrorChatMessage = `I could not run \`mvn clean test-compile\` to build your project. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootMvnFailure}).` + +export const cleanTestCompileErrorNotification = `Amazon Q could not run \`mvn clean test-compile\` to build your project. For more information, see the [Amazon Q documentation](${codeTransformTroubleshootMvnFailure}).` + +export const enterJavaHomeChatMessage = 'Enter the path to JDK' + +export const projectPromptChatMessage = + "I can upgrade your Java project. To start the transformation, I need some information from you. Choose the project you want to upgrade and the target code version to upgrade to. Then, choose Confirm.\n\nAfter successfully transforming to Java 17 or 21, an additional transformation is required to upgrade your libraries and dependencies. Choose the same source code version and target code version (for example, 17 to 17) to do this.\n\nI will perform the transformation based on your project's requests, descriptions, and content. To maintain security, avoid including external, unvetted artifacts in your project repository prior to starting the transformation and always validate transformed code for both functionality and security. Do not turn off or close your machine during the transformation because a stable network connection is required." + +export const windowsJavaHomeHelpChatMessage = + 'To find the JDK path, run the following commands in a new terminal: `cd "C:/Program Files/Java"` and then `dir`. If you see your JDK version, run `cd ` and then `cd` to show the path.' + +export const macJavaVersionHomeHelpChatMessage = (version: number) => + `To find the JDK path, run the following command in a new terminal: \`/usr/libexec/java_home -v ${version}\`` + +export const linuxJavaHomeHelpChatMessage = + 'To find the JDK path, run the following command in a new terminal: `update-java-alternatives --list`' + +export const JDK8VersionNumber = '52' + +export const JDK11VersionNumber = '55' + +export const chooseProjectFormTitle = 'Choose a project to transform' + +export const chooseSourceVersionFormTitle = 'Choose the source code version' + +export const chooseTargetVersionFormTitle = 'Choose the target code version' + +export const chooseSchemaFormTitle = 'Choose the schema of the database' + +export const chooseProjectSchemaFormMessage = 'To continue, choose the project and schema for this transformation.' + +export const skipUnitTestsFormTitle = 'Choose to skip unit tests' + +export const skipUnitTestsFormMessage = + 'I will build generated code in your local environment, not on the server side. For information on how I scan code to reduce security risks associated with building the code in your local environment, see the [documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/code-transformation.html#java-local-builds).\n\nI will build your project using `mvn clean test` by default. If you would like me to build your project without running unit tests, I will use `mvn clean test-compile`.' + +export const runUnitTestsMessage = 'Run unit tests' + +export const doNotSkipUnitTestsBuildCommand = 'clean test' + +export const skipUnitTestsMessage = 'Skip unit tests' + +export const skipUnitTestsBuildCommand = 'clean test-compile' + +export const planTitle = 'Code Transformation plan by Amazon Q' + +export const planIntroductionMessage = + 'Amazon Q reviewed your code and generated a transformation plan. Amazon Q will suggest code changes according to the plan, and you can review the updated code before accepting changes to your files.' + +export const planHeaderMessage = 'Planned transformation changes' + +export const planDisclaimerMessage = + 'Amazon Q will use the proposed changes as guidance during the transformation. The final code updates might differ from this plan.' + +export const formattedStringMap = new Map([ + ['linesOfCode', 'Lines of code in your application'], + ['plannedDependencyChanges', 'Dependencies to be replaced'], + ['plannedDeprecatedApiChanges', 'Deprecated code instances to be replaced'], + ['plannedFileChanges', 'Files to be changed'], + ['dependencyName', 'Dependency'], + ['action', 'Action'], + ['currentVersion', 'Current version'], + ['targetVersion', 'Target version'], + ['relativePath', 'File'], + ['apiFullyQualifiedName', 'Deprecated code'], + ['numChangedFiles', 'Files to be changed'], +]) + +export const refreshInProgressChatMessage = 'A job refresh is currently in progress. Please wait for it to complete.' + +export const refreshingJobChatMessage = (jobId: string) => + `I am now resuming your job (id: ${jobId}). This can take 10 to 30 minutes to complete.` + +export const jobHistoryButtonText = 'Open job history' + +export const viewHistoryMessage = (numInProgress: number) => + numInProgress > 0 + ? `You have ${numInProgress} job${numInProgress > 1 ? 's' : ''} in progress. You can resume ${numInProgress > 1 ? 'them' : 'it'} in the transformation history table.` + : 'View previous transformations run from the IDE' + +export const transformationHistoryTableDescription = + 'This table lists the most recent jobs that you have run in the past 30 days. To open the diff patch and summary files, click the provided links. To get an updated job status, click the refresh icon. The diff patch and summary will appear once they are available.

' + + 'Jobs with a status of FAILED may still be in progress. Resume these jobs within 12 hours of starting the job to get an updated job status and artifacts.' + +export const refreshErrorChatMessage = + "Sorry, I couldn't refresh the job. Please try again or start a new transformation." + +export const refreshErrorNotification = (jobId: string) => `There was an error refreshing this job. Job Id: ${jobId}` + +export const refreshCompletedChatMessage = + 'Job refresh completed. Please see the transformation history table for the updated status and artifacts.' + +export const refreshCompletedNotification = (jobId: string) => `Job refresh completed. (Job Id: ${jobId})` + +export const refreshNoUpdatesNotification = (jobId: string) => `No updates. (Job Id: ${jobId})` + +// end of QCT Strings + +export enum UserGroup { + Classifier = 'Classifier', + CrossFile = 'CrossFile', + Control = 'Control', + RightContext = 'RightContext', +} + +export const isClassifierEnabledKey = 'CODEWHISPERER_CLASSIFIER_TRIGGER_ENABLED' + +export const supplemetalContextFetchingTimeoutMsg = 'Amazon Q supplemental context fetching timeout' + +export const codeFixAppliedFailedMessage = 'Failed to apply suggested code fix.' + +export const runSecurityScanButtonTitle = 'Run security review' + +export const startProjectScan = 'Review Project' + +export const startFileScan = 'Review Current File in Focus' + +export const noOpenProjectsFound = `Sorry, I couldn\'t find a project in the workspace. Open a project in your IDE and retry the review.` + +export const noOpenFileFound = `Sorry, I couldn\'t find an active file in the editor. Open a file in your IDE and retry the review.` + +export const crossFileContextConfig = { + numberOfChunkToFetch: 60, + topK: 3, + numberOfLinesEachChunk: 50, + maximumTotalLength: 20480, + maxLengthEachChunk: 10240, + maxContextCount: 5, +} + +export const utgConfig = { + maxSegmentSize: 10200, +} + +export enum CodeAnalysisScope { + FILE_AUTO = 'FILE_AUTO', + FILE_ON_DEMAND = 'FILE_ON_DEMAND', + PROJECT = 'PROJECT', + AGENTIC = 'AGENTIC', +} + +export enum TestGenerationJobStatus { + IN_PROGRESS = 'IN_PROGRESS', + FAILED = 'FAILED', + COMPLETED = 'COMPLETED', +} + +export enum FeatureUseCase { + TEST_GENERATION = 'TEST_GENERATION', + CODE_SCAN = 'CODE_SCAN', +} + +export const amazonqIgnoreNextLine = 'amazonq-ignore-next-line' + +export enum TestGenerationBuildStep { + START_STEP, + INSTALL_DEPENDENCIES, + RUN_BUILD, + RUN_EXECUTION_TESTS, + FIXING_TEST_CASES, + PROCESS_TEST_RESULTS, +} + +export enum SecurityScanStep { + GENERATE_ZIP, + UPLOAD_TO_S3, + CREATE_SCAN_JOB, + POLL_SCAN_STATUS, + PROCESS_SCAN_RESULTS, +} + +export const amazonqCodeIssueDetailsTabTitle = 'Code Issue Details' + +export const testGenExcludePatterns = [ + '**/annotation-generated-src/*', + '**/annotation-generated-tst/*', + '**/build/*', + '**/env/*', + '**/release-info/*', + '**/*.jar', + '**/*.exe', + '**/*.a', + '**/*.map', + '**/*.graph', + '**/*.so', + '**/*.csv', + '**/*.dylib', + '**/*.parquet', + '**/*.xlsx', + '**/*.tar.gz', + '**/*.tar', + '**/*.pack', + '**/*.pkg', + '**/*.pkl', + '**/*.deb', + '**/*.model', +] + +export const predictionTrackerDefaultConfig = { + maxStorageSizeKb: 5000, + debounceIntervalMs: 2000, + maxAgeMs: 30000, + maxSupplementalContext: 15, +} + +export const codeReviewFindingsSuffix = '_codeReviewFindings' +export const displayFindingsSuffix = '_displayFindings' + +export const displayFindingsDetectorName = 'DisplayFindings' +export const findingsSuffix = '_codeReviewFindings' diff --git a/packages/core/src/codewhisperer/models/errors.ts b/packages/core/src/codewhisperer/models/errors.ts new file mode 100644 index 00000000000..34927f01c0a --- /dev/null +++ b/packages/core/src/codewhisperer/models/errors.ts @@ -0,0 +1,184 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { ToolkitError } from '../../shared/errors' +import { i18n } from '../../shared/i18n-helper' +import { + DefaultCodeScanErrorMessage, + FileSizeExceededErrorMessage, + ProjectSizeExceededErrorMessage, + UploadArtifactToS3ErrorMessage, + defaultCodeFixErrorMessage, + defaultTestGenErrorMessage, + noActiveFileErrorMessage, + noSourceFilesErrorMessage, +} from './constants' + +export class SecurityScanError extends ToolkitError { + constructor( + error: string, + code: string, + public customerFacingMessage: string + ) { + super(error, { code }) + } +} + +export class FileSizeExceededError extends SecurityScanError { + constructor() { + super('Payload size limit reached', 'FileSizeExceeded', FileSizeExceededErrorMessage) + } +} + +export class ProjectSizeExceededError extends SecurityScanError { + constructor() { + super('Payload size limit reached', 'ProjectSizeExceeded', ProjectSizeExceededErrorMessage) + } +} + +export class DefaultError extends SecurityScanError { + constructor() { + super('Security scan failed.', 'DefaultError', DefaultCodeScanErrorMessage) + } +} + +export class InvalidSourceZipError extends SecurityScanError { + constructor() { + super('Failed to create valid source zip', 'InvalidSourceZip', DefaultCodeScanErrorMessage) + } +} + +export class NoSourceFilesError extends SecurityScanError { + constructor() { + super('Project does not contain valid files.', 'NoSourceFilesError', noSourceFilesErrorMessage) + } +} + +export class NoActiveFileError extends SecurityScanError { + constructor() { + super('Open valid file to run a file scan', 'NoActiveFileError', noActiveFileErrorMessage) + } +} + +export class CreateUploadUrlError extends SecurityScanError { + constructor(error: string) { + super(error, 'CreateUploadUrlError', DefaultCodeScanErrorMessage) + } +} + +export class UploadArtifactToS3Error extends SecurityScanError { + constructor(error: string) { + super(error, 'UploadArtifactToS3Error', UploadArtifactToS3ErrorMessage) + } +} + +export class CreateCodeScanError extends SecurityScanError { + constructor(error: string) { + super(error, 'CreateCodeScanError', DefaultCodeScanErrorMessage) + } +} + +export class CreateCodeScanFailedError extends SecurityScanError { + constructor(error: string) { + super(error, 'CreateCodeScanFailedError', DefaultCodeScanErrorMessage) + } +} + +export class SecurityScanTimedOutError extends SecurityScanError { + constructor() { + super('Security Scan failed. Amazon Q timed out.', 'SecurityScanTimedOutError', DefaultCodeScanErrorMessage) + } +} + +export class CodeScanJobFailedError extends SecurityScanError { + constructor() { + super('Security scan failed.', 'CodeScanJobFailedError', DefaultCodeScanErrorMessage) + } +} + +export class MaximumFileScanReachedError extends SecurityScanError { + constructor() { + super( + 'Maximum file review count reached for this month.', + 'MaximumFileScanReachedError', + i18n('AWS.amazonq.featureDev.error.monthlyLimitReached') + ) + } +} + +export class MaximumProjectScanReachedError extends SecurityScanError { + constructor() { + super( + 'Maximum project review count reached for this month', + 'MaximumProjectScanReachedError', + i18n('AWS.amazonq.featureDev.error.monthlyLimitReached') + ) + } +} + +export class TestGenError extends ToolkitError { + constructor( + error: string, + code: string, + public customerFacingMessage: string + ) { + super(error, { code }) + } +} + +export class TestGenTimedOutError extends TestGenError { + constructor() { + super('Test generation failed. Amazon Q timed out.', 'TestGenTimedOutError', defaultTestGenErrorMessage) + } +} + +export class TestGenStoppedError extends TestGenError { + constructor() { + super('Test generation stopped by user.', 'TestGenCancelled', defaultTestGenErrorMessage) + } +} + +export class TestGenFailedError extends TestGenError { + constructor(error?: string) { + super(error ?? 'Test generation failed', 'TestGenFailedError', defaultTestGenErrorMessage) + } +} + +export class CodeFixError extends ToolkitError { + constructor( + error: string, + code: string, + public customerFacingMessage: string + ) { + super(error, { code }) + } +} + +export class CreateCodeFixError extends CodeFixError { + constructor() { + super('Code fix generation failed', 'CreateCodeFixFailed', defaultCodeFixErrorMessage) + } +} + +export class CodeFixJobTimedOutError extends CodeFixError { + constructor() { + super('Code fix generation failed. Amazon Q timed out.', 'CodeFixTimedOutError', defaultCodeFixErrorMessage) + } +} + +export class CodeFixJobStoppedError extends CodeFixError { + constructor() { + super('Code fix generation stopped by user.', 'CodeFixCancelled', defaultCodeFixErrorMessage) + } +} + +export class MonthlyCodeFixLimitError extends CodeFixError { + constructor() { + super( + i18n('AWS.amazonq.codefix.error.monthlyLimitReached'), + MonthlyCodeFixLimitError.name, + defaultCodeFixErrorMessage + ) + } +} diff --git a/packages/core/src/codewhisperer/models/model.ts b/packages/core/src/codewhisperer/models/model.ts new file mode 100644 index 00000000000..f074fe74bd6 --- /dev/null +++ b/packages/core/src/codewhisperer/models/model.ts @@ -0,0 +1,1216 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { ToolkitError } from '../../shared/errors' +import { getIcon, Icon } from '../../shared/icons' +import { + CodewhispererCodeScanScope, + CodewhispererCompletionType, + CodewhispererLanguage, + CodewhispererTriggerType, + MetricBase, + Result, +} from '../../shared/telemetry/telemetry' +import { References } from '../client/codewhisperer' +import globals from '../../shared/extensionGlobals' +import { ChatControllerEventEmitters } from '../../amazonqGumby/chat/controller/controller' +import { TransformationSteps } from '../client/codewhispereruserclient' +import { Messenger } from '../../amazonqGumby/chat/controller/messenger/messenger' +import { ScanChatControllerEventEmitters } from '../../amazonqScan/controller' +import { localize } from '../../shared/utilities/vsCodeUtils' + +// unavoidable global variables +interface VsCodeState { + /** + * Flag indicates intelli sense pop up is active or not + * Adding this since VS Code intelliSense API does not expose this variable + */ + isIntelliSenseActive: boolean + /** + * Flag indicates whether codewhisperer is doing vscode.TextEditor.edit + */ + isCodeWhispererEditing: boolean + /** + * Keeps track of whether or not recommendations are currently running + */ + isRecommendationsActive: boolean + /** + * Timestamp of previous user edit + */ + lastUserModificationTime: number + + isFreeTierLimitReached: boolean + + lastManualTriggerTime: number +} + +export const vsCodeState: VsCodeState = { + isIntelliSenseActive: false, + isCodeWhispererEditing: false, + // hack to globally keep track of whether or not recommendations are currently running. This allows us to know + // when recommendations have ran during e2e tests + isRecommendationsActive: false, + lastUserModificationTime: 0, + isFreeTierLimitReached: false, + lastManualTriggerTime: 0, +} + +export interface CodeWhispererConfig { + readonly region: string + readonly endpoint: string +} + +export interface RegionProfile { + name: string + region: string + arn: string + description: string +} + +export type UtgStrategy = 'byName' | 'byContent' + +export type CrossFileStrategy = 'opentabs' | 'codemap' | 'bm25' | 'default' + +export type SupplementalContextStrategy = CrossFileStrategy | UtgStrategy | 'empty' + +export interface CodeWhispererSupplementalContext { + isUtg: boolean + isProcessTimeout: boolean + supplementalContextItems: CodeWhispererSupplementalContextItem[] + contentsLength: number + latency: number + strategy: SupplementalContextStrategy +} + +export interface CodeWhispererSupplementalContextItem { + content: string + filePath: string + score?: number +} + +// This response struct can contain more info as needed +export interface GetRecommendationsResponse { + readonly result: 'Succeeded' | 'Failed' + readonly recommendationCount: number + readonly errorMessage: string | undefined +} + +/** Manages the state of CodeWhisperer code suggestions */ +export class CodeSuggestionsState { + /** The initial state if suggestion state was not defined */ + #fallback: boolean + #onDidChangeState = new vscode.EventEmitter() + /** Set a callback for when the state of code suggestions changes */ + onDidChangeState = this.#onDidChangeState.event + + static #instance: CodeSuggestionsState + static get instance() { + return (this.#instance ??= new this()) + } + + protected constructor(fallback: boolean = true) { + this.#fallback = fallback + } + + async toggleSuggestions() { + const autoTriggerEnabled = this.isSuggestionsEnabled() + const toSet: boolean = !autoTriggerEnabled + await globals.globalState.update('CODEWHISPERER_AUTO_TRIGGER_ENABLED', toSet) + this.#onDidChangeState.fire(toSet) + return toSet + } + + async setSuggestionsEnabled(isEnabled: boolean) { + if (this.isSuggestionsEnabled() !== isEnabled) { + await this.toggleSuggestions() + } + } + + isSuggestionsEnabled(): boolean { + const isEnabled = globals.globalState.tryGet('CODEWHISPERER_AUTO_TRIGGER_ENABLED', Boolean) + return isEnabled !== undefined ? isEnabled : this.#fallback + } +} + +export interface AcceptedSuggestionEntry { + readonly time: Date + readonly fileUrl: vscode.Uri + readonly originalString: string + readonly startPosition: vscode.Position + readonly endPosition: vscode.Position + readonly requestId: string + readonly sessionId: string + readonly index: number + readonly triggerType: CodewhispererTriggerType + readonly completionType: CodewhispererCompletionType + readonly language: CodewhispererLanguage +} + +export interface OnRecommendationAcceptanceEntry { + readonly editor: vscode.TextEditor | undefined + readonly range: vscode.Range + readonly effectiveRange: vscode.Range + readonly acceptIndex: number + readonly recommendation: string + readonly requestId: string + readonly sessionId: string + readonly triggerType: CodewhispererTriggerType + readonly completionType: CodewhispererCompletionType + readonly language: CodewhispererLanguage + readonly references: References | undefined +} + +export interface ConfigurationEntry { + readonly isShowMethodsEnabled: boolean + readonly isManualTriggerEnabled: boolean + readonly isAutomatedTriggerEnabled: boolean + readonly isSuggestionsWithCodeReferencesEnabled: boolean +} + +export interface InlineCompletionItem { + content: string + index: number +} + +/** + * Q Security Scans + */ + +enum ScanStatus { + NotStarted, + Running, + Cancelling, +} + +type IconPath = { light: vscode.Uri; dark: vscode.Uri; toString: () => string } | Icon + +abstract class BaseScanState { + protected scanState: ScanStatus = ScanStatus.NotStarted + + protected chatControllers: ScanChatControllerEventEmitters | undefined = undefined + + public isNotStarted(): boolean { + return this.scanState === ScanStatus.NotStarted + } + + public isRunning(): boolean { + return this.scanState === ScanStatus.Running + } + + public isCancelling(): boolean { + return this.scanState === ScanStatus.Cancelling + } + + public setToNotStarted(): void { + this.scanState = ScanStatus.NotStarted + } + + public setToCancelling(): void { + this.scanState = ScanStatus.Cancelling + } + + public setToRunning(): void { + this.scanState = ScanStatus.Running + } + + public getPrefixTextForButton(): string { + switch (this.scanState) { + case ScanStatus.NotStarted: + return 'Run' + case ScanStatus.Running: + return 'Stop' + case ScanStatus.Cancelling: + return 'Stopping' + } + } + + public setChatControllers(controllers: ScanChatControllerEventEmitters) { + this.chatControllers = controllers + } + public getChatControllers() { + return this.chatControllers + } + + public abstract getIconForButton(): IconPath +} + +export class CodeScanState extends BaseScanState { + public getIconForButton(): IconPath { + switch (this.scanState) { + case ScanStatus.NotStarted: + return getIcon('vscode-debug-all') + case ScanStatus.Running: + return getIcon('vscode-stop-circle') + case ScanStatus.Cancelling: + return getIcon('vscode-loading~spin') + } + } +} + +export class OnDemandFileScanState extends BaseScanState { + public getIconForButton(): IconPath { + switch (this.scanState) { + case ScanStatus.NotStarted: + return getIcon('vscode-debug-all') + case ScanStatus.Running: + return getIcon('vscode-stop-circle') + case ScanStatus.Cancelling: + return getIcon('vscode-icons:loading~spin') + } + } +} +export const codeScanState: CodeScanState = new CodeScanState() +export const onDemandFileScanState: OnDemandFileScanState = new OnDemandFileScanState() + +export class CodeScansState { + /** The initial state if scan state was not defined */ + #fallback: boolean + #onDidChangeState = new vscode.EventEmitter() + /** Set a callback for when state of code scans changes */ + onDidChangeState = this.#onDidChangeState.event + + private exceedsMonthlyQuota = false + private latestScanTime: number | undefined = undefined + + static #instance: CodeScansState + static get instance() { + return (this.#instance ??= new this()) + } + + protected constructor(fallback: boolean = false) { + this.#fallback = fallback + } + + async toggleScans() { + const autoScansEnabled = this.isScansEnabled() + const toSet: boolean = !autoScansEnabled + await globals.globalState.update('CODEWHISPERER_AUTO_SCANS_ENABLED', toSet) + this.#onDidChangeState.fire(toSet) + return toSet + } + + async setScansEnabled(isEnabled: boolean) { + if (this.isScansEnabled() !== isEnabled) { + await this.toggleScans() + } + } + + isScansEnabled(): boolean { + const isEnabled = globals.globalState.tryGet('CODEWHISPERER_AUTO_SCANS_ENABLED', Boolean) + return isEnabled !== undefined ? isEnabled : this.#fallback + } + + setMonthlyQuotaExceeded() { + this.exceedsMonthlyQuota = true + } + + isMonthlyQuotaExceeded() { + return this.exceedsMonthlyQuota + } + + setLatestScanTime(time: number) { + this.latestScanTime = time + } + + getLatestScanTime() { + return this.latestScanTime + } +} + +export class CodeScanStoppedError extends ToolkitError { + constructor() { + super('Security scan stopped by user.', { cancelled: true }) + } +} + +export interface CodeScanTelemetryEntry extends MetricBase { + codewhispererCodeScanJobId?: string + codewhispererLanguage: CodewhispererLanguage + codewhispererCodeScanProjectBytes?: number + codewhispererCodeScanSrcPayloadBytes: number + codewhispererCodeScanBuildPayloadBytes?: number + codewhispererCodeScanSrcZipFileBytes: number + codewhispererCodeScanBuildZipFileBytes?: number + codewhispererCodeScanLines: number + duration: number + contextTruncationDuration: number + artifactsUploadDuration: number + codeScanServiceInvocationsDuration: number + result: Result + reason?: string + reasonDesc?: string + codewhispererCodeScanTotalIssues: number + codewhispererCodeScanIssuesWithFixes: number + credentialStartUrl: string | undefined + codewhispererCodeScanScope: CodewhispererCodeScanScope + source?: string +} + +export interface RecommendationDescription { + text: string + markdown: string +} + +export interface Recommendation { + text: string + url: string +} + +export interface SuggestedFix { + description: string + code?: string + references?: References +} + +export interface Remediation { + recommendation: Recommendation + suggestedFixes: SuggestedFix[] +} + +export interface CodeLine { + content: string + number: number +} + +enum CodeFixStatus { + NotStarted, + Running, + Cancelling, +} + +export class CodeFixState { + // Define a constructor for this class + private codeFixState: CodeFixStatus = CodeFixStatus.NotStarted + + public isNotStarted() { + return this.codeFixState === CodeFixStatus.NotStarted + } + + public isRunning() { + return this.codeFixState === CodeFixStatus.Running + } + + public isCancelling() { + return this.codeFixState === CodeFixStatus.Cancelling + } + + public setToNotStarted() { + this.codeFixState = CodeFixStatus.NotStarted + } + + public setToCancelling() { + this.codeFixState = CodeFixStatus.Cancelling + } + + public setToRunning() { + this.codeFixState = CodeFixStatus.Running + } +} + +export const codeFixState: CodeFixState = new CodeFixState() + +/** + * Security Scan Interfaces + */ + +export interface RawCodeScanIssue { + filePath: string + startLine: number + endLine: number + title: string + description: RecommendationDescription + detectorId: string + detectorName: string + findingId: string + ruleId?: string + relatedVulnerabilities: string[] + severity: string + remediation: Remediation + codeSnippet: CodeLine[] +} + +export interface CodeScanIssue { + startLine: number + endLine: number + comment: string + title: string + description: RecommendationDescription + detectorId: string + detectorName: string + findingId: string + ruleId?: string + relatedVulnerabilities: string[] + severity: string + recommendation: Recommendation + suggestedFixes: SuggestedFix[] + visible: boolean + scanJobId: string + language: string + fixJobId?: string + autoDetected?: boolean +} + +export interface AggregatedCodeScanIssue { + filePath: string + issues: CodeScanIssue[] +} + +export interface SecurityPanelItem { + path: string + range: vscode.Range + severity: vscode.DiagnosticSeverity + message: string + issue: CodeScanIssue + decoration: vscode.DecorationOptions +} + +export interface SecurityPanelSet { + path: string + uri: vscode.Uri + items: SecurityPanelItem[] +} + +export const severities = ['Critical', 'High', 'Medium', 'Low', 'Info'] as const +export type Severity = (typeof severities)[number] + +export interface SecurityIssueFilters { + severity: { + Critical: boolean + High: boolean + Medium: boolean + Low: boolean + Info: boolean + } +} +const defaultVisibilityState: SecurityIssueFilters = { + severity: { + Critical: true, + High: true, + Medium: true, + Low: true, + Info: true, + }, +} + +export class SecurityTreeViewFilterState { + #fallback: SecurityIssueFilters + #onDidChangeState = new vscode.EventEmitter() + onDidChangeState = this.#onDidChangeState.event + + static #instance: SecurityTreeViewFilterState + static get instance() { + return (this.#instance ??= new this()) + } + + protected constructor(fallback: SecurityIssueFilters = defaultVisibilityState) { + this.#fallback = fallback + } + + public getState(): SecurityIssueFilters { + return globals.globalState.tryGet('aws.amazonq.securityIssueFilters', Object) ?? this.#fallback + } + + public async setState(state: SecurityIssueFilters) { + await globals.globalState.update('aws.amazonq.securityIssueFilters', state) + this.#onDidChangeState.fire(state) + } + + public getHiddenSeverities() { + return Object.entries(this.getState().severity) + .filter(([_, value]) => !value) + .map(([key]) => key) + } + + public resetFilters() { + return this.setState(defaultVisibilityState) + } +} + +export enum CodeIssueGroupingStrategy { + Severity = 'Severity', + FileLocation = 'FileLocation', +} +const defaultCodeIssueGroupingStrategy = CodeIssueGroupingStrategy.Severity + +export const codeIssueGroupingStrategies = Object.values(CodeIssueGroupingStrategy) +export const codeIssueGroupingStrategyLabel: Record = { + [CodeIssueGroupingStrategy.Severity]: localize('AWS.amazonq.scans.severity', 'Severity'), + [CodeIssueGroupingStrategy.FileLocation]: localize('AWS.amazonq.scans.fileLocation', 'File Location'), +} + +export class CodeIssueGroupingStrategyState { + #fallback: CodeIssueGroupingStrategy + #onDidChangeState = new vscode.EventEmitter() + onDidChangeState = this.#onDidChangeState.event + + static #instance: CodeIssueGroupingStrategyState + static get instance() { + return (this.#instance ??= new this()) + } + + protected constructor(fallback: CodeIssueGroupingStrategy = defaultCodeIssueGroupingStrategy) { + this.#fallback = fallback + } + + public getState(): CodeIssueGroupingStrategy { + const state = globals.globalState.tryGet('aws.amazonq.codescan.groupingStrategy', String) + return this.isValidGroupingStrategy(state) ? state : this.#fallback + } + + public async setState(_state: unknown) { + const state = this.isValidGroupingStrategy(_state) ? _state : this.#fallback + await globals.globalState.update('aws.amazonq.codescan.groupingStrategy', state) + this.#onDidChangeState.fire(state) + } + + private isValidGroupingStrategy(strategy: unknown): strategy is CodeIssueGroupingStrategy { + return Object.values(CodeIssueGroupingStrategy).includes(strategy as CodeIssueGroupingStrategy) + } + + public reset() { + return this.setState(this.#fallback) + } +} + +/** + * Q - Transform + */ + +// for internal use; store status of job +export enum TransformByQStatus { + NotStarted = 'Not Started', + Running = 'Running', // includes creating job, uploading code, analyzing, testing, transforming, etc. + WaitingUserInput = 'WaitingForUserInput', // The human in the loop, this period is waiting for user input to continue + Cancelled = 'Cancelled', // if user manually cancels + Failed = 'Failed', // if job is rejected or if any other error experienced; user will receive specific error message + Succeeded = 'Succeeded', + PartiallySucceeded = 'Partially Succeeded', +} + +export enum TransformationType { + LANGUAGE_UPGRADE = 'Language Upgrade', + SQL_CONVERSION = 'SQL Conversion', +} + +export enum TransformByQReviewStatus { + NotStarted = 'NotStarted', + PreparingReview = 'PreparingReview', + InReview = 'InReview', +} + +export enum StepProgress { + NotStarted = 'Not Started', + Pending = 'Pending', + Succeeded = 'Succeeded', + Failed = 'Failed', +} + +export enum JDKVersion { + JDK8 = '8', + JDK11 = '11', + JDK17 = '17', + JDK21 = '21', + UNSUPPORTED = 'UNSUPPORTED', +} + +export enum DB { + ORACLE = 'ORACLE', + RDS_POSTGRESQL = 'POSTGRESQL', + AURORA_POSTGRESQL = 'AURORA_POSTGRESQL', + OTHER = 'OTHER', +} + +export enum BuildSystem { + Maven = 'Maven', + Gradle = 'Gradle', + Unknown = 'Unknown', +} + +export class ZipManifest { + sourcesRoot: string = 'sources/' + dependenciesRoot: string = 'dependencies/' + version: string = '1.0' + hilCapabilities: string[] = ['HIL_1pDependency_VersionUpgrade'] + transformCapabilities: string[] = ['EXPLAINABILITY_V1', 'SELECTIVE_TRANSFORMATION_V2', 'CLIENT_SIDE_BUILD', 'IDE'] + noInteractiveMode: boolean = true + dependencyUpgradeConfigFile?: string = undefined + compilationsJsonFile: string = 'compilations.json' + customBuildCommand: string = 'clean test' + requestedConversions?: { + sqlConversion?: { + source?: string + target?: string + schema?: string + host?: string + sctFileName?: string + } + } +} + +export interface IHilZipManifestParams { + pomGroupId: string + pomArtifactId: string + targetPomVersion: string + dependenciesRoot?: string +} +export class HilZipManifest { + hilCapability: string = 'HIL_1pDependency_VersionUpgrade' + hilInput: IHilZipManifestParams = { + pomGroupId: '', + pomArtifactId: '', + targetPomVersion: '', + dependenciesRoot: 'dependencies/', + } + constructor({ pomGroupId, pomArtifactId, targetPomVersion }: IHilZipManifestParams) { + this.hilInput.pomGroupId = pomGroupId + this.hilInput.pomArtifactId = pomArtifactId + this.hilInput.targetPomVersion = targetPomVersion + } +} + +export enum DropdownStep { + STEP_1 = 1, + STEP_2 = 2, +} + +export const jobPlanProgress: { + uploadCode: StepProgress + buildCode: StepProgress + generatePlan: StepProgress + transformCode: StepProgress +} = { + uploadCode: StepProgress.NotStarted, + buildCode: StepProgress.NotStarted, + generatePlan: StepProgress.NotStarted, + transformCode: StepProgress.NotStarted, +} + +export let sessionJobHistory: { + [jobId: string]: { + startTime: string + projectName: string + status: string + duration: string + transformationType: string + sourceJDKVersion: string + targetJDKVersion: string + customDependencyVersionsFilePath: string + customBuildCommand: string + } +} = {} + +export class TransformByQState { + private transformByQState: TransformByQStatus = TransformByQStatus.NotStarted + + private transformationType: TransformationType | undefined = undefined + + private projectName: string = '' + private projectPath: string = '' + + private startTime: string = '' + + private jobId: string = '' + + private sourceJDKVersion: JDKVersion | undefined = undefined + + private targetJDKVersion: JDKVersion | undefined = undefined + + private jdkVersionToPath: Map = new Map() + + private customBuildCommand: string = '' + + private sourceDB: DB | undefined = undefined + + private targetDB: DB | undefined = undefined + + private schema: string = '' + + private schemaOptions: Set = new Set() + + private sourceServerName: string = '' + + private metadataPathSQL: string = '' + + private customVersionPath: string = '' + + private linesOfCodeSubmitted: number | undefined = undefined + + private planFilePath: string = '' + private summaryFilePath: string = '' + private preBuildLogFilePath: string = '' + private jobHistoryPath: string = '' + + private resultArchiveFilePath: string = '' + private projectCopyFilePath: string = '' + + private polledJobStatus: string = '' + + private hasSeenTransforming: boolean = false + + private payloadFilePath: string = '' + + private jobFailureErrorNotification: string | undefined = undefined + + private jobFailureErrorChatMessage: string | undefined = undefined + + private buildLog: string = '' + + private mavenName: string = '' + + private sourceJavaHome: string | undefined = undefined + + private targetJavaHome: string | undefined = undefined + + private chatControllers: ChatControllerEventEmitters | undefined = undefined + private chatMessenger: Messenger | undefined = undefined + + private dependencyFolderInfo: FolderInfo | undefined = undefined + + private planSteps: TransformationSteps | undefined = undefined + + private intervalId: NodeJS.Timeout | undefined = undefined + + private refreshInProgress: boolean = false + + public isNotStarted() { + return this.transformByQState === TransformByQStatus.NotStarted + } + + public isRunning() { + return this.transformByQState === TransformByQStatus.Running + } + + public isCancelled() { + return this.transformByQState === TransformByQStatus.Cancelled + } + + public isFailed() { + return this.transformByQState === TransformByQStatus.Failed + } + + public isSucceeded() { + return this.transformByQState === TransformByQStatus.Succeeded + } + + public isPartiallySucceeded() { + return this.transformByQState === TransformByQStatus.PartiallySucceeded + } + + public isRefreshInProgress() { + return this.refreshInProgress + } + + public getHasSeenTransforming() { + return this.hasSeenTransforming + } + + public getTransformationType() { + return this.transformationType + } + + public getProjectName() { + return this.projectName + } + + public getProjectPath() { + return this.projectPath + } + + public getCustomBuildCommand() { + return this.customBuildCommand + } + + public getLinesOfCodeSubmitted() { + return this.linesOfCodeSubmitted + } + + public getPreBuildLogFilePath() { + return this.preBuildLogFilePath + } + + public getStartTime() { + return this.startTime + } + + public getJobId() { + return this.jobId + } + + public getSourceJDKVersion() { + return this.sourceJDKVersion + } + + public getTargetJDKVersion() { + return this.targetJDKVersion + } + + public getPathFromJdkVersion(version: JDKVersion | undefined) { + if (version) { + return this.jdkVersionToPath.get(version) + } else { + return undefined + } + } + + public getSourceDB() { + return this.sourceDB + } + + public getTargetDB() { + return this.targetDB + } + + public getSchema() { + return this.schema + } + + public getSchemaOptions() { + return this.schemaOptions + } + + public getSourceServerName() { + return this.sourceServerName + } + + public getMetadataPathSQL() { + return this.metadataPathSQL + } + + public getCustomDependencyVersionFilePath() { + return this.customVersionPath + } + + public getStatus() { + return this.transformByQState + } + + public getPolledJobStatus() { + return this.polledJobStatus + } + + public getPlanFilePath() { + return this.planFilePath + } + + public getSummaryFilePath() { + return this.summaryFilePath + } + + public getJobHistoryPath() { + return this.jobHistoryPath + } + + public getResultArchiveFilePath() { + return this.resultArchiveFilePath + } + + public getProjectCopyFilePath() { + return this.projectCopyFilePath + } + + public getPayloadFilePath() { + return this.payloadFilePath + } + + public getJobFailureErrorNotification() { + return this.jobFailureErrorNotification + } + + public getJobFailureErrorChatMessage() { + return this.jobFailureErrorChatMessage + } + + public getBuildLog() { + return this.buildLog + } + + public getMavenName() { + return this.mavenName + } + + public getSourceJavaHome() { + return this.sourceJavaHome + } + + public getTargetJavaHome() { + return this.targetJavaHome + } + + public setJdkVersionToPath(jdkVersion: JDKVersion | undefined, path: string) { + if (jdkVersion) { + this.jdkVersionToPath.set(jdkVersion, path) + } + } + + public getChatControllers() { + return this.chatControllers + } + + public getChatMessenger() { + return this.chatMessenger + } + + public getDependencyFolderInfo(): FolderInfo | undefined { + return this.dependencyFolderInfo + } + + public getPlanSteps() { + return this.planSteps + } + + public getIntervalId() { + return this.intervalId + } + + public appendToBuildLog(message: string) { + this.buildLog += `${message}\n\n` + } + + public clearBuildLog() { + this.buildLog = '' + } + + public setToNotStarted() { + this.transformByQState = TransformByQStatus.NotStarted + } + + public setToRunning() { + this.transformByQState = TransformByQStatus.Running + } + + public setToCancelled() { + this.transformByQState = TransformByQStatus.Cancelled + } + + public setToFailed() { + this.transformByQState = TransformByQStatus.Failed + } + + public setToSucceeded() { + this.transformByQState = TransformByQStatus.Succeeded + } + + public setToPartiallySucceeded() { + this.transformByQState = TransformByQStatus.PartiallySucceeded + } + + public setRefreshInProgress(inProgress: boolean) { + this.refreshInProgress = inProgress + } + + public setHasSeenTransforming(hasSeen: boolean) { + this.hasSeenTransforming = hasSeen + } + + public setTransformationType(type: TransformationType) { + this.transformationType = type + } + + public setProjectName(name: string) { + this.projectName = name + } + + public setProjectPath(path: string) { + this.projectPath = path + } + + public setCustomBuildCommand(command: string) { + this.customBuildCommand = command + } + + public setLinesOfCodeSubmitted(lines: number) { + this.linesOfCodeSubmitted = lines + } + + public setStartTime(time: string) { + this.startTime = time + } + + public setJobId(id: string) { + this.jobId = id + } + + public setSourceJDKVersion(version: JDKVersion | undefined) { + this.sourceJDKVersion = version + } + + public setTargetJDKVersion(version: JDKVersion) { + this.targetJDKVersion = version + } + + public setSourceDB(db: DB) { + this.sourceDB = db + } + + public setTargetDB(db: DB) { + this.targetDB = db + } + + public setSchema(schema: string) { + this.schema = schema + } + + public setSchemaOptions(schemaOptions: Set) { + this.schemaOptions = schemaOptions + } + + public setSourceServerName(serverName: string) { + this.sourceServerName = serverName + } + + public setMetadataPathSQL(path: string) { + this.metadataPathSQL = path + } + + public setCustomDependencyVersionFilePath(path: string) { + this.customVersionPath = path + } + + public setPlanFilePath(filePath: string) { + this.planFilePath = filePath + } + + public setPolledJobStatus(status: string) { + this.polledJobStatus = status + } + + public setSummaryFilePath(filePath: string) { + this.summaryFilePath = filePath + } + + public setJobHistoryPath(filePath: string) { + this.jobHistoryPath = filePath + } + + public setResultArchiveFilePath(filePath: string) { + this.resultArchiveFilePath = filePath + } + + public setProjectCopyFilePath(filePath: string) { + this.projectCopyFilePath = filePath + } + + public setPayloadFilePath(payloadFilePath: string) { + this.payloadFilePath = payloadFilePath + } + + public setJobFailureErrorNotification(errorNotification: string) { + this.jobFailureErrorNotification = errorNotification + } + + public setJobFailureErrorChatMessage(errorChatMessage: string) { + this.jobFailureErrorChatMessage = errorChatMessage + } + + public setMavenName(mavenName: string) { + this.mavenName = mavenName + } + + public setSourceJavaHome(javaHome: string) { + this.sourceJavaHome = javaHome + } + + public setTargetJavaHome(javaHome: string) { + this.targetJavaHome = javaHome + } + + public setChatControllers(controllers: ChatControllerEventEmitters) { + this.chatControllers = controllers + } + + public setChatMessenger(messenger: Messenger) { + this.chatMessenger = messenger + } + + public setDependencyFolderInfo(folderInfo: FolderInfo) { + this.dependencyFolderInfo = folderInfo + } + + public setIntervalId(id: NodeJS.Timeout | undefined) { + this.intervalId = id + } + + public setPlanSteps(steps: TransformationSteps) { + this.planSteps = steps + } + + public setPreBuildLogFilePath(path: string) { + this.preBuildLogFilePath = path + } + + public resetPlanSteps() { + this.planSteps = undefined + } + + public resetSessionJobHistory() { + sessionJobHistory = {} + } + + public setJobDefaults() { + this.setToNotStarted() + this.refreshInProgress = false + this.hasSeenTransforming = false + this.jobFailureErrorNotification = undefined + this.jobFailureErrorChatMessage = undefined + this.payloadFilePath = '' + this.metadataPathSQL = '' + this.customVersionPath = '' + this.sourceJDKVersion = undefined + this.targetJDKVersion = undefined + this.sourceDB = undefined + this.targetDB = undefined + this.sourceServerName = '' + this.schemaOptions.clear() + this.schema = '' + this.buildLog = '' + this.customBuildCommand = '' + this.intervalId = undefined + this.jobHistoryPath = '' + } +} + +export const transformByQState: TransformByQState = new TransformByQState() + +export class TransformByQStoppedError extends ToolkitError { + constructor() { + super('Transform by Q stopped by user.', { cancelled: true }) + } +} + +export interface TransformationCandidateProject { + name: string + path: string + JDKVersion?: JDKVersion +} + +export interface FolderInfo { + path: string + name: string +} + +export interface Reference { + licenseName?: string + repository?: string + url?: string + recommendationContentSpan?: { + start: number + end: number + } +} + +// TODO: remove ShortAnswer because it will be deprecated +export interface ShortAnswer { + testFilePath: string + buildCommands: string[] + planSummary: string + sourceFilePath?: string + testFramework?: string + executionCommands?: string[] + testCoverage?: number + stopIteration?: string + errorMessage?: string + codeReferences?: References + numberOfTestMethods?: number +} diff --git a/packages/core/src/codewhisperer/nextEditPrediction/activation.ts b/packages/core/src/codewhisperer/nextEditPrediction/activation.ts new file mode 100644 index 00000000000..f302cec2ad5 --- /dev/null +++ b/packages/core/src/codewhisperer/nextEditPrediction/activation.ts @@ -0,0 +1,32 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { PredictionTracker } from './predictionTracker' +import { PredictionKeyStrokeHandler } from './predictionKeyStrokeHandler' +import { getLogger } from '../../shared/logger/logger' +import { ExtContext } from '../../shared/extensions' + +export let predictionTracker: PredictionTracker | undefined +let keyStrokeHandler: PredictionKeyStrokeHandler | undefined + +export function activateEditTracking(context: ExtContext): void { + try { + predictionTracker = new PredictionTracker(context.extensionContext) + + keyStrokeHandler = new PredictionKeyStrokeHandler(predictionTracker) + context.extensionContext.subscriptions.push( + vscode.Disposable.from({ + dispose: () => { + keyStrokeHandler?.dispose() + }, + }) + ) + + getLogger('nextEditPrediction').debug('Next Edit Prediction activated') + } catch (error) { + getLogger('nextEditPrediction').error(`Error in activateEditTracking: ${error}`) + } +} diff --git a/packages/core/src/codewhisperer/nextEditPrediction/diffContextGenerator.ts b/packages/core/src/codewhisperer/nextEditPrediction/diffContextGenerator.ts new file mode 100644 index 00000000000..9f379b82a4e --- /dev/null +++ b/packages/core/src/codewhisperer/nextEditPrediction/diffContextGenerator.ts @@ -0,0 +1,154 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as diff from 'diff' +import { getLogger } from '../../shared/logger/logger' +import * as codewhispererClient from '../client/codewhisperer' +import { supplementalContextMaxTotalLength, charactersLimit } from '../models/constants' + +const logger = getLogger('nextEditPrediction') + +/** + * Generates a unified diff format between old and new file contents + */ +function generateUnifiedDiffWithTimestamps( + oldFilePath: string, + newFilePath: string, + oldContent: string, + newContent: string, + oldTimestamp: number, + newTimestamp: number, + contextSize: number = 3 +): string { + const patchResult = diff.createTwoFilesPatch( + oldFilePath, + newFilePath, + oldContent, + newContent, + String(oldTimestamp), + String(newTimestamp), + { context: contextSize } + ) + + // Remove unused headers + const lines = patchResult.split('\n') + if (lines.length >= 2 && lines[0].startsWith('Index:')) { + lines.splice(0, 2) + return lines.join('\n') + } + + return patchResult +} + +export interface SnapshotContent { + filePath: string + content: string + timestamp: number +} + +/** + * Generates supplemental contexts from snapshot contents and current content + * + * @param filePath - Path to the file + * @param currentContent - Current content of the file + * @param snapshotContents - List of snapshot contents sorted by timestamp (oldest first) + * @param maxContexts - Maximum number of supplemental contexts to return + * @returns Array of SupplementalContext objects, T_0 being the snapshot of current file content: + * U0: udiff of T_0 and T_1 + * U1: udiff of T_0 and T_2 + * U2: udiff of T_0 and T_3 + */ +export function generateDiffContexts( + filePath: string, + currentContent: string, + snapshotContents: SnapshotContent[], + maxContexts: number +): codewhispererClient.SupplementalContext[] { + if (snapshotContents.length === 0) { + return [] + } + + const supplementalContexts: codewhispererClient.SupplementalContext[] = [] + const currentTimestamp = Date.now() + + for (let i = snapshotContents.length - 1; i >= 0; i--) { + const snapshot = snapshotContents[i] + try { + const unifiedDiff = generateUnifiedDiffWithTimestamps( + snapshot.filePath, + filePath, + snapshot.content, + currentContent, + snapshot.timestamp, + currentTimestamp + ) + + supplementalContexts.push({ + filePath: snapshot.filePath, + content: unifiedDiff, + type: 'PreviousEditorState', + metadata: { + previousEditorStateMetadata: { + timeOffset: currentTimestamp - snapshot.timestamp, + }, + }, + }) + } catch (err) { + logger.error(`Failed to generate diff: ${err}`) + } + } + + const trimmedContext = trimSupplementalContexts(supplementalContexts, maxContexts) + logger.debug( + `supplemental contexts: ${trimmedContext.length} contexts, total size: ${trimmedContext.reduce((sum, ctx) => sum + ctx.content.length, 0)} characters` + ) + return trimmedContext +} + +/** + * Trims the supplementalContexts array to ensure it doesn't exceed the max number + * of contexts or total character length limit + * + * @param supplementalContexts - Array of SupplementalContext objects (already sorted with newest first) + * @param maxContexts - Maximum number of supplemental contexts allowed + * @returns Trimmed array of SupplementalContext objects + */ +export function trimSupplementalContexts( + supplementalContexts: codewhispererClient.SupplementalContext[], + maxContexts: number +): codewhispererClient.SupplementalContext[] { + if (supplementalContexts.length === 0) { + return supplementalContexts + } + + // First filter out any individual context that exceeds the character limit + let result = supplementalContexts.filter((context) => { + return context.content.length <= charactersLimit + }) + + // Then limit by max number of contexts + if (result.length > maxContexts) { + result = result.slice(0, maxContexts) + } + + // Lastly enforce total character limit + let totalLength = 0 + let i = 0 + + while (i < result.length) { + totalLength += result[i].content.length + if (totalLength > supplementalContextMaxTotalLength) { + break + } + i++ + } + + if (i === result.length) { + return result + } + + const trimmedContexts = result.slice(0, i) + return trimmedContexts +} diff --git a/packages/core/src/codewhisperer/nextEditPrediction/predictionKeyStrokeHandler.ts b/packages/core/src/codewhisperer/nextEditPrediction/predictionKeyStrokeHandler.ts new file mode 100644 index 00000000000..b09272f0d8b --- /dev/null +++ b/packages/core/src/codewhisperer/nextEditPrediction/predictionKeyStrokeHandler.ts @@ -0,0 +1,117 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { PredictionTracker } from './predictionTracker' + +/** + * Monitors document changes in the editor and track them for prediction. + */ +export class PredictionKeyStrokeHandler { + private disposables: vscode.Disposable[] = [] + private tracker: PredictionTracker + private shadowCopies: Map = new Map() + + /** + * Creates a new PredictionKeyStrokeHandler + * @param context The extension context + * @param tracker The prediction tracker instance + * @param config Configuration options + */ + constructor(tracker: PredictionTracker) { + this.tracker = tracker + + // Initialize shadow copies for currently visible editors when extension starts + this.initializeVisibleDocuments() + + // Register event handlers + this.registerVisibleDocumentListener() + this.registerTextDocumentChangeListener() + } + + /** + * Initializes shadow copies for all currently visible text editors + */ + private initializeVisibleDocuments(): void { + const editors = vscode.window.visibleTextEditors + + for (const editor of editors) { + if (editor.document.uri.scheme === 'file') { + this.updateShadowCopy(editor.document) + } + } + } + + /** + * Registers listeners for visibility events to maintain shadow copies of document content + * Only store and update shadow copies for currently visible editors + * And remove shadow copies for files that are no longer visible + * And edits are processed only if a shadow copy exists + * This avoids the memory problem if hidden files are bulk edited, i.e. with global find/replace + */ + private registerVisibleDocumentListener(): void { + // Track when documents become visible (switched to) + const visibleDisposable = vscode.window.onDidChangeVisibleTextEditors((editors) => { + const currentVisibleFiles = new Set() + + for (const editor of editors) { + if (editor.document.uri.scheme === 'file') { + const filePath = editor.document.uri.fsPath + currentVisibleFiles.add(filePath) + this.updateShadowCopy(editor.document) + } + } + + for (const filePath of this.shadowCopies.keys()) { + if (!currentVisibleFiles.has(filePath)) { + this.shadowCopies.delete(filePath) + } + } + }) + + this.disposables.push(visibleDisposable) + } + + private updateShadowCopy(document: vscode.TextDocument): void { + if (document.uri.scheme === 'file') { + this.shadowCopies.set(document.uri.fsPath, document.getText()) + } + } + + /** + * Registers listener for text document changes to send to tracker + */ + private registerTextDocumentChangeListener(): void { + // Listen for document changes + const changeDisposable = vscode.workspace.onDidChangeTextDocument(async (event) => { + const filePath = event.document.uri.fsPath + const prevContent = this.shadowCopies.get(filePath) + + // Skip if there are no content changes or if the file is not visible + if ( + event.contentChanges.length === 0 || + event.document.uri.scheme !== 'file' || + prevContent === undefined + ) { + return + } + + await this.tracker.processEdit(event.document, prevContent) + this.updateShadowCopy(event.document) + }) + + this.disposables.push(changeDisposable) + } + + /** + * Disposes of all resources used by this handler + */ + public dispose(): void { + for (const disposable of this.disposables) { + disposable.dispose() + } + this.disposables = [] + } +} diff --git a/packages/core/src/codewhisperer/nextEditPrediction/predictionTracker.ts b/packages/core/src/codewhisperer/nextEditPrediction/predictionTracker.ts new file mode 100644 index 00000000000..1bfafb53114 --- /dev/null +++ b/packages/core/src/codewhisperer/nextEditPrediction/predictionTracker.ts @@ -0,0 +1,236 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger } from '../../shared/logger/logger' +import * as diffGenerator from './diffContextGenerator' +import * as codewhispererClient from '../client/codewhisperer' +import { predictionTrackerDefaultConfig } from '../models/constants' +import globals from '../../shared/extensionGlobals' + +// defaul values are stored in codewhisperer/model/constants +export interface FileTrackerConfig { + maxStorageSizeKb: number + debounceIntervalMs: number + maxAgeMs: number + maxSupplementalContext: number +} + +/** + * Represents a snapshot of a file at a specific point in time + */ +export interface FileSnapshot { + filePath: string + size: number + timestamp: number + content: string +} + +export class PredictionTracker { + private snapshots: Map = new Map() + private logger = getLogger('nextEditPrediction') + readonly config: FileTrackerConfig + private storageSize: number = 0 + + constructor(extensionContext: vscode.ExtensionContext, config?: Partial) { + this.config = { + ...predictionTrackerDefaultConfig, + ...config, + } + } + + /** + * Processes an edit to a document and takes a snapshot if needed + * @param document The document being edited + * @param previousContent The content of the document before the edit + */ + public async processEdit(document: vscode.TextDocument, previousContent: string): Promise { + const filePath = document.uri.fsPath + + try { + // Get existing snapshots for this file + const fileSnapshots = this.snapshots.get(filePath) || [] + const timestamp = globals.clock.Date.now() + + // Anti-throttling, only add snap shot after the debounce is cleared + const shouldAddSnapshot = + fileSnapshots.length === 0 || + timestamp - fileSnapshots[fileSnapshots.length - 1].timestamp > this.config.debounceIntervalMs + + if (!shouldAddSnapshot) { + return + } + + const content = previousContent + const size = Buffer.byteLength(content, 'utf8') + const snapshot: FileSnapshot = { + filePath, + size, + timestamp, + content, + } + + fileSnapshots.push(snapshot) + this.snapshots.set(filePath, fileSnapshots) + this.storageSize += size + this.logger.debug( + `Snapshot taken for file: ${filePath}, total snapshots: ${this.getTotalSnapshotCount()}, total size: ${Math.round(this.storageSize / 1024)} KB` + ) + + await this.enforceMemoryLimits() + this.enforceTimeLimits(snapshot) + } catch (err) { + this.logger.error(`Failed to save snapshot: ${err}`) + } + } + + /** + * Sets up a timeout to delete the given snapshot after it exceeds the max age + */ + private enforceTimeLimits(snapshot: FileSnapshot): void { + const fileSnapshots = this.snapshots.get(snapshot.filePath) + if (fileSnapshots === undefined) { + return + } + + setTimeout(() => { + // find the snapshot and remove it + const index = fileSnapshots.indexOf(snapshot) + if (index !== -1) { + fileSnapshots.splice(index, 1) + this.storageSize -= snapshot.size + if (fileSnapshots.length === 0) { + this.snapshots.delete(snapshot.filePath) + } + this.logger.debug( + `Snapshot deleted (aged out) for file: ${snapshot.filePath}, remaining snapshots: ${this.getTotalSnapshotCount()}, new size: ${Math.round(this.storageSize / 1024)} KB` + ) + } + }, this.config.maxAgeMs) + } + + /** + * Enforces memory limits by removing old snapshots if necessary + */ + private async enforceMemoryLimits(): Promise { + while (this.storageSize > this.config.maxStorageSizeKb * 1024) { + const oldestFile = this.findOldestFile() + if (!oldestFile) { + break + } + + const fileSnapshots = this.snapshots.get(oldestFile) + if (!fileSnapshots || fileSnapshots.length === 0) { + this.snapshots.delete(oldestFile) + continue + } + + const removedSnapshot = fileSnapshots.shift() + if (removedSnapshot) { + this.storageSize -= removedSnapshot.size + this.logger.debug( + `Snapshot deleted (memory limit) for file: ${removedSnapshot.filePath}, remaining snapshots: ${this.getTotalSnapshotCount()}, new size: ${Math.round(this.storageSize / 1024)} KB` + ) + } + + if (fileSnapshots.length === 0) { + this.snapshots.delete(oldestFile) + } + } + } + + /** + * Finds the file with the oldest snapshot + * @returns The file path of the oldest snapshot + */ + private findOldestFile(): string | undefined { + let oldestTime = Number.MAX_SAFE_INTEGER + let oldestFile: string | undefined + + for (const [filePath, snapshots] of this.snapshots.entries()) { + if (snapshots.length === 0) { + continue + } + + const oldestSnapshot = snapshots[0] + if (oldestSnapshot.timestamp < oldestTime) { + oldestTime = oldestSnapshot.timestamp + oldestFile = filePath + } + } + + return oldestFile + } + + /** + * Gets all snapshots for a specific file + * @param filePath The path to the file + * @returns Array of snapshots for the file + */ + public getFileSnapshots(filePath: string): FileSnapshot[] { + return this.snapshots.get(filePath) || [] + } + + /** + * Gets all tracked files + * @returns Array of file paths + */ + public getTrackedFiles(): string[] { + return Array.from(this.snapshots.keys()) + } + + public getTotalSnapshotCount(): number { + return Array.from(this.snapshots.values()).reduce((count, snapshots) => count + snapshots.length, 0) + } + + public async getSnapshotContent(snapshot: FileSnapshot): Promise { + return snapshot.content + } + + /** + * Generates unified diffs between adjacent snapshots of a file + * and between the newest snapshot and the current file content + * + * @returns Array of SupplementalContext objects containing diffs between snapshots and current content + */ + public async generatePredictionSupplementalContext(): Promise { + try { + const activeEditor = vscode.window.activeTextEditor + if (activeEditor === undefined) { + return [] + } + const filePath = activeEditor.document.uri.fsPath + const currentContent = activeEditor.document.getText() + const snapshots = this.getFileSnapshots(filePath) + + if (snapshots.length === 0) { + return [] + } + + // Create SnapshotContent array from snapshots + const snapshotContents: diffGenerator.SnapshotContent[] = snapshots.map((snapshot) => ({ + filePath: snapshot.filePath, + content: snapshot.content, + timestamp: snapshot.timestamp, + })) + + // Use the diffGenerator module to generate supplemental contexts + return diffGenerator.generateDiffContexts( + filePath, + currentContent, + snapshotContents, + this.config.maxSupplementalContext + ) + } catch (err) { + // this ensures we are not breaking inline requests + this.logger.error(`Failed to generate prediction supplemental context: ${err}`) + return [] + } + } + + public getTotalSize() { + return this.storageSize + } +} diff --git a/packages/core/src/codewhisperer/paginators.json b/packages/core/src/codewhisperer/paginators.json new file mode 100644 index 00000000000..19508654dad --- /dev/null +++ b/packages/core/src/codewhisperer/paginators.json @@ -0,0 +1,19 @@ +{ + "pagination": { + "ListCodeScanFindings": { + "input_token": "nextToken", + "output_token": "nextToken" + }, + "ListProfiles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "profiles" + }, + "ListRecommendations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + } + } +} diff --git a/packages/core/src/codewhisperer/region/regionProfileManager.ts b/packages/core/src/codewhisperer/region/regionProfileManager.ts new file mode 100644 index 00000000000..aab2ed04dab --- /dev/null +++ b/packages/core/src/codewhisperer/region/regionProfileManager.ts @@ -0,0 +1,416 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getIcon } from '../../shared/icons' +import { DataQuickPickItem } from '../../shared/ui/pickerPrompter' +import { CodeWhispererConfig, RegionProfile } from '../models/model' +import { showConfirmationMessage } from '../../shared/utilities/messages' +import { + Connection, + isBuilderIdConnection, + isIdcSsoConnection, + isSsoConnection, + SsoConnection, +} from '../../auth/connection' +import globals from '../../shared/extensionGlobals' +import { once } from '../../shared/utilities/functionUtils' +import CodeWhispererUserClient from '../client/codewhispereruserclient' +import { AwsCredentialIdentity } from '@aws-sdk/types' +import { Service } from 'aws-sdk' +import { ServiceOptions } from '../../shared/awsClientBuilder' +import userApiConfig = require('../client/user-service-2.json') +import { createConstantMap } from '../../shared/utilities/tsUtils' +import { getLogger } from '../../shared/logger/logger' +import { pageableToCollection } from '../../shared/utilities/collectionUtils' +import { parse } from '@aws-sdk/util-arn-parser' +import { isAwsError, ToolkitError } from '../../shared/errors' +import { telemetry } from '../../shared/telemetry/telemetry' +import { localize } from '../../shared/utilities/vsCodeUtils' +import { Commands } from '../../shared/vscode/commands2' +import { CachedResource } from '../../shared/utilities/resourceCache' + +// TODO: is there a better way to manage all endpoint strings in one place? +export const defaultServiceConfig: CodeWhispererConfig = { + region: 'us-east-1', + endpoint: 'https://codewhisperer.us-east-1.amazonaws.com/', +} + +// Hack until we have a single discovery endpoint. We will call each endpoint one by one to fetch profile before then. +const endpoints = createConstantMap({ + 'us-east-1': 'https://q.us-east-1.amazonaws.com/', + 'eu-central-1': 'https://q.eu-central-1.amazonaws.com/', +}) + +/** + * 'user' -> users change the profile through Q menu + * 'auth' -> users change the profile through webview profile selector page + * 'update' -> plugin auto select the profile on users' behalf as there is only 1 profile + * 'reload' -> on plugin restart, plugin will try to reload previous selected profile + */ +export type ProfileSwitchIntent = 'user' | 'auth' | 'update' | 'reload' | 'customization' + +export type ProfileChangedEvent = { + profile: RegionProfile | undefined + intent: ProfileSwitchIntent +} + +export class RegionProfileManager { + private static logger = getLogger() + private _activeRegionProfile: RegionProfile | undefined + private _onDidChangeRegionProfile = new vscode.EventEmitter() + public readonly onDidChangeRegionProfile = this._onDidChangeRegionProfile.event + + // Store the last API results (for UI propuse) so we don't need to call service again if doesn't require "latest" result + private _profiles: RegionProfile[] = [] + + private readonly cache = new (class extends CachedResource { + constructor(private readonly profileProvider: () => Promise) { + super( + 'aws.amazonq.regionProfiles.cache', + 3600000, + { + resource: { + locked: false, + timestamp: 0, + result: undefined, + }, + }, + { timeout: 15000, interval: 500, truthy: true } + ) + } + + override resourceProvider(): Promise { + return this.profileProvider() + } + })(this.listRegionProfile.bind(this)) + + get activeRegionProfile() { + const conn = this.connectionProvider() + if (isBuilderIdConnection(conn)) { + return undefined + } + return this._activeRegionProfile + } + + get clientConfig(): CodeWhispererConfig { + const conn = this.connectionProvider() + if (!conn) { + throw new ToolkitError('trying to get client configuration without credential') + } + + // builder id should simply use default IAD + if (isBuilderIdConnection(conn)) { + return defaultServiceConfig + } + + // idc + const p = this.activeRegionProfile + if (p) { + const region = p.region + const endpoint = endpoints.get(p.region) + if (endpoint === undefined) { + RegionProfileManager.logger.error( + `Not found endpoint for region ${region}, not able to initialize a codewhisperer client` + ) + throw new ToolkitError(`Q client configuration error, endpoint not found for region ${region}`) + } + return { + region: region, + endpoint: endpoint, + } + } + + return defaultServiceConfig + } + + get profiles(): RegionProfile[] { + return this._profiles + } + + constructor(private readonly connectionProvider: () => Connection | undefined) {} + + async getProfiles(): Promise { + return this.cache.getResource() + } + + async listRegionProfile(): Promise { + this._profiles = [] + + const conn = this.connectionProvider() + if (conn === undefined || !isSsoConnection(conn)) { + return [] + } + const availableProfiles: RegionProfile[] = [] + const failedRegions: string[] = [] + + for (const [region, endpoint] of endpoints.entries()) { + const client = await this._createQClient(region, endpoint, conn as SsoConnection) + const requester = async (request: CodeWhispererUserClient.ListAvailableProfilesRequest) => + client.listAvailableProfiles(request).promise() + const request: CodeWhispererUserClient.ListAvailableProfilesRequest = {} + try { + const profiles = await pageableToCollection(requester, request, 'nextToken', 'profiles') + .flatten() + .promise() + const mappedPfs = profiles.map((it) => { + let accntId = '' + try { + accntId = parse(it.arn).accountId + } catch (e) {} + + return { + name: it.profileName, + region: region, + arn: it.arn, + description: accntId, + } + }) + + availableProfiles.push(...mappedPfs) + RegionProfileManager.logger.debug(`Found ${mappedPfs.length} profiles in region ${region}`) + } catch (e) { + const logMsg = isAwsError(e) ? `requestId=${e.requestId}; message=${e.message}` : (e as Error).message + RegionProfileManager.logger.error(`Failed to list profiles for region ${region}: ${logMsg}`) + failedRegions.push(region) + } + } + + // Throw error if any regional API calls failed and no profiles are available + if (failedRegions.length > 0 && availableProfiles.length === 0) { + throw new ToolkitError(`Failed to list Q Developer profiles for regions: ${failedRegions.join(', ')}`, { + code: 'ListQDeveloperProfilesFailed', + }) + } + + // Throw an error if all listAvailableProfile calls succeeded, but user has no Q developer profiles + // This is not an expected state + if (failedRegions.length === 0 && availableProfiles.length === 0) { + throw new ToolkitError('This user has no Q Developer profiles', { code: 'QDeveloperProfileNotFound' }) + } + + this._profiles = availableProfiles + return availableProfiles + } + + async switchRegionProfile(regionProfile: RegionProfile | undefined, source: ProfileSwitchIntent) { + const conn = this.connectionProvider() + if (conn === undefined || !isIdcSsoConnection(conn)) { + return + } + + if (regionProfile && this.activeRegionProfile && regionProfile.arn === this.activeRegionProfile.arn) { + return + } + + // TODO: make it typesafe + const ssoConn = this.connectionProvider() as SsoConnection + + // only prompt to users when users switch from A profile to B profile + if (source !== 'customization' && this.activeRegionProfile !== undefined && regionProfile !== undefined) { + const response = await showConfirmationMessage({ + prompt: localize( + 'AWS.amazonq.profile.confirmation', + "Do you want to change your Q Developer profile to '{0}'?\n When you change profiles, you will no longer have access to your current customizations, chats, code reviews, or any other code or content being generated by Amazon Q", + regionProfile?.name + ), + confirm: 'Switch profiles', + cancel: 'Cancel', + type: 'info', + }) + + if (!response) { + telemetry.amazonq_didSelectProfile.emit({ + source: source, + amazonQProfileRegion: this.activeRegionProfile?.region ?? 'not-set', + ssoRegion: ssoConn.ssoRegion, + result: 'Cancelled', + credentialStartUrl: ssoConn.startUrl, + profileCount: this.profiles.length, + }) + return + } + } + + if (source === 'reload' || source === 'update') { + telemetry.amazonq_profileState.emit({ + source: source, + amazonQProfileRegion: regionProfile?.region ?? 'not-set', + result: 'Succeeded', + }) + } else { + telemetry.amazonq_didSelectProfile.emit({ + source: source, + amazonQProfileRegion: regionProfile?.region ?? 'not-set', + ssoRegion: ssoConn.ssoRegion, + result: 'Succeeded', + credentialStartUrl: ssoConn.startUrl, + profileCount: this.profiles.length, + }) + } + + await this._switchRegionProfile(regionProfile, source) + } + + private async _switchRegionProfile(regionProfile: RegionProfile | undefined, source: ProfileSwitchIntent) { + this._activeRegionProfile = regionProfile + + this._onDidChangeRegionProfile.fire({ + profile: regionProfile, + intent: source, + }) + // dont show if it's a default (fallback) + if (regionProfile && this.profiles.length > 1) { + void vscode.window.showInformationMessage(`You are using the ${regionProfile.name} profile for Q.`).then() + } + + // persist to state + await this.persistSelectRegionProfile() + + // Force status bar to reflect this change in state + await Commands.tryExecute('aws.amazonq.refreshStatusBar') + } + + restoreProfileSelection = once(async () => { + const conn = this.connectionProvider() + if (conn) { + await this.restoreRegionProfile(conn) + } + }) + + // Note: should be called after [AuthUtil.instance.conn] returns non null + async restoreRegionProfile(conn: Connection) { + const previousSelected = this.loadPersistedRegionProfle()[conn.id] || undefined + if (!previousSelected) { + return + } + + await this.switchRegionProfile(previousSelected, 'reload') + } + + private loadPersistedRegionProfle(): { [label: string]: RegionProfile } { + const previousPersistedState = globals.globalState.tryGet<{ [label: string]: RegionProfile }>( + 'aws.amazonq.regionProfiles', + Object, + {} + ) + + return previousPersistedState + } + + async persistSelectRegionProfile() { + const conn = this.connectionProvider() + + // default has empty arn and shouldn't be persisted because it's just a fallback + if (!conn || this.activeRegionProfile === undefined) { + return + } + + // persist connectionId to profileArn + const previousPersistedState = globals.globalState.tryGet<{ [label: string]: RegionProfile }>( + 'aws.amazonq.regionProfiles', + Object, + {} + ) + + previousPersistedState[conn.id] = this.activeRegionProfile + await globals.globalState.update('aws.amazonq.regionProfiles', previousPersistedState) + } + + async generateQuickPickItem(): Promise[]> { + const selected = this.activeRegionProfile + let profiles: RegionProfile[] = [] + try { + profiles = await this.getProfiles() + } catch (e) { + return [ + { + label: '[Failed to list available profiles]', + detail: `${(e as Error).message}`, + data: '', + }, + ] + } + const icon = getIcon('vscode-account') + const quickPickItems: DataQuickPickItem[] = profiles.map((it) => { + const label = it.name + const onClick = async () => { + await this.switchRegionProfile(it, 'user') + } + const data = it.arn + const description = it.region + const isRecentlyUsed = selected ? selected.arn === it.arn : false + + return { + label: `${icon} ${label}`, + onClick: onClick, + data: data, + description: description, + recentlyUsed: isRecentlyUsed, + detail: it.description, + } + }) + + return quickPickItems + } + + async invalidateProfile(arn: string | undefined) { + if (arn) { + if (this.activeRegionProfile && this.activeRegionProfile.arn === arn) { + this._activeRegionProfile = undefined + } + + const profiles = this.loadPersistedRegionProfle() + const updatedProfiles = Object.fromEntries( + Object.entries(profiles).filter(([connId, profile]) => profile.arn !== arn) + ) + await globals.globalState.update('aws.amazonq.regionProfiles', updatedProfiles) + } + } + + // Should be called on connection changed in case users change to a differnet connection and use the wrong resultset. + async clearCache() { + await this.cache.clearCache() + } + + // TODO: Should maintain sdk client in a better way + async createQClient(profile: RegionProfile): Promise { + const conn = this.connectionProvider() + if (conn === undefined || !isSsoConnection(conn)) { + throw new Error('No valid SSO connection') + } + const endpoint = endpoints.get(profile.region) + if (!endpoint) { + throw new Error(`trying to initiatize Q client with unrecognizable region ${profile.region}`) + } + return this._createQClient(profile.region, endpoint, conn) + } + + // Visible for testing only, do not use this directly, please use createQClient(profile) + async _createQClient(region: string, endpoint: string, conn: SsoConnection): Promise { + const token = (await conn.getToken()).accessToken + const serviceOption: ServiceOptions = { + apiConfig: userApiConfig, + region: region, + endpoint: endpoint, + credentials: { accessKeyId: 'xxx', secretAccessKey: 'xxx' } as AwsCredentialIdentity, + onRequestSetup: [ + (req) => { + req.on('build', ({ httpRequest }) => { + httpRequest.headers['Authorization'] = `Bearer ${token}` + }) + }, + ], + } as ServiceOptions + + const c = (await globals.sdkClientBuilder.createAwsService( + Service, + serviceOption, + undefined + )) as CodeWhispererUserClient + + return c + } +} diff --git a/packages/core/src/codewhisperer/region/utils.ts b/packages/core/src/codewhisperer/region/utils.ts new file mode 100644 index 00000000000..dd988f74a30 --- /dev/null +++ b/packages/core/src/codewhisperer/region/utils.ts @@ -0,0 +1,49 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as nls from 'vscode-nls' +const localize = nls.loadMessageBundle() +import { AmazonQPromptSettings } from '../../shared/settings' +import { telemetry } from '../../shared/telemetry/telemetry' +import vscode from 'vscode' +import { selectRegionProfileCommand } from '../commands/basicCommands' +import { placeholder } from '../../shared/vscode/commands2' +import { toastMessage } from '../commands/types' + +/** + * Creates a toast message telling the user they need to select a Developer Profile + */ +export async function notifySelectDeveloperProfile() { + const suppressId = 'amazonQSelectDeveloperProfile' + const settings = AmazonQPromptSettings.instance + const shouldShow = settings.isPromptEnabled(suppressId) + if (!shouldShow) { + return + } + + const message = localize( + 'aws.amazonq.profile.mustSelectMessage', + 'You must select a Q Developer Profile for Amazon Q features to work.' + ) + const selectProfile = 'Select Profile' + const dontShowAgain = 'Dont Show Again' + + await telemetry.toolkit_showNotification.run(async () => { + telemetry.record({ id: 'mustSelectDeveloperProfileMessage' }) + void vscode.window.showWarningMessage(message, selectProfile, dontShowAgain).then(async (resp) => { + await telemetry.toolkit_invokeAction.run(async () => { + if (resp === selectProfile) { + // Show Profile + telemetry.record({ action: 'select' }) + void selectRegionProfileCommand.execute(placeholder, toastMessage) + } else if (resp === dontShowAgain) { + telemetry.record({ action: 'dontShowAgain' }) + await settings.disablePrompt(suppressId) + } else { + telemetry.record({ action: 'ignore' }) + } + }) + }) + }) +} diff --git a/packages/core/src/codewhisperer/service/classifierTrigger.ts b/packages/core/src/codewhisperer/service/classifierTrigger.ts new file mode 100644 index 00000000000..842d5312e68 --- /dev/null +++ b/packages/core/src/codewhisperer/service/classifierTrigger.ts @@ -0,0 +1,609 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import os from 'os' +import * as vscode from 'vscode' +import { CodewhispererAutomatedTriggerType } from '../../shared/telemetry/telemetry' +import { extractContextForCodeWhisperer } from '../util/editorContext' +import { TelemetryHelper } from '../util/telemetryHelper' +import { ProgrammingLanguage } from '../client/codewhispereruserclient' + +interface normalizedCoefficients { + readonly lineNum: number + readonly lenLeftCur: number + readonly lenLeftPrev: number + readonly lenRight: number +} +/* + uses ML classifier to determine if user input should trigger CWSPR service + */ +export class ClassifierTrigger { + static #instance: ClassifierTrigger + + public static get instance() { + return (this.#instance ??= new this()) + } + + // ML classifier trigger threshold + private triggerThreshold = 0.43 + + // ML classifier coefficients + // os coefficient + private osCoefficientMap: Readonly> = { + 'Mac OS X': -0.1552, + 'Windows 10': -0.0238, + Windows: 0.0412, + win32: -0.0559, + } + + // trigger type coefficient + private triggerTypeCoefficientMap: Readonly> = { + SpecialCharacters: 0.0209, + Enter: 0.2853, + } + + private languageCoefficientMap: Readonly> = { + java: -0.4622, + javascript: -0.4688, + python: -0.3052, + typescript: -0.6084, + tsx: -0.6084, + jsx: -0.4688, + shell: -0.4718, + ruby: -0.7356, + sql: -0.4937, + rust: -0.4309, + kotlin: -0.4739, + php: -0.3917, + csharp: -0.3475, + go: -0.3504, + scala: -0.534, + cpp: -0.1734, + json: 0, + yaml: -0.3, + tf: -0.55, + } + + // other metadata coefficient + private lineNumCoefficient = -0.0416 + private lengthOfLeftCurrentCoefficient = -1.1747 + private lengthOfLeftPrevCoefficient = 0.4033 + private lengthOfRightCoefficient = -0.3321 + private prevDecisionAcceptCoefficient = 0.5397 + private prevDecisionRejectCoefficient = -0.1656 + private prevDecisionOtherCoefficient = 0 + private ideVscode = -0.1905 + private lengthLeft0To5 = -0.8756 + private lengthLeft5To10 = -0.5463 + private lengthLeft10To20 = -0.4081 + private lengthLeft20To30 = -0.3272 + private lengthLeft30To40 = -0.2442 + private lengthLeft40To50 = -0.1471 + + // intercept of logistic regression classifier + private intercept = 0.3738713 + + private maxx: normalizedCoefficients = { + lineNum: 4631.0, + lenLeftCur: 157.0, + lenLeftPrev: 176.0, + lenRight: 10239.0, + } + + private minn: normalizedCoefficients = { + lineNum: 0.0, + lenLeftCur: 0.0, + lenLeftPrev: 0.0, + lenRight: 0.0, + } + + // character and keywords coefficient + private charCoefficient: Readonly> = { + throw: 1.5868, + ';': -1.268, + any: -1.1565, + '7': -1.1347, + false: -1.1307, + nil: -1.0653, + elif: 1.0122, + '9': -1.0098, + pass: -1.0058, + True: -1.0002, + False: -0.9434, + '6': -0.9222, + true: -0.9142, + None: -0.9027, + '8': -0.9013, + break: -0.8475, + '}': -0.847, + '5': -0.8414, + '4': -0.8197, + '1': -0.8085, + '\\': -0.8019, + static: -0.7748, + '0': -0.77, + end: -0.7617, + '(': 0.7239, + '/': -0.7104, + where: -0.6981, + readonly: -0.6741, + async: -0.6723, + '3': -0.654, + continue: -0.6413, + struct: -0.64, + try: -0.6369, + float: -0.6341, + using: 0.6079, + '@': 0.6016, + '|': 0.5993, + impl: 0.5808, + private: -0.5746, + for: 0.5741, + '2': -0.5634, + let: -0.5187, + foreach: 0.5186, + select: -0.5148, + export: -0.5, + mut: -0.4921, + ')': -0.463, + ']': -0.4611, + when: 0.4602, + virtual: -0.4583, + extern: -0.4465, + catch: 0.4446, + new: 0.4394, + val: -0.4339, + map: 0.4284, + case: 0.4271, + throws: 0.4221, + null: -0.4197, + protected: -0.4133, + q: 0.4125, + except: 0.4115, + ': ': 0.4072, + '^': -0.407, + ' ': 0.4066, + $: 0.3981, + this: 0.3962, + switch: 0.3947, + '*': -0.3931, + module: 0.3912, + array: 0.385, + '=': 0.3828, + p: 0.3728, + ON: 0.3708, + '`': 0.3693, + u: 0.3658, + a: 0.3654, + require: 0.3646, + '>': -0.3644, + const: -0.3476, + o: 0.3423, + sizeof: 0.3416, + object: 0.3362, + w: 0.3345, + print: 0.3344, + range: 0.3336, + if: 0.3324, + abstract: -0.3293, + var: -0.3239, + i: 0.321, + while: 0.3138, + J: 0.3137, + c: 0.3118, + await: -0.3072, + from: 0.3057, + f: 0.302, + echo: 0.2995, + '#': 0.2984, + e: 0.2962, + r: 0.2925, + mod: 0.2893, + loop: 0.2874, + t: 0.2832, + '~': 0.282, + final: -0.2816, + del: 0.2785, + override: -0.2746, + ref: -0.2737, + h: 0.2693, + m: 0.2681, + '{': 0.2674, + implements: 0.2672, + inline: -0.2642, + match: 0.2613, + with: -0.261, + x: 0.2597, + namespace: -0.2596, + operator: 0.2573, + double: -0.2563, + source: -0.2482, + import: -0.2419, + NULL: -0.2399, + l: 0.239, + or: 0.2378, + s: 0.2366, + then: 0.2354, + W: 0.2354, + y: 0.2333, + local: 0.2288, + is: 0.2282, + n: 0.2254, + '+': -0.2251, + G: 0.223, + public: -0.2229, + WHERE: 0.2224, + list: 0.2204, + Q: 0.2204, + '[': 0.2136, + VALUES: 0.2134, + H: 0.2105, + g: 0.2094, + else: -0.208, + bool: -0.2066, + long: -0.2059, + R: 0.2025, + S: 0.2021, + d: 0.2003, + V: 0.1974, + K: -0.1961, + '<': 0.1958, + debugger: -0.1929, + NOT: -0.1911, + b: 0.1907, + boolean: -0.1891, + z: -0.1866, + LIKE: -0.1793, + raise: 0.1782, + L: 0.1768, + fn: 0.176, + delete: 0.1714, + unsigned: -0.1675, + auto: -0.1648, + finally: 0.1616, + k: 0.1599, + as: 0.156, + instanceof: 0.1558, + '&': 0.1554, + E: 0.1551, + M: 0.1542, + I: 0.1503, + Y: 0.1493, + typeof: 0.1475, + j: 0.1445, + INTO: 0.1442, + IF: 0.1437, + next: 0.1433, + undef: -0.1427, + THEN: -0.1416, + v: 0.1415, + C: 0.1383, + P: 0.1353, + AND: -0.1345, + constructor: 0.1337, + void: -0.1336, + class: -0.1328, + defer: 0.1316, + begin: 0.1306, + FROM: -0.1304, + SET: 0.1291, + decimal: -0.1278, + friend: 0.1277, + SELECT: -0.1265, + event: 0.1259, + lambda: 0.1253, + enum: 0.1215, + A: 0.121, + lock: 0.1187, + ensure: 0.1184, + '%': 0.1177, + isset: 0.1175, + O: 0.1174, + '.': 0.1146, + UNION: -0.1145, + alias: -0.1129, + template: -0.1102, + WHEN: 0.1093, + rescue: 0.1083, + DISTINCT: -0.1074, + trait: -0.1073, + D: 0.1062, + in: 0.1045, + internal: -0.1029, + ',': 0.1027, + static_cast: 0.1016, + do: -0.1005, + OR: 0.1003, + AS: -0.1001, + interface: 0.0996, + super: 0.0989, + B: 0.0963, + U: 0.0962, + T: 0.0943, + CALL: -0.0918, + BETWEEN: -0.0915, + N: 0.0897, + yield: 0.0867, + done: -0.0857, + string: -0.0837, + out: -0.0831, + volatile: -0.0819, + retry: 0.0816, + '?': -0.0796, + number: -0.0791, + short: 0.0787, + sealed: -0.0776, + package: 0.0765, + OPEN: -0.0756, + base: 0.0735, + and: 0.0729, + exit: 0.0726, + _: 0.0721, + keyof: -0.072, + def: 0.0713, + crate: -0.0706, + '-': -0.07, + FUNCTION: 0.0692, + declare: -0.0678, + include: 0.0671, + COUNT: -0.0669, + INDEX: -0.0666, + CLOSE: -0.0651, + fi: -0.0644, + uint: 0.0624, + params: 0.0575, + HAVING: 0.0575, + byte: -0.0575, + clone: -0.0552, + char: -0.054, + func: 0.0538, + never: -0.053, + unset: -0.0524, + unless: -0.051, + esac: -0.0509, + shift: -0.0507, + require_once: 0.0486, + ELSE: -0.0477, + extends: 0.0461, + elseif: 0.0452, + mutable: -0.0451, + asm: 0.0449, + '!': 0.0446, + LIMIT: 0.0444, + ushort: -0.0438, + '"': -0.0433, + Z: 0.0431, + exec: -0.0431, + IS: -0.0429, + DECLARE: -0.0425, + __LINE__: -0.0424, + BEGIN: -0.0418, + typedef: 0.0414, + EXIT: -0.0412, + "'": 0.041, + function: -0.0393, + dyn: -0.039, + wchar_t: -0.0388, + unique: -0.0383, + include_once: 0.0367, + stackalloc: 0.0359, + RETURN: -0.0356, + const_cast: 0.035, + MAX: 0.0341, + assert: -0.0331, + JOIN: -0.0328, + use: 0.0318, + GET: 0.0317, + VIEW: 0.0314, + move: 0.0308, + typename: 0.0308, + die: 0.0305, + asserts: -0.0304, + reinterpret_cast: -0.0302, + USING: -0.0289, + elsif: -0.0285, + FIRST: -0.028, + self: -0.0278, + RETURNING: -0.0278, + symbol: -0.0273, + OFFSET: 0.0263, + bigint: 0.0253, + register: -0.0237, + union: -0.0227, + return: -0.0227, + until: -0.0224, + endfor: -0.0213, + implicit: -0.021, + LOOP: 0.0195, + pub: 0.0182, + global: 0.0179, + EXCEPTION: 0.0175, + delegate: 0.0173, + signed: -0.0163, + FOR: 0.0156, + unsafe: 0.014, + NEXT: -0.0133, + IN: 0.0129, + MIN: -0.0123, + go: -0.0112, + type: -0.0109, + explicit: -0.0107, + eval: -0.0104, + int: -0.0099, + CASE: -0.0096, + END: 0.0084, + UPDATE: 0.0074, + default: 0.0072, + chan: 0.0068, + fixed: 0.0066, + not: -0.0052, + X: -0.0047, + endforeach: 0.0031, + goto: 0.0028, + empty: 0.0022, + checked: 0.0012, + F: -0.001, + } + + public getThreshold() { + return this.triggerThreshold + } + + public recordClassifierResultForManualTrigger(editor: vscode.TextEditor) { + this.shouldTriggerFromClassifier(undefined, editor, undefined, true) + } + + public recordClassifierResultForAutoTrigger( + editor: vscode.TextEditor, + triggerType?: CodewhispererAutomatedTriggerType, + event?: vscode.TextDocumentChangeEvent + ) { + if (!triggerType) { + return + } + this.shouldTriggerFromClassifier(event, editor, triggerType, true) + } + + public shouldTriggerFromClassifier( + event: vscode.TextDocumentChangeEvent | undefined, + editor: vscode.TextEditor, + autoTriggerType: string | undefined, + shouldRecordResult: boolean = false + ): boolean { + const fileContext = extractContextForCodeWhisperer(editor) + const osPlatform = this.normalizeOsName(os.platform(), os.version()) + const char = event ? event.contentChanges[0].text : '' + const lineNum = editor.selection.active.line + const classifierResult = this.getClassifierResult( + fileContext.leftFileContent, + fileContext.rightFileContent, + osPlatform, + autoTriggerType, + char, + lineNum, + fileContext.programmingLanguage + ) + + const threshold = this.getThreshold() + + const shouldTrigger = classifierResult > threshold + if (shouldRecordResult) { + TelemetryHelper.instance.setClassifierResult(classifierResult) + TelemetryHelper.instance.setClassifierThreshold(threshold) + } + return shouldTrigger + } + + private getClassifierResult( + leftContext: string, + rightContext: string, + os: string, + triggerType: string | undefined, + char: string, + lineNum: number, + language: ProgrammingLanguage + ): number { + const leftContextLines = leftContext.split(/\r?\n/) + const leftContextAtCurrentLine = leftContextLines[leftContextLines.length - 1] + const tokens = leftContextAtCurrentLine.trim().split(' ') + let keyword = '' + const lastToken = tokens[tokens.length - 1] + if (lastToken && lastToken.length > 1) { + keyword = lastToken + } + const lengthOfLeftCurrent = leftContextLines[leftContextLines.length - 1].length + const lengthOfLeftPrev = leftContextLines[leftContextLines.length - 2]?.length ?? 0 + const lengthOfRight = rightContext.trim().length + + const triggerTypeCoefficient: number = this.triggerTypeCoefficientMap[triggerType || ''] ?? 0 + const osCoefficient: number = this.osCoefficientMap[os] ?? 0 + const charCoefficient: number = this.charCoefficient[char] ?? 0 + const keyWordCoefficient: number = this.charCoefficient[keyword] ?? 0 + const ideCoefficient = this.ideVscode + + const previousDecision = TelemetryHelper.instance.getLastTriggerDecisionForClassifier() + const languageCoefficients = Object.values(this.languageCoefficientMap) + const avrgCoefficient = + languageCoefficients.length > 0 + ? languageCoefficients.reduce((a, b) => a + b) / languageCoefficients.length + : 0 + const languageCoefficient = this.languageCoefficientMap[language.languageName] ?? avrgCoefficient + + let previousDecisionCoefficient = 0 + if (previousDecision === 'Accept') { + previousDecisionCoefficient = this.prevDecisionAcceptCoefficient + } else if (previousDecision === 'Reject') { + previousDecisionCoefficient = this.prevDecisionRejectCoefficient + } else if (previousDecision === 'Discard' || previousDecision === 'Empty') { + previousDecisionCoefficient = this.prevDecisionOtherCoefficient + } + + let leftContextLengthCoefficient = 0 + if (leftContext.length >= 0 && leftContext.length < 5) { + leftContextLengthCoefficient = this.lengthLeft0To5 + } else if (leftContext.length >= 5 && leftContext.length < 10) { + leftContextLengthCoefficient = this.lengthLeft5To10 + } else if (leftContext.length >= 10 && leftContext.length < 20) { + leftContextLengthCoefficient = this.lengthLeft10To20 + } else if (leftContext.length >= 20 && leftContext.length < 30) { + leftContextLengthCoefficient = this.lengthLeft20To30 + } else if (leftContext.length >= 30 && leftContext.length < 40) { + leftContextLengthCoefficient = this.lengthLeft30To40 + } else if (leftContext.length >= 40 && leftContext.length < 50) { + leftContextLengthCoefficient = this.lengthLeft40To50 + } + + const result = + (this.lengthOfRightCoefficient * (lengthOfRight - this.minn.lenRight)) / + (this.maxx.lenRight - this.minn.lenRight) + + (this.lengthOfLeftCurrentCoefficient * (lengthOfLeftCurrent - this.minn.lenLeftCur)) / + (this.maxx.lenLeftCur - this.minn.lenLeftCur) + + (this.lengthOfLeftPrevCoefficient * (lengthOfLeftPrev - this.minn.lenLeftPrev)) / + (this.maxx.lenLeftPrev - this.minn.lenLeftPrev) + + (this.lineNumCoefficient * (lineNum - this.minn.lineNum)) / (this.maxx.lineNum - this.minn.lineNum) + + osCoefficient + + triggerTypeCoefficient + + charCoefficient + + keyWordCoefficient + + ideCoefficient + + this.intercept + + previousDecisionCoefficient + + languageCoefficient + + leftContextLengthCoefficient + + return sigmoid(result) + } + + private normalizeOsName(name: string, version: string | undefined): string { + const lowercaseName = name.toLowerCase() + if (lowercaseName.includes('windows')) { + if (!version) { + return 'Windows' + } else if (version.includes('Windows NT 10') || version.startsWith('10')) { + return 'Windows 10' + } else if (version.includes('6.1')) { + return 'Windows 7' + } else if (version.includes('6.3')) { + return 'Windows 8.1' + } else { + return 'Windows' + } + } else if ( + lowercaseName.includes('macos') || + lowercaseName.includes('mac os') || + lowercaseName.includes('darwin') + ) { + return 'Mac OS X' + } else if (lowercaseName.includes('linux')) { + return 'Linux' + } else { + return name + } + } +} + +const sigmoid = (x: number) => { + return 1 / (1 + Math.exp(-x)) +} diff --git a/packages/core/src/codewhisperer/service/codeFixHandler.ts b/packages/core/src/codewhisperer/service/codeFixHandler.ts new file mode 100644 index 00000000000..68dc25e7cf3 --- /dev/null +++ b/packages/core/src/codewhisperer/service/codeFixHandler.ts @@ -0,0 +1,127 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CodeWhispererUserClient, RegionProfile } from '../indexNode' +import * as CodeWhispererConstants from '../models/constants' +import { codeFixState } from '../models/model' +import { ArtifactMap, CreateUploadUrlRequest, DefaultCodeWhispererClient } from '../client/codewhisperer' +import { + CodeFixJobStoppedError, + CodeFixJobTimedOutError, + CreateCodeFixError, + CreateUploadUrlError, + MonthlyCodeFixLimitError, +} from '../models/errors' +import { uploadArtifactToS3 } from './securityScanHandler' +import { getLogger } from '../../shared/logger/logger' +import { isAwsError } from '../../shared/errors' +import { sleep } from '../../shared/utilities/timeoutUtils' + +export async function getPresignedUrlAndUpload( + client: DefaultCodeWhispererClient, + zipFilePath: string, + codeFixName: string, + profile: RegionProfile | undefined +) { + const srcReq: CreateUploadUrlRequest = { + artifactType: 'SourceCode', + uploadIntent: CodeWhispererConstants.codeFixUploadIntent, + uploadContext: { codeFixUploadContext: { codeFixName } }, + profileArn: profile?.arn, + } + getLogger().verbose(`Prepare for uploading src context...`) + const srcResp = await client.createUploadUrl(srcReq).catch((err) => { + getLogger().error('Failed getting presigned url for uploading src context. %O', err) + throw new CreateUploadUrlError(err.message) + }) + getLogger().verbose(`CreateUploadUrlRequest requestId: ${srcResp.$response.requestId}`) + getLogger().verbose(`Complete Getting presigned Url for uploading src context.`) + getLogger().verbose(`Uploading src context...`) + await uploadArtifactToS3(zipFilePath, srcResp, CodeWhispererConstants.FeatureUseCase.CODE_SCAN) + getLogger().verbose(`Complete uploading src context.`) + const artifactMap: ArtifactMap = { + SourceCode: srcResp.uploadId, + } + return artifactMap +} + +export async function createCodeFixJob( + client: DefaultCodeWhispererClient, + uploadId: string, + snippetRange: CodeWhispererUserClient.Range, + description: string, + referenceTrackerConfiguration: CodeWhispererUserClient.ReferenceTrackerConfiguration, + codeFixName?: string, + ruleId?: string, + profile?: RegionProfile +) { + getLogger().verbose(`Creating code fix job...`) + const req: CodeWhispererUserClient.StartCodeFixJobRequest = { + uploadId, + snippetRange, + codeFixName, + ruleId, + description, + referenceTrackerConfiguration, + profileArn: profile?.arn, + } + + const resp = await client.startCodeFixJob(req).catch((err) => { + getLogger().error('Failed creating code fix job. %O', err) + if (isAwsError(err) && err.code === 'ThrottlingException' && err.message.includes('reached for this month')) { + throw new MonthlyCodeFixLimitError() + } + throw new CreateCodeFixError() + }) + getLogger().info(`AmazonQ generate fix Request id: ${resp.$response.requestId}`) + return resp +} + +export async function pollCodeFixJobStatus(client: DefaultCodeWhispererClient, jobId: string, profile?: RegionProfile) { + const pollingStartTime = performance.now() + await sleep(CodeWhispererConstants.codeFixJobPollingDelayMs) + + getLogger().verbose(`Polling code fix job status...`) + let status: string | undefined = 'InProgress' + while (true) { + throwIfCancelled() + const req: CodeWhispererUserClient.GetCodeFixJobRequest = { + jobId, + profileArn: profile?.arn, + } + const resp = await client.getCodeFixJob(req) + getLogger().verbose(`GetCodeFixJobRequest requestId: ${resp.$response.requestId}`) + if (resp.jobStatus !== 'InProgress') { + status = resp.jobStatus + getLogger().verbose(`Code fix job status: ${status}`) + getLogger().verbose(`Complete polling code fix job status.`) + break + } + throwIfCancelled() + await sleep(CodeWhispererConstants.codeFixJobPollingIntervalMs) + const elapsedTime = performance.now() - pollingStartTime + if (elapsedTime > CodeWhispererConstants.codeFixJobTimeoutMs) { + getLogger().verbose(`Code fix job status: ${status}`) + getLogger().verbose(`Code fix job failed. Amazon Q timed out.`) + throw new CodeFixJobTimedOutError() + } + } + return status +} + +export async function getCodeFixJob(client: DefaultCodeWhispererClient, jobId: string, profile?: RegionProfile) { + const req: CodeWhispererUserClient.GetCodeFixJobRequest = { + jobId, + profileArn: profile?.arn, + } + const resp = await client.getCodeFixJob(req) + return resp +} + +export function throwIfCancelled() { + if (codeFixState.isCancelling()) { + throw new CodeFixJobStoppedError() + } +} diff --git a/packages/core/src/codewhisperer/service/diagnosticsProvider.ts b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts new file mode 100644 index 00000000000..b7a950bba5c --- /dev/null +++ b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts @@ -0,0 +1,170 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { CodeScanIssue, AggregatedCodeScanIssue } from '../models/model' +import { CodeAnalysisScope, codewhispererDiagnosticSourceLabel } from '../models/constants' +import { SecurityIssueTreeViewProvider } from './securityIssueTreeViewProvider' +import { SecurityIssueProvider } from './securityIssueProvider' + +export interface SecurityDiagnostic extends vscode.Diagnostic { + findingId?: string +} + +interface SecurityScanRender { + securityDiagnosticCollection: vscode.DiagnosticCollection | undefined + initialized: boolean +} + +export const securityScanRender: SecurityScanRender = { + securityDiagnosticCollection: undefined, + initialized: false, +} + +export function initSecurityScanRender( + securityRecommendationList: AggregatedCodeScanIssue[], + editor: vscode.TextEditor | undefined, + scope: CodeAnalysisScope, + fromQCA: boolean = true +) { + // fromQCA parameter is used to determine if the findings are coming from QCA or from displayFindings tool. + // if the incoming findings are from QCA review, then keep only existing findings from displayFindings + // if the incoming findings are not from QCA review, then keep only the existing QCA findings + securityScanRender.securityDiagnosticCollection = createSecurityDiagnosticCollection() + securityScanRender.initialized = false + if (scope === CodeAnalysisScope.FILE_ON_DEMAND && editor) { + securityScanRender.securityDiagnosticCollection?.delete(editor.document.uri) + } else if (scope === CodeAnalysisScope.PROJECT) { + securityScanRender.securityDiagnosticCollection?.clear() + } + for (const securityRecommendation of securityRecommendationList) { + updateSecurityDiagnosticCollection(securityRecommendation) + updateSecurityIssuesForProviders(securityRecommendation, scope === CodeAnalysisScope.FILE_AUTO, fromQCA) + } + securityScanRender.initialized = true +} + +function updateSecurityIssuesForProviders( + securityRecommendation: AggregatedCodeScanIssue, + isAutoScope?: boolean, + fromQCA: boolean = true +) { + if (isAutoScope) { + SecurityIssueProvider.instance.mergeIssues(securityRecommendation) + } else { + SecurityIssueProvider.instance.mergeIssuesDisplayFindings(securityRecommendation, fromQCA) + } + SecurityIssueTreeViewProvider.instance.refresh() +} + +export function updateSecurityDiagnosticCollection(securityRecommendation: AggregatedCodeScanIssue) { + const filePath = securityRecommendation.filePath + const uri = vscode.Uri.file(filePath) + const securityDiagnosticCollection = createSecurityDiagnosticCollection() + const securityDiagnostics: vscode.Diagnostic[] = vscode.languages + .getDiagnostics(uri) + .filter((diagnostic) => diagnostic.source === codewhispererDiagnosticSourceLabel) + for (const securityIssue of securityRecommendation.issues) { + const existingDiagnosticIndex = securityDiagnostics.findIndex( + (diagnostic) => + (diagnostic.message === securityIssue.title && + diagnostic.range.start.line === securityIssue.startLine && + diagnostic.range.end.line === securityIssue.endLine) || + (diagnostic.message === 'Re-scan to validate the fix: ' + securityIssue.title && + diagnostic.range.start.line === securityIssue.startLine && + diagnostic.range.end.line === securityIssue.startLine) + ) + if (existingDiagnosticIndex !== -1) { + securityDiagnostics.splice(existingDiagnosticIndex, 1) + } + if (securityIssue.visible) { + securityDiagnostics.push(createSecurityDiagnostic(securityIssue)) + } + } + securityDiagnosticCollection.set(uri, securityDiagnostics) +} + +export function createSecurityDiagnostic(securityIssue: CodeScanIssue) { + const range = new vscode.Range(securityIssue.startLine, 0, securityIssue.endLine, 0) + const securityDiagnostic: SecurityDiagnostic = new vscode.Diagnostic( + range, + securityIssue.title, + vscode.DiagnosticSeverity.Warning + ) + securityDiagnostic.source = codewhispererDiagnosticSourceLabel + securityDiagnostic.code = securityIssue.ruleId + securityDiagnostic.findingId = securityIssue.findingId + return securityDiagnostic +} + +export function createSecurityDiagnosticCollection() { + if (securityScanRender.securityDiagnosticCollection === undefined) { + securityScanRender.securityDiagnosticCollection = + vscode.languages.createDiagnosticCollection('Amazon Q Security Scan') + } + return securityScanRender.securityDiagnosticCollection +} + +export function disposeSecurityDiagnostic(event: vscode.TextDocumentChangeEvent) { + const uri = event.document.uri + if (!securityScanRender.initialized || !securityScanRender.securityDiagnosticCollection?.has(uri)) { + return + } + const currentSecurityDiagnostics = securityScanRender.securityDiagnosticCollection?.get(uri) + const newSecurityDiagnostics: vscode.Diagnostic[] = [] + + const { changedRange, changedText, lineOffset } = event.contentChanges.reduce( + (acc, change) => ({ + changedRange: acc.changedRange.union(change.range), + changedText: acc.changedText + change.text, + lineOffset: acc.lineOffset + getLineOffset(change.range, change.text), + }), + { + changedRange: event.contentChanges[0].range, + changedText: '', + lineOffset: 0, + } + ) + + if (currentSecurityDiagnostics) { + for (const issue of currentSecurityDiagnostics) { + const intersection = changedRange.intersection(issue.range) + if ( + issue.severity === vscode.DiagnosticSeverity.Warning && + intersection && + (/\S/.test(changedText) || changedText === '') + ) { + issue.severity = vscode.DiagnosticSeverity.Information + issue.message = 'Re-scan to validate the fix: ' + issue.message + issue.range = new vscode.Range(intersection.start, intersection.start) + } else if (issue.range.start.line >= changedRange.end.line) { + issue.range = new vscode.Range( + issue.range.start.line + lineOffset, + issue.range.start.character, + issue.range.end.line + lineOffset, + issue.range.end.character + ) + } + newSecurityDiagnostics.push(issue) + } + } + securityScanRender.securityDiagnosticCollection?.set(uri, newSecurityDiagnostics) +} + +function getLineOffset(range: vscode.Range, text: string) { + const originLines = range.end.line - range.start.line + 1 + const changedLines = text.split('\n').length + return changedLines - originLines +} + +export function removeDiagnostic(uri: vscode.Uri, issue: CodeScanIssue) { + const currentSecurityDiagnostics = securityScanRender.securityDiagnosticCollection?.get(uri) + if (currentSecurityDiagnostics) { + const newSecurityDiagnostics = currentSecurityDiagnostics.filter((diagnostic: SecurityDiagnostic) => { + return diagnostic.findingId !== issue.findingId + }) + securityScanRender.securityDiagnosticCollection?.set(uri, newSecurityDiagnostics) + } +} diff --git a/packages/core/src/codewhisperer/service/importAdderProvider.ts b/packages/core/src/codewhisperer/service/importAdderProvider.ts new file mode 100644 index 00000000000..98b7c36adfd --- /dev/null +++ b/packages/core/src/codewhisperer/service/importAdderProvider.ts @@ -0,0 +1,132 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { Recommendation } from '../client/codewhisperer' +import { CodeWhispererSettings } from '../util/codewhispererSettings' +import { findLineToInsertImportStatement } from '../util/importAdderUtil' +import { application } from '../util/codeWhispererApplication' + +/** + * ImportAdderProvider + * For each code suggestion provided by CodeWhisperer, + * it may contain un-imported variables. In this case, + * the backend will also return the missing imports in the response + * This provide will render a codeLens hint on screen to inform user + * that once user accept the recommendation, import statement will also be inserted. + */ +export class ImportAdderProvider implements vscode.CodeLensProvider { + private codeLens: vscode.CodeLens | undefined + + private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter() + public readonly onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event + // This is the vsc languageIds that import adder service supports. + private readonly supportedLanguages: string[] = ['java', 'javascript', 'javascriptreact', 'python'] + + static #instance: ImportAdderProvider + + constructor() { + application().clearCodeWhispererUIListener((_) => { + this.clear() + }) + } + + public static get instance() { + return (this.#instance ??= new this()) + } + + public async onAcceptRecommendation( + editor: vscode.TextEditor, + r: Recommendation | undefined, + firstLineOfRecommendation: number + ) { + this.clear() + if (this.isNotEnabled(editor.document.languageId)) { + return + } + if ( + r && + 'mostRelevantMissingImports' in r && + r.mostRelevantMissingImports && + r.mostRelevantMissingImports!.length > 0 + ) { + const line = findLineToInsertImportStatement(editor, firstLineOfRecommendation) + let mergedStatements = `` + // eslint-disable-next-line unicorn/no-array-for-each + r.mostRelevantMissingImports?.forEach(async (i) => { + // trust service response that this to-be-added import is necessary + if (i.statement) { + mergedStatements += i.statement + '\n' + } + }) + await editor.edit( + (builder) => { + builder.insert(new vscode.Position(line, 0), mergedStatements) + }, + { undoStopAfter: true, undoStopBefore: true } + ) + } + } + + private isNotEnabled(languageId: string): boolean { + return ( + !this.supportedLanguages.includes(languageId) || + !CodeWhispererSettings.instance.isImportRecommendationEnabled() + ) + } + + public onShowRecommendation( + document: vscode.TextDocument, + line: number, + r: Recommendation + ): vscode.CodeLens | undefined { + if (this.isNotEnabled(document.languageId)) { + return undefined + } + this.codeLens = undefined + // show it under the inline toolbar if current line is not the last line + line = document.lineCount > line + 1 ? line + 1 : line + if ( + 'mostRelevantMissingImports' in r && + r.mostRelevantMissingImports !== undefined && + r.mostRelevantMissingImports.length > 0 + ) { + const n = r.mostRelevantMissingImports.length + const stmt = r.mostRelevantMissingImports[0].statement + let message = '' + if (n === 1) { + message = `If you accept, "${stmt}" will also be added.` + } else if (n === 2) { + message = `If you accept, "${stmt}" and 1 other import will also be added.` + } else { + message = `If you accept, "${stmt}" and ${n - 1} other imports will also be added.` + } + const codeLens = new vscode.CodeLens(new vscode.Range(line, 0, line, 1)) + codeLens.command = { + title: message, + tooltip: 'Import statement', + command: '', + } + this.codeLens = codeLens + } + this._onDidChangeCodeLenses.fire() + return this.codeLens + } + + public clear() { + this.codeLens = undefined + this._onDidChangeCodeLenses.fire() + } + + public provideCodeLenses( + document: vscode.TextDocument, + token: vscode.CancellationToken + ): vscode.CodeLens[] | Thenable { + if (this.codeLens) { + return [this.codeLens] + } + return [] + } +} diff --git a/packages/core/src/codewhisperer/service/inlineCompletionItemProvider.ts b/packages/core/src/codewhisperer/service/inlineCompletionItemProvider.ts new file mode 100644 index 00000000000..a6c424c321d --- /dev/null +++ b/packages/core/src/codewhisperer/service/inlineCompletionItemProvider.ts @@ -0,0 +1,194 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import vscode, { Position } from 'vscode' +import { getPrefixSuffixOverlap } from '../util/commonUtil' +import { Recommendation } from '../client/codewhisperer' +import { session } from '../util/codeWhispererSession' +import { TelemetryHelper } from '../util/telemetryHelper' +import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { ReferenceInlineProvider } from './referenceInlineProvider' +import { ImportAdderProvider } from './importAdderProvider' +import { application } from '../util/codeWhispererApplication' +import path from 'path' +import { UserWrittenCodeTracker } from '../tracker/userWrittenCodeTracker' + +export class CWInlineCompletionItemProvider implements vscode.InlineCompletionItemProvider { + private activeItemIndex: number | undefined + private nextMove: number + private recommendations: Recommendation[] + private requestId: string + private startPos: Position + private nextToken: string + + private _onDidShow: vscode.EventEmitter = new vscode.EventEmitter() + public readonly onDidShow: vscode.Event = this._onDidShow.event + + public constructor( + itemIndex: number | undefined, + firstMove: number, + recommendations: Recommendation[], + requestId: string, + startPos: Position, + nextToken: string + ) { + this.activeItemIndex = itemIndex + this.nextMove = firstMove + this.recommendations = recommendations + this.requestId = requestId + this.startPos = startPos + this.nextToken = nextToken + } + + get getActiveItemIndex() { + return this.activeItemIndex + } + + public clearActiveItemIndex() { + this.activeItemIndex = undefined + } + + // iterate suggestions and stop at index 0 or index len - 1 + private getIteratingIndexes() { + const len = this.recommendations.length + const startIndex = this.activeItemIndex ? this.activeItemIndex : 0 + const index = [] + if (this.nextMove === 0) { + for (let i = 0; i < len; i++) { + index.push((startIndex + i) % len) + } + } else if (this.nextMove === -1) { + for (let i = startIndex - 1; i >= 0; i--) { + index.push(i) + } + index.push(startIndex) + } else { + for (let i = startIndex + 1; i < len; i++) { + index.push(i) + } + index.push(startIndex) + } + return index + } + + truncateOverlapWithRightContext(document: vscode.TextDocument, suggestion: string, pos: vscode.Position): string { + const trimmedSuggestion = suggestion.trim() + // limit of 5000 for right context matching + const rightContext = document.getText(new vscode.Range(pos, document.positionAt(document.offsetAt(pos) + 5000))) + const overlap = getPrefixSuffixOverlap(trimmedSuggestion, rightContext) + const overlapIndex = suggestion.lastIndexOf(overlap) + if (overlapIndex >= 0) { + const truncated = suggestion.slice(0, overlapIndex) + return truncated.trim().length ? truncated : '' + } else { + return suggestion + } + } + + getInlineCompletionItem( + document: vscode.TextDocument, + r: Recommendation, + start: vscode.Position, + end: vscode.Position, + index: number, + prefix: string + ): vscode.InlineCompletionItem | undefined { + if (!r.content.startsWith(prefix)) { + return undefined + } + const effectiveStart = document.positionAt(document.offsetAt(start) + prefix.length) + const truncatedSuggestion = this.truncateOverlapWithRightContext(document, r.content, end) + if (truncatedSuggestion.length === 0) { + if (session.getSuggestionState(index) !== 'Showed') { + session.setSuggestionState(index, 'Discard') + } + return undefined + } + TelemetryHelper.instance.lastSuggestionInDisplay = truncatedSuggestion + return { + insertText: truncatedSuggestion, + range: new vscode.Range(start, end), + command: { + command: 'aws.amazonq.accept', + title: 'On acceptance', + arguments: [ + new vscode.Range(start, end), + new vscode.Range(effectiveStart, end), + index, + truncatedSuggestion, + this.requestId, + session.sessionId, + session.triggerType, + session.getCompletionType(index), + runtimeLanguageContext.getLanguageContext(document.languageId, path.extname(document.fileName)) + .language, + r.references, + ], + }, + } + } + + // the returned completion items will always only contain one valid item + // this is to trace the current index of visible completion item + // so that reference tracker can show + // This hack can be removed once inlineCompletionAdditions API becomes public + provideInlineCompletionItems( + document: vscode.TextDocument, + position: vscode.Position, + _context: vscode.InlineCompletionContext, + _token: vscode.CancellationToken + ): vscode.ProviderResult { + if (position.line < 0 || position.isBefore(this.startPos)) { + application()._clearCodeWhispererUIListener.fire() + this.activeItemIndex = undefined + return + } + + // There's a chance that the startPos is no longer valid in the current document (e.g. + // when CodeWhisperer got triggered by 'Enter', the original startPos is with indentation + // but then this indentation got removed by VSCode when another new line is inserted, + // before the code reaches here). In such case, we need to update the startPos to be a + // valid one. Otherwise, inline completion which utilizes this position will function + // improperly. + const start = document.validatePosition(this.startPos) + const end = position + const iteratingIndexes = this.getIteratingIndexes() + const prefix = document.getText(new vscode.Range(start, end)).replace(/\r\n/g, '\n') + const matchedCount = session.recommendations.filter( + (r) => r.content.length > 0 && r.content.startsWith(prefix) && r.content !== prefix + ).length + for (const i of iteratingIndexes) { + const r = session.recommendations[i] + const item = this.getInlineCompletionItem(document, r, start, end, i, prefix) + if (item === undefined) { + continue + } + this.activeItemIndex = i + session.setSuggestionState(i, 'Showed') + ReferenceInlineProvider.instance.setInlineReference(this.startPos.line, r.content, r.references) + ImportAdderProvider.instance.onShowRecommendation(document, this.startPos.line, r) + this.nextMove = 0 + TelemetryHelper.instance.setFirstSuggestionShowTime() + session.setPerceivedLatency() + UserWrittenCodeTracker.instance.onQStartsMakingEdits() + this._onDidShow.fire() + if (matchedCount >= 2 || this.nextToken !== '') { + const result = [item] + for (let j = 0; j < matchedCount - 1; j++) { + result.push({ + insertText: `${ + typeof item.insertText === 'string' ? item.insertText : item.insertText.value + }${j}`, + range: item.range, + }) + } + return result + } + return [item] + } + application()._clearCodeWhispererUIListener.fire() + this.activeItemIndex = undefined + return [] + } +} diff --git a/packages/core/src/codewhisperer/service/inlineCompletionService.ts b/packages/core/src/codewhisperer/service/inlineCompletionService.ts new file mode 100644 index 00000000000..cd37663af49 --- /dev/null +++ b/packages/core/src/codewhisperer/service/inlineCompletionService.ts @@ -0,0 +1,163 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { CodeSuggestionsState, ConfigurationEntry, GetRecommendationsResponse, vsCodeState } from '../models/model' +import * as CodeWhispererConstants from '../models/constants' +import { DefaultCodeWhispererClient } from '../client/codewhisperer' +import { RecommendationHandler } from './recommendationHandler' +import { CodewhispererAutomatedTriggerType, CodewhispererTriggerType } from '../../shared/telemetry/telemetry' +import { showTimedMessage } from '../../shared/utilities/messages' +import { getLogger } from '../../shared/logger/logger' +import { TelemetryHelper } from '../util/telemetryHelper' +import { AuthUtil } from '../util/authUtil' +import { shared } from '../../shared/utilities/functionUtils' +import { ClassifierTrigger } from './classifierTrigger' +import { session } from '../util/codeWhispererSession' +import { noSuggestions } from '../models/constants' +import { CodeWhispererStatusBarManager } from './statusBar' + +export class InlineCompletionService { + private maxPage = 100 + private statusBar: CodeWhispererStatusBarManager + private _showRecommendationTimer?: NodeJS.Timer + + constructor(statusBar: CodeWhispererStatusBarManager = CodeWhispererStatusBarManager.instance) { + this.statusBar = statusBar + + RecommendationHandler.instance.onDidReceiveRecommendation((e) => { + this.startShowRecommendationTimer() + }) + + CodeSuggestionsState.instance.onDidChangeState(() => { + return this.statusBar.refreshStatusBar() + }) + } + + static #instance: InlineCompletionService + + public static get instance() { + return (this.#instance ??= new this()) + } + + filePath(): string | undefined { + return RecommendationHandler.instance.documentUri?.fsPath + } + + private sharedTryShowRecommendation = shared( + RecommendationHandler.instance.tryShowRecommendation.bind(RecommendationHandler.instance) + ) + + private startShowRecommendationTimer() { + if (this._showRecommendationTimer) { + clearInterval(this._showRecommendationTimer) + this._showRecommendationTimer = undefined + } + this._showRecommendationTimer = setInterval(() => { + const delay = Date.now() - vsCodeState.lastUserModificationTime + if (delay < CodeWhispererConstants.inlineSuggestionShowDelay) { + return + } + this.sharedTryShowRecommendation() + .catch((e) => { + getLogger().error('tryShowRecommendation failed: %s', (e as Error).message) + }) + .finally(() => { + if (this._showRecommendationTimer) { + clearInterval(this._showRecommendationTimer) + this._showRecommendationTimer = undefined + } + }) + }, CodeWhispererConstants.showRecommendationTimerPollPeriod) + } + + async getPaginatedRecommendation( + client: DefaultCodeWhispererClient, + editor: vscode.TextEditor, + triggerType: CodewhispererTriggerType, + config: ConfigurationEntry, + autoTriggerType?: CodewhispererAutomatedTriggerType, + event?: vscode.TextDocumentChangeEvent + ): Promise { + if (vsCodeState.isCodeWhispererEditing || RecommendationHandler.instance.isSuggestionVisible()) { + return { + result: 'Failed', + errorMessage: 'Amazon Q is already running', + recommendationCount: 0, + } + } + + // Call report user decisions once to report recommendations leftover from last invocation. + RecommendationHandler.instance.reportUserDecisions(-1) + TelemetryHelper.instance.setInvokeSuggestionStartTime() + ClassifierTrigger.instance.recordClassifierResultForAutoTrigger(editor, autoTriggerType, event) + + const triggerChar = event?.contentChanges[0]?.text + if (autoTriggerType === 'SpecialCharacters' && triggerChar) { + TelemetryHelper.instance.setTriggerCharForUserTriggerDecision(triggerChar) + } + const isAutoTrigger = triggerType === 'AutoTrigger' + if (AuthUtil.instance.isConnectionExpired()) { + await AuthUtil.instance.notifyReauthenticate(isAutoTrigger) + return { + result: 'Failed', + errorMessage: 'auth', + recommendationCount: 0, + } + } + + await this.statusBar.setLoading() + + RecommendationHandler.instance.checkAndResetCancellationTokens() + RecommendationHandler.instance.documentUri = editor.document.uri + let response: GetRecommendationsResponse = { + result: 'Failed', + errorMessage: undefined, + recommendationCount: 0, + } + try { + let page = 0 + while (page < this.maxPage) { + response = await RecommendationHandler.instance.getRecommendations( + client, + editor, + triggerType, + config, + autoTriggerType, + true, + page + ) + if (RecommendationHandler.instance.checkAndResetCancellationTokens()) { + RecommendationHandler.instance.reportUserDecisions(-1) + await vscode.commands.executeCommand('aws.amazonq.refreshStatusBar') + if (triggerType === 'OnDemand' && session.recommendations.length === 0) { + void showTimedMessage(response.errorMessage ? response.errorMessage : noSuggestions, 2000) + } + return { + result: 'Failed', + errorMessage: 'cancelled', + recommendationCount: 0, + } + } + if (!RecommendationHandler.instance.hasNextToken()) { + break + } + page++ + } + } catch (error) { + getLogger().error(`Error ${error} in getPaginatedRecommendation`) + } + await vscode.commands.executeCommand('aws.amazonq.refreshStatusBar') + if (triggerType === 'OnDemand' && session.recommendations.length === 0) { + void showTimedMessage(response.errorMessage ? response.errorMessage : noSuggestions, 2000) + } + TelemetryHelper.instance.tryRecordClientComponentLatency() + + return { + result: 'Succeeded', + errorMessage: undefined, + recommendationCount: session.recommendations.length, + } + } +} diff --git a/packages/core/src/codewhisperer/service/keyStrokeHandler.ts b/packages/core/src/codewhisperer/service/keyStrokeHandler.ts new file mode 100644 index 00000000000..312e31c248a --- /dev/null +++ b/packages/core/src/codewhisperer/service/keyStrokeHandler.ts @@ -0,0 +1,267 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { DefaultCodeWhispererClient } from '../client/codewhisperer' +import * as CodeWhispererConstants from '../models/constants' +import { ConfigurationEntry } from '../models/model' +import { getLogger } from '../../shared/logger/logger' +import { RecommendationHandler } from './recommendationHandler' +import { CodewhispererAutomatedTriggerType } from '../../shared/telemetry/telemetry' +import { getTabSizeSetting } from '../../shared/utilities/editorUtilities' +import { isInlineCompletionEnabled } from '../util/commonUtil' +import { ClassifierTrigger } from './classifierTrigger' +import { extractContextForCodeWhisperer } from '../util/editorContext' +import { RecommendationService } from './recommendationService' + +/** + * This class is for CodeWhisperer auto trigger + */ +export class KeyStrokeHandler { + /** + * Special character which automated triggers codewhisperer + */ + public specialChar: string + /** + * Key stroke count for automated trigger + */ + + private idleTriggerTimer?: NodeJS.Timer + + public lastInvocationTime?: number + + constructor() { + this.specialChar = '' + } + + static #instance: KeyStrokeHandler + + public static get instance() { + return (this.#instance ??= new this()) + } + + public startIdleTimeTriggerTimer( + event: vscode.TextDocumentChangeEvent, + editor: vscode.TextEditor, + client: DefaultCodeWhispererClient, + config: ConfigurationEntry + ) { + if (this.idleTriggerTimer) { + clearInterval(this.idleTriggerTimer) + this.idleTriggerTimer = undefined + } + if (!this.shouldTriggerIdleTime()) { + return + } + this.idleTriggerTimer = setInterval(() => { + const duration = (Date.now() - RecommendationHandler.instance.lastInvocationTime) / 1000 + if (duration < CodeWhispererConstants.invocationTimeIntervalThreshold) { + return + } + + this.invokeAutomatedTrigger('IdleTime', editor, client, config, event) + .catch((e) => { + getLogger().error('invokeAutomatedTrigger failed: %s', (e as Error).message) + }) + .finally(() => { + if (this.idleTriggerTimer) { + clearInterval(this.idleTriggerTimer) + this.idleTriggerTimer = undefined + } + }) + }, CodeWhispererConstants.idleTimerPollPeriod) + } + + public shouldTriggerIdleTime(): boolean { + if (isInlineCompletionEnabled() && RecommendationService.instance.isRunning) { + return false + } + return true + } + + async processKeyStroke( + event: vscode.TextDocumentChangeEvent, + editor: vscode.TextEditor, + client: DefaultCodeWhispererClient, + config: ConfigurationEntry + ): Promise { + try { + if (!config.isAutomatedTriggerEnabled) { + return + } + + // Skip when output channel gains focus and invoke + if (editor.document.languageId === 'Log') { + return + } + + const { rightFileContent } = extractContextForCodeWhisperer(editor) + const rightContextLines = rightFileContent.split(/\r?\n/) + const rightContextAtCurrentLine = rightContextLines[0] + // we do not want to trigger when there is immediate right context on the same line + // with "}" being an exception because of IDE auto-complete + if ( + rightContextAtCurrentLine.length && + !rightContextAtCurrentLine.startsWith(' ') && + rightContextAtCurrentLine.trim() !== '}' && + rightContextAtCurrentLine.trim() !== ')' + ) { + return + } + + let triggerType: CodewhispererAutomatedTriggerType | undefined + const changedSource = new DefaultDocumentChangedType(event.contentChanges).checkChangeSource() + + switch (changedSource) { + case DocumentChangedSource.EnterKey: { + triggerType = 'Enter' + break + } + case DocumentChangedSource.SpecialCharsKey: { + triggerType = 'SpecialCharacters' + break + } + case DocumentChangedSource.RegularKey: { + triggerType = ClassifierTrigger.instance.shouldTriggerFromClassifier(event, editor, triggerType) + ? 'Classifier' + : undefined + break + } + default: { + break + } + } + + if (triggerType) { + await this.invokeAutomatedTrigger(triggerType, editor, client, config, event) + } + } catch (error) { + getLogger().verbose(`Automated Trigger Exception : ${error}`) + } + } + + async invokeAutomatedTrigger( + autoTriggerType: CodewhispererAutomatedTriggerType, + editor: vscode.TextEditor, + client: DefaultCodeWhispererClient, + config: ConfigurationEntry, + event: vscode.TextDocumentChangeEvent + ): Promise { + if (!editor) { + return + } + + // RecommendationHandler.instance.reportUserDecisionOfRecommendation(editor, -1) + await RecommendationService.instance.generateRecommendation( + client, + editor, + 'AutoTrigger', + config, + autoTriggerType + ) + } +} + +export abstract class DocumentChangedType { + constructor(protected readonly contentChanges: ReadonlyArray) { + this.contentChanges = contentChanges + } + + abstract checkChangeSource(): DocumentChangedSource + + // Enter key should always start with ONE '\n' or '\r\n' and potentially following spaces due to IDE reformat + protected isEnterKey(str: string): boolean { + if (str.length === 0) { + return false + } + return ( + (str.startsWith('\r\n') && str.substring(2).trim() === '') || + (str[0] === '\n' && str.substring(1).trim() === '') + ) + } + + // Tab should consist of space char only ' ' and the length % tabSize should be 0 + protected isTabKey(str: string): boolean { + const tabSize = getTabSizeSetting() + if (str.length % tabSize === 0 && str.trim() === '') { + return true + } + return false + } + + protected isUserTypingSpecialChar(str: string): boolean { + return ['(', '()', '[', '[]', '{', '{}', ':'].includes(str) + } + + protected isSingleLine(str: string): boolean { + let newLineCounts = 0 + for (const ch of str) { + if (ch === '\n') { + newLineCounts += 1 + } + } + + // since pressing Enter key possibly will generate string like '\n ' due to indention + if (this.isEnterKey(str)) { + return true + } + if (newLineCounts >= 1) { + return false + } + return true + } +} + +export class DefaultDocumentChangedType extends DocumentChangedType { + constructor(contentChanges: ReadonlyArray) { + super(contentChanges) + } + + checkChangeSource(): DocumentChangedSource { + if (this.contentChanges.length === 0) { + return DocumentChangedSource.Unknown + } + + // event.contentChanges.length will be 2 when user press Enter key multiple times + if (this.contentChanges.length > 2) { + return DocumentChangedSource.Reformatting + } + + // Case when event.contentChanges.length === 1 + const changedText = this.contentChanges[0].text + + if (this.isSingleLine(changedText)) { + if (changedText === '') { + return DocumentChangedSource.Deletion + } else if (this.isEnterKey(changedText)) { + return DocumentChangedSource.EnterKey + } else if (this.isTabKey(changedText)) { + return DocumentChangedSource.TabKey + } else if (this.isUserTypingSpecialChar(changedText)) { + return DocumentChangedSource.SpecialCharsKey + } else if (changedText.length === 1) { + return DocumentChangedSource.RegularKey + } else if (new RegExp('^[ ]+$').test(changedText)) { + // single line && single place reformat should consist of space chars only + return DocumentChangedSource.Reformatting + } else { + return DocumentChangedSource.Unknown + } + } + + // Won't trigger cwspr on multi-line changes + return DocumentChangedSource.Unknown + } +} + +export enum DocumentChangedSource { + SpecialCharsKey = 'SpecialCharsKey', + RegularKey = 'RegularKey', + TabKey = 'TabKey', + EnterKey = 'EnterKey', + Reformatting = 'Reformatting', + Deletion = 'Deletion', + Unknown = 'Unknown', +} diff --git a/packages/core/src/codewhisperer/service/recommendationHandler.ts b/packages/core/src/codewhisperer/service/recommendationHandler.ts new file mode 100644 index 00000000000..1cd64ccbe4b --- /dev/null +++ b/packages/core/src/codewhisperer/service/recommendationHandler.ts @@ -0,0 +1,731 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { extensionVersion } from '../../shared/vscode/env' +import { RecommendationsList, DefaultCodeWhispererClient, CognitoCredentialsError } from '../client/codewhisperer' +import * as EditorContext from '../util/editorContext' +import * as CodeWhispererConstants from '../models/constants' +import { ConfigurationEntry, GetRecommendationsResponse, vsCodeState } from '../models/model' +import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { ServiceException } from '@smithy/smithy-client' +import { isServiceException } from '../../shared/errors' +import { TelemetryHelper } from '../util/telemetryHelper' +import { getLogger } from '../../shared/logger/logger' +import { hasVendedIamCredentials } from '../../auth/auth' +import { + asyncCallWithTimeout, + isInlineCompletionEnabled, + isVscHavingRegressionInlineCompletionApi, +} from '../util/commonUtil' +import { showTimedMessage } from '../../shared/utilities/messages' +import { + CodewhispererAutomatedTriggerType, + CodewhispererCompletionType, + CodewhispererGettingStartedTask, + CodewhispererTriggerType, + telemetry, +} from '../../shared/telemetry/telemetry' +import { CodeWhispererCodeCoverageTracker } from '../tracker/codewhispererCodeCoverageTracker' +import { invalidCustomizationMessage } from '../models/constants' +import { getSelectedCustomization, switchToBaseCustomizationAndNotify } from '../util/customizationUtil' +import { session } from '../util/codeWhispererSession' +import { Commands } from '../../shared/vscode/commands2' +import globals from '../../shared/extensionGlobals' +import { noSuggestions, updateInlineLockKey } from '../models/constants' +import AsyncLock from 'async-lock' +import { AuthUtil } from '../util/authUtil' +import { CWInlineCompletionItemProvider } from './inlineCompletionItemProvider' +import { application } from '../util/codeWhispererApplication' +import { openUrl } from '../../shared/utilities/vsCodeUtils' +import { indent } from '../../shared/utilities/textUtilities' +import path from 'path' +import { isIamConnection } from '../../auth/connection' +import { UserWrittenCodeTracker } from '../tracker/userWrittenCodeTracker' +import { BaseLanguageClient } from 'vscode-languageclient' + +/** + * This class is for getRecommendation/listRecommendation API calls and its states + * It does not contain UI/UX related logic + */ + +/** + * Commands as a level of indirection so that declare doesn't intercept any registrations for the + * language server implementation. + * + * Otherwise you'll get: + * "Unable to launch amazonq language server: Command "aws.amazonq.rejectCodeSuggestion" has already been declared by the Toolkit" + */ +function createCommands() { + // below commands override VS Code inline completion commands + const prevCommand = Commands.declare('editor.action.inlineSuggest.showPrevious', () => async () => { + await RecommendationHandler.instance.showRecommendation(-1) + }) + const nextCommand = Commands.declare('editor.action.inlineSuggest.showNext', () => async () => { + await RecommendationHandler.instance.showRecommendation(1) + }) + + const rejectCommand = Commands.declare('aws.amazonq.rejectCodeSuggestion', () => async () => { + telemetry.record({ + traceId: TelemetryHelper.instance.traceId, + }) + + await vscode.commands.executeCommand('editor.action.inlineSuggest.hide') + RecommendationHandler.instance.reportUserDecisions(-1) + await Commands.tryExecute('aws.amazonq.refreshAnnotation') + }) + + return { + prevCommand, + nextCommand, + rejectCommand, + } +} + +const lock = new AsyncLock({ maxPending: 1 }) + +export class RecommendationHandler { + public lastInvocationTime: number + // TODO: remove this requestId + public requestId: string + private nextToken: string + private cancellationToken: vscode.CancellationTokenSource + private _onDidReceiveRecommendation: vscode.EventEmitter = new vscode.EventEmitter() + public readonly onDidReceiveRecommendation: vscode.Event = this._onDidReceiveRecommendation.event + private inlineCompletionProvider?: CWInlineCompletionItemProvider + private inlineCompletionProviderDisposable?: vscode.Disposable + private reject: vscode.Disposable + private next: vscode.Disposable + private prev: vscode.Disposable + private _timer?: NodeJS.Timer + private languageClient?: BaseLanguageClient + documentUri: vscode.Uri | undefined = undefined + + constructor() { + this.requestId = '' + this.nextToken = '' + this.lastInvocationTime = Date.now() - CodeWhispererConstants.invocationTimeIntervalThreshold * 1000 + this.cancellationToken = new vscode.CancellationTokenSource() + this.prev = new vscode.Disposable(() => {}) + this.next = new vscode.Disposable(() => {}) + this.reject = new vscode.Disposable(() => {}) + } + + static #instance: RecommendationHandler + + public static get instance() { + return (this.#instance ??= new this()) + } + + isValidResponse(): boolean { + return session.recommendations.some((r) => r.content.trim() !== '') + } + + setLanguageClient(languageClient: BaseLanguageClient) { + this.languageClient = languageClient + } + + async getServerResponse( + triggerType: CodewhispererTriggerType, + isManualTriggerOn: boolean, + promise: Promise + ): Promise { + const timeoutMessage = hasVendedIamCredentials() + ? 'Generate recommendation timeout.' + : 'List recommendation timeout' + if (isManualTriggerOn && triggerType === 'OnDemand' && hasVendedIamCredentials()) { + return vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: CodeWhispererConstants.pendingResponse, + cancellable: false, + }, + async () => { + return await asyncCallWithTimeout( + promise, + timeoutMessage, + CodeWhispererConstants.promiseTimeoutLimit * 1000 + ) + } + ) + } + return await asyncCallWithTimeout(promise, timeoutMessage, CodeWhispererConstants.promiseTimeoutLimit * 1000) + } + + async getTaskTypeFromEditorFileName(filePath: string): Promise { + if (filePath.includes('CodeWhisperer_generate_suggestion')) { + return 'autoTrigger' + } else if (filePath.includes('CodeWhisperer_manual_invoke')) { + return 'manualTrigger' + } else if (filePath.includes('CodeWhisperer_use_comments')) { + return 'commentAsPrompt' + } else if (filePath.includes('CodeWhisperer_navigate_suggestions')) { + return 'navigation' + } else if (filePath.includes('Generate_unit_tests')) { + return 'unitTest' + } else { + return undefined + } + } + + async getRecommendations( + client: DefaultCodeWhispererClient, + editor: vscode.TextEditor, + triggerType: CodewhispererTriggerType, + config: ConfigurationEntry, + autoTriggerType?: CodewhispererAutomatedTriggerType, + pagination: boolean = true, + page: number = 0, + generate: boolean = isIamConnection(AuthUtil.instance.conn) + ): Promise { + let invocationResult: 'Succeeded' | 'Failed' = 'Failed' + let errorMessage: string | undefined = undefined + let errorCode: string | undefined = undefined + + if (!editor) { + return Promise.resolve({ + result: invocationResult, + errorMessage: errorMessage, + recommendationCount: 0, + }) + } + let recommendations: RecommendationsList = [] + let requestId = '' + let sessionId = '' + let reason = '' + let startTime = 0 + let latency = 0 + let nextToken = '' + let shouldRecordServiceInvocation = true + session.language = runtimeLanguageContext.getLanguageContext( + editor.document.languageId, + path.extname(editor.document.fileName) + ).language + session.taskType = await this.getTaskTypeFromEditorFileName(editor.document.fileName) + + if (pagination && !generate) { + if (page === 0) { + session.requestContext = await EditorContext.buildListRecommendationRequest( + editor as vscode.TextEditor, + this.nextToken, + config.isSuggestionsWithCodeReferencesEnabled, + this.languageClient + ) + } else { + session.requestContext = { + request: { + ...session.requestContext.request, + // Putting nextToken assignment in the end so it overwrites the existing nextToken + nextToken: this.nextToken, + }, + supplementalMetadata: session.requestContext.supplementalMetadata, + } + } + } else { + session.requestContext = await EditorContext.buildGenerateRecommendationRequest(editor as vscode.TextEditor) + } + const request = session.requestContext.request + // record preprocessing end time + TelemetryHelper.instance.setPreprocessEndTime() + + // set start pos for non pagination call or first pagination call + if (!pagination || (pagination && page === 0)) { + session.startPos = editor.selection.active + session.startCursorOffset = editor.document.offsetAt(session.startPos) + session.leftContextOfCurrentLine = EditorContext.getLeftContext(editor, session.startPos.line) + session.triggerType = triggerType + session.autoTriggerType = autoTriggerType + + /** + * Validate request + */ + if (!EditorContext.validateRequest(request)) { + getLogger().verbose('Invalid Request: %O', request) + const languageName = request.fileContext.programmingLanguage.languageName + if (!runtimeLanguageContext.isLanguageSupported(languageName)) { + errorMessage = `${languageName} is currently not supported by Amazon Q inline suggestions` + } + return Promise.resolve({ + result: invocationResult, + errorMessage: errorMessage, + recommendationCount: 0, + }) + } + } + + try { + startTime = Date.now() + this.lastInvocationTime = startTime + const mappedReq = runtimeLanguageContext.mapToRuntimeLanguage(request) + const codewhispererPromise = + pagination && !generate + ? client.listRecommendations(mappedReq) + : client.generateRecommendations(mappedReq) + const resp = await this.getServerResponse(triggerType, config.isManualTriggerEnabled, codewhispererPromise) + TelemetryHelper.instance.setSdkApiCallEndTime() + latency = startTime !== 0 ? Date.now() - startTime : 0 + if ('recommendations' in resp) { + recommendations = (resp && resp.recommendations) || [] + } else { + recommendations = (resp && resp.completions) || [] + } + invocationResult = 'Succeeded' + requestId = resp?.$response && resp?.$response?.requestId + nextToken = resp?.nextToken ? resp?.nextToken : '' + sessionId = resp?.$response?.httpResponse?.headers['x-amzn-sessionid'] + TelemetryHelper.instance.setFirstResponseRequestId(requestId) + if (page === 0) { + session.setTimeToFirstRecommendation(Date.now()) + } + if (nextToken === '') { + TelemetryHelper.instance.setAllPaginationEndTime() + } + } catch (error) { + if (error instanceof CognitoCredentialsError) { + shouldRecordServiceInvocation = false + } + if (latency === 0) { + latency = startTime !== 0 ? Date.now() - startTime : 0 + } + getLogger().error('amazonq inline-suggest: Invocation Exception : %s', (error as Error).message) + if (isServiceException(error)) { + errorMessage = error.message + requestId = error.$metadata.requestId || '' + errorCode = error.name + reason = `CodeWhisperer Invocation Exception: ${error?.name ?? 'unknown'}` + await this.onThrottlingException(error, triggerType) + + if (error?.name === 'AccessDeniedException' && errorMessage?.includes('no identity-based policy')) { + getLogger().error('amazonq inline-suggest: AccessDeniedException : %s', (error as Error).message) + void vscode.window + .showErrorMessage(`CodeWhisperer: ${error?.message}`, CodeWhispererConstants.settingsLearnMore) + .then(async (resp) => { + if (resp === CodeWhispererConstants.settingsLearnMore) { + void openUrl(vscode.Uri.parse(CodeWhispererConstants.learnMoreUri)) + } + }) + await vscode.commands.executeCommand('aws.amazonq.enableCodeSuggestions', false) + } + } else { + errorMessage = error instanceof Error ? error.message : String(error) + reason = error ? String(error) : 'unknown' + } + } finally { + const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone + + let msg = indent( + `codewhisperer: request-id: ${requestId}, + timestamp(epoch): ${Date.now()}, + timezone: ${timezone}, + datetime: ${new Date().toLocaleString([], { timeZone: timezone })}, + vscode version: '${vscode.version}', + extension version: '${extensionVersion}', + filename: '${EditorContext.getFileName(editor)}', + left context of line: '${session.leftContextOfCurrentLine}', + line number: ${session.startPos.line}, + character location: ${session.startPos.character}, + latency: ${latency} ms. + Recommendations:`, + 4, + true + ).trimStart() + for (const [index, item] of recommendations.entries()) { + msg += `\n ${index.toString().padStart(2, '0')}: ${indent(item.content, 8, true).trim()}` + session.requestIdList.push(requestId) + } + getLogger().debug(msg) + if (invocationResult === 'Succeeded') { + CodeWhispererCodeCoverageTracker.getTracker(session.language)?.incrementServiceInvocationCount() + UserWrittenCodeTracker.instance.onQFeatureInvoked() + } else { + if ( + (errorMessage?.includes(invalidCustomizationMessage) && errorCode === 'AccessDeniedException') || + errorCode === 'ResourceNotFoundException' + ) { + getLogger() + .debug(`The selected customization is no longer available. Retrying with the default model. + Failed request id: ${requestId}`) + await switchToBaseCustomizationAndNotify() + await this.getRecommendations( + client, + editor, + triggerType, + config, + autoTriggerType, + pagination, + page, + true + ) + } + } + + if (shouldRecordServiceInvocation) { + TelemetryHelper.instance.recordServiceInvocationTelemetry( + requestId, + sessionId, + session.recommendations.length + recommendations.length - 1, + invocationResult, + latency, + session.language, + session.taskType, + reason, + session.requestContext.supplementalMetadata + ) + } + } + + if (this.isCancellationRequested()) { + return Promise.resolve({ + result: invocationResult, + errorMessage: errorMessage, + recommendationCount: session.recommendations.length, + }) + } + + const typedPrefix = editor.document + .getText(new vscode.Range(session.startPos, editor.selection.active)) + .replace('\r\n', '\n') + if (recommendations.length > 0) { + TelemetryHelper.instance.setTypeAheadLength(typedPrefix.length) + // mark suggestions that does not match typeahead when arrival as Discard + // these suggestions can be marked as Showed if typeahead can be removed with new inline API + for (const [i, r] of recommendations.entries()) { + const recommendationIndex = i + session.recommendations.length + if ( + !r.content.startsWith(typedPrefix) && + session.getSuggestionState(recommendationIndex) === undefined + ) { + session.setSuggestionState(recommendationIndex, 'Discard') + } + session.setCompletionType(recommendationIndex, r) + } + session.recommendations = pagination ? session.recommendations.concat(recommendations) : recommendations + if (isInlineCompletionEnabled() && this.hasAtLeastOneValidSuggestion(typedPrefix)) { + this._onDidReceiveRecommendation.fire() + } + } + + this.requestId = requestId + session.sessionId = sessionId + this.nextToken = nextToken + + // send Empty userDecision event if user receives no recommendations in this session at all. + if (invocationResult === 'Succeeded' && nextToken === '') { + // case 1: empty list of suggestion [] + if (session.recommendations.length === 0) { + session.requestIdList.push(requestId) + // Received an empty list of recommendations + TelemetryHelper.instance.recordUserDecisionTelemetryForEmptyList( + session.requestIdList, + sessionId, + page, + runtimeLanguageContext.getLanguageContext( + editor.document.languageId, + path.extname(editor.document.fileName) + ).language, + session.requestContext.supplementalMetadata + ) + } + // case 2: non empty list of suggestion but with (a) empty content or (b) non-matching typeahead + else if (!this.hasAtLeastOneValidSuggestion(typedPrefix)) { + this.reportUserDecisions(-1) + } + } + return Promise.resolve({ + result: invocationResult, + errorMessage: errorMessage, + recommendationCount: session.recommendations.length, + }) + } + + hasAtLeastOneValidSuggestion(typedPrefix: string): boolean { + return session.recommendations.some((r) => r.content.trim() !== '' && r.content.startsWith(typedPrefix)) + } + + cancelPaginatedRequest() { + this.nextToken = '' + this.cancellationToken.cancel() + } + + isCancellationRequested() { + return this.cancellationToken.token.isCancellationRequested + } + + checkAndResetCancellationTokens() { + if (this.isCancellationRequested()) { + this.cancellationToken.dispose() + this.cancellationToken = new vscode.CancellationTokenSource() + this.nextToken = '' + return true + } + return false + } + /** + * Clear recommendation state + */ + clearRecommendations() { + session.requestIdList = [] + session.recommendations = [] + session.suggestionStates = new Map() + session.completionTypes = new Map() + this.requestId = '' + session.sessionId = '' + this.nextToken = '' + session.requestContext.supplementalMetadata = undefined + } + + async clearInlineCompletionStates() { + try { + vsCodeState.isCodeWhispererEditing = false + application()._clearCodeWhispererUIListener.fire() + this.cancelPaginatedRequest() + this.clearRecommendations() + this.disposeInlineCompletion() + await vscode.commands.executeCommand('aws.amazonq.refreshStatusBar') + // fix a regression that requires user to hit Esc twice to clear inline ghost text + // because disposing a provider does not clear the UX + if (isVscHavingRegressionInlineCompletionApi()) { + await vscode.commands.executeCommand('editor.action.inlineSuggest.hide') + } + } finally { + this.clearRejectionTimer() + } + } + + reportDiscardedUserDecisions() { + for (const [i, _] of session.recommendations.entries()) { + session.setSuggestionState(i, 'Discard') + } + this.reportUserDecisions(-1) + } + + /** + * Emits telemetry reflecting user decision for current recommendation. + */ + reportUserDecisions(acceptIndex: number) { + if (session.sessionId === '' || this.requestId === '') { + return + } + TelemetryHelper.instance.recordUserDecisionTelemetry( + session.requestIdList, + session.sessionId, + session.recommendations, + acceptIndex, + session.recommendations.length, + session.completionTypes, + session.suggestionStates, + session.requestContext.supplementalMetadata + ) + if (isInlineCompletionEnabled()) { + this.clearInlineCompletionStates().catch((e) => { + getLogger().error('clearInlineCompletionStates failed: %s', (e as Error).message) + }) + } + } + + hasNextToken(): boolean { + return this.nextToken !== '' + } + + canShowRecommendationInIntelliSense( + editor: vscode.TextEditor, + showPrompt: boolean = false, + response: GetRecommendationsResponse + ): boolean { + const reject = () => { + this.reportUserDecisions(-1) + } + if (!this.isValidResponse()) { + if (showPrompt) { + void showTimedMessage(response.errorMessage ? response.errorMessage : noSuggestions, 3000) + } + reject() + return false + } + // do not show recommendation if cursor is before invocation position + // also mark as Discard + if (editor.selection.active.isBefore(session.startPos)) { + for (const [i, _] of session.recommendations.entries()) { + session.setSuggestionState(i, 'Discard') + } + reject() + return false + } + + // do not show recommendation if typeahead does not match + // also mark as Discard + const typedPrefix = editor.document.getText( + new vscode.Range( + session.startPos.line, + session.startPos.character, + editor.selection.active.line, + editor.selection.active.character + ) + ) + if (!session.recommendations[0].content.startsWith(typedPrefix.trimStart())) { + for (const [i, _] of session.recommendations.entries()) { + session.setSuggestionState(i, 'Discard') + } + reject() + return false + } + return true + } + + async onThrottlingException(awsError: ServiceException, triggerType: CodewhispererTriggerType) { + if ( + awsError.name === 'ThrottlingException' && + awsError.message.includes(CodeWhispererConstants.throttlingMessage) + ) { + if (triggerType === 'OnDemand') { + void vscode.window.showErrorMessage(CodeWhispererConstants.freeTierLimitReached) + } + vsCodeState.isFreeTierLimitReached = true + } + } + + public disposeInlineCompletion() { + this.inlineCompletionProviderDisposable?.dispose() + this.inlineCompletionProvider = undefined + } + + private disposeCommandOverrides() { + this.prev.dispose() + this.reject.dispose() + this.next.dispose() + } + + // These commands override the vs code inline completion commands + // They are subscribed when suggestion starts and disposed when suggestion is accepted/rejected + // to avoid impacting other plugins or user who uses this API + private registerCommandOverrides() { + const { prevCommand, nextCommand, rejectCommand } = createCommands() + this.prev = prevCommand.register() + this.next = nextCommand.register() + this.reject = rejectCommand.register() + } + + subscribeSuggestionCommands() { + this.disposeCommandOverrides() + this.registerCommandOverrides() + globals.context.subscriptions.push(this.prev) + globals.context.subscriptions.push(this.next) + globals.context.subscriptions.push(this.reject) + } + + async showRecommendation(indexShift: number, noSuggestionVisible: boolean = false) { + await lock.acquire(updateInlineLockKey, async () => { + if (!vscode.window.state.focused) { + this.reportDiscardedUserDecisions() + return + } + const inlineCompletionProvider = new CWInlineCompletionItemProvider( + this.inlineCompletionProvider?.getActiveItemIndex, + indexShift, + session.recommendations, + this.requestId, + session.startPos, + this.nextToken + ) + this.inlineCompletionProviderDisposable?.dispose() + // when suggestion is active, registering a new provider will let VS Code invoke inline API automatically + this.inlineCompletionProviderDisposable = vscode.languages.registerInlineCompletionItemProvider( + Object.assign([], CodeWhispererConstants.platformLanguageIds), + inlineCompletionProvider + ) + this.inlineCompletionProvider = inlineCompletionProvider + + if (isVscHavingRegressionInlineCompletionApi() && !noSuggestionVisible) { + // fix a regression in new VS Code when disposing and re-registering + // a new provider does not auto refresh the inline suggestion widget + // by manually refresh it + await vscode.commands.executeCommand('editor.action.inlineSuggest.hide') + await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') + } + if (noSuggestionVisible) { + await vscode.commands.executeCommand(`editor.action.inlineSuggest.trigger`) + this.sendPerceivedLatencyTelemetry() + } + }) + } + + async onEditorChange() { + this.reportUserDecisions(-1) + } + + async onFocusChange() { + this.reportUserDecisions(-1) + } + + async onCursorChange(e: vscode.TextEditorSelectionChangeEvent) { + // we do not want to reset the states for keyboard events because they can be typeahead + if ( + e.kind !== vscode.TextEditorSelectionChangeKind.Keyboard && + vscode.window.activeTextEditor === e.textEditor + ) { + application()._clearCodeWhispererUIListener.fire() + // when cursor change due to mouse movement we need to reset the active item index for inline + if (e.kind === vscode.TextEditorSelectionChangeKind.Mouse) { + this.inlineCompletionProvider?.clearActiveItemIndex() + } + } + } + + isSuggestionVisible(): boolean { + return this.inlineCompletionProvider?.getActiveItemIndex !== undefined + } + + async tryShowRecommendation() { + const editor = vscode.window.activeTextEditor + if (editor === undefined) { + return + } + if (this.isSuggestionVisible()) { + // do not force refresh the tooltip to avoid suggestion "flashing" + return + } + if ( + editor.selection.active.isBefore(session.startPos) || + editor.document.uri.fsPath !== this.documentUri?.fsPath + ) { + for (const [i, _] of session.recommendations.entries()) { + session.setSuggestionState(i, 'Discard') + } + this.reportUserDecisions(-1) + } else if (session.recommendations.length > 0) { + await this.showRecommendation(0, true) + } + } + + private clearRejectionTimer() { + if (this._timer !== undefined) { + clearInterval(this._timer) + this._timer = undefined + } + } + + private sendPerceivedLatencyTelemetry() { + if (vscode.window.activeTextEditor) { + const languageContext = runtimeLanguageContext.getLanguageContext( + vscode.window.activeTextEditor.document.languageId, + vscode.window.activeTextEditor.document.fileName.substring( + vscode.window.activeTextEditor.document.fileName.lastIndexOf('.') + 1 + ) + ) + telemetry.codewhisperer_perceivedLatency.emit({ + codewhispererRequestId: this.requestId, + codewhispererSessionId: session.sessionId, + codewhispererTriggerType: session.triggerType, + codewhispererCompletionType: session.getCompletionType(0), + codewhispererCustomizationArn: getSelectedCustomization().arn, + codewhispererLanguage: languageContext.language, + duration: Date.now() - this.lastInvocationTime, + passive: true, + credentialStartUrl: AuthUtil.instance.startUrl, + result: 'Succeeded', + }) + } + } +} diff --git a/packages/core/src/codewhisperer/service/recommendationService.ts b/packages/core/src/codewhisperer/service/recommendationService.ts new file mode 100644 index 00000000000..de78b435913 --- /dev/null +++ b/packages/core/src/codewhisperer/service/recommendationService.ts @@ -0,0 +1,122 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { ConfigurationEntry, GetRecommendationsResponse } from '../models/model' +import { isInlineCompletionEnabled } from '../util/commonUtil' +import { + CodewhispererAutomatedTriggerType, + CodewhispererTriggerType, + telemetry, +} from '../../shared/telemetry/telemetry' +import { InlineCompletionService } from '../service/inlineCompletionService' +import { ClassifierTrigger } from './classifierTrigger' +import { DefaultCodeWhispererClient } from '../client/codewhisperer' +import { randomUUID } from '../../shared/crypto' +import { TelemetryHelper } from '../util/telemetryHelper' +import { AuthUtil } from '../util/authUtil' + +export interface SuggestionActionEvent { + readonly editor: vscode.TextEditor | undefined + readonly isRunning: boolean + readonly triggerType: CodewhispererTriggerType + readonly response: GetRecommendationsResponse | undefined +} + +export class RecommendationService { + static #instance: RecommendationService + + private _isRunning: boolean = false + get isRunning() { + return this._isRunning + } + + private _onSuggestionActionEvent = new vscode.EventEmitter() + get suggestionActionEvent(): vscode.Event { + return this._onSuggestionActionEvent.event + } + + private _acceptedSuggestionCount: number = 0 + get acceptedSuggestionCount() { + return this._acceptedSuggestionCount + } + + private _totalValidTriggerCount: number = 0 + get totalValidTriggerCount() { + return this._totalValidTriggerCount + } + + public static get instance() { + return (this.#instance ??= new RecommendationService()) + } + + incrementAcceptedCount() { + this._acceptedSuggestionCount++ + } + + incrementValidTriggerCount() { + this._totalValidTriggerCount++ + } + + async generateRecommendation( + client: DefaultCodeWhispererClient, + editor: vscode.TextEditor, + triggerType: CodewhispererTriggerType, + config: ConfigurationEntry, + autoTriggerType?: CodewhispererAutomatedTriggerType, + event?: vscode.TextDocumentChangeEvent + ) { + // TODO: should move all downstream auth check(inlineCompletionService, recommendationHandler etc) to here(upstream) instead of spreading everywhere + if (AuthUtil.instance.isConnected() && AuthUtil.instance.requireProfileSelection()) { + return + } + + if (this._isRunning) { + return + } + + /** + * Use an existing trace ID if invoked through a command (e.g., manual invocation), + * otherwise generate a new trace ID + */ + const traceId = telemetry.attributes?.traceId ?? randomUUID() + TelemetryHelper.instance.setTraceId(traceId) + await telemetry.withTraceId(async () => { + if (isInlineCompletionEnabled()) { + if (triggerType === 'OnDemand') { + ClassifierTrigger.instance.recordClassifierResultForManualTrigger(editor) + } + + this._isRunning = true + let response: GetRecommendationsResponse | undefined = undefined + + try { + this._onSuggestionActionEvent.fire({ + editor: editor, + isRunning: true, + triggerType: triggerType, + response: undefined, + }) + + response = await InlineCompletionService.instance.getPaginatedRecommendation( + client, + editor, + triggerType, + config, + autoTriggerType, + event + ) + } finally { + this._isRunning = false + this._onSuggestionActionEvent.fire({ + editor: editor, + isRunning: false, + triggerType: triggerType, + response: response, + }) + } + } + }, traceId) + } +} diff --git a/src/codewhisperer/service/referenceHoverProvider.ts b/packages/core/src/codewhisperer/service/referenceHoverProvider.ts similarity index 90% rename from src/codewhisperer/service/referenceHoverProvider.ts rename to packages/core/src/codewhisperer/service/referenceHoverProvider.ts index d220d113f89..bce371994b5 100644 --- a/src/codewhisperer/service/referenceHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/referenceHoverProvider.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -31,9 +31,9 @@ export class ReferenceHoverProvider implements vscode.HoverProvider { let refLength = codeRef.code.length let refCode = codeRef.code if ( - reference.recommendationContentSpan != undefined && - reference.recommendationContentSpan.start != undefined && - reference.recommendationContentSpan.end != undefined + reference.recommendationContentSpan !== undefined && + reference.recommendationContentSpan.start !== undefined && + reference.recommendationContentSpan.end !== undefined ) { refLength = reference.recommendationContentSpan.end - reference.recommendationContentSpan.start refCode = codeRef.code.substring( @@ -47,7 +47,7 @@ export class ReferenceHoverProvider implements vscode.HoverProvider { new vscode.Range(document.positionAt(leftOffset), document.positionAt(rightOffset)) ) const index = subDocument.indexOf(refCode) - if (index != -1) { + if (index !== -1) { return new vscode.Hover( CodeWhispererConstants.hoverInlayText(reference.licenseName, reference.repository), new vscode.Range( diff --git a/src/codewhisperer/service/referenceInlineProvider.ts b/packages/core/src/codewhisperer/service/referenceInlineProvider.ts similarity index 79% rename from src/codewhisperer/service/referenceInlineProvider.ts rename to packages/core/src/codewhisperer/service/referenceInlineProvider.ts index f2e8d7f50d1..6fe0cf122f2 100644 --- a/src/codewhisperer/service/referenceInlineProvider.ts +++ b/packages/core/src/codewhisperer/service/referenceInlineProvider.ts @@ -1,16 +1,16 @@ /*! - * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ import * as vscode from 'vscode' import * as CodeWhispererConstants from '../models/constants' -import { getLogger } from '../../shared/logger' +import { getLogger } from '../../shared/logger/logger' import { References } from '../client/codewhisperer' import { LicenseUtil } from '../util/licenseUtil' import { isInlineCompletionEnabled } from '../util/commonUtil' - -const performance = globalThis.performance ?? require('perf_hooks').performance +import { application } from '../util/codeWhispererApplication' +import { placeholder } from '../../shared/vscode/commands2' /** * ReferenceInlineProvider @@ -22,7 +22,11 @@ export class ReferenceInlineProvider implements vscode.CodeLensProvider { private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter() public readonly onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event - constructor() {} + constructor() { + application().clearCodeWhispererUIListener((_) => { + this.removeInlineReference() + }) + } static #instance: ReferenceInlineProvider @@ -31,7 +35,7 @@ export class ReferenceInlineProvider implements vscode.CodeLensProvider { } public setInlineReference(line: number, suggestion: string, references: References | undefined) { - const startTime = performance.now() + const startTime = Date.now() this.ranges = [] this.refs = [] if ( @@ -49,7 +53,7 @@ export class ReferenceInlineProvider implements vscode.CodeLensProvider { const licenses = [...n].join(', ') this.ranges.push(new vscode.Range(line, 0, line, 1)) this.refs.push(CodeWhispererConstants.suggestionDetailReferenceText(licenses)) - const duration = performance.now() - startTime + const duration = Date.now() - startTime if (duration > 100) { getLogger().warn(`setInlineReference takes ${duration}ms`) } @@ -66,18 +70,19 @@ export class ReferenceInlineProvider implements vscode.CodeLensProvider { document: vscode.TextDocument, token: vscode.CancellationToken ): vscode.CodeLens[] | Thenable { - const startTime = performance.now() + const startTime = Date.now() const codeLenses: vscode.CodeLens[] = [] for (let i = 0; i < this.ranges.length; i++) { const codeLens = new vscode.CodeLens(this.ranges[i]) codeLens.command = { title: this.refs[i], tooltip: 'Reference code', - command: 'aws.codeWhisperer.openReferencePanel', + command: 'aws.amazonq.openReferencePanel', + arguments: [placeholder, 'codelens'], } codeLenses.push(codeLens) } - const duration = performance.now() - startTime + const duration = Date.now() - startTime if (duration > 100) { getLogger().warn(`setInlineReference takes ${duration}ms`) } diff --git a/packages/core/src/codewhisperer/service/referenceLogViewProvider.ts b/packages/core/src/codewhisperer/service/referenceLogViewProvider.ts new file mode 100644 index 00000000000..d51424b1c46 --- /dev/null +++ b/packages/core/src/codewhisperer/service/referenceLogViewProvider.ts @@ -0,0 +1,188 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { LicenseUtil } from '../util/licenseUtil' +import * as CodeWhispererConstants from '../models/constants' +import { CodeWhispererSettings } from '../util/codewhispererSettings' +import globals from '../../shared/extensionGlobals' +import { AuthUtil } from '../util/authUtil' +import { session } from '../util/codeWhispererSession' +import CodeWhispererClient from '../client/codewhispererclient' +import CodeWhispererUserClient from '../client/codewhispereruserclient' +import { InlineCompletionItemWithReferences } from '@aws/language-server-runtimes-types' + +export class ReferenceLogViewProvider implements vscode.WebviewViewProvider { + public static readonly viewType = 'aws.codeWhisperer.referenceLog' + private _view?: vscode.WebviewView + private _referenceLogs: string[] = [] + private _extensionUri: vscode.Uri = globals.context.extensionUri + constructor() {} + static #instance: ReferenceLogViewProvider + + public static get instance() { + return (this.#instance ??= new this()) + } + + public resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + token: vscode.CancellationToken + ): void | Thenable { + this._view = webviewView + + this._view.webview.options = { + // Allow scripts in the webview + enableScripts: true, + localResourceRoots: [this._extensionUri], + } + this._view.webview.html = this.getHtml( + webviewView.webview, + CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled() + ) + this._view.webview.onDidReceiveMessage(async (data) => { + await vscode.commands.executeCommand('aws.amazonq.configure', 'codewhisperer') + }) + } + + public update() { + if (this._view) { + const showPrompt = CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled() + this._view.webview.html = this.getHtml(this._view.webview, showPrompt) + } + } + + public static getReferenceLog(recommendation: string, references: Reference[], editor: vscode.TextEditor): string { + const filePath = editor.document.uri.path + const time = new Date().toLocaleString() + let text = `` + for (const reference of references) { + const standardReference = toStandardReference(reference) + if ( + standardReference.position === undefined || + standardReference.position.start === undefined || + standardReference.position.end === undefined + ) { + continue + } + const { start, end } = standardReference.position + const code = recommendation.substring(start, end) + const firstCharLineNumber = editor.document.positionAt(session.startCursorOffset + start).line + 1 + const lastCharLineNumber = editor.document.positionAt(session.startCursorOffset + end - 1).line + 1 + let lineInfo = `` + if (firstCharLineNumber === lastCharLineNumber) { + lineInfo = `(line at ${firstCharLineNumber})` + } else { + lineInfo = `(lines from ${firstCharLineNumber} to ${lastCharLineNumber})` + } + if (text !== '') { + text += `And ` + } + + let license = `${standardReference.licenseName}` + let repository = standardReference.repository?.length ? standardReference.repository : 'unknown' + if (standardReference.url?.length) { + repository = `${standardReference.repository}` + license = `${standardReference.licenseName || 'unknown'}` + } + + text += + CodeWhispererConstants.referenceLogText( + `
${code}
`, + license, + repository, + filePath, + lineInfo + ) + '
' + } + if (text === ``) { + return '' + } + return `[${time}] Accepted recommendation ${text}
` + } + + public addReferenceLog(referenceLog: string) { + if (referenceLog !== '') { + this._referenceLogs.push(referenceLog) + } + this.update() + } + private getHtml(webview: vscode.Webview, showPrompt: boolean): string { + const styleVSCodeUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'src', 'codewhisperer', 'views/css/codewhispererReferenceLog.css') + ) + + let prompt = '' + if (showPrompt) { + if (AuthUtil.instance.isEnterpriseSsoInUse()) { + prompt = CodeWhispererConstants.referenceLogPromptTextEnterpriseSSO + } else { + prompt = CodeWhispererConstants.referenceLogPromptText + } + } + + return ` + + + + + +

${prompt}

+

${this._referenceLogs.join('')}

+ + + ` + } +} + +/** + * Reference log needs to support references directly from CW, as well as those from Flare. These references have different shapes, so we standarize them here. + */ +type GetInnerType = T extends (infer U)[] ? U : never +type Reference = + | CodeWhispererClient.Reference + | CodeWhispererUserClient.Reference + | GetInnerType + +type StandardizedReference = { + licenseName?: string + position?: { + start?: number + end?: number + } + repository?: string + url?: string +} + +/** + * Convert a general reference to the standardized format expected by the reference log. + * @param ref + * @returns + */ +function toStandardReference(ref: Reference): StandardizedReference { + const isCWReference = (ref: any) => ref.recommendationContentSpan !== undefined + + if (isCWReference(ref)) { + const castRef = ref as CodeWhispererClient.Reference + return { + licenseName: castRef.licenseName!, + position: { start: castRef.recommendationContentSpan?.start, end: castRef.recommendationContentSpan?.end }, + repository: castRef.repository, + url: castRef.url, + } + } + const castRef = ref as GetInnerType + return { + licenseName: castRef.licenseName, + position: { start: castRef.position?.startCharacter, end: castRef.position?.endCharacter }, + repository: castRef.referenceName, + url: castRef.referenceUrl, + } +} diff --git a/packages/core/src/codewhisperer/service/securityIssueCodeActionProvider.ts b/packages/core/src/codewhisperer/service/securityIssueCodeActionProvider.ts new file mode 100644 index 00000000000..4dc7bceebe7 --- /dev/null +++ b/packages/core/src/codewhisperer/service/securityIssueCodeActionProvider.ts @@ -0,0 +1,106 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { SecurityIssueProvider } from './securityIssueProvider' +import { CodeScanIssue } from '../models/model' +import { Component } from '../../shared/telemetry/telemetry' +import { amazonqCodeIssueDetailsTabTitle } from '../models/constants' + +export class SecurityIssueCodeActionProvider implements vscode.CodeActionProvider { + static #instance: SecurityIssueCodeActionProvider + private issueProvider = SecurityIssueProvider.instance + + public static get instance() { + return (this.#instance ??= new this()) + } + + public provideCodeActions( + document: vscode.TextDocument, + range: vscode.Range | vscode.Selection, + _context: vscode.CodeActionContext, + _token: vscode.CancellationToken + ): vscode.CodeAction[] { + const codeActions: vscode.CodeAction[] = [] + + for (const group of this.issueProvider.issues) { + if (document.fileName !== group.filePath) { + continue + } + + for (const issue of group.issues) { + if (!issue.visible) { + continue + } + const issueRange = new vscode.Range(issue.startLine, 0, issue.endLine, 0) + if (issueRange.contains(range)) { + const [suggestedFix] = issue.suggestedFixes + if (suggestedFix) { + const fixIssue = new vscode.CodeAction( + `Amazon Q: Fix "${issue.title}"`, + vscode.CodeActionKind.QuickFix + ) + const args: [CodeScanIssue, string, Component] = [issue, group.filePath, 'quickfix'] + fixIssue.command = { + title: 'Fix with Amazon Q', + command: 'aws.amazonq.applySecurityFix', + arguments: args, + } + codeActions.push(fixIssue) + } + const openIssue = new vscode.CodeAction( + `Amazon Q: View details for "${issue.title}"`, + vscode.CodeActionKind.QuickFix + ) + const args: [CodeScanIssue, string] = [issue, group.filePath] + openIssue.command = { + title: `Open "${amazonqCodeIssueDetailsTabTitle}"`, + command: 'aws.amazonq.openSecurityIssuePanel', + arguments: args, + } + codeActions.push(openIssue) + + const explainWithQ = new vscode.CodeAction( + `Amazon Q: Explain "${issue.title}"`, + vscode.CodeActionKind.QuickFix + ) + const explainWithQArgs = [issue, group.filePath] + explainWithQ.command = { + title: 'Explain with Amazon Q', + command: 'aws.amazonq.explainIssue', + arguments: explainWithQArgs, + } + codeActions.push(explainWithQ) + + const ignoreIssue = new vscode.CodeAction( + `Amazon Q: Ignore this "${issue.title}" issue`, + vscode.CodeActionKind.QuickFix + ) + const ignoreIssueArgs = [issue, group.filePath, 'quickfix'] + ignoreIssue.command = { + title: 'Ignore this issue', + command: 'aws.amazonq.security.ignore', + arguments: ignoreIssueArgs, + } + codeActions.push(ignoreIssue) + + const ignoreAllIssues = new vscode.CodeAction( + `Amazon Q: Ignore all "${issue.title}" issues`, + vscode.CodeActionKind.QuickFix + ) + const ignoreAllIssuesArgs = [issue, 'quickfix'] + ignoreAllIssues.command = { + title: 'Ignore similar issues', + command: 'aws.amazonq.security.ignoreAll', + arguments: ignoreAllIssuesArgs, + } + codeActions.push(ignoreAllIssues) + } + } + } + + return codeActions + } +} diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts new file mode 100644 index 00000000000..bb9fe2cafa4 --- /dev/null +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -0,0 +1,131 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { CodeScanIssue } from '../models/model' +import globals from '../../shared/extensionGlobals' +import { telemetry } from '../../shared/telemetry/telemetry' +import path from 'path' +import { AuthUtil } from '../util/authUtil' +import { TelemetryHelper } from '../util/telemetryHelper' +import { SecurityIssueProvider } from './securityIssueProvider' + +export class SecurityIssueHoverProvider implements vscode.HoverProvider { + static #instance: SecurityIssueHoverProvider + private issueProvider: SecurityIssueProvider = SecurityIssueProvider.instance + + public static get instance() { + return (this.#instance ??= new this()) + } + + public provideHover( + document: vscode.TextDocument, + position: vscode.Position, + _token: vscode.CancellationToken + ): vscode.Hover { + const contents: vscode.MarkdownString[] = [] + + for (const group of this.issueProvider.issues) { + if (document.fileName !== group.filePath) { + continue + } + + for (const issue of group.issues) { + if (!issue.visible) { + continue + } + const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0) + if (range.contains(position)) { + contents.push(this._getContent(group.filePath, issue)) + telemetry.codewhisperer_codeScanIssueHover.emit({ + findingId: issue.findingId, + detectorId: issue.detectorId, + ruleId: issue.ruleId, + includesFix: !!issue.suggestedFixes.length, + credentialStartUrl: AuthUtil.instance.startUrl, + autoDetected: issue.autoDetected, + }) + TelemetryHelper.instance.sendCodeScanRemediationsEvent( + document.languageId, + 'CODESCAN_ISSUE_HOVER', + issue.detectorId, + issue.findingId, + issue.ruleId, + undefined, + undefined, + undefined, + !!issue.suggestedFixes.length + ) + } + } + } + + return new vscode.Hover(contents) + } + + private _getContent(filePath: string, issue: CodeScanIssue) { + const markdownString = new vscode.MarkdownString() + markdownString.isTrusted = true + markdownString.supportHtml = true + markdownString.supportThemeIcons = true + markdownString.baseUri = vscode.Uri.file(path.join(globals.context.extensionPath, 'resources/images/')) + + const [suggestedFix] = issue.suggestedFixes + + markdownString.appendMarkdown(`## ${issue.title} ${this._makeSeverityBadge(issue.severity)}\n`) + markdownString.appendMarkdown( + `${suggestedFix?.code && suggestedFix.description !== '' ? suggestedFix.description : issue.recommendation.text}\n\n` + ) + + const explainWithQCommand = this._getCommandMarkdown( + 'aws.amazonq.explainIssue', + [issue, filePath], + 'comment', + 'Explain', + 'Explain with Amazon Q' + ) + markdownString.appendMarkdown(explainWithQCommand) + + const generateFixCommand = this._getCommandMarkdown( + 'aws.amazonq.generateFix', + [issue, filePath], + 'wrench', + 'Fix', + 'Fix with Amazon Q' + ) + markdownString.appendMarkdown(' | ' + generateFixCommand) + + const ignoreIssueCommand = this._getCommandMarkdown( + 'aws.amazonq.security.ignore', + [issue, filePath, 'hover'], + 'error', + 'Ignore', + 'Ignore Issue' + ) + markdownString.appendMarkdown(' | ' + ignoreIssueCommand) + + const ignoreSimilarIssuesCommand = this._getCommandMarkdown( + 'aws.amazonq.security.ignoreAll', + [issue, 'hover'], + 'error', + 'Ignore All', + 'Ignore Similar Issues' + ) + markdownString.appendMarkdown(' | ' + ignoreSimilarIssuesCommand) + + return markdownString + } + + private _getCommandMarkdown(command: string, args: any, icon: string, text: string, description: string) { + const commandUri = vscode.Uri.parse(`command:${command}?${encodeURIComponent(JSON.stringify(args))}`) + return `[$(${icon}) ${text}](${commandUri} '${description}')\n` + } + + private _makeSeverityBadge(severity: string) { + if (!severity) { + return '' + } + return `![${severity}](severity-${severity.toLowerCase()}.svg)` + } +} diff --git a/packages/core/src/codewhisperer/service/securityIssueProvider.ts b/packages/core/src/codewhisperer/service/securityIssueProvider.ts new file mode 100644 index 00000000000..01f1cd880bd --- /dev/null +++ b/packages/core/src/codewhisperer/service/securityIssueProvider.ts @@ -0,0 +1,196 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { AggregatedCodeScanIssue, CodeScanIssue, SuggestedFix } from '../models/model' +import { randomUUID } from '../../shared/crypto' +import { displayFindingsDetectorName } from '../models/constants' + +export class SecurityIssueProvider { + static #instance: SecurityIssueProvider + public static get instance() { + return (this.#instance ??= new this()) + } + + private _issues: AggregatedCodeScanIssue[] = [] + public get issues() { + return this._issues + } + + public set issues(issues: AggregatedCodeScanIssue[]) { + this._issues = issues + } + + private _id: string = randomUUID() + public get id() { + return this._id + } + + public set id(id: string) { + this._id = id + } + + public handleDocumentChange(event: vscode.TextDocumentChangeEvent) { + // handleDocumentChange function may be triggered while testing by our own code generation. + if (!event.contentChanges || event.contentChanges.length === 0) { + return + } + const { changedRange, changedText, lineOffset } = event.contentChanges.reduce( + (acc, change) => ({ + changedRange: acc.changedRange.union(change.range), + changedText: acc.changedText + change.text, + lineOffset: acc.lineOffset + this._getLineOffset(change.range, change.text), + }), + { + changedRange: event.contentChanges[0].range, + changedText: '', + lineOffset: 0, + } + ) + + this._issues = this._issues.map((group) => { + if (group.filePath !== event.document.fileName) { + return group + } + return { + ...group, + issues: group.issues + .filter((issue) => { + const range = new vscode.Range( + issue.startLine, + 0, + issue.startLine === issue.endLine ? issue.endLine + 1 : issue.endLine, + 0 + ) + + const intersection = changedRange.intersection(range) + return !(intersection && (/\S/.test(changedText) || changedText === '')) + }) + .map((issue) => { + if (issue.startLine < changedRange.end.line) { + return issue + } + return { + ...issue, + startLine: issue.startLine + lineOffset, + endLine: issue.endLine + lineOffset, + suggestedFixes: issue.suggestedFixes.map((fix) => + this._offsetSuggestedFix(fix, lineOffset) + ), + } + }), + } + }) + } + + private _getLineOffset(range: vscode.Range, text: string) { + const originLines = range.end.line - range.start.line + 1 + const changedLines = text.split('\n').length + return changedLines - originLines + } + + private _offsetSuggestedFix(suggestedFix: SuggestedFix, lines: number): SuggestedFix { + return { + ...suggestedFix, + code: suggestedFix.code?.replace( + /^(@@ -)(\d+)(,\d+ \+)(\d+)(,\d+ @@)/, + function (_fullMatch, ...groups: string[]) { + return ( + groups[0] + + String(parseInt(groups[1]) + lines) + + groups[2] + + String(parseInt(groups[3]) + lines) + + groups[4] + ) + } + ), + references: + suggestedFix.references?.map((ref) => ({ + ...ref, + recommendationContentSpan: { + ...ref.recommendationContentSpan, + start: Number(ref.recommendationContentSpan?.start) + lines, + end: Number(ref.recommendationContentSpan?.end) + lines, + }, + })) ?? [], + } + } + + public removeIssue(uri: vscode.Uri, issue: CodeScanIssue) { + this._issues = this._issues.map((group) => { + if (group.filePath !== uri.fsPath) { + return group + } + return { + ...group, + issues: group.issues.filter((i) => i.findingId !== issue.findingId), + } + }) + } + + public updateIssue(issue: CodeScanIssue, filePath?: string) { + this._issues = this._issues.map((group) => { + if (filePath && group.filePath !== filePath) { + return group + } + return { + ...group, + issues: group.issues.map((i) => (i.findingId === issue.findingId ? issue : i)), + } + }) + } + + public mergeIssues(newIssues: AggregatedCodeScanIssue) { + const existingGroup = this._issues.find((group) => group.filePath === newIssues.filePath) + if (!existingGroup) { + this._issues.push(newIssues) + return + } + + this._issues = this._issues.map((group) => + group.filePath !== newIssues.filePath + ? group + : { + ...group, + issues: [ + ...group.issues, + ...newIssues.issues.filter((issue) => !this.isExistingIssue(issue, newIssues.filePath)), + ], + } + ) + } + + public mergeIssuesDisplayFindings(newIssues: AggregatedCodeScanIssue, fromQCA: boolean) { + const existingGroup = this._issues.find((group) => group.filePath === newIssues.filePath) + if (!existingGroup) { + this._issues.push(newIssues) + return + } + + this._issues = this._issues.map((group) => + group.filePath !== newIssues.filePath + ? group + : { + ...group, + issues: [ + ...group.issues.filter( + // if the incoming findings are from QCA review, then keep only existing findings from displayFindings + // if the incoming findings are not from QCA review, then keep only the existing QCA findings + (issue) => fromQCA === (issue.detectorName === displayFindingsDetectorName) + ), + ...newIssues.issues, + ], + } + ) + } + + private isExistingIssue(issue: CodeScanIssue, filePath: string) { + return this._issues + .find((group) => group.filePath === filePath) + ?.issues.find( + (i) => i.title === issue.title && i.startLine === issue.startLine && i.endLine === issue.endLine + ) + } +} diff --git a/packages/core/src/codewhisperer/service/securityIssueTreeViewProvider.ts b/packages/core/src/codewhisperer/service/securityIssueTreeViewProvider.ts new file mode 100644 index 00000000000..9990b50fd96 --- /dev/null +++ b/packages/core/src/codewhisperer/service/securityIssueTreeViewProvider.ts @@ -0,0 +1,217 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import path from 'path' +import { + CodeIssueGroupingStrategy, + CodeIssueGroupingStrategyState, + CodeScanIssue, + SecurityTreeViewFilterState, + severities, + Severity, +} from '../models/model' +import globals from '../../shared/extensionGlobals' +import { getLogger } from '../../shared/logger/logger' +import { SecurityIssueProvider } from './securityIssueProvider' +import { sasRuleId } from '../models/constants' + +export type SecurityViewTreeItem = FileItem | IssueItem | SeverityItem +type CodeScanIssueWithFilePath = CodeScanIssue & { filePath: string } + +export class SecurityIssueTreeViewProvider implements vscode.TreeDataProvider { + public static readonly viewType = 'aws.amazonq.SecurityIssuesTree' + + private _onDidChangeTreeData: vscode.EventEmitter< + SecurityViewTreeItem | SecurityViewTreeItem[] | undefined | null | void + > = new vscode.EventEmitter() + readonly onDidChangeTreeData: vscode.Event< + SecurityViewTreeItem | SecurityViewTreeItem[] | undefined | null | void + > = this._onDidChangeTreeData.event + + static #instance: SecurityIssueTreeViewProvider + private issueProvider = SecurityIssueProvider.instance + + public static get instance() { + return (this.#instance ??= new this()) + } + + public getTreeItem(element: SecurityViewTreeItem): vscode.TreeItem | Thenable { + return element + } + + public getChildren(element?: SecurityViewTreeItem | undefined): vscode.ProviderResult { + const groupingStrategy = CodeIssueGroupingStrategyState.instance.getState() + switch (groupingStrategy) { + case CodeIssueGroupingStrategy.FileLocation: + return this.getChildrenGroupedByFileLocation(element) + case CodeIssueGroupingStrategy.Severity: + default: + return this.getChildrenGroupedBySeverity(element) + } + } + + private getChildrenGroupedBySeverity(element: SecurityViewTreeItem | undefined) { + const filterHiddenSeverities = (severity: Severity) => + !SecurityTreeViewFilterState.instance.getHiddenSeverities().includes(severity) + + if (element instanceof SeverityItem) { + return element.issues + .filter((issue) => issue.visible) + .sort((a, b) => a.filePath.localeCompare(b.filePath) || a.startLine - b.startLine) + .map((issue) => new IssueItem(issue.filePath, issue)) + } + const result = severities.filter(filterHiddenSeverities).map( + (severity) => + new SeverityItem( + severity, + this.issueProvider.issues.reduce( + (accumulator, current) => + accumulator.concat( + current.issues + .filter((issue) => issue.severity === severity) + .filter((issue) => issue.visible) + .map((issue) => ({ ...issue, filePath: current.filePath })) + ), + [] as CodeScanIssueWithFilePath[] + ) + ) + ) + + this._onDidChangeTreeData.fire(result) + return result + } + + private getChildrenGroupedByFileLocation(element: SecurityViewTreeItem | undefined) { + const filterHiddenSeverities = (issue: CodeScanIssue) => + !SecurityTreeViewFilterState.instance.getHiddenSeverities().includes(issue.severity) + + if (element instanceof FileItem) { + return element.issues + .filter(filterHiddenSeverities) + .filter((issue) => issue.visible) + .sort((a, b) => a.startLine - b.startLine) + .map((issue) => new IssueItem(element.filePath, issue)) + } + + const result = this.issueProvider.issues + .filter((group) => group.issues.some(filterHiddenSeverities)) + .filter((group) => group.issues.some((issue) => issue.visible)) + .sort((a, b) => a.filePath.localeCompare(b.filePath)) + .map((group) => new FileItem(group.filePath, group.issues.filter(filterHiddenSeverities))) + this._onDidChangeTreeData.fire(result) + return result + } + + public refresh(): void { + this._onDidChangeTreeData.fire() + } + + public static focus() { + void vscode.commands.executeCommand('aws.amazonq.SecurityIssuesTree.focus').then(undefined, (e) => { + getLogger().error('SecurityIssuesTree focus failed: %s', e.message) + }) + } +} + +enum ContextValue { + FILE = 'file', + ISSUE_WITH_FIX = 'issueWithFix', + ISSUE_WITHOUT_FIX = 'issueWithoutFix', + ISSUE_WITH_FIX_DISABLED = 'issueWithFixDisabled', + SEVERITY = 'severity', +} + +export class SeverityItem extends vscode.TreeItem { + constructor( + public readonly severity: string, + public readonly issues: CodeScanIssueWithFilePath[] + ) { + super(severity) + this.description = `${this.issues.length} ${this.issues.length === 1 ? 'issue' : 'issues'}` + this.iconPath = this.getSeverityIcon() + this.contextValue = ContextValue.SEVERITY + this.collapsibleState = this.getCollapsibleState() + } + + private getSeverityIcon() { + return globals.context.asAbsolutePath(`resources/icons/aws/amazonq/severity-${this.severity.toLowerCase()}.svg`) + } + + private getCollapsibleState() { + return this.severity === 'Critical' || this.severity === 'High' + ? vscode.TreeItemCollapsibleState.Expanded + : vscode.TreeItemCollapsibleState.Collapsed + } +} + +export class FileItem extends vscode.TreeItem { + constructor( + public readonly filePath: string, + public readonly issues: CodeScanIssue[] + ) { + super(path.basename(filePath), vscode.TreeItemCollapsibleState.Expanded) + this.resourceUri = vscode.Uri.file(this.filePath) + this.description = vscode.workspace.asRelativePath(path.dirname(this.filePath)) + this.iconPath = new vscode.ThemeIcon('file') + this.contextValue = ContextValue.FILE + } +} + +export class IssueItem extends vscode.TreeItem { + constructor( + public readonly filePath: string, + public readonly issue: CodeScanIssue + ) { + super(issue.title, vscode.TreeItemCollapsibleState.None) + this.description = this.getDescription() + this.iconPath = this.getSeverityIcon() + this.tooltip = this.getTooltipMarkdown() + this.command = { + title: 'Focus Issue', + command: 'aws.amazonq.security.focusIssue', + arguments: [this.issue, this.filePath], + } + this.contextValue = this.getContextValue() + } + + private getSeverityImage() { + return globals.context.asAbsolutePath(`resources/images/severity-${this.issue.severity.toLowerCase()}.svg`) + } + + private getSeverityIcon() { + const iconPath = globals.context.asAbsolutePath( + `resources/icons/aws/amazonq/severity-${this.issue.severity.toLowerCase()}.svg` + ) + const groupingStrategy = CodeIssueGroupingStrategyState.instance.getState() + return groupingStrategy !== CodeIssueGroupingStrategy.Severity ? iconPath : undefined + } + + private getDescription() { + const positionStr = `[Ln ${this.issue.startLine + 1}]` + const groupingStrategy = CodeIssueGroupingStrategyState.instance.getState() + return groupingStrategy !== CodeIssueGroupingStrategy.FileLocation + ? `${path.basename(this.filePath)} ${positionStr}` + : positionStr + } + + private getContextValue() { + return this.issue.ruleId === sasRuleId + ? ContextValue.ISSUE_WITH_FIX_DISABLED + : this.issue.suggestedFixes.length === 0 || !this.issue.suggestedFixes[0].code + ? ContextValue.ISSUE_WITHOUT_FIX + : ContextValue.ISSUE_WITH_FIX + } + + private getTooltipMarkdown() { + const markdown = new vscode.MarkdownString() + markdown.isTrusted = true + markdown.supportHtml = true + markdown.supportThemeIcons = true + markdown.appendMarkdown(`## ${this.issue.title} ![${this.issue.severity}](${this.getSeverityImage()})\n`) + markdown.appendMarkdown(this.issue.recommendation.text) + + return markdown + } +} diff --git a/packages/core/src/codewhisperer/service/securityScanHandler.ts b/packages/core/src/codewhisperer/service/securityScanHandler.ts new file mode 100644 index 00000000000..14485642aed --- /dev/null +++ b/packages/core/src/codewhisperer/service/securityScanHandler.ts @@ -0,0 +1,465 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DefaultCodeWhispererClient } from '../client/codewhisperer' +import { getLogger } from '../../shared/logger/logger' +import * as vscode from 'vscode' +import { + AggregatedCodeScanIssue, + CodeScanIssue, + CodeScansState, + codeScanState, + CodeScanStoppedError, + onDemandFileScanState, + RegionProfile, +} from '../models/model' +import { sleep } from '../../shared/utilities/timeoutUtils' +import * as codewhispererClient from '../client/codewhisperer' +import * as CodeWhispererConstants from '../models/constants' +import { existsSync, statSync, readFileSync } from 'fs' // eslint-disable-line no-restricted-imports +import { RawCodeScanIssue } from '../models/model' +import * as crypto from 'crypto' +import path = require('path') +import { pageableToCollection } from '../../shared/utilities/collectionUtils' +import { ArtifactMap, CreateUploadUrlRequest, CreateUploadUrlResponse } from '../client/codewhispereruserclient' +import { TelemetryHelper } from '../util/telemetryHelper' +import request, { RequestError } from '../../shared/request' +import { ZipMetadata } from '../util/zipUtil' +import { getNullLogger } from '../../shared/logger/logger' +import { + CreateCodeScanError, + CreateUploadUrlError, + InvalidSourceZipError, + SecurityScanTimedOutError, + UploadArtifactToS3Error, +} from '../models/errors' +import { getTelemetryReasonDesc } from '../../shared/errors' +import { CodeWhispererSettings } from '../util/codewhispererSettings' +import { detectCommentAboveLine } from '../../shared/utilities/commentUtils' +import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { FeatureUseCase } from '../models/constants' +import { AmazonqCreateUpload, Span, telemetry } from '../../shared/telemetry/telemetry' +import { AuthUtil } from '../util/authUtil' + +export async function listScanResults( + client: DefaultCodeWhispererClient, + jobId: string, + codeScanFindingsSchema: string, + projectPaths: string[], + scope: CodeWhispererConstants.CodeAnalysisScope, + editor: vscode.TextEditor | undefined, + profile?: RegionProfile +) { + const logger = getLoggerForScope(scope) + const codeScanIssueMap: Map = new Map() + const aggregatedCodeScanIssueList: AggregatedCodeScanIssue[] = [] + const requester = (request: codewhispererClient.ListCodeScanFindingsRequest) => + client.listCodeScanFindings(request, profile?.arn) + const request: codewhispererClient.ListCodeScanFindingsRequest = { + jobId, + codeAnalysisFindingsSchema: codeScanFindingsSchema, + profileArn: profile?.arn, + } + const collection = pageableToCollection(requester, request, 'nextToken') + const issues = await collection + .flatten() + .map((resp) => { + logger.verbose(`ListCodeScanFindingsRequest requestId: ${resp.$response.requestId}`) + if ('codeScanFindings' in resp) { + return resp.codeScanFindings + } + return resp.codeAnalysisFindings + }) + .promise() + for (const issue of issues) { + mapToAggregatedList(codeScanIssueMap, issue, editor, scope) + } + for (const [key, issues] of codeScanIssueMap.entries()) { + // Project path example: /Users/username/project + // Key example: project/src/main/java/com/example/App.java + const mappedProjectPaths: Set = new Set() + for (const projectPath of projectPaths) { + // There could be multiple projectPaths with the same parent dir + // In that case, make sure to break out of this loop after a filePath is found + // or else it might result in duplicate findings. + const filePath = path.join(projectPath, '..', key) + if (existsSync(filePath) && statSync(filePath).isFile()) { + mappedProjectPaths.add(filePath) + const document = await vscode.workspace.openTextDocument(filePath) + const aggregatedCodeScanIssue: AggregatedCodeScanIssue = { + filePath: filePath, + issues: issues.map((issue) => mapRawToCodeScanIssue(issue, document, jobId, scope)), + } + aggregatedCodeScanIssueList.push(aggregatedCodeScanIssue) + break + } + } + const maybeAbsolutePath = `/${key}` + if ( + !mappedProjectPaths.has(maybeAbsolutePath) && + existsSync(maybeAbsolutePath) && + statSync(maybeAbsolutePath).isFile() + ) { + const document = await vscode.workspace.openTextDocument(maybeAbsolutePath) + const aggregatedCodeScanIssue: AggregatedCodeScanIssue = { + filePath: maybeAbsolutePath, + issues: issues.map((issue) => mapRawToCodeScanIssue(issue, document, jobId, scope)), + } + aggregatedCodeScanIssueList.push(aggregatedCodeScanIssue) + } + } + return aggregatedCodeScanIssueList +} + +function mapRawToCodeScanIssue( + issue: RawCodeScanIssue, + document: vscode.TextDocument, + jobId: string, + scope: CodeWhispererConstants.CodeAnalysisScope +): CodeScanIssue { + const isIssueTitleIgnored = CodeWhispererSettings.instance.getIgnoredSecurityIssues().includes(issue.title) + const isSingleIssueIgnored = detectCommentAboveLine( + document, + issue.startLine - 1, + CodeWhispererConstants.amazonqIgnoreNextLine + ) + const language = runtimeLanguageContext.getLanguageContext( + document.languageId, + path.extname(document.fileName) + ).language + return { + startLine: issue.startLine - 1 >= 0 ? issue.startLine - 1 : 0, + endLine: issue.endLine, + comment: `${issue.title.trim()}: ${issue.description.text.trim()}`, + title: issue.title, + description: issue.description, + detectorId: issue.detectorId, + detectorName: issue.detectorName, + findingId: issue.findingId, + ruleId: issue.ruleId, + relatedVulnerabilities: issue.relatedVulnerabilities, + severity: issue.severity, + recommendation: issue.remediation.recommendation, + suggestedFixes: issue.remediation.suggestedFixes, + visible: !isIssueTitleIgnored && !isSingleIssueIgnored, + scanJobId: jobId, + language, + autoDetected: scope === CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO, + } +} + +export function mapToAggregatedList( + codeScanIssueMap: Map, + json: string, + editor: vscode.TextEditor | undefined, + scope: CodeWhispererConstants.CodeAnalysisScope +) { + const codeScanIssues: RawCodeScanIssue[] = JSON.parse(json) + const filteredIssues = codeScanIssues.filter((issue) => { + if ( + (scope === CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO || + scope === CodeWhispererConstants.CodeAnalysisScope.FILE_ON_DEMAND) && + editor + ) { + for (let lineNumber = issue.startLine; lineNumber <= issue.endLine; lineNumber++) { + const line = editor.document.lineAt(lineNumber - 1)?.text + const codeContent = issue.codeSnippet.find((codeIssue) => codeIssue.number === lineNumber)?.content + if (codeContent?.includes('***')) { + // CodeSnippet contains redacted code so we can't do a direct comparison + return line.length === codeContent.length + } else { + return line === codeContent + } + } + } + return true + }) + + for (const issue of filteredIssues) { + const filePath = issue.filePath + if (codeScanIssueMap.has(filePath)) { + if (!isExistingIssue(issue, codeScanIssueMap)) { + codeScanIssueMap.get(filePath)?.push(issue) + } else { + getLogger().warn('Found duplicate issue %O, ignoring...', issue) + } + } else { + codeScanIssueMap.set(filePath, [issue]) + } + } +} + +function isDuplicateIssue(issueA: RawCodeScanIssue, issueB: RawCodeScanIssue) { + return ( + issueA.filePath === issueB.filePath && + issueA.title === issueB.title && + issueA.startLine === issueB.startLine && + issueA.endLine === issueB.endLine + ) +} + +function isExistingIssue(issue: RawCodeScanIssue, codeScanIssueMap: Map) { + return codeScanIssueMap.get(issue.filePath)?.some((existingIssue) => isDuplicateIssue(issue, existingIssue)) +} + +export async function pollScanJobStatus( + client: DefaultCodeWhispererClient, + jobId: string, + scope: CodeWhispererConstants.CodeAnalysisScope, + codeScanStartTime: number, + profile?: RegionProfile +) { + const pollingStartTime = performance.now() + // We don't expect to get results immediately, so sleep for some time initially to not make unnecessary calls + await sleep(getPollingDelayMsForScope(scope)) + + const logger = getLoggerForScope(scope) + logger.verbose(`Polling scan job status...`) + let status: string = 'Pending' + while (true) { + throwIfCancelled(scope, codeScanStartTime) + const req: codewhispererClient.GetCodeScanRequest = { + jobId: jobId, + profileArn: profile?.arn, + } + const resp = await client.getCodeScan(req) + logger.verbose(`GetCodeScanRequest requestId: ${resp.$response.requestId}`) + if (resp.status !== 'Pending') { + status = resp.status + logger.verbose(`Scan job status: ${status}`) + logger.verbose(`Complete Polling scan job status.`) + break + } + throwIfCancelled(scope, codeScanStartTime) + await sleep(CodeWhispererConstants.codeScanJobPollingIntervalSeconds * 1000) + const elapsedTime = performance.now() - pollingStartTime + if (elapsedTime > getPollingTimeoutMsForScope(scope)) { + logger.verbose(`Scan job status: ${status}`) + logger.verbose(`Security Scan failed. Amazon Q timed out.`) + throw new SecurityScanTimedOutError() + } + } + return status +} + +export async function createScanJob( + client: DefaultCodeWhispererClient, + artifactMap: codewhispererClient.ArtifactMap, + languageId: string, + scope: CodeWhispererConstants.CodeAnalysisScope, + scanName: string, + profile?: RegionProfile +) { + const logger = getLoggerForScope(scope) + logger.verbose(`Creating scan job...`) + const codeAnalysisScope = scope === CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO ? 'FILE' : 'PROJECT' + const req: codewhispererClient.CreateCodeScanRequest = { + artifacts: artifactMap, + programmingLanguage: { + languageName: languageId, + }, + scope: codeAnalysisScope, + codeScanName: scanName, + profileArn: profile?.arn, + } + const resp = await client.createCodeScan(req).catch((err) => { + getLogger().error(`Failed creating scan job. Request id: ${err.requestId}`) + if ( + err.message === CodeWhispererConstants.scansLimitReachedErrorMessage && + err.code === 'ThrottlingException' + ) { + throw err + } + throw new CreateCodeScanError(err) + }) + getLogger().info( + `Amazon Q Code Review requestId: ${resp.$response.requestId} and Amazon Q Code Review jobId: ${resp.jobId}` + ) + TelemetryHelper.instance.sendCodeScanEvent(languageId, resp.$response.requestId) + return resp +} + +export async function getPresignedUrlAndUpload( + client: DefaultCodeWhispererClient, + zipMetadata: ZipMetadata, + scope: CodeWhispererConstants.CodeAnalysisScope, + scanName: string, + profile?: RegionProfile +) { + const artifactMap = await telemetry.amazonq_createUpload.run(async (span) => { + const logger = getLoggerForScope(scope) + if (zipMetadata.zipFilePath === '') { + getLogger().error('Failed to create valid source zip') + throw new InvalidSourceZipError() + } + const uploadIntent = getUploadIntent(scope) + span.record({ + amazonqUploadIntent: uploadIntent, + amazonqRepositorySize: zipMetadata.srcPayloadSizeInBytes, + credentialStartUrl: AuthUtil.instance.startUrl, + }) + const srcReq: CreateUploadUrlRequest = { + contentMd5: getMd5(zipMetadata.zipFilePath), + artifactType: 'SourceCode', + uploadIntent: uploadIntent, + uploadContext: { + codeAnalysisUploadContext: { + codeScanName: scanName, + }, + }, + profileArn: profile?.arn, + } + logger.verbose(`Prepare for uploading src context...`) + const srcResp = await client.createUploadUrl(srcReq).catch((err) => { + getLogger().error(`Failed getting presigned url for uploading src context. Request id: ${err.requestId}`) + span.record({ requestId: err.requestId }) + throw new CreateUploadUrlError(err.message) + }) + logger.verbose(`CreateUploadUrlRequest request id: ${srcResp.$response.requestId}`) + logger.verbose(`Complete Getting presigned Url for uploading src context.`) + logger.verbose(`Uploading src context...`) + await uploadArtifactToS3(zipMetadata.zipFilePath, srcResp, FeatureUseCase.CODE_SCAN, scope, span) + logger.verbose(`Complete uploading src context.`) + const artifactMap: ArtifactMap = { + SourceCode: srcResp.uploadId, + } + return artifactMap + }) + return artifactMap +} + +function getUploadIntent(scope: CodeWhispererConstants.CodeAnalysisScope) { + if ( + scope === CodeWhispererConstants.CodeAnalysisScope.PROJECT || + scope === CodeWhispererConstants.CodeAnalysisScope.FILE_ON_DEMAND + ) { + return CodeWhispererConstants.projectScanUploadIntent + } else { + return CodeWhispererConstants.fileScanUploadIntent + } +} + +export function getMd5(fileName: string) { + const hasher = crypto.createHash('md5') + hasher.update(readFileSync(fileName)) + return hasher.digest('base64') +} + +export function throwIfCancelled(scope: CodeWhispererConstants.CodeAnalysisScope, codeScanStartTime: number) { + switch (scope) { + case CodeWhispererConstants.CodeAnalysisScope.PROJECT: + if (codeScanState.isCancelling()) { + throw new CodeScanStoppedError() + } + break + case CodeWhispererConstants.CodeAnalysisScope.FILE_ON_DEMAND: + if (onDemandFileScanState.isCancelling()) { + throw new CodeScanStoppedError() + } + break + case CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO: { + const latestCodeScanStartTime = CodeScansState.instance.getLatestScanTime() + if ( + !CodeScansState.instance.isScansEnabled() || + (latestCodeScanStartTime && latestCodeScanStartTime > codeScanStartTime) + ) { + throw new CodeScanStoppedError() + } + break + } + default: + getLogger().warn(`Unknown code analysis scope: ${scope}`) + break + } +} +// TODO: Refactor this +export async function uploadArtifactToS3( + fileName: string, + resp: CreateUploadUrlResponse, + featureUseCase: FeatureUseCase, + scope?: CodeWhispererConstants.CodeAnalysisScope, + span?: Span +) { + const logger = getLoggerForScope(scope) + const encryptionContext = `{"uploadId":"${resp.uploadId}"}` + const headersObj: Record = { + 'Content-MD5': getMd5(fileName), + 'x-amz-server-side-encryption': 'aws:kms', + 'Content-Type': 'application/zip', + 'x-amz-server-side-encryption-context': Buffer.from(encryptionContext, 'utf8').toString('base64'), + } + + if (resp.kmsKeyArn !== '' && resp.kmsKeyArn !== undefined) { + headersObj['x-amz-server-side-encryption-aws-kms-key-id'] = resp.kmsKeyArn + } + + let requestId: string | undefined = undefined + let id2: string | undefined = undefined + let responseCode: string = '' + + try { + const response = await request.fetch('PUT', resp.uploadUrl, { + body: readFileSync(fileName), + headers: resp?.requestHeaders ?? headersObj, + }).response + logger.debug(`StatusCode: ${response.status}, Text: ${response.statusText}`) + requestId = response.headers?.get('x-amz-request-id') ?? undefined + id2 = response.headers?.get('x-amz-id-2') ?? undefined + responseCode = response.status.toString() + } catch (error) { + if (span && error instanceof RequestError) { + requestId = error.response.headers.get('x-amz-request-id') ?? undefined + id2 = error.response.headers.get('x-amz-id-2') ?? undefined + responseCode = error.code.toString() + } + let errorMessage = '' + const isCodeScan = featureUseCase === FeatureUseCase.CODE_SCAN + const featureType = isCodeScan ? 'security scans' : 'unit test generation' + const defaultMessage = isCodeScan ? 'Security scan failed.' : 'Test generation failed.' + getLogger().error( + `Amazon Q is unable to upload workspace artifacts to Amazon S3 for ${featureType}. ` + + 'For more information, see the Amazon Q documentation or contact your network or organization administrator.' + ) + const errorDesc = getTelemetryReasonDesc(error) + if (errorDesc?.includes('"PUT" request failed with code "403"')) { + errorMessage = '"PUT" request failed with code "403"' + } else if (errorDesc?.includes('"PUT" request failed with code "503"')) { + errorMessage = '"PUT" request failed with code "503"' + } else { + errorMessage = errorDesc ?? defaultMessage + } + throw new UploadArtifactToS3Error(errorMessage) + } finally { + getLogger().debug(`Upload to S3 response details: x-amz-request-id: ${requestId}, x-amz-id-2: ${id2}`) + if (span) { + span.record({ + requestId: requestId, + requestId2: id2, + requestServiceType: 's3', + httpStatusCode: responseCode, + }) + } + } +} + +// TODO: Refactor this +export function getLoggerForScope(scope?: CodeWhispererConstants.CodeAnalysisScope) { + return scope === CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO ? getNullLogger() : getLogger() +} + +function getPollingDelayMsForScope(scope: CodeWhispererConstants.CodeAnalysisScope) { + return ( + (scope === CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO || + scope === CodeWhispererConstants.CodeAnalysisScope.FILE_ON_DEMAND + ? CodeWhispererConstants.fileScanPollingDelaySeconds + : CodeWhispererConstants.projectScanPollingDelaySeconds) * 1000 + ) +} + +function getPollingTimeoutMsForScope(scope: CodeWhispererConstants.CodeAnalysisScope) { + return scope === CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO + ? CodeWhispererConstants.expressScanTimeoutMs + : CodeWhispererConstants.standardScanTimeoutMs +} diff --git a/packages/core/src/codewhisperer/service/serviceContainer.ts b/packages/core/src/codewhisperer/service/serviceContainer.ts new file mode 100644 index 00000000000..7215a73c616 --- /dev/null +++ b/packages/core/src/codewhisperer/service/serviceContainer.ts @@ -0,0 +1,44 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AuthUtil } from '../util/authUtil' +import { LineAnnotationController } from '../views/lineAnnotationController' +import { ActiveStateController } from '../views/activeStateController' +import { LineTracker } from '../tracker/lineTracker' + +/** + * Container for CodeWhisperer sub-components + * Please utilize this container class as the bridge to access other components to avoid create singleton objects when it's not necessary. + * Example: + * class SubComponent { + * constructor(private readonly container: Container) {} + * + * public doSomething() { + * const isConnected = this.container.authProvider.isConnected() + * this.anotherComponent.update(isConnected) + * } + * } + */ +export class Container { + static #instance: Container | undefined + + static get instance(): Container { + return (Container.#instance ??= new Container(AuthUtil.instance)) + } + + readonly lineTracker: LineTracker + readonly lineAnnotationController: LineAnnotationController + readonly activeStateController: ActiveStateController + + protected constructor(readonly auth: AuthUtil) { + this.lineTracker = new LineTracker() + this.lineAnnotationController = new LineAnnotationController(this) + this.activeStateController = new ActiveStateController(this) + } + + ready() { + this.lineTracker.ready() + } +} diff --git a/packages/core/src/codewhisperer/service/statusBar.ts b/packages/core/src/codewhisperer/service/statusBar.ts new file mode 100644 index 00000000000..6aacfec73b7 --- /dev/null +++ b/packages/core/src/codewhisperer/service/statusBar.ts @@ -0,0 +1,147 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { CodeSuggestionsState } from '../models/model' +import { AuthUtil } from '../util/authUtil' +import { getSelectedCustomization } from '../util/customizationUtil' +import { codicon, getIcon } from '../../shared/icons' +import { Commands } from '../../shared/vscode/commands2' +import { listCodeWhispererCommandsId } from '../ui/statusBarMenu' + +export class CodeWhispererStatusBarManager { + private statusBar: CodeWhispererStatusBar + + constructor(statusBar: CodeWhispererStatusBar = CodeWhispererStatusBar.instance) { + this.statusBar = statusBar + + CodeSuggestionsState.instance.onDidChangeState(() => { + return this.refreshStatusBar() + }) + } + + static #instance: CodeWhispererStatusBarManager + + public static get instance() { + return (this.#instance ??= new this()) + } + + /** Updates the status bar to represent the latest CW state */ + refreshStatusBar() { + if (AuthUtil.instance.isConnectionValid()) { + if (AuthUtil.instance.requireProfileSelection()) { + return this.setState('needsProfile') + } + return this.setState('ok') + } else if (AuthUtil.instance.isConnectionExpired()) { + return this.setState('expired') + } else { + return this.setState('notConnected') + } + } + + /** + * Sets the status bar in to a "loading state", effectively showing + * the spinning circle. + * + * When loading is done, call {@link refreshStatusBar} to update the + * status bar to the latest state. + */ + async setLoading(): Promise { + await this.setState('loading') + } + + private async setState(state: keyof typeof states) { + switch (state) { + case 'loading': { + await this.statusBar.setState('loading') + break + } + case 'ok': { + await this.statusBar.setState('ok', CodeSuggestionsState.instance.isSuggestionsEnabled()) + break + } + case 'expired': { + await this.statusBar.setState('expired') + break + } + case 'notConnected': { + await this.statusBar.setState('notConnected') + break + } + case 'needsProfile': { + await this.statusBar.setState('needsProfile') + break + } + } + } +} + +/** The states that the completion service can be in */ +const states = { + loading: 'loading', + ok: 'ok', + expired: 'expired', + notConnected: 'notConnected', + needsProfile: 'needsProfile', +} as const + +class CodeWhispererStatusBar { + protected statusBar: vscode.StatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 1) + + static #instance: CodeWhispererStatusBar + static get instance() { + return (this.#instance ??= new this()) + } + + protected constructor() {} + + async setState(state: keyof Omit): Promise + async setState(status: keyof Pick, isSuggestionsEnabled: boolean): Promise + async setState(status: keyof typeof states, isSuggestionsEnabled?: boolean): Promise { + const statusBar = this.statusBar + statusBar.command = listCodeWhispererCommandsId + statusBar.backgroundColor = undefined + + const title = 'Amazon Q' + switch (status) { + case 'loading': { + const selectedCustomization = getSelectedCustomization() + statusBar.text = codicon` ${getIcon('vscode-loading~spin')} ${title}${ + selectedCustomization.arn === '' ? '' : ` | ${selectedCustomization.name}` + }` + break + } + case 'ok': { + const selectedCustomization = getSelectedCustomization() + const icon = isSuggestionsEnabled ? getIcon('vscode-debug-start') : getIcon('vscode-debug-pause') + statusBar.text = codicon`${icon} ${title}${ + selectedCustomization.arn === '' ? '' : ` | ${selectedCustomization.name}` + }` + break + } + + case 'expired': { + statusBar.text = codicon` ${getIcon('vscode-debug-disconnect')} ${title}` + statusBar.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground') + break + } + case 'needsProfile': + case 'notConnected': + statusBar.text = codicon` ${getIcon('vscode-chrome-close')} ${title}` + statusBar.backgroundColor = new vscode.ThemeColor('statusBarItem.errorBackground') + break + } + + statusBar.show() + } +} + +/** In this module due to circular dependency issues */ +export const refreshStatusBar = Commands.declare( + { id: 'aws.amazonq.refreshStatusBar', logging: false }, + () => async () => { + await CodeWhispererStatusBarManager.instance.refreshStatusBar() + } +) diff --git a/packages/core/src/codewhisperer/service/transformByQ/humanInTheLoopManager.ts b/packages/core/src/codewhisperer/service/transformByQ/humanInTheLoopManager.ts new file mode 100644 index 00000000000..1646864e066 --- /dev/null +++ b/packages/core/src/codewhisperer/service/transformByQ/humanInTheLoopManager.ts @@ -0,0 +1,128 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import * as os from 'os' +import path from 'path' +import { FolderInfo, transformByQState } from '../../models/model' +import fs from '../../../shared/fs/fs' +import { createPomCopy, replacePomVersion } from './transformFileHandler' +import { getLogger } from '../../../shared/logger/logger' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { CodeTransformTelemetryState } from '../../../amazonqGumby/telemetry/codeTransformTelemetryState' +import { MetadataResult } from '../../../shared/telemetry/telemetryClient' + +export interface IManifestFile { + pomArtifactId: string + pomFolderName: string + hilCapability: string + pomGroupId: string + sourcePomVersion: string +} + +/** + * @description This class helps encapsulate the "human in the loop" behavior of Amazon Q transform. Users + * will be prompted for input during the transformation process. Amazon Q will make some temporary folders + * and take action on behalf of the user. To make sure those actions are executed and cleanup up properly, + * we have encapsulated in this class. + */ +export class HumanInTheLoopManager { + public readonly diagnosticCollection = vscode.languages.createDiagnosticCollection('hilPomFileDiagnostics') + + private readonly osTmpDir = os.tmpdir() + private readonly localPathToXmlDependencyList = '/target/dependency-updates-aggregate-report.xml' + private readonly pomReplacementDelimiter = '*****' + private readonly tmpDownloadsFolderName = 'q-hil-dependency-artifacts' + private readonly tmpDependencyListFolderName = 'q-pom-dependency-list' + private readonly userDependencyUpdateFolderName = 'q-pom-dependency-update' + private readonly tmpDependencyListDir = path.join(this.osTmpDir, this.tmpDependencyListFolderName) + private readonly userDependencyUpdateDir = path.join(this.osTmpDir, this.userDependencyUpdateFolderName) + private readonly tmpDownloadsDir = path.join(this.osTmpDir, this.tmpDownloadsFolderName) + + private tmpSessionFiles: string[] = [] + private pomFileVirtualFileReference!: vscode.Uri + private manifestFileValues!: IManifestFile + private newPomFileVirtualFileReference!: vscode.Uri + + public getTmpDependencyListDir = () => this.tmpDependencyListDir + public getUserDependencyUpdateDir = () => this.userDependencyUpdateDir + public getTmpDownloadsDir = () => this.tmpDownloadsDir + public getPomFileVirtualFileReference = () => this.pomFileVirtualFileReference + public getManifestFileValues = () => this.manifestFileValues + public getNewPomFileVirtualFileReference = () => this.newPomFileVirtualFileReference + + public setPomFileVirtualFileReference = (file: vscode.Uri) => (this.pomFileVirtualFileReference = file) + public setManifestFileValues = (manifestFileValues: IManifestFile) => (this.manifestFileValues = manifestFileValues) + public setNewPomFileVirtualFileReference = (file: vscode.Uri) => (this.newPomFileVirtualFileReference = file) + + public getUploadFolderInfo = (): FolderInfo => { + return { + name: this.userDependencyUpdateFolderName, + path: this.userDependencyUpdateDir, + } + } + + public getCompileDependencyListFolderInfo = (): FolderInfo => { + return { + name: this.tmpDependencyListFolderName, + path: this.tmpDependencyListDir, + } + } + + public getDependencyListXmlOutput = async () => + await fs.readFileText(path.join(this.tmpDependencyListDir, this.localPathToXmlDependencyList)) + + public createPomFileCopy = async (outputDirectoryPath: string, pomFileVirtualFileReference: vscode.Uri) => { + const newPomCopyRef = await createPomCopy(outputDirectoryPath, pomFileVirtualFileReference, 'pom.xml') + this.tmpSessionFiles.push(newPomCopyRef.fsPath) + return newPomCopyRef + } + + public replacePomFileVersion = async (pomFileVirtualFileReference: vscode.Uri, version: string) => + await replacePomVersion(pomFileVirtualFileReference, version, this.pomReplacementDelimiter) + + public cleanUpArtifacts = async () => { + try { + await fs.delete(this.userDependencyUpdateDir, { recursive: true }) + } catch (e: any) { + this.logArtifactError(e) + } + try { + await fs.delete(this.tmpDependencyListDir, { recursive: true }) + } catch (e: any) { + this.logArtifactError(e) + } + try { + await fs.delete(this.tmpDownloadsDir, { recursive: true }) + } catch (e: any) { + this.logArtifactError(e) + } + for (let i = 0; i < this.tmpSessionFiles.length; i++) { + try { + await fs.delete(this.tmpSessionFiles[i], { recursive: true }) + } catch (e: any) { + this.logArtifactError(e) + } + } + this.tmpSessionFiles = [] + } + + private logArtifactError(e: any) { + const errorMessage = 'Error cleaning up artifacts' + const artifactCleanUpErrorMessage = (e: any) => `CodeTransformation: ${errorMessage} = ${e?.message}` + getLogger().error(artifactCleanUpErrorMessage(e)) + telemetry.codeTransform_logGeneralError.emit({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + codeTransformJobId: transformByQState.getJobId(), + result: MetadataResult.Fail, + reason: errorMessage, + }) + } + + static #instance: HumanInTheLoopManager | undefined + + public static get instance() { + return (this.#instance ??= new this()) + } +} diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts new file mode 100644 index 00000000000..c2934dc24ba --- /dev/null +++ b/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts @@ -0,0 +1,1041 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import * as nodefs from 'fs' // eslint-disable-line no-restricted-imports +import * as path from 'path' +import * as os from 'os' +import * as codeWhisperer from '../../client/codewhisperer' +import * as crypto from 'crypto' +import * as CodeWhispererConstants from '../../models/constants' +import { + FolderInfo, + HilZipManifest, + IHilZipManifestParams, + jobPlanProgress, + RegionProfile, + sessionJobHistory, + StepProgress, + TransformationType, + transformByQState, + TransformByQStatus, + TransformByQStoppedError, + ZipManifest, +} from '../../models/model' +import { getLogger } from '../../../shared/logger/logger' +import { + CreateUploadUrlResponse, + ProgressUpdates, + TransformationProgressUpdate, + TransformationSteps, + TransformationUserActionStatus, + UploadContext, +} from '../../client/codewhispereruserclient' +import { sleep } from '../../../shared/utilities/timeoutUtils' +import AdmZip from 'adm-zip' +import globals from '../../../shared/extensionGlobals' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { CodeTransformTelemetryState } from '../../../amazonqGumby/telemetry/codeTransformTelemetryState' +import { calculateTotalLatency } from '../../../amazonqGumby/telemetry/codeTransformTelemetry' +import { MetadataResult } from '../../../shared/telemetry/telemetryClient' +import request from '../../../shared/request' +import { JobStoppedError } from '../../../amazonqGumby/errors' +import { createLocalBuildUploadZip, extractOriginalProjectSources, writeAndShowBuildLogs } from './transformFileHandler' +import { createCodeWhispererChatStreamingClient } from '../../../shared/clients/codewhispererChatClient' +import { downloadExportResultArchive } from '../../../shared/utilities/download' +import { ExportContext, ExportIntent, TransformationDownloadArtifactType } from '@amzn/codewhisperer-streaming' +import fs from '../../../shared/fs/fs' +import { encodeHTML } from '../../../shared/utilities/textUtilities' +import { convertToTimeString } from '../../../shared/datetime' +import { getAuthType } from '../../../auth/utils' +import { UserWrittenCodeTracker } from '../../tracker/userWrittenCodeTracker' +import { setContext } from '../../../shared/vscode/setContext' +import { AuthUtil } from '../../util/authUtil' +import { DiffModel } from './transformationResultsViewProvider' +import { spawnSync } from 'child_process' // eslint-disable-line no-restricted-imports + +export function getSha256(buffer: Buffer) { + const hasher = crypto.createHash('sha256') + hasher.update(buffer) + return hasher.digest('base64') +} + +export function throwIfCancelled() { + if (transformByQState.isCancelled()) { + throw new TransformByQStoppedError() + } +} + +export function updateJobHistory() { + if (transformByQState.getJobId() !== '' && transformByQState.getSourceJDKVersion() !== undefined) { + sessionJobHistory[transformByQState.getJobId()] = { + startTime: transformByQState.getStartTime(), + projectName: transformByQState.getProjectName(), + status: transformByQState.getPolledJobStatus(), + duration: convertToTimeString(calculateTotalLatency(CodeTransformTelemetryState.instance.getStartTime())), + transformationType: transformByQState.getTransformationType() ?? 'N/A', + sourceJDKVersion: + transformByQState.getTransformationType() === TransformationType.LANGUAGE_UPGRADE + ? (transformByQState.getSourceJDKVersion() ?? 'N/A') + : 'N/A', + targetJDKVersion: + transformByQState.getTransformationType() === TransformationType.LANGUAGE_UPGRADE + ? (transformByQState.getTargetJDKVersion() ?? 'N/A') + : 'N/A', + customDependencyVersionsFilePath: + transformByQState.getTransformationType() === TransformationType.LANGUAGE_UPGRADE + ? transformByQState.getCustomDependencyVersionFilePath() || 'N/A' + : 'N/A', + customBuildCommand: + transformByQState.getTransformationType() === TransformationType.LANGUAGE_UPGRADE + ? transformByQState.getCustomBuildCommand() || 'N/A' + : 'N/A', + } + } + return sessionJobHistory +} + +export function getHeadersObj(sha256: string, kmsKeyArn: string | undefined) { + let headersObj = {} + if (kmsKeyArn === undefined || kmsKeyArn.length === 0) { + headersObj = { + 'x-amz-checksum-sha256': sha256, + 'Content-Type': 'application/zip', + } + } else { + headersObj = { + 'x-amz-checksum-sha256': sha256, + 'Content-Type': 'application/zip', + 'x-amz-server-side-encryption': 'aws:kms', + 'x-amz-server-side-encryption-aws-kms-key-id': kmsKeyArn, + } + } + return headersObj +} + +// Consider enhancing the S3 client to include this functionality +export async function uploadArtifactToS3( + fileName: string, + resp: CreateUploadUrlResponse, + sha256: string, + buffer: Buffer +) { + throwIfCancelled() + try { + const uploadFileByteSize = (await nodefs.promises.stat(fileName)).size + getLogger().info( + `CodeTransformation: Uploading project artifact at %s with checksum %s using uploadId: %s and size %s kB`, + fileName, + sha256, + resp.uploadId, + Math.round(uploadFileByteSize / 1000) + ) + + let response = undefined + /* The existing S3 client has built-in retries but it requires the bucket name, so until + * CreateUploadUrl can be modified to return the S3 bucket name, manually implement retries. + * Alternatively, when waitUntil supports a fixed number of retries and retriableCodes, use that. + */ + const retriableCodes = [408, 429, 500, 502, 503, 504] + for (let i = 0; i < 4; i++) { + try { + response = await request.fetch('PUT', resp.uploadUrl, { + body: buffer, + headers: getHeadersObj(sha256, resp.kmsKeyArn), + }).response + getLogger().info(`CodeTransformation: upload to S3 status on attempt ${i + 1}/4 = ${response.status}`) + if (response.status === 200) { + break + } + throw new Error( + `Upload failed, status = ${response.status}; full response: ${JSON.stringify(response)}` + ) + } catch (e: any) { + if (response && !retriableCodes.includes(response.status)) { + throw new Error(`Upload failed with status code = ${response.status}; did not automatically retry`) + } + if (i !== 3) { + await sleep(1000 * Math.pow(2, i)) + } + } + } + if (!response || response.status !== 200) { + const uploadFailedError = `Upload failed after up to 4 attempts with status code = ${response?.status ?? 'unavailable'}` + getLogger().error(`CodeTransformation: ${uploadFailedError}`) + throw new Error(uploadFailedError) + } + getLogger().info('CodeTransformation: Upload to S3 succeeded') + } catch (e: any) { + let errorMessage = `The upload failed due to: ${(e as Error).message}. For more information, see the [Amazon Q documentation](${CodeWhispererConstants.codeTransformTroubleshootUploadError})` + if (errorMessage.includes('Request has expired')) { + errorMessage = CodeWhispererConstants.errorUploadingWithExpiredUrl + } else if (errorMessage.includes('Failed to establish a socket connection')) { + errorMessage = CodeWhispererConstants.socketConnectionFailed + } else if (errorMessage.includes('self signed certificate in certificate chain')) { + errorMessage = CodeWhispererConstants.selfSignedCertificateError + } + getLogger().error(`CodeTransformation: UploadZip error = ${e}`) + throw new Error(errorMessage) + } +} + +export async function resumeTransformationJob(jobId: string, userActionStatus: TransformationUserActionStatus) { + try { + const response = await codeWhisperer.codeWhispererClient.codeModernizerResumeTransformation({ + transformationJobId: jobId, + userActionStatus, // can be "COMPLETED" or "REJECTED" + }) + getLogger().info( + `CodeTransformation: resumeTransformation API status code = ${response.$response.httpResponse.statusCode}` + ) + return response.transformationStatus + } catch (e: any) { + const errorMessage = `Resuming the job failed due to: ${(e as Error).message}` + getLogger().error(`CodeTransformation: ResumeTransformation error = %O`, e) + throw new Error(errorMessage) + } +} + +export async function stopJob(jobId: string) { + if (!jobId) { + return + } + + getLogger().info(`CodeTransformation: Stopping transformation job with ID: ${jobId}`) + + try { + await codeWhisperer.codeWhispererClient.codeModernizerStopCodeTransformation({ + transformationJobId: jobId, + }) + } catch (e: any) { + getLogger().error(`CodeTransformation: StopTransformation error = %O`, e) + throw new Error('Stop job failed') + } +} + +export async function uploadPayload( + payloadFileName: string, + profile: RegionProfile | undefined, + uploadContext?: UploadContext +) { + const buffer = Buffer.from(await fs.readFileBytes(payloadFileName)) + const sha256 = getSha256(buffer) + + throwIfCancelled() + let response = undefined + try { + response = await codeWhisperer.codeWhispererClient.createUploadUrl({ + contentChecksum: sha256, + contentChecksumType: CodeWhispererConstants.contentChecksumType, + uploadIntent: CodeWhispererConstants.uploadIntent, + uploadContext, + profileArn: profile?.arn, + }) + } catch (e: any) { + const errorMessage = `Creating the upload URL failed due to: ${(e as Error).message}` + getLogger().error(`CodeTransformation: CreateUploadUrl error: = %O`, e) + throw new Error(errorMessage) + } + + getLogger().info('CodeTransformation: created upload URL successfully') + + try { + await uploadArtifactToS3(payloadFileName, response, sha256, buffer) + } catch (e: any) { + const errorMessage = (e as Error).message + getLogger().error(`CodeTransformation: UploadArtifactToS3 error: = ${errorMessage}`) + throw new Error(errorMessage) + } + + // UploadContext only exists for subsequent uploads, and they will return a uploadId that is NOT + // the jobId. Only the initial call will uploadId be the jobId + if (!uploadContext) { + transformByQState.setJobId(encodeHTML(response.uploadId)) + } + jobPlanProgress['uploadCode'] = StepProgress.Succeeded + if (transformByQState.getTransformationType() === TransformationType.SQL_CONVERSION) { + // if doing a SQL conversion, we don't build the code or generate a plan, so mark these steps as succeeded immediately so that next step renders + jobPlanProgress['buildCode'] = StepProgress.Succeeded + jobPlanProgress['generatePlan'] = StepProgress.Succeeded + } + updateJobHistory() + return response.uploadId +} + +/** + * Array of file extensions used by Maven as metadata in the local repository. + * Files with these extensions influence Maven's behavior during compile time, + * particularly in checking the availability of source repositories and potentially + * re-downloading dependencies if the source is not accessible. Removing these + * files can prevent Maven from attempting to download dependencies again. + */ +const mavenExcludedExtensions = ['.repositories', '.sha1'] + +// exclude .DS_Store (not relevant) and Maven executables (can cause permissions issues when building if user has not ran 'chmod') +const sourceExcludedExtensions = ['.DS_Store', 'mvnw', 'mvnw.cmd'] + +/** + * Determines if the specified file path corresponds to a Maven metadata file + * by checking against known metadata file extensions. This is used to identify + * files that might trigger Maven to recheck or redownload dependencies from source repositories. + * + * @param path The file path to evaluate for exclusion based on its extension. + * @returns {boolean} Returns true if the path ends with an extension associated with Maven metadata files; otherwise, false. + */ +function isExcludedDependencyFile(path: string): boolean { + return mavenExcludedExtensions.some((extension) => path.endsWith(extension)) +} + +// do not zip the .DS_Store file as it may appear in the diff.patch +function isExcludedSourceFile(path: string): boolean { + return sourceExcludedExtensions.some((extension) => path.endsWith(extension)) +} + +// zip all dependency files and all source files +// excludes "target" (contains large JARs) plus ".git", ".idea", and ".github" (may appear in diff.patch) +export function getFilesRecursively(dir: string, isDependenciesFolder: boolean): string[] { + const entries = nodefs.readdirSync(dir, { withFileTypes: true }) + const files = entries.flatMap((entry) => { + const res = path.resolve(dir, entry.name) + if (entry.isDirectory()) { + if (isDependenciesFolder) { + // include all dependency files + return getFilesRecursively(res, isDependenciesFolder) + } else if ( + entry.name !== 'target' && + entry.name !== '.git' && + entry.name !== '.idea' && + entry.name !== '.github' + ) { + // exclude the above directories when zipping source code + return getFilesRecursively(res, isDependenciesFolder) + } else { + return [] + } + } else { + return [res] + } + }) + return files +} + +interface IZipManifestParams { + hilZipParams?: IHilZipManifestParams +} +export function createZipManifest({ hilZipParams }: IZipManifestParams) { + const zipManifest = hilZipParams ? new HilZipManifest(hilZipParams) : new ZipManifest() + return zipManifest +} + +interface IZipCodeParams { + dependenciesFolder?: FolderInfo + projectPath?: string + zipManifest: ZipManifest | HilZipManifest +} + +interface ZipCodeResult { + tempFilePath: string + fileSize: number +} + +export async function zipCode( + { dependenciesFolder, projectPath, zipManifest }: IZipCodeParams, + zip: AdmZip = new AdmZip() +) { + let tempFilePath = undefined + try { + throwIfCancelled() + + // if no project Path is passed in, we are not uploaded the source folder + // we only upload dependencies for human in the loop work + if (projectPath) { + const sourceFiles = getFilesRecursively(projectPath, false) + let sourceFilesSize = 0 + for (const file of sourceFiles) { + if (nodefs.statSync(file).isDirectory() || isExcludedSourceFile(file)) { + getLogger().info('CodeTransformation: Skipping file') + continue + } + const relativePath = path.relative(projectPath, file) + const paddedPath = path.join('sources', relativePath) + zip.addLocalFile(file, path.dirname(paddedPath)) + sourceFilesSize += (await nodefs.promises.stat(file)).size + } + getLogger().info(`CodeTransformation: source code files size = ${sourceFilesSize}`) + } + + if ( + transformByQState.getTransformationType() === TransformationType.SQL_CONVERSION && + zipManifest instanceof ZipManifest + ) { + // note that zipManifest must be a ZipManifest since only other option is HilZipManifest which is not used for SQL conversions + const metadataZip = new AdmZip(transformByQState.getMetadataPathSQL()) + zipManifest.requestedConversions = { + sqlConversion: { + source: transformByQState.getSourceDB(), + target: transformByQState.getTargetDB(), + schema: transformByQState.getSchema(), + host: transformByQState.getSourceServerName(), + sctFileName: metadataZip.getEntries().filter((entry) => entry.name.endsWith('.sct'))[0].name, + }, + } + for (const entry of metadataZip.getEntries()) { + zip.addFile(path.join(zipManifest.dependenciesRoot, entry.name), entry.getData()) + } + const sqlMetadataSize = (await nodefs.promises.stat(transformByQState.getMetadataPathSQL())).size + getLogger().info(`CodeTransformation: SQL metadata file size = ${sqlMetadataSize}`) + } + + throwIfCancelled() + + let dependencyFiles: string[] = [] + if (dependenciesFolder && (await fs.exists(dependenciesFolder.path))) { + dependencyFiles = getFilesRecursively(dependenciesFolder.path, true) + } + + if (dependenciesFolder && dependencyFiles.length > 0) { + let dependencyFilesSize = 0 + for (const file of dependencyFiles) { + if (isExcludedDependencyFile(file)) { + continue + } + const relativePath = path.relative(dependenciesFolder.path, file) + if (relativePath.includes('compilations.json')) { + let fileContents = await nodefs.promises.readFile(file, 'utf-8') + if (os.platform() === 'win32') { + fileContents = fileContents.replace(/\\\\/g, '/') + } + zip.addFile('compilations.json', Buffer.from(fileContents, 'utf-8')) + } else { + zip.addLocalFile(file, path.dirname(relativePath)) + } + dependencyFilesSize += (await nodefs.promises.stat(file)).size + } + getLogger().info(`CodeTransformation: dependency files size = ${dependencyFilesSize}`) + } + + if (transformByQState.getCustomDependencyVersionFilePath() && zipManifest instanceof ZipManifest) { + zip.addLocalFile( + transformByQState.getCustomDependencyVersionFilePath(), + 'sources', + 'dependency_upgrade.yml' + ) + zipManifest.dependencyUpgradeConfigFile = 'dependency_upgrade.yml' + } + + zip.addFile('manifest.json', Buffer.from(JSON.stringify(zipManifest)), 'utf-8') + + throwIfCancelled() + + tempFilePath = path.join(os.tmpdir(), 'zipped-code.zip') + await fs.writeFile(tempFilePath, zip.toBuffer()) + if (dependenciesFolder?.path) { + await fs.delete(dependenciesFolder.path, { recursive: true, force: true }) + } + } catch (e: any) { + getLogger().error(`CodeTransformation: zipCode error = ${e}`) + throw Error('Failed to zip project') + } + + const fileSize = (await nodefs.promises.stat(tempFilePath)).size + + getLogger().info(`CodeTransformation: created ZIP of size ${fileSize} at ${tempFilePath}`) + + return { tempFilePath: tempFilePath, fileSize: fileSize } as ZipCodeResult +} + +export async function startJob(uploadId: string, profile: RegionProfile | undefined) { + const sourceLanguageVersion = `JAVA_${transformByQState.getSourceJDKVersion()}` + const targetLanguageVersion = `JAVA_${transformByQState.getTargetJDKVersion()}` + try { + const response = await codeWhisperer.codeWhispererClient.codeModernizerStartCodeTransformation({ + workspaceState: { + uploadId: uploadId, + programmingLanguage: { languageName: CodeWhispererConstants.defaultLanguage.toLowerCase() }, + }, + transformationSpec: { + transformationType: CodeWhispererConstants.transformationType, // shared b/w language upgrades & sql conversions for now + source: { language: sourceLanguageVersion }, // dummy value of JDK8 used for SQL conversions just so that this API can be called + target: { language: targetLanguageVersion }, // JAVA_17 or JAVA_21 + }, + profileArn: profile?.arn, + }) + getLogger().info('CodeTransformation: called startJob API successfully') + return response.transformationJobId + } catch (e: any) { + const errorMessage = `Starting the job failed due to: ${(e as Error).message}` + getLogger().error(`CodeTransformation: StartTransformation error = %O`, e) + throw new Error(errorMessage) + } +} + +export function getImageAsBase64(filePath: string) { + const fileContents = nodefs.readFileSync(filePath, { encoding: 'base64' }) + return `data:image/svg+xml;base64,${fileContents}` +} + +/* + * Given the icon name from core/resources/icons/aws/amazonq, get the appropriate icon according to the user's theme. + * ex. getIcon('transform-file') returns the 'transform-file-light.svg' icon if user has a light theme enabled, + * otherwise 'transform-file-dark.svg' is returned. + */ +export function getTransformationIcon(name: string) { + let iconPath = '' + switch (name) { + case 'linesOfCode': + iconPath = 'transform-variables' + break + case 'plannedDependencyChanges': + iconPath = 'transform-dependencies' + break + case 'plannedDeprecatedApiChanges': + iconPath = 'transform-step-into' + break + case 'plannedFileChanges': + iconPath = 'transform-file' + break + case 'upArrow': + iconPath = 'transform-arrow' + break + case 'transformLogo': + return getImageAsBase64(globals.context.asAbsolutePath('resources/icons/aws/amazonq/transform-logo.svg')) + default: + iconPath = 'transform-default' + break + } + const themeColor = vscode.window.activeColorTheme.kind + if (themeColor === vscode.ColorThemeKind.Light || themeColor === vscode.ColorThemeKind.HighContrastLight) { + iconPath += '-light.svg' + } else { + iconPath += '-dark.svg' + } + return getImageAsBase64(globals.context.asAbsolutePath(path.join('resources/icons/aws/amazonq', iconPath))) +} + +export function getFormattedString(s: string) { + return CodeWhispererConstants.formattedStringMap.get(s) ?? s +} + +export function addTableMarkdown(plan: string, stepId: string, tableMapping: { [key: string]: string[] }) { + const tableObjects = tableMapping[stepId] + if (!tableObjects || tableObjects.length === 0 || tableObjects.every((table: string) => table === '')) { + // no tables for this stepId + return plan + } + const tables: any[] = [] + // eslint-disable-next-line unicorn/no-array-for-each + tableObjects.forEach((tableObj: string) => { + try { + const table = JSON.parse(tableObj) + if (table) { + tables.push(table) + } + } catch (e) { + getLogger().error(`CodeTransformation: Failed to parse table JSON, skipping: ${e}`) + } + }) + + if (tables.every((table: any) => table.rows.length === 0)) { + // empty tables for this stepId + plan += `\n\nThere are no ${tables[0].name.toLowerCase()} to display.\n\n` + return plan + } + // table name and columns are shared, so only add to plan once + plan += `\n\n\n${tables[0].name}\n|` + const columns = tables[0].columnNames + // eslint-disable-next-line unicorn/no-array-for-each + columns.forEach((columnName: string) => { + plan += ` ${getFormattedString(columnName)} |` + }) + plan += '\n|' + // eslint-disable-next-line unicorn/no-array-for-each + columns.forEach((_: any) => { + plan += '-----|' + }) + // add all rows of all tables + // eslint-disable-next-line unicorn/no-array-for-each + tables.forEach((table: any) => { + // eslint-disable-next-line unicorn/no-array-for-each + table.rows.forEach((row: any) => { + plan += '\n|' + // eslint-disable-next-line unicorn/no-array-for-each + columns.forEach((columnName: string) => { + if (columnName === 'relativePath') { + // add markdown link only for file paths + plan += ` [${row[columnName]}](${row[columnName]}) |` + } else { + plan += ` ${row[columnName]} |` + } + }) + }) + }) + plan += '\n\n' + return plan +} + +export function getTableMapping(stepZeroProgressUpdates: ProgressUpdates) { + const map: { [key: string]: string[] } = {} + for (const update of stepZeroProgressUpdates) { + if (!map[update.name]) { + map[update.name] = [] + } + // empty string allows us to skip this table when rendering + map[update.name].push(update.description ?? '') + } + return map +} + +export function getJobStatisticsHtml(jobStatistics: any) { + let htmlString = '' + if (jobStatistics.length === 0) { + return htmlString + } + htmlString += `
` + // eslint-disable-next-line unicorn/no-array-for-each + jobStatistics.forEach((stat: { name: string; value: string }) => { + if (stat.name === 'linesOfCode') { + htmlString += `

${getFormattedString(stat.name)}: ${stat.value}

` + } + }) + htmlString += `
` + return htmlString +} + +export async function getTransformationPlan(jobId: string, profile: RegionProfile | undefined) { + let response = undefined + try { + response = await codeWhisperer.codeWhispererClient.codeModernizerGetCodeTransformationPlan({ + transformationJobId: jobId, + profileArn: profile?.arn, + }) + + const stepZeroProgressUpdates = response.transformationPlan.transformationSteps[0].progressUpdates + + if (!stepZeroProgressUpdates || stepZeroProgressUpdates.length === 0) { + // means backend API response wrong and table data is missing + throw new Error('No progress updates found in step 0') + } + + // gets a mapping between the ID ('name' field) of each progressUpdate (substep) and the associated table + const tableMapping = getTableMapping(stepZeroProgressUpdates) + + const jobStatistics = JSON.parse(tableMapping['0'][0]).rows // ID of '0' reserved for job statistics table; only 1 table there + + // get logo directly since we only use one logo regardless of color theme + const logoIcon = getTransformationIcon('transformLogo') + + const arrowIcon = getTransformationIcon('upArrow') + + let plan = `\n\n

${CodeWhispererConstants.planTitle}


` + const authType = await getAuthType() + const linesOfCode = Number( + jobStatistics.find((stat: { name: string; value: string }) => stat.name === 'linesOfCode').value + ) + transformByQState.setLinesOfCodeSubmitted(linesOfCode) + if (authType === 'iamIdentityCenter' && linesOfCode > CodeWhispererConstants.codeTransformLocThreshold) { + plan += CodeWhispererConstants.codeTransformBillingText(linesOfCode) + } + plan += `

${ + CodeWhispererConstants.planIntroductionMessage + }

${getJobStatisticsHtml(jobStatistics)}
` + plan += `

${CodeWhispererConstants.planHeaderMessage}

${CodeWhispererConstants.planDisclaimerMessage} Read more.

` + for (const step of response.transformationPlan.transformationSteps.slice(1)) { + plan += `

${step.name}

Scroll to top

${step.description}

` + plan = addTableMarkdown(plan, step.id, tableMapping) + plan += `

` + } + plan += `

` + return plan + } catch (e: any) { + const errorMessage = (e as Error).message + getLogger().error(`CodeTransformation: GetTransformationPlan error = %O`, e) + + // GetTransformationPlan API call failed, but if response is defined, a display/parsing error occurred, so continue transformation + if (response === undefined) { + throw new Error(errorMessage) + } + } +} + +export async function getTransformationSteps(jobId: string, profile: RegionProfile | undefined) { + try { + const response = await codeWhisperer.codeWhispererClient.codeModernizerGetCodeTransformationPlan({ + transformationJobId: jobId, + profileArn: profile?.arn, + }) + return response.transformationPlan.transformationSteps.slice(1) // skip step 0 (contains supplemental info) + } catch (e: any) { + getLogger().error(`CodeTransformation: GetTransformationPlan error = %O`, e) + throw e + } +} + +export async function pollTransformationJob(jobId: string, validStates: string[], profile: RegionProfile | undefined) { + let status: string = '' + let isPlanComplete = false + while (true) { + throwIfCancelled() + try { + const response = await codeWhisperer.codeWhispererClient.codeModernizerGetCodeTransformation({ + transformationJobId: jobId, + profileArn: profile?.arn, + }) + status = response.transformationJob.status! + if (CodeWhispererConstants.validStatesForBuildSucceeded.includes(status)) { + jobPlanProgress['buildCode'] = StepProgress.Succeeded + } + if (status === 'TRANSFORMING') { + transformByQState.setHasSeenTransforming(true) + } + // emit metric when job status changes + if (status !== transformByQState.getPolledJobStatus()) { + telemetry.codeTransform_jobStatusChanged.emit({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + codeTransformJobId: jobId, + codeTransformStatus: status, + result: MetadataResult.Pass, + codeTransformPreviousStatus: transformByQState.getPolledJobStatus(), + }) + } + transformByQState.setPolledJobStatus(status) + getLogger().info(`CodeTransformation: polled job status = ${status}`) + + const errorMessage = response.transformationJob.reason + if (errorMessage !== undefined) { + getLogger().error( + `CodeTransformation: GetTransformation returned transformation error reason = ${errorMessage}` + ) + transformByQState.setJobFailureErrorChatMessage( + `${CodeWhispererConstants.failedToCompleteJobGenericChatMessage} ${errorMessage}` + ) + transformByQState.setJobFailureErrorNotification( + `${CodeWhispererConstants.failedToCompleteJobGenericNotification} ${errorMessage}` + ) + } + + if ( + CodeWhispererConstants.validStatesForPlanGenerated.includes(status) && + transformByQState.getTransformationType() === TransformationType.LANGUAGE_UPGRADE && + !isPlanComplete + ) { + const plan = await openTransformationPlan(jobId, profile) + if (plan?.toLowerCase().includes('dependency changes')) { + // final plan is complete; show to user + isPlanComplete = true + } + // for JDK upgrades without a YAML file, we show a static plan so no need to keep refreshing it + if ( + plan && + transformByQState.getSourceJDKVersion() !== transformByQState.getTargetJDKVersion() && + !transformByQState.getCustomDependencyVersionFilePath() + ) { + isPlanComplete = true + } + } + + if (validStates.includes(status)) { + break + } + + // TO-DO: later, handle case where PlannerAgent needs to run mvn dependency:tree during PLANNING stage; not needed for now + if ( + transformByQState.getHasSeenTransforming() && + transformByQState.getTransformationType() === TransformationType.LANGUAGE_UPGRADE + ) { + // client-side build is N/A for SQL conversions + await attemptLocalBuild() + } + + /** + * If we find a paused state, we need the user to take action. We will set the global + * state for polling status and early exit. + */ + if (CodeWhispererConstants.pausedStates.includes(status)) { + transformByQState.setPolledJobStatus(TransformByQStatus.WaitingUserInput) + break + } + /* + * Below IF is only relevant for pollTransformationStatusUntilPlanReady, when pollTransformationStatusUntilComplete + * is called, we break above on validStatesForCheckingDownloadUrl and check final status in finalizeTransformationJob + */ + if (CodeWhispererConstants.failureStates.includes(status)) { + throw new JobStoppedError() + } + await sleep(CodeWhispererConstants.transformationJobPollingIntervalSeconds * 1000) + } catch (e: any) { + getLogger().error(`CodeTransformation: error = %O`, e) + throw e + } + } + return status +} + +async function openTransformationPlan(jobId: string, profile?: RegionProfile) { + let plan = undefined + try { + plan = await getTransformationPlan(jobId, profile) + } catch (error) { + // means API call failed + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToCompleteJobNotification}`, error) + transformByQState.setJobFailureErrorNotification( + `${CodeWhispererConstants.failedToGetPlanNotification} ${(error as Error).message}` + ) + transformByQState.setJobFailureErrorChatMessage( + `${CodeWhispererConstants.failedToGetPlanChatMessage} ${(error as Error).message}` + ) + throw new Error('Get plan failed') + } + + if (plan) { + const planFilePath = path.join(transformByQState.getProjectPath(), 'transformation-plan.md') + nodefs.writeFileSync(planFilePath, plan) + await vscode.commands.executeCommand('markdown.showPreview', vscode.Uri.file(planFilePath)) + transformByQState.setPlanFilePath(planFilePath) + await setContext('gumby.isPlanAvailable', true) + } + return plan +} + +async function attemptLocalBuild() { + const jobId = transformByQState.getJobId() + let artifactId + try { + artifactId = await getClientInstructionArtifactId(jobId) + getLogger().info(`CodeTransformation: found artifactId = ${artifactId}`) + } catch (e: any) { + // don't throw error so that we can try to get progress updates again in next polling cycle + getLogger().error(`CodeTransformation: failed to get client instruction artifact ID = %O`, e) + } + if (artifactId) { + const clientInstructionsPath = await downloadClientInstructions(jobId, artifactId) + getLogger().info( + `CodeTransformation: downloaded clientInstructions with diff.patch at: ${clientInstructionsPath}` + ) + await processClientInstructions(jobId, clientInstructionsPath, artifactId) + } +} + +async function getClientInstructionArtifactId(jobId: string) { + const steps = await getTransformationSteps(jobId, AuthUtil.instance.regionProfileManager.activeRegionProfile) + const progressUpdate = findDownloadArtifactProgressUpdate(steps) + + let artifactId = undefined + if (progressUpdate?.downloadArtifacts) { + artifactId = progressUpdate.downloadArtifacts[0].downloadArtifactId + } + return artifactId +} + +async function downloadClientInstructions(jobId: string, artifactId: string) { + const exportDestination = `downloadClientInstructions_${jobId}_${artifactId}` + const exportZipPath = path.join(os.tmpdir(), exportDestination) + + const exportContext: ExportContext = { + transformationExportContext: { + downloadArtifactType: TransformationDownloadArtifactType.CLIENT_INSTRUCTIONS, + downloadArtifactId: artifactId, + }, + } + + await downloadAndExtractResultArchive(jobId, exportZipPath, exportContext) + return path.join(exportZipPath, 'diff.patch') +} + +async function processClientInstructions(jobId: string, clientInstructionsPath: any, artifactId: string) { + const destinationPath = path.join(os.tmpdir(), `originalCopy_${jobId}_${artifactId}`) + await extractOriginalProjectSources(destinationPath) + getLogger().info(`CodeTransformation: copied project to ${destinationPath}`) + const diffContents = await fs.readFileText(clientInstructionsPath) + if (diffContents.trim()) { + // show user the diff.patch + const doc = await vscode.workspace.openTextDocument(clientInstructionsPath) + await vscode.window.showTextDocument(doc, { viewColumn: vscode.ViewColumn.One }) + const diffModel = new DiffModel() + diffModel.parseDiff(clientInstructionsPath, path.join(destinationPath, 'sources'), true) + } else { + // still need to set the project copy so that we can use it below + transformByQState.setProjectCopyFilePath(path.join(destinationPath, 'sources')) + getLogger().info(`CodeTransformation: diff.patch is empty`) + } + await runClientSideBuild(transformByQState.getProjectCopyFilePath(), artifactId) +} + +export async function runClientSideBuild(projectCopyDir: string, clientInstructionArtifactId: string) { + const baseCommand = transformByQState.getMavenName() + const args = ['clean'] + if (transformByQState.getCustomBuildCommand() === CodeWhispererConstants.skipUnitTestsBuildCommand) { + args.push('test-compile') + } else { + args.push('test') + } + const environment = { ...process.env, JAVA_HOME: transformByQState.getTargetJavaHome() } + + const argString = args.join(' ') + const spawnResult = spawnSync(baseCommand, args, { + cwd: projectCopyDir, + shell: true, + encoding: 'utf-8', + env: environment, + }) + + const buildLogs = `Intermediate build result from running mvn ${argString}:\n\n${spawnResult.stdout}` + transformByQState.clearBuildLog() + transformByQState.appendToBuildLog(buildLogs) + await writeAndShowBuildLogs() + + const uploadZipDir = path.join( + os.tmpdir(), + `clientInstructionsResult_${transformByQState.getJobId()}_${clientInstructionArtifactId}` + ) + const uploadZipPath = await createLocalBuildUploadZip(uploadZipDir, spawnResult.status, spawnResult.stdout) + + // upload build results + const uploadContext: UploadContext = { + transformationUploadContext: { + jobId: transformByQState.getJobId(), + uploadArtifactType: 'ClientBuildResult', + }, + } + getLogger().info(`CodeTransformation: uploading client build results at ${uploadZipPath} and resuming job now`) + try { + await uploadPayload(uploadZipPath, AuthUtil.instance.regionProfileManager.activeRegionProfile, uploadContext) + await resumeTransformationJob(transformByQState.getJobId(), 'COMPLETED') + } catch (err: any) { + getLogger().error(`CodeTransformation: upload client build results / resumeTransformation error = %O`, err) + transformByQState.setJobFailureErrorChatMessage( + `${CodeWhispererConstants.failedToCompleteJobGenericChatMessage} ${err.message}` + ) + transformByQState.setJobFailureErrorNotification( + `${CodeWhispererConstants.failedToCompleteJobGenericNotification} ${err.message}` + ) + // in case server-side execution times out, still call resumeTransformationJob + if (err.message.includes('find a step in desired state:AWAITING_CLIENT_ACTION')) { + getLogger().info('CodeTransformation: resuming job after server-side execution timeout') + await resumeTransformationJob(transformByQState.getJobId(), 'COMPLETED') + } else { + throw err + } + } finally { + await fs.delete(projectCopyDir, { recursive: true }) + await fs.delete(uploadZipDir, { recursive: true }) + await fs.delete(uploadZipPath, { force: true }) + const exportZipDir = path.join( + os.tmpdir(), + `downloadClientInstructions_${transformByQState.getJobId()}_${clientInstructionArtifactId}` + ) + await fs.delete(exportZipDir, { recursive: true }) + getLogger().info( + `CodeTransformation: deleted projectCopy, clientInstructionsResult, and downloadClientInstructions directories/files` + ) + } +} + +export function getArtifactsFromProgressUpdate(progressUpdate: TransformationProgressUpdate) { + const artifactType = progressUpdate?.downloadArtifacts?.[0]?.downloadArtifactType + const artifactId = progressUpdate?.downloadArtifacts?.[0]?.downloadArtifactId + return { + artifactId, + artifactType, + } +} + +// used for client-side build +export function findDownloadArtifactProgressUpdate(transformationSteps: TransformationSteps) { + return transformationSteps + .flatMap((step) => step.progressUpdates ?? []) + .find( + (update) => update.status === 'AWAITING_CLIENT_ACTION' && update.downloadArtifacts?.[0]?.downloadArtifactId + ) +} + +// used for HIL +export function findDownloadArtifactStep(transformationSteps: TransformationSteps) { + for (let i = 0; i < transformationSteps.length; i++) { + const progressUpdates = transformationSteps[i].progressUpdates + if (progressUpdates?.length) { + for (let j = 0; j < progressUpdates.length; j++) { + if ( + progressUpdates[j].downloadArtifacts?.[0]?.downloadArtifactType || + progressUpdates[j].downloadArtifacts?.[0]?.downloadArtifactId + ) { + return { + transformationStep: transformationSteps[i], + progressUpdate: progressUpdates[j], + } + } + } + } + } + return { + transformationStep: undefined, + progressUpdate: undefined, + } +} + +export async function downloadResultArchive(jobId: string, pathToArchive: string, exportContext?: ExportContext) { + const cwStreamingClient = await createCodeWhispererChatStreamingClient() + + try { + const args = exportContext + ? { + exportId: jobId, + exportIntent: ExportIntent.TRANSFORMATION, + exportContext: exportContext, + } + : { + exportId: jobId, + exportIntent: ExportIntent.TRANSFORMATION, + } + await downloadExportResultArchive( + cwStreamingClient, + args, + pathToArchive, + AuthUtil.instance.regionProfileManager.activeRegionProfile + ) + } catch (e: any) { + getLogger().error(`CodeTransformation: ExportResultArchive error = %O`, e) + throw e + } finally { + cwStreamingClient.destroy() + UserWrittenCodeTracker.instance.onQFeatureInvoked() + } +} + +export async function downloadAndExtractResultArchive( + jobId: string, + pathToArchiveDir: string, + exportContext?: ExportContext +) { + const archivePathExists = await fs.existsDir(pathToArchiveDir) + if (!archivePathExists) { + await fs.mkdir(pathToArchiveDir) + } + + const pathToArchive = path.join(pathToArchiveDir, 'ExportResultsArchive.zip') + + let downloadErrorMessage = undefined + try { + // Download and deserialize the zip + await downloadResultArchive(jobId, pathToArchive, exportContext) + const zip = new AdmZip(pathToArchive) + zip.extractAllTo(pathToArchiveDir) + getLogger().info(`CodeTransformation: downloaded result archive to: ${pathToArchiveDir}`) + } catch (e) { + downloadErrorMessage = (e as Error).message + getLogger().error(`CodeTransformation: ExportResultArchive error = %O`, e) + throw new Error('Error downloading transformation result artifacts: ' + downloadErrorMessage) + } +} + +export async function downloadHilResultArchive(jobId: string, downloadArtifactId: string, pathToArchiveDir: string) { + await downloadAndExtractResultArchive(jobId, pathToArchiveDir) + + // manifest.json + // pomFolder/pom.xml or manifest has pomFolderName path + const manifestFileVirtualFileReference = vscode.Uri.file(path.join(pathToArchiveDir, 'manifest.json')) + const pomFileVirtualFileReference = vscode.Uri.file(path.join(pathToArchiveDir, 'pomFolder', 'pom.xml')) + return { manifestFileVirtualFileReference, pomFileVirtualFileReference } +} diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformFileHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformFileHandler.ts new file mode 100644 index 00000000000..b16ea64022c --- /dev/null +++ b/packages/core/src/codewhisperer/service/transformByQ/transformFileHandler.ts @@ -0,0 +1,438 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as path from 'path' +import * as os from 'os' +import * as YAML from 'js-yaml' +import xml2js = require('xml2js') +import * as CodeWhispererConstants from '../../models/constants' +import { existsSync, readFileSync, writeFileSync } from 'fs' // eslint-disable-line no-restricted-imports +import { BuildSystem, DB, FolderInfo, transformByQState } from '../../models/model' +import fs from '../../../shared/fs/fs' +import globals from '../../../shared/extensionGlobals' +import { ChatSessionManager } from '../../../amazonqGumby/chat/storages/chatSession' +import { AbsolutePathDetectedError } from '../../../amazonqGumby/errors' +import { getLogger } from '../../../shared/logger/logger' +import AdmZip from 'adm-zip' +import { IManifestFile } from './humanInTheLoopManager' +import { ExportResultArchiveStructure } from '../../../shared/utilities/download' +import { isFileNotFoundError } from '../../../shared/errors' + +export async function getDependenciesFolderInfo(): Promise { + const dependencyFolderName = `${CodeWhispererConstants.dependencyFolderName}${globals.clock.Date.now()}` + const dependencyFolderPath = path.join(os.tmpdir(), dependencyFolderName) + await fs.mkdir(dependencyFolderPath) + return { + name: dependencyFolderName, + path: dependencyFolderPath, + } +} + +export async function writeAndShowBuildLogs(isLocalInstall: boolean = false) { + const logFilePath = path.join(os.tmpdir(), 'build-logs.txt') + writeFileSync(logFilePath, transformByQState.getBuildLog()) + const doc = await vscode.workspace.openTextDocument(logFilePath) + const logs = transformByQState.getBuildLog().toLowerCase() + if (logs.includes('intermediate build result') || logs.includes('maven jar failed')) { + // only show the log if the build failed; show it in second column for intermediate builds only + const options = isLocalInstall ? undefined : { viewColumn: vscode.ViewColumn.Two } + await vscode.window.showTextDocument(doc, options) + } +} + +export async function createLocalBuildUploadZip(baseDir: string, exitCode: number | null, stdout: string) { + const manifestFilePath = path.join(baseDir, 'manifest.json') + const buildResultsManifest = { + capability: 'CLIENT_SIDE_BUILD', + exitCode: exitCode, + commandLogFileName: 'build-output.log', + } + const formattedManifest = JSON.stringify(buildResultsManifest) + await fs.writeFile(manifestFilePath, formattedManifest) + + const buildLogsFilePath = path.join(baseDir, 'build-output.log') + await fs.writeFile(buildLogsFilePath, stdout) + + const zip = new AdmZip() + zip.addLocalFile(buildLogsFilePath) + zip.addLocalFile(manifestFilePath) + + const zipPath = `${baseDir}.zip` + zip.writeZip(zipPath) + getLogger().info(`CodeTransformation: created local build upload zip at ${zipPath}`) + return zipPath +} + +// extract the 'sources' directory of the upload ZIP so that we can apply the diff.patch to a copy of the source code +export async function extractOriginalProjectSources(destinationPath: string) { + const zip = new AdmZip(transformByQState.getPayloadFilePath()) + const zipEntries = zip.getEntries() + for (const zipEntry of zipEntries) { + if (zipEntry.entryName.startsWith('sources')) { + zip.extractEntryTo(zipEntry, destinationPath, true, true) + } + } +} + +export async function checkBuildSystem(projectPath: string) { + const mavenBuildFilePath = path.join(projectPath, 'pom.xml') + if (existsSync(mavenBuildFilePath)) { + return BuildSystem.Maven + } + return BuildSystem.Unknown +} + +export async function parseBuildFile() { + try { + const absolutePaths = ['users/', 'system/', 'volumes/', 'c:\\', 'd:\\'] + const alias = path.basename(os.homedir()) + absolutePaths.push(alias) + const buildFilePath = path.join(transformByQState.getProjectPath(), 'pom.xml') + if (existsSync(buildFilePath)) { + const buildFileContents = readFileSync(buildFilePath).toString().toLowerCase() + const detectedPaths = [] + for (const absolutePath of absolutePaths) { + if (buildFileContents.includes(absolutePath)) { + detectedPaths.push(absolutePath) + } + } + if (detectedPaths.length > 0) { + const warningMessage = CodeWhispererConstants.absolutePathDetectedMessage( + detectedPaths.length, + path.basename(buildFilePath), + detectedPaths.join(', ') + ) + transformByQState.getChatControllers()?.errorThrown.fire({ + error: new AbsolutePathDetectedError(warningMessage), + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + getLogger().info('CodeTransformation: absolute path potentially in build file') + return warningMessage + } + } + } catch (err: any) { + // swallow error + getLogger().error(`CodeTransformation: error scanning for absolute paths, tranformation continuing: ${err}`) + } + return undefined +} + +// return an error message, or undefined if YAML file is valid +export function validateCustomVersionsFile(fileContents: string) { + const requiredKeys = ['dependencyManagement', 'identifier', 'targetVersion', 'originType'] + for (const key of requiredKeys) { + if (!fileContents.includes(key)) { + getLogger().info(`CodeTransformation: .YAML file is missing required key: ${key}`) + return `Missing required key: \`${key}\`` + } + } + try { + const yaml = YAML.load(fileContents) as any + const dependencies = yaml?.dependencyManagement?.dependencies || [] + const plugins = yaml?.dependencyManagement?.plugins || [] + const dependenciesAndPlugins = dependencies.concat(plugins) + + if (dependenciesAndPlugins.length === 0) { + getLogger().info('CodeTransformation: .YAML file must contain at least dependencies or plugins') + return `YAML file must contain at least \`dependencies\` or \`plugins\` under \`dependencyManagement\`` + } + for (const item of dependencies) { + const errorMessage = validateItem(item, false) + if (errorMessage) { + return errorMessage + } + } + for (const item of plugins) { + const errorMessage = validateItem(item, true) + if (errorMessage) { + return errorMessage + } + } + return undefined + } catch (err: any) { + getLogger().info(`CodeTransformation: Invalid YAML format: ${err.message}`) + return `Invalid YAML format: ${err.message}` + } +} + +// return an error message, or undefined if item is valid +function validateItem(item: any, isPlugin: boolean) { + const validOriginTypes = ['FIRST_PARTY', 'THIRD_PARTY'] + if (!isPlugin && !/^[^\s:]+:[^\s:]+$/.test(item.identifier)) { + getLogger().info(`CodeTransformation: Invalid identifier format: ${item.identifier}`) + return `Invalid dependency identifier format: \`${item.identifier}\`. Must be in format \`groupId:artifactId\` without spaces` + } + if (isPlugin && !item.identifier?.trim()) { + getLogger().info('CodeTransformation: Missing identifier in plugin') + return 'Missing `identifier` in plugin' + } + if (!validOriginTypes.includes(item.originType)) { + getLogger().info(`CodeTransformation: Invalid originType: ${item.originType}`) + return `Invalid originType: \`${item.originType}\`. Must be either \`FIRST_PARTY\` or \`THIRD_PARTY\`` + } + if (!item.targetVersion?.trim()) { + getLogger().info(`CodeTransformation: Missing targetVersion in: ${item.identifier}`) + return `Missing \`targetVersion\` in: \`${item.identifier}\`` + } + return undefined +} + +export async function validateSQLMetadataFile(fileContents: string, message: any) { + try { + const sctData = await xml2js.parseStringPromise(fileContents) + const dbEntities = sctData['tree']['instances'][0]['ProjectModel'][0]['entities'][0] + const sourceDB = dbEntities['sources'][0]['DbServer'][0]['$']['vendor'].trim().toUpperCase() + const targetDB = dbEntities['targets'][0]['DbServer'][0]['$']['vendor'].trim().toUpperCase() + const sourceServerName = dbEntities['sources'][0]['DbServer'][0]['$']['name'].trim() + transformByQState.setSourceServerName(sourceServerName) + if (sourceDB !== DB.ORACLE) { + transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('unsupported-source-db', message.tabID) + return false + } else if (targetDB !== DB.AURORA_POSTGRESQL && targetDB !== DB.RDS_POSTGRESQL) { + transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('unsupported-target-db', message.tabID) + return false + } + transformByQState.setSourceDB(sourceDB) + transformByQState.setTargetDB(targetDB) + + const serverNodeLocations = + sctData['tree']['instances'][0]['ProjectModel'][0]['relations'][0]['server-node-location'] + const schemaNames = new Set() + // eslint-disable-next-line unicorn/no-array-for-each + serverNodeLocations.forEach((serverNodeLocation: any) => { + const schemaNodes = serverNodeLocation['FullNameNodeInfoList'][0]['nameParts'][0][ + 'FullNameNodeInfo' + ].filter((node: any) => node['$']['typeNode'].toLowerCase() === 'schema') + // eslint-disable-next-line unicorn/no-array-for-each + schemaNodes.forEach((node: any) => { + schemaNames.add(node['$']['nameNode'].toUpperCase()) + }) + }) + transformByQState.setSchemaOptions(schemaNames) // user will choose one of these + getLogger().info( + `CodeTransformation: Parsed .sct file with source DB: ${sourceDB}, target DB: ${targetDB}, source host name: ${sourceServerName}, and schema names: ${Array.from(schemaNames)}` + ) + } catch (err: any) { + getLogger().error('CodeTransformation: Error parsing .sct file. %O', err) + transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('error-parsing-sct-file', message.tabID) + return false + } + return true +} + +export function setMaven() { + // avoid using maven wrapper since we can run into permissions issues + transformByQState.setMavenName('mvn') +} + +export async function openBuildLogFile() { + const logFilePath = transformByQState.getPreBuildLogFilePath() + const doc = await vscode.workspace.openTextDocument(logFilePath) + await vscode.window.showTextDocument(doc) +} + +export async function createPomCopy( + dirname: string, + pomFileVirtualFileReference: vscode.Uri, + fileName: string +): Promise { + const newFilePath = path.join(dirname, fileName) + const pomFileContents = await fs.readFileText(pomFileVirtualFileReference.fsPath) + const directoryExits = await fs.exists(dirname) + if (!directoryExits) { + await fs.mkdir(dirname) + } + await fs.writeFile(newFilePath, pomFileContents) + return vscode.Uri.file(newFilePath) +} + +export async function replacePomVersion(pomFileVirtualFileReference: vscode.Uri, version: string, delimiter: string) { + const pomFileText = await fs.readFileText(pomFileVirtualFileReference.fsPath) + const pomFileTextWithNewVersion = pomFileText.replace(delimiter, version) + writeFileSync(pomFileVirtualFileReference.fsPath, pomFileTextWithNewVersion) +} + +export async function getJsonValuesFromManifestFile( + manifestFileVirtualFileReference: vscode.Uri +): Promise { + const manifestFileContents = await fs.readFileText(manifestFileVirtualFileReference.fsPath) + const jsonValues = JSON.parse(manifestFileContents.toString()) + return { + hilCapability: jsonValues?.hilType, + pomFolderName: jsonValues?.pomFolderName, + sourcePomVersion: jsonValues?.sourcePomVersion || '1.0', + pomArtifactId: jsonValues?.pomArtifactId, + pomGroupId: jsonValues?.pomGroupId, + } +} + +export interface IHighlightPomIssueParams { + pomFileVirtualFileReference: vscode.Uri + currentVersion: string + latestVersion: string + latestMajorVersion: string +} + +export async function highlightPomIssueInProject( + pomFileVirtualFileReference: vscode.Uri, + collection: vscode.DiagnosticCollection, + currentVersion: string +) { + const diagnostics = vscode.languages.getDiagnostics(pomFileVirtualFileReference) + + // Open the editor and set this pom to activeTextEditor + await vscode.window.showTextDocument(pomFileVirtualFileReference, { + preview: true, + }) + + // Find line number for "latestVersion" or set to first line in file + const highlightLineNumberVersion = findLineNumber( + pomFileVirtualFileReference, + `${currentVersion}` + ) + if (highlightLineNumberVersion) { + await setHilAnnotationObjectDetails(highlightLineNumberVersion) + await addDiagnosticOverview(collection, diagnostics, highlightLineNumberVersion) + } +} + +async function addDiagnosticOverview( + collection: vscode.DiagnosticCollection, + diagnostics: vscode.Diagnostic[], + lineNumber: number = 0 +) { + // Get the diff editor + const documentUri = vscode.window.activeTextEditor?.document?.uri + if (documentUri) { + collection.clear() + diagnostics = [ + { + code: 'Amazon Q', + message: 'Amazon Q experienced an issue upgrading this dependency version', + range: new vscode.Range(new vscode.Position(lineNumber, 0), new vscode.Position(lineNumber, 50)), + severity: vscode.DiagnosticSeverity.Error, + relatedInformation: [ + new vscode.DiagnosticRelatedInformation( + new vscode.Location( + documentUri, + new vscode.Range(new vscode.Position(1, 0), new vscode.Position(1, 50)) + ), + 'This dependency is not compatible with a Java 17 upgrade. Use Amazon Q chat to upgrade the version of this dependency to a Java 17 compatible version.' + ), + ], + tags: [1, 2], + }, + ] + collection.set(documentUri, diagnostics) + } +} + +export async function getCodeIssueSnippetFromPom(pomFileVirtualFileReference: vscode.Uri) { + // TODO[gumby]: not great that we read this file multiple times + const pomFileContents = await fs.readFileText(pomFileVirtualFileReference.fsPath) + + const dependencyRegEx = /]*>(.*?)<\/dependencies>/ms + const match = dependencyRegEx.exec(pomFileContents) + const snippet = match ? match[0] : '' + + // Remove white space and convert to 2 space indented code snippet + return snippet.trim() +} + +async function setHilAnnotationObjectDetails(lineNumber: number = 0) { + // Get active diff editor + const diffEditor = vscode.window.activeTextEditor + const backgroundColor = new vscode.ThemeColor('editor.wordHighlightBackground') + const highlightDecorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor, + isWholeLine: true, + overviewRulerColor: new vscode.ThemeColor('warning'), + }) + + // Set the decorations + diffEditor?.setDecorations(highlightDecorationType, [ + { + range: new vscode.Range(lineNumber, 0, lineNumber, 50), + }, + ]) +} + +function findLineNumber(uri: vscode.Uri, searchString: string): number | undefined { + const textDocument = vscode.workspace.textDocuments.find((doc) => doc.uri.toString() === uri.toString()) + if (!textDocument) { + return undefined + } + + const text = textDocument.getText() + let lineNumber = 0 + const lines = text.split('\n') + + for (const line of lines) { + if (line.includes(searchString)) { + return lineNumber + } + lineNumber++ + } + + return undefined +} + +export interface IParsedXmlDependencyOutput { + latestVersion: string + majorVersions: string[] + minorVersions: string[] + status?: string +} +export async function parseVersionsListFromPomFile(xmlString: string): Promise { + const parser = new xml2js.Parser() + const parsedOutput = await parser.parseStringPromise(xmlString) + + const report = parsedOutput.DependencyUpdatesReport.dependencies[0].dependency[0] + + const latestVersion = report?.lastVersion?.[0] + const majorVersions = report?.majors?.[0]?.major || [] + const minorVersions = report?.minors?.[0]?.minor || [] + const status = report.status?.[0] + + return { latestVersion, majorVersions, minorVersions, status } +} + +/** + * Saves a copy of the diff patch, summary, and build logs (if any) locally + * + * @param pathToArchiveDir path to the archive directory where the artifacts are unzipped + * @param pathToDestinationDir destination directory (will create directories if path doesn't exist already) + */ +export async function copyArtifacts(pathToArchiveDir: string, pathToDestinationDir: string) { + // create destination path if doesn't exist already + // mkdir() will not raise an error if path exists + await fs.mkdir(pathToDestinationDir) + + const diffPath = path.join(pathToArchiveDir, ExportResultArchiveStructure.PathToDiffPatch) + const summaryPath = path.join(pathToArchiveDir, ExportResultArchiveStructure.PathToSummary) + + try { + await fs.copy(diffPath, path.join(pathToDestinationDir, 'diff.patch')) + // make summary directory if needed + await fs.mkdir(path.join(pathToDestinationDir, 'summary')) + await fs.copy(summaryPath, path.join(pathToDestinationDir, 'summary', 'summary.md')) + } catch (error) { + getLogger().error('Code Transformation: Error saving local copy of artifacts: %s', (error as Error).message) + } + + const buildLogsPath = path.join(path.dirname(summaryPath), 'buildCommandOutput.log') + try { + await fs.copy(buildLogsPath, path.join(pathToDestinationDir, 'summary', 'buildCommandOutput.log')) + } catch (error) { + // build logs won't exist for SQL conversions (not an error) + if (!isFileNotFoundError(error)) { + getLogger().error( + 'Code Transformation: Error saving local copy of build logs: %s', + (error as Error).message + ) + } + } +} diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformMavenHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformMavenHandler.ts new file mode 100644 index 00000000000..22fcd1e6fa1 --- /dev/null +++ b/packages/core/src/codewhisperer/service/transformByQ/transformMavenHandler.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 { FolderInfo, transformByQState } from '../../models/model' +import { getLogger } from '../../../shared/logger/logger' +import * as CodeWhispererConstants from '../../models/constants' +// Consider using ChildProcess once we finalize all spawnSync calls +import { spawnSync } from 'child_process' // eslint-disable-line no-restricted-imports +import { setMaven } from './transformFileHandler' +import { throwIfCancelled } from './transformApiHandler' +import { sleep } from '../../../shared/utilities/timeoutUtils' +import path from 'path' +import globals from '../../../shared/extensionGlobals' + +function collectDependenciesAndMetadata(dependenciesFolderPath: string, workingDirPath: string) { + getLogger().info('CodeTransformation: running mvn clean test-compile with maven JAR') + + const baseCommand = transformByQState.getMavenName() // always 'mvn' + const jarPath = globals.context.asAbsolutePath(path.join('resources', 'amazonQCT', 'QCT-Maven-1-0-156-0.jar')) + + getLogger().info('CodeTransformation: running Maven extension with JAR') + + const args = [ + `-Dmaven.ext.class.path="${jarPath}"`, + `-Dcom.amazon.aws.developer.transform.jobDirectory="${dependenciesFolderPath}"`, + 'clean', + 'test-compile', + ] + + let environment = process.env + if (transformByQState.getSourceJavaHome() !== undefined) { + environment = { ...process.env, JAVA_HOME: transformByQState.getSourceJavaHome() } + } + + const spawnResult = spawnSync(baseCommand, args, { + cwd: workingDirPath, + shell: true, + encoding: 'utf-8', + env: environment, + }) + + getLogger().info( + `CodeTransformation: Ran mvn clean test-compile with maven JAR; status code = ${spawnResult.status}}` + ) + + if (spawnResult.status !== 0) { + let errorLog = '' + errorLog += spawnResult.error ? JSON.stringify(spawnResult.error) : '' + errorLog += `${spawnResult.stderr}\n${spawnResult.stdout}` + errorLog = errorLog.toLowerCase().replace('elasticgumby', 'QCT') + transformByQState.appendToBuildLog(`mvn clean test-compile with maven JAR failed:\n${errorLog}`) + getLogger().error(`CodeTransformation: Error in running mvn clean test-compile with maven JAR = ${errorLog}`) + throw new Error('mvn clean test-compile with maven JAR failed') + } + getLogger().info( + `CodeTransformation: mvn clean test-compile with maven JAR succeeded; dependencies copied to ${dependenciesFolderPath}` + ) +} + +export async function prepareProjectDependencies(dependenciesFolderPath: string, workingDirPath: string) { + setMaven() + // pause to give chat time to update + await sleep(100) + try { + collectDependenciesAndMetadata(dependenciesFolderPath, workingDirPath) + } catch (err) { + getLogger().error('CodeTransformation: collectDependenciesAndMetadata failed') + void vscode.window.showErrorMessage(CodeWhispererConstants.cleanTestCompileErrorNotification) + throw err + } + throwIfCancelled() + void vscode.window.showInformationMessage(CodeWhispererConstants.buildSucceededNotification) +} + +export function runMavenDependencyUpdateCommands(dependenciesFolder: FolderInfo) { + const baseCommand = transformByQState.getMavenName() + + const args = [ + 'versions:dependency-updates-aggregate-report', + `-DoutputDirectory=${dependenciesFolder.path}`, + '-DonlyProjectDependencies=true', + '-DdependencyUpdatesReportFormats=xml', + ] + + let environment = process.env + + if (transformByQState.getSourceJavaHome()) { + environment = { ...process.env, JAVA_HOME: transformByQState.getSourceJavaHome() } + } + + const spawnResult = spawnSync(baseCommand, args, { + // default behavior is looks for pom.xml in this root + cwd: dependenciesFolder.path, + shell: true, + encoding: 'utf-8', + env: environment, + maxBuffer: CodeWhispererConstants.maxBufferSize, + }) + + if (spawnResult.status !== 0) { + throw new Error(spawnResult.stderr) + } else { + return spawnResult.stdout + } +} diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformProjectValidationHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformProjectValidationHandler.ts new file mode 100644 index 00000000000..ebc2caeda4c --- /dev/null +++ b/packages/core/src/codewhisperer/service/transformByQ/transformProjectValidationHandler.ts @@ -0,0 +1,74 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { BuildSystem, TransformationCandidateProject } from '../../models/model' +import * as vscode from 'vscode' +import { + NoJavaProjectsFoundError, + NoMavenJavaProjectsFoundError, + NoOpenProjectsError, +} from '../../../amazonqGumby/errors' +import { checkBuildSystem } from './transformFileHandler' + +export async function getOpenProjects(): Promise { + const folders = vscode.workspace.workspaceFolders + + if (folders === undefined || folders.length === 0) { + throw new NoOpenProjectsError() + } + + const openProjects: TransformationCandidateProject[] = [] + for (const folder of folders) { + openProjects.push({ + name: folder.name, + path: folder.uri.fsPath, + }) + } + + return openProjects +} + +export async function getJavaProjects(projects: TransformationCandidateProject[]) { + const javaProjects = [] + for (const project of projects) { + const projectPath = project.path + const javaFiles = await vscode.workspace.findFiles( + new vscode.RelativePattern(projectPath!, '**/*.java'), + '**/node_modules/**', + 1 + ) + if (javaFiles.length > 0) { + javaProjects.push(project) + } + } + if (javaProjects.length === 0) { + throw new NoJavaProjectsFoundError() + } + return javaProjects +} + +async function getMavenJavaProjects(javaProjects: TransformationCandidateProject[]) { + const mavenJavaProjects = [] + + for (const project of javaProjects) { + const projectPath = project.path + const buildSystem = await checkBuildSystem(projectPath!) + if (buildSystem === BuildSystem.Maven) { + mavenJavaProjects.push(project) + } + } + + if (mavenJavaProjects.length === 0) { + throw new NoMavenJavaProjectsFoundError() + } + + return mavenJavaProjects +} + +// This function filters all open projects by first searching for a .java file and then searching for a pom.xml file in all projects. +export async function validateOpenProjects(projects: TransformationCandidateProject[]) { + const javaProjects = await getJavaProjects(projects) + const mavenJavaProjects = await getMavenJavaProjects(javaProjects) + return mavenJavaProjects +} diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformationHistoryHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformationHistoryHandler.ts new file mode 100644 index 00000000000..6aba818b4fc --- /dev/null +++ b/packages/core/src/codewhisperer/service/transformByQ/transformationHistoryHandler.ts @@ -0,0 +1,416 @@ +/*! + * 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 os from 'os' +import * as CodeWhispererConstants from '../../models/constants' +import { JDKVersion, TransformationType, transformByQState } from '../../models/model' +import { getLogger } from '../../../shared/logger/logger' +import { codeWhispererClient } from '../../../codewhisperer/client/codewhisperer' +import { downloadAndExtractResultArchive, pollTransformationJob } from './transformApiHandler' +import { ChatSessionManager } from '../../../amazonqGumby/chat/storages/chatSession' +import { AuthUtil } from '../../util/authUtil' +import { setMaven } from './transformFileHandler' +import { convertToTimeString, isWithin30Days } from '../../../shared/datetime' +import { copyArtifacts } from './transformFileHandler' + +export interface HistoryObject { + startTime: string + projectName: string + status: string + duration: string + diffPath: string + summaryPath: string + jobId: string + transformationType: string + sourceJDKVersion: string + targetJDKVersion: string + customDependencyVersionFilePath: string + customBuildCommand: string +} + +export interface JobMetadata { + jobId: string + projectName: string + transformationType: TransformationType + sourceJDKVersion: JDKVersion + targetJDKVersion: JDKVersion + customDependencyVersionFilePath: string + customBuildCommand: string + targetJavaHome: string + projectPath: string + startTime: string +} + +/** + * Reads 'transformation_history.tsv' (history) file + * + * @returns history array of 10 most recent jobs from within past 30 days + */ +export async function readHistoryFile(): Promise { + const history: HistoryObject[] = [] + const jobHistoryFilePath = path.join(os.homedir(), '.aws', 'transform', 'transformation_history.tsv') + + if (!(await fs.existsFile(jobHistoryFilePath))) { + return history + } + + const historyFile = await fs.readFileText(jobHistoryFilePath) + const jobs = historyFile.split('\n') + jobs.shift() // removes headers + + // Process from end, stop at 10 valid entries + for (let i = jobs.length - 1; i >= 0 && history.length < 10; i--) { + const job = jobs[i] + if (job && isWithin30Days(job.split('\t')[0])) { + const jobInfo = job.split('\t') + history.push({ + startTime: jobInfo[0], + projectName: jobInfo[1], + status: jobInfo[2], + duration: jobInfo[3], + diffPath: jobInfo[4], + summaryPath: jobInfo[5], + jobId: jobInfo[6], + transformationType: jobInfo[7], + sourceJDKVersion: jobInfo[8], + targetJDKVersion: jobInfo[9], + customDependencyVersionFilePath: jobInfo[10], + customBuildCommand: jobInfo[11], + }) + } + } + return history +} + +/** + * Creates temporary metadata JSON file with transformation config info and saves a copy of upload zip + * + * These files are used when a job is resumed after interruption + * + * @param payloadFilePath path to upload zip + * @param metadata + * @returns + */ +export async function createMetadataFile(payloadFilePath: string, metadata: JobMetadata): Promise { + const jobHistoryPath = path.join(os.homedir(), '.aws', 'transform', metadata.projectName, metadata.jobId) + + // create job history folders + await fs.mkdir(jobHistoryPath) + + // save a copy of the upload zip + try { + await fs.copy(payloadFilePath, path.join(jobHistoryPath, 'zipped-code.zip')) + } catch (error) { + getLogger().error('Code Transformation: error saving copy of upload zip: %s', (error as Error).message) + } + + // create metadata file with transformation config info + try { + await fs.writeFile(path.join(jobHistoryPath, 'metadata.json'), JSON.stringify(metadata)) + } catch (error) { + getLogger().error('Code Transformation: error creating metadata file: %s', (error as Error).message) + } + + return jobHistoryPath +} + +/** + * Writes job details to history file + * + * @param startTime job start timestamp (ex. "01/01/23, 12:00 AM") + * @param projectName + * @param status + * @param duration job duration in hr / min / sec format (ex. "1 hr 15 min") + * @param jobId + * @param jobHistoryPath path to where job's history details are stored (ex. "~/.aws/transform/proj_name/job_id") + */ +export async function writeToHistoryFile( + startTime: string, + projectName: string, + status: string, + duration: string, + jobId: string, + jobHistoryPath: string, + transformationType: string, + sourceJDKVersion: string, + targetJDKVersion: string, + customDependencyVersionFilePath: string, + customBuildCommand: string +) { + const historyLogFilePath = path.join(os.homedir(), '.aws', 'transform', 'transformation_history.tsv') + // create transform folder if necessary + if (!(await fs.existsFile(historyLogFilePath))) { + await fs.mkdir(path.dirname(historyLogFilePath)) + // create headers of new transformation history file + await fs.writeFile( + historyLogFilePath, + 'date\tproject_name\tstatus\tduration\tdiff_patch\tsummary\tjob_id\ttransformation_type\tsource_jdk_version\ttarget_jdk_version\tcustom_dependency_version_file_path\tcustom_build_command\n' + ) + } + const artifactsExist = status === 'COMPLETED' || status === 'PARTIALLY_COMPLETED' + const fields = [ + startTime, + projectName, + status, + duration, + artifactsExist ? path.join(jobHistoryPath, 'diff.patch') : '', + artifactsExist ? path.join(jobHistoryPath, 'summary', 'summary.md') : '', + jobId, + transformationType, + sourceJDKVersion, + targetJDKVersion, + customDependencyVersionFilePath, + customBuildCommand, + ] + + const jobDetails = fields.join('\t') + '\n' + await fs.appendFile(historyLogFilePath, jobDetails) + + // update Transformation Hub table + await vscode.commands.executeCommand('aws.amazonq.transformationHub.updateContent', 'job history', undefined, true) +} + +/** + * Delete temporary files at the end of a transformation + * + * @param jobHistoryPath path to history directory for this job + * @param jobStatus final transformation status + * @param payloadFilePath path to original upload zip; providing this param will also delete any temp build logs + */ +export async function cleanupTempJobFiles(jobHistoryPath: string, jobStatus: string, payloadFilePath?: string) { + if (payloadFilePath) { + // delete original upload ZIP + await fs.delete(payloadFilePath, { force: true }) + // delete temporary build logs file + const logFilePath = path.join(os.tmpdir(), 'build-logs.txt') + await fs.delete(logFilePath, { force: true }) + } + + // delete metadata file and upload zip copy if no longer need them (i.e. will not be resuming) + if (jobStatus !== 'FAILED') { + await fs.delete(path.join(jobHistoryPath, 'metadata.json'), { force: true }) + await fs.delete(path.join(jobHistoryPath, 'zipped-code.zip'), { force: true }) + } +} + +/* Job refresh-related functions */ + +export async function refreshJob(jobId: string, currentStatus: string, projectName: string) { + // fetch status from server + let status = '' + let duration = '' + if (currentStatus === 'COMPLETED' || currentStatus === 'PARTIALLY_COMPLETED') { + // job is already completed, no need to fetch status + status = currentStatus + } else { + try { + const response = await codeWhispererClient.codeModernizerGetCodeTransformation({ + transformationJobId: jobId, + profileArn: undefined, + }) + status = response.transformationJob.status ?? currentStatus + if (response.transformationJob.endExecutionTime && response.transformationJob.creationTime) { + duration = convertToTimeString( + response.transformationJob.endExecutionTime.getTime() - + response.transformationJob.creationTime.getTime() + ) + } + + getLogger().debug( + 'Code Transformation: Job refresh - Fetched status for job id: %s\n{Status: %s; Duration: %s}', + jobId, + status, + duration + ) + } catch (error) { + const errorMessage = (error as Error).message + getLogger().error('Code Transformation: Error fetching status (job id: %s): %s', jobId, errorMessage) + if (errorMessage.includes('not authorized to make this call')) { + // job not available on backend + status = 'FAILED' // won't allow retries for this job + } else { + // some other error (e.g. network error) + return + } + } + } + + // retrieve artifacts and updated duration if available + let jobHistoryPath: string = '' + if (status === 'COMPLETED' || status === 'PARTIALLY_COMPLETED') { + // artifacts should be available to download + jobHistoryPath = await retrieveArtifacts(jobId, projectName) + + await cleanupTempJobFiles(path.join(os.homedir(), '.aws', 'transform', projectName, jobId), status) + } else if (CodeWhispererConstants.validStatesForBuildSucceeded.includes(status)) { + // still in progress on server side + if (transformByQState.isRunning()) { + getLogger().warn( + 'Code Transformation: There is a job currently running (id: %s). Cannot resume another job (id: %s)', + transformByQState.getJobId(), + jobId + ) + return + } + transformByQState.setRefreshInProgress(true) + const messenger = transformByQState.getChatMessenger() + const tabID = ChatSessionManager.Instance.getSession().tabID + messenger?.sendJobRefreshInProgressMessage(tabID!, jobId) + await vscode.commands.executeCommand('aws.amazonq.transformationHub.updateContent', 'job history') // refreshing the table disables all jobs' refresh buttons while this one is resuming + + // resume job and bring to completion + try { + status = await resumeJob(jobId, projectName, status) + } catch (error) { + getLogger().error('Code Transformation: Error resuming job (id: %s): %s', jobId, (error as Error).message) + transformByQState.setJobDefaults() + messenger?.sendJobFinishedMessage(tabID!, CodeWhispererConstants.refreshErrorChatMessage) + void vscode.window.showErrorMessage(CodeWhispererConstants.refreshErrorNotification(jobId)) + await vscode.commands.executeCommand('aws.amazonq.transformationHub.updateContent', 'job history') + return + } + + // download artifacts if available + if ( + CodeWhispererConstants.validStatesForCheckingDownloadUrl.includes(status) && + !CodeWhispererConstants.failureStates.includes(status) + ) { + duration = convertToTimeString(Date.now() - new Date(transformByQState.getStartTime()).getTime()) + jobHistoryPath = await retrieveArtifacts(jobId, projectName) + } + + // reset state + transformByQState.setJobDefaults() + messenger?.sendJobFinishedMessage(tabID!, CodeWhispererConstants.refreshCompletedChatMessage) + } else { + // FAILED or STOPPED job + getLogger().info('Code Transformation: No artifacts available to download (job status = %s)', status) + if (status === 'FAILED') { + // if job failed on backend, mark it to disable the refresh button + status = 'FAILED_BE' // this will be truncated to just 'FAILED' in the table + } + await cleanupTempJobFiles(path.join(os.homedir(), '.aws', 'transform', projectName, jobId), status) + } + + if (status === currentStatus && !jobHistoryPath) { + // no changes, no need to update file/table + void vscode.window.showInformationMessage(CodeWhispererConstants.refreshNoUpdatesNotification(jobId)) + return + } + + void vscode.window.showInformationMessage(CodeWhispererConstants.refreshCompletedNotification(jobId)) + // update local file and history table + + await updateHistoryFile(status, duration, jobHistoryPath, jobId) +} + +async function retrieveArtifacts(jobId: string, projectName: string) { + const resultsPath = path.join(os.homedir(), '.aws', 'transform', projectName, 'results') // temporary directory for extraction + let jobHistoryPath = path.join(os.homedir(), '.aws', 'transform', projectName, jobId) + + if (await fs.existsFile(path.join(jobHistoryPath, 'diff.patch'))) { + getLogger().info('Code Transformation: Diff patch already exists for job id: %s', jobId) + jobHistoryPath = '' + } else { + try { + await downloadAndExtractResultArchive(jobId, resultsPath) + await copyArtifacts(resultsPath, jobHistoryPath) + } catch (error) { + jobHistoryPath = '' + } finally { + // delete temporary extraction directory + await fs.delete(resultsPath, { recursive: true, force: true }) + } + } + return jobHistoryPath +} + +async function updateHistoryFile(status: string, duration: string, jobHistoryPath: string, jobId: string) { + const history: string[][] = [] + const historyLogFilePath = path.join(os.homedir(), '.aws', 'transform', 'transformation_history.tsv') + if (await fs.existsFile(historyLogFilePath)) { + const historyFile = await fs.readFileText(historyLogFilePath) + const jobs = historyFile.split('\n') + jobs.shift() // removes headers + if (jobs.length > 0) { + for (const job of jobs) { + if (job) { + const jobInfo = job.split('\t') + // 0: startTime, 1: projectName, 2: status, 3: duration, 4: diffPath, 5: summaryPath, 6: jobId + // 7: transformationType, 8: sourceJDKVersion, 9: targetJDKVersion, 10: customDependencyVersionFilePath, 11: customBuildCommand + if (jobInfo[6] === jobId) { + // update any values if applicable + jobInfo[2] = status + if (duration) { + jobInfo[3] = duration + } + if (jobHistoryPath) { + jobInfo[4] = path.join(jobHistoryPath, 'diff.patch') + jobInfo[5] = path.join(jobHistoryPath, 'summary', 'summary.md') + } + } + history.push(jobInfo) + } + } + } + } + + if (history.length === 0) { + return + } + + // rewrite file + await fs.writeFile( + historyLogFilePath, + 'date\tproject_name\tstatus\tduration\tdiff_patch\tsummary\tjob_id\ttransformation_type\tsource_jdk_version\ttarget_jdk_version\tcustom_dependency_version_file_path\tcustom_build_command\n' + ) + const tsvContent = history.map((row) => row.join('\t')).join('\n') + '\n' + await fs.appendFile(historyLogFilePath, tsvContent) + + // update table content + await vscode.commands.executeCommand('aws.amazonq.transformationHub.updateContent', 'job history', undefined, true) +} + +async function resumeJob(jobId: string, projectName: string, status: string) { + // set state to prepare to resume job + await setupTransformationState(jobId, projectName, status) + // resume polling the job + return await pollAndCompleteTransformation(jobId) +} + +async function setupTransformationState(jobId: string, projectName: string, status: string) { + transformByQState.setJobId(jobId) + transformByQState.setPolledJobStatus(status) + transformByQState.setJobHistoryPath(path.join(os.homedir(), '.aws', 'transform', projectName, jobId)) + + const metadata: JobMetadata = JSON.parse( + await fs.readFileText(path.join(transformByQState.getJobHistoryPath(), 'metadata.json')) + ) + transformByQState.setTransformationType(metadata.transformationType) + transformByQState.setSourceJDKVersion(metadata.sourceJDKVersion) + transformByQState.setTargetJDKVersion(metadata.targetJDKVersion) + transformByQState.setCustomDependencyVersionFilePath(metadata.customDependencyVersionFilePath) + transformByQState.setPayloadFilePath( + path.join(os.homedir(), '.aws', 'transform', projectName, jobId, 'zipped-code.zip') + ) + setMaven() + transformByQState.setCustomBuildCommand(metadata.customBuildCommand) + transformByQState.setTargetJavaHome(metadata.targetJavaHome) + transformByQState.setProjectPath(metadata.projectPath) + transformByQState.setStartTime(metadata.startTime) +} + +async function pollAndCompleteTransformation(jobId: string) { + const status = await pollTransformationJob( + jobId, + CodeWhispererConstants.validStatesForCheckingDownloadUrl, + AuthUtil.instance.regionProfileManager.activeRegionProfile + ) + await cleanupTempJobFiles(transformByQState.getJobHistoryPath(), status, transformByQState.getPayloadFilePath()) + return status +} diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformationHubViewProvider.ts b/packages/core/src/codewhisperer/service/transformByQ/transformationHubViewProvider.ts new file mode 100644 index 00000000000..97e69570c76 --- /dev/null +++ b/packages/core/src/codewhisperer/service/transformByQ/transformationHubViewProvider.ts @@ -0,0 +1,675 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import globals from '../../../shared/extensionGlobals' +import * as CodeWhispererConstants from '../../models/constants' +import { + StepProgress, + TransformationType, + jobPlanProgress, + sessionJobHistory, + transformByQState, +} from '../../models/model' +import { getLogger } from '../../../shared/logger/logger' +import { getTransformationSteps } from './transformApiHandler' +import { + TransformationSteps, + ProgressUpdates, + TransformationStatus, +} from '../../../codewhisperer/client/codewhispereruserclient' +import { startInterval } from '../../commands/startTransformByQ' +import { CodeTransformTelemetryState } from '../../../amazonqGumby/telemetry/codeTransformTelemetryState' +import { convertToTimeString } from '../../../shared/datetime' +import { AuthUtil } from '../../util/authUtil' +import { refreshJob, readHistoryFile, HistoryObject } from './transformationHistoryHandler' + +export class TransformationHubViewProvider implements vscode.WebviewViewProvider { + public static readonly viewType = 'aws.amazonq.transformationHub' + private _view?: vscode.WebviewView + private lastClickedButton: string = '' + private _extensionUri: vscode.Uri = globals.context.extensionUri + private transformationHistory: HistoryObject[] = [] + constructor() {} + static #instance: TransformationHubViewProvider + + public async updateContent( + button: 'job history' | 'plan progress', + startTime: number = CodeTransformTelemetryState.instance.getStartTime(), + historyFileUpdated?: boolean + ) { + this.lastClickedButton = button + if (historyFileUpdated) { + this.transformationHistory = await readHistoryFile() + } + if (this._view) { + if (this.lastClickedButton === 'job history') { + clearInterval(transformByQState.getIntervalId()) + transformByQState.setIntervalId(undefined) + this._view!.webview.html = this.showJobHistory() + } else { + if (transformByQState.getIntervalId() === undefined && transformByQState.isRunning()) { + startInterval() + } + await this.showPlanProgress(startTime) + .then((jobPlanProgress) => { + this._view!.webview.html = jobPlanProgress + }) + .catch((e) => { + getLogger().error('showPlanProgress failed: %s', (e as Error).message) + }) + } + } + } + + public static get instance() { + return (this.#instance ??= new this()) + } + + public async resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + token: vscode.CancellationToken + ) { + this._view = webviewView + + this._view.webview.onDidReceiveMessage((message) => { + switch (message.command) { + case 'refreshJob': + void refreshJob(message.jobId, message.currentStatus, message.projectName) + break + case 'openSummaryPreview': + void vscode.commands.executeCommand('markdown.showPreview', vscode.Uri.file(message.filePath)) + break + case 'openDiffFile': + void vscode.commands.executeCommand('vscode.open', vscode.Uri.file(message.filePath)) + break + } + }) + + this._view.webview.options = { + enableScripts: true, + localResourceRoots: [this._extensionUri], + } + + this.transformationHistory = await readHistoryFile() + if (this.lastClickedButton === 'job history') { + this._view!.webview.html = this.showJobHistory() + } else { + this.showPlanProgress(globals.clock.Date.now()) + .then((jobPlanProgress) => { + this._view!.webview.html = jobPlanProgress + }) + .catch((e) => { + getLogger().error('showPlanProgress failed: %s', (e as Error).message) + }) + } + } + + private showJobHistory(): string { + const jobsToDisplay: HistoryObject[] = [...this.transformationHistory] + if (transformByQState.isRunning()) { + const current = sessionJobHistory[transformByQState.getJobId()] + jobsToDisplay.unshift({ + startTime: current.startTime, + projectName: current.projectName, + status: current.status, + duration: current.duration, + diffPath: '', + summaryPath: '', + jobId: transformByQState.getJobId(), + transformationType: current.transformationType, + sourceJDKVersion: current.sourceJDKVersion, + targetJDKVersion: current.targetJDKVersion, + customDependencyVersionFilePath: current.customDependencyVersionsFilePath, + customBuildCommand: current.customBuildCommand, + }) + } + return ` + + + Transformation Hub + + + +

Transformation History

+

${CodeWhispererConstants.transformationHistoryTableDescription}

+ ${ + jobsToDisplay.length === 0 + ? `


${CodeWhispererConstants.noJobHistoryMessage}

` + : this.getTableMarkup(jobsToDisplay) + } + + + ` + } + + private getTableMarkup(history: HistoryObject[]) { + return ` + + + + + + + + + + + + + + + + + + + + + ${history + .map( + (job) => ` + + + + + + + + + + + + + + + + ` + ) + .join('')} + +
DateProjectStatusDurationDiff PatchSummary FileJob IdRefresh JobTransformation TypeSource JDK VersionTarget JDK VersionCustom Dependency Version File PathCustom Build Command
${job.startTime}${job.projectName}${job.status === 'FAILED_BE' ? 'FAILED' : job.status}${job.duration}${job.diffPath ? `diff.patch` : ''}${job.summaryPath ? `summary.md` : ''}${job.jobId} + + ${job.transformationType ?? ''}${job.sourceJDKVersion ?? ''}${job.targetJDKVersion ?? ''}${job.customDependencyVersionFilePath ?? ''}${job.customBuildCommand ? `mvn ${job.customBuildCommand}` : ''}
+ ` + } + + private generateTransformationStepMarkup( + name: string, + startTime: Date | undefined, + endTime: Date | undefined, + previousStatus: string, + isFirstStep: boolean, + isLastStep: boolean, + stepProgress: StepProgress, + stepId: number, + isCurrentlyProcessing: boolean + ) { + // include check for the previous step not being CREATED, as this means it has finished, so we can display the next step + if (startTime && endTime && (isFirstStep || previousStatus !== 'CREATED')) { + const stepTime = endTime.toLocaleDateString('en-US', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }) + const stepDuration = convertToTimeString(endTime.getTime() - startTime.getTime()) + const isAllStepsComplete = isLastStep && (stepProgress === StepProgress.Succeeded || StepProgress.Failed) + return ` +

+ ${this.getProgressIconMarkup(stepProgress)} ${name} + [finished on ${stepTime}] ${stepDuration} +

` + } else if (previousStatus !== 'CREATED' && isCurrentlyProcessing) { + return ` +

+ ${this.getProgressIconMarkup(stepProgress)} ${name} +

` + } else if (previousStatus !== 'CREATED') { + return ` +

+ ${this.getProgressIconMarkup(stepProgress)} ${name} +

` + } + } + + private stepStatusToStepProgress(stepStatus: string, transformFailed: boolean) { + if (stepStatus === 'COMPLETED' || stepStatus === 'PARTIALLY_COMPLETED') { + return StepProgress.Succeeded + } else if (stepStatus === 'STOPPED' || stepStatus === 'FAILED' || transformFailed) { + return StepProgress.Failed + } else { + return StepProgress.Pending + } + } + + private selectSubstepIcon(status: string) { + switch (status) { + case 'IN_PROGRESS': + // In case transform is cancelled by user, the state transitions to not started after some time. + // This may contradict the last result from the API, so need to be handled here. + if (transformByQState.isNotStarted()) { + return '

𐔧

' + } + return '

' + case 'COMPLETED': + return '

' + case 'AWAITING_CLIENT_ACTION': + return '

' + case 'FAILED': + default: + return '

𐔧

' + } + } + + private generateSubstepMarkup(progressUpdates: ProgressUpdates, stepId: number, stepTitle: string) { + const substepParagraphs = [] + for (const subStep of progressUpdates) { + substepParagraphs.push(` +
+
${this.selectSubstepIcon(subStep.status)}
+
+

${subStep.name}

+ ${subStep.description ? `

${subStep.description}

` : ''} +
+
+ `) + } + return `
+

${stepTitle}

+ ${substepParagraphs.join('\n')} +
` + } + + /** + * Generates markup for each step in a transformation plan + * @param planSteps The transformation steps of the transformation plan + * @param isTransformFailed boolean of if the transform failed during the transformation step + * @returns Tuple where first element is the markup for the steps and the second element is the markup of all substeps + */ + private getTransformationStepProgressMarkup( + planSteps: TransformationSteps | undefined, + isTransformFailed: boolean + ) { + const steps = [] + const substeps = [] + if (planSteps !== undefined) { + const stepStatuses = [] + for (const step of planSteps) { + stepStatuses.push(step.status) + } + let lastPendingStep = undefined + for (let i = 0; i < planSteps.length; i++) { + const step = planSteps[i] + const stepProgress = this.stepStatusToStepProgress(step.status, isTransformFailed) + lastPendingStep = + lastPendingStep === undefined && stepProgress === StepProgress.Pending ? i : lastPendingStep + const stepMarkup = this.generateTransformationStepMarkup( + step.name, + step.startTime, + step.endTime, + stepStatuses[i - 1], + i === 0, + i === planSteps.length - 1, + stepProgress, + i, + lastPendingStep === i + ) + steps.push(stepMarkup) + if (step.progressUpdates) { + substeps.push(this.generateSubstepMarkup(step.progressUpdates, i, step.name)) + } + } + } + + return [steps.join(''), substeps.join('\n')] + } + + private getLatestGenericStepDetails(currentJobStatus: TransformationStatus) { + switch (currentJobStatus) { + case 'CREATED': + case 'ACCEPTED': + case 'STARTED': + return CodeWhispererConstants.filesUploadedMessage + case 'PREPARING': + case 'PREPARED': + // for SQL conversions, skip to transformingMessage since we don't build the code + return transformByQState.getTransformationType() === TransformationType.SQL_CONVERSION + ? CodeWhispererConstants.transformingMessage + : CodeWhispererConstants.buildingCodeMessage.replace( + 'JAVA_VERSION_HERE', + transformByQState.getSourceJDKVersion() ?? '' + ) + case 'PLANNING': + case 'PLANNED': + // for SQL conversions, skip to transformingMessage since we don't generate a plan + return transformByQState.getTransformationType() === TransformationType.SQL_CONVERSION + ? CodeWhispererConstants.transformingMessage + : CodeWhispererConstants.planningMessage + case 'TRANSFORMING': + case 'TRANSFORMED': + case 'COMPLETED': + case 'PARTIALLY_COMPLETED': + return CodeWhispererConstants.transformingMessage + case 'STOPPING': + case 'STOPPED': + return CodeWhispererConstants.stoppingJobMessage + case 'FAILED': + case 'REJECTED': + return CodeWhispererConstants.failedStepMessage + default: + if (transformByQState.isCancelled()) { + return CodeWhispererConstants.stoppingJobMessage + } else if (transformByQState.isFailed()) { + return CodeWhispererConstants.failedStepMessage + } else if (transformByQState.isRunning()) { + return CodeWhispererConstants.scanningProjectMessage + } else if (transformByQState.isPartiallySucceeded() || transformByQState.isSucceeded()) { + return CodeWhispererConstants.jobCompletedMessage // this should never have to be shown since substeps will block the generic details, added for completeness + } else { + return CodeWhispererConstants.noOngoingJobMessage + } + } + } + + public async showPlanProgress(startTime: number): Promise { + const styleSheet = this._view?.webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'resources', 'css', 'amazonqTransformationHub.css') + ) + const simpleStep = (icon: string, text: string, isActive: boolean) => { + return isActive + ? `

${icon} ${text}

` + : `

${icon} ${text}

` + } + + let planSteps = transformByQState.getPlanSteps() + // no plan for SQL conversions + if ( + transformByQState.getTransformationType() !== TransformationType.SQL_CONVERSION && + jobPlanProgress['generatePlan'] === StepProgress.Succeeded && + transformByQState.isRunning() + ) { + try { + planSteps = await getTransformationSteps( + transformByQState.getJobId(), + AuthUtil.instance.regionProfileManager.activeRegionProfile + ) + transformByQState.setPlanSteps(planSteps) + } catch (e: any) { + // no-op; re-use current plan steps and try again in next polling cycle + getLogger().error( + `CodeTransformation: failed to get plan steps to show updates in transformation hub, continuing transformation; error = %O`, + e + ) + } + } + let progressHtml + // for each step that has succeeded, increment activeStepId by 1 + let activeStepId = [ + jobPlanProgress.uploadCode, + jobPlanProgress.buildCode, + jobPlanProgress.generatePlan, + jobPlanProgress.transformCode, + ] + .map((it) => (it === StepProgress.Succeeded ? 1 : 0) as number) + .reduce((prev, current) => prev + current) + // When we receive plan step details, we want those to be active -> increment activeStepId + activeStepId += planSteps === undefined || planSteps.length === 0 ? 0 : 1 + + if (jobPlanProgress['transformCode'] !== StepProgress.NotStarted) { + const waitingMarkup = simpleStep( + this.getProgressIconMarkup(jobPlanProgress['uploadCode']), + CodeWhispererConstants.uploadingCodeStepMessage, + activeStepId === 0 + ) + const buildMarkup = + activeStepId >= 1 && transformByQState.getTransformationType() !== TransformationType.SQL_CONVERSION // for SQL conversions, don't show buildCode step + ? simpleStep( + this.getProgressIconMarkup(jobPlanProgress['buildCode']), + CodeWhispererConstants.buildCodeStepMessage, + activeStepId === 1 + ) + : '' + const planMarkup = + activeStepId >= 2 && transformByQState.getTransformationType() !== TransformationType.SQL_CONVERSION // for SQL conversions, don't show generatePlan step + ? simpleStep( + this.getProgressIconMarkup(jobPlanProgress['generatePlan']), + CodeWhispererConstants.generatePlanStepMessage, + activeStepId === 2 + ) + : '' + const transformMarkup = + activeStepId >= 3 + ? simpleStep( + this.getProgressIconMarkup(jobPlanProgress['transformCode']), + CodeWhispererConstants.transformStepMessage, + activeStepId === 3 + ) + : '' + + const isTransformFailed = jobPlanProgress['transformCode'] === StepProgress.Failed + const progress = this.getTransformationStepProgressMarkup(planSteps, isTransformFailed) + const latestGenericStepDetails = this.getLatestGenericStepDetails(transformByQState.getPolledJobStatus()) + const jobId = transformByQState.getJobId() + progressHtml = ` +
+

Transformation Progress

+

${jobId ? `Job ID: ${jobId}` : ''}

+ ${waitingMarkup} + ${buildMarkup} + ${planMarkup} + ${transformMarkup} + ${progress[0]} +
+
+
+
+

+

${latestGenericStepDetails}

+
+
+ ${progress[1]} +
+ ` + } else { + progressHtml = ` +
+

Transformation Progress

+

No job ongoing

+
` + } + return ` + + + + Transformation Hub + + + +
+
+
+ ${progressHtml} +
+
+
+ + + ` + } + + private getProgressIconMarkup(stepStatus: StepProgress) { + if (stepStatus === StepProgress.Succeeded) { + return `` + } else if (stepStatus === StepProgress.Pending) { + return `` + } else { + return `` + } + } +} diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts b/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts new file mode 100644 index 00000000000..68d11b800fc --- /dev/null +++ b/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts @@ -0,0 +1,536 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import AdmZip from 'adm-zip' +import os from 'os' +import fs from 'fs' // eslint-disable-line no-restricted-imports +import { parsePatch, applyPatches, ParsedDiff } from 'diff' +import path from 'path' +import vscode from 'vscode' +import { ExportIntent } from '@amzn/codewhisperer-streaming' +import { TransformByQReviewStatus, transformByQState, TransformationType } from '../../models/model' +import { ExportResultArchiveStructure, downloadExportResultArchive } from '../../../shared/utilities/download' +import { getLogger } from '../../../shared/logger/logger' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { CodeTransformTelemetryState } from '../../../amazonqGumby/telemetry/codeTransformTelemetryState' +import * as CodeWhispererConstants from '../../models/constants' +import { createCodeWhispererChatStreamingClient } from '../../../shared/clients/codewhispererChatClient' +import { ChatSessionManager } from '../../../amazonqGumby/chat/storages/chatSession' +import { setContext } from '../../../shared/vscode/setContext' +import * as codeWhisperer from '../../client/codewhisperer' +import { UserWrittenCodeTracker } from '../../tracker/userWrittenCodeTracker' +import { AuthUtil } from '../../util/authUtil' +import { copyArtifacts } from './transformFileHandler' + +export abstract class ProposedChangeNode { + abstract readonly resourcePath: string + + abstract generateCommand(): vscode.Command + abstract generateDescription(): string + abstract saveFile(): void + + public saveChange(): void { + try { + this.saveFile() + } catch (err) { + // to do: file system-related error handling + if (err instanceof Error) { + getLogger().error(err.message) + } + } + } + + reviewState: ReviewState = ReviewState.ToReview +} + +export class ModifiedChangeNode extends ProposedChangeNode { + readonly originalPath: string + readonly tmpChangedPath: string + override resourcePath: string + + constructor(originalPath: string, tmpChangedPath: string) { + super() + this.originalPath = originalPath + this.tmpChangedPath = tmpChangedPath + + this.resourcePath = tmpChangedPath + } + + override generateCommand(): vscode.Command { + return { + command: 'vscode.diff', + arguments: [vscode.Uri.file(this.originalPath), vscode.Uri.file(this.tmpChangedPath)], + title: `${path.basename(this.originalPath)}: Original <-> ${path.basename(this.tmpChangedPath)}`, + } + } + override generateDescription(): string { + return 'M' + } + + override saveFile(): void { + fs.copyFileSync(this.tmpChangedPath, this.originalPath) + } +} + +export class AddedChangeNode extends ProposedChangeNode { + readonly pathToTmpFile: string + readonly pathToWorkspaceFile: string + + override resourcePath: string + + constructor(pathToWorkspaceFile: string, pathToTmpFile: string) { + super() + this.pathToWorkspaceFile = pathToWorkspaceFile + this.pathToTmpFile = pathToTmpFile + + this.resourcePath = pathToTmpFile + } + + override generateCommand(): vscode.Command { + return { + command: 'vscode.open', + arguments: [vscode.Uri.file(this.pathToTmpFile)], + title: 'Added Change', + } + } + override generateDescription(): string { + return 'A' + } + + override saveFile(): void { + // create parent directory before copying files (ex. for the summary/ and assets/ folders) + const parentDir = path.dirname(this.pathToWorkspaceFile) + if (!fs.existsSync(parentDir)) { + fs.mkdirSync(parentDir, { recursive: true }) + } + fs.copyFileSync(this.pathToTmpFile, this.pathToWorkspaceFile) + } +} + +export class PatchFileNode { + label: string + readonly patchFilePath: string + children: ProposedChangeNode[] = [] + + constructor(patchFilePath: string) { + this.patchFilePath = patchFilePath + this.label = path.basename(patchFilePath) + } +} + +enum ReviewState { + ToReview, + Reviewed_Accepted, + Reviewed_Rejected, +} + +export class DiffModel { + patchFileNodes: PatchFileNode[] = [] + currentPatchIndex: number = 0 + + /** + * This function creates a copy of the changed files of the user's project so that the diff.patch can be applied to them + * @param pathToWorkspace Path to the project that was transformed + * @param changedFiles List of files that were changed + * @returns Path to the folder containing the copied files + */ + public copyProject(pathToWorkspace: string, changedFiles: ParsedDiff[]) { + const pathToTmpSrcDir = path.join(os.tmpdir(), `project-copy-${Date.now()}`) + fs.mkdirSync(pathToTmpSrcDir) + for (const file of changedFiles) { + const pathToTmpFile = path.join(pathToTmpSrcDir, file.oldFileName!.substring(2)) + // use mkdirsSync to create parent directories in pathToTmpFile too + fs.mkdirSync(path.dirname(pathToTmpFile), { recursive: true }) + const pathToOldFile = path.join(pathToWorkspace, file.oldFileName!.substring(2)) + // pathToOldFile will not exist for new files such as summary.md + if (fs.existsSync(pathToOldFile)) { + fs.copyFileSync(pathToOldFile, pathToTmpFile) + } + } + return pathToTmpSrcDir + } + + /** + * @param pathToDiff Path to the diff.patch file expected to be located in the archive returned by ExportResultsArchive + * @param pathToWorkspace Path to the project that was transformed + * @returns List of nodes containing the paths of files that were modified, added, or removed + */ + public parseDiff(pathToDiff: string, pathToWorkspace: string, isIntermediateBuild: boolean = false): PatchFileNode { + this.patchFileNodes = [] + const diffContents = fs.readFileSync(pathToDiff, 'utf8') + + if (!diffContents.trim()) { + getLogger().error(`CodeTransformation: diff.patch file is empty`) + throw new Error(CodeWhispererConstants.noChangesMadeMessage) + } + + getLogger().info(`CodeTransformation: parsing patch file at ${pathToDiff}`) + + let changedFiles = parsePatch(diffContents) + // exclude dependency_upgrade.yml from patch application + changedFiles = changedFiles.filter((file) => !file.oldFileName?.includes('dependency_upgrade')) + getLogger().info('CodeTransformation: parsed patch file successfully') + // if doing intermediate client-side build, pathToWorkspace is the path to the unzipped project's 'sources' directory (re-using upload ZIP) + // otherwise, we are at the very end of the transformation and need to copy the changed files in the project to show the diff(s) + const pathToTmpSrcDir = isIntermediateBuild ? pathToWorkspace : this.copyProject(pathToWorkspace, changedFiles) + transformByQState.setProjectCopyFilePath(pathToTmpSrcDir) + + applyPatches(changedFiles, { + loadFile: function (fileObj, callback) { + // load original contents of file + const filePath = path.join(pathToWorkspace, fileObj.oldFileName!.substring(2)) + if (!fs.existsSync(filePath)) { + // must be a new file (ex. summary.md), so pass empty string as original contents and do not pass error + callback(undefined, '') + } else { + // must be a modified file (most common), so pass original contents + const fileContents = fs.readFileSync(filePath, 'utf-8') + callback(undefined, fileContents) + } + }, + // by now, 'content' contains the changes from the patch + patched: function (fileObj, content, callback) { + const filePath = path.join(pathToTmpSrcDir, fileObj.newFileName!.substring(2)) + // write changed contents to the copy of the original file (or create a new file) + fs.writeFileSync(filePath, content) + callback(undefined) + }, + complete: function (err) { + if (err) { + getLogger().error(`CodeTransformation: ${err} when applying patch`) + } else { + getLogger().info('CodeTransformation: Patch applied successfully') + } + }, + }) + const patchFileNode = new PatchFileNode(pathToDiff) + patchFileNode.children = changedFiles.flatMap((file) => { + /* ex. file.oldFileName = 'a/src/java/com/project/component/MyFile.java' + * ex. file.newFileName = 'b/src/java/com/project/component/MyFile.java' + * use substring(2) to ignore the 'a/' and 'b/' + */ + const originalPath = path.join(pathToWorkspace, file.oldFileName!.substring(2)) + const tmpChangedPath = path.join(pathToTmpSrcDir, file.newFileName!.substring(2)) + + const originalFileExists = fs.existsSync(originalPath) + const changedFileExists = fs.existsSync(tmpChangedPath) + + if (originalFileExists && changedFileExists) { + return new ModifiedChangeNode(originalPath, tmpChangedPath) + } else if (!originalFileExists && changedFileExists) { + return new AddedChangeNode(originalPath, tmpChangedPath) + } + return [] + }) + this.patchFileNodes.push(patchFileNode) + return patchFileNode + } + + public getChanges() { + return this.patchFileNodes.flatMap((patchFileNode) => patchFileNode.children) + } + + public getRoot() { + return this.patchFileNodes.length > 0 ? this.patchFileNodes[0] : undefined + } + + public saveChanges() { + for (const patchFileNode of this.patchFileNodes) { + for (const changeNode of patchFileNode.children) { + changeNode.saveChange() + } + } + } + + public rejectChanges() { + this.clearChanges() + } + + public clearChanges() { + this.patchFileNodes = [] + this.currentPatchIndex = 0 + } +} + +export class TransformationResultsProvider implements vscode.TreeDataProvider { + public static readonly viewType = 'aws.amazonq.transformationProposedChangesTree' + + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter() + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event + + constructor(private readonly model: DiffModel) {} + + public refresh(): any { + this._onDidChangeTreeData.fire(undefined) + } + + public getTreeItem(element: ProposedChangeNode | PatchFileNode): vscode.TreeItem { + if (element instanceof PatchFileNode) { + return { + label: element.label, + collapsibleState: vscode.TreeItemCollapsibleState.Expanded, + } + } else { + return { + resourceUri: vscode.Uri.file(element.resourcePath), + command: element.generateCommand(), + description: element.generateDescription(), + } + } + } + + /* + Here we check if the element is a PatchFileNode instance. If it is, we return its + children array, which contains ProposedChangeNode instances. This ensures that when the user expands a + PatchFileNode (representing a diff.patch file), its children (proposed change nodes) are displayed as indented nodes under it. + */ + public getChildren( + element?: ProposedChangeNode | PatchFileNode + ): (ProposedChangeNode | PatchFileNode)[] | Thenable<(ProposedChangeNode | PatchFileNode)[]> { + if (!element) { + return this.model.patchFileNodes + } else if (element instanceof PatchFileNode) { + return element.children + } else { + return Promise.resolve([]) + } + } + + public getParent(element: ProposedChangeNode | PatchFileNode): PatchFileNode | undefined { + if (element instanceof ProposedChangeNode) { + const patchFileNode = this.model.patchFileNodes.find((p) => p.children.includes(element)) + return patchFileNode + } + return undefined + } +} + +export class ProposedTransformationExplorer { + private changeViewer: vscode.TreeView + + public static TmpDir = os.tmpdir() + + constructor(context: vscode.ExtensionContext) { + const diffModel = new DiffModel() + const transformDataProvider = new TransformationResultsProvider(diffModel) + this.changeViewer = vscode.window.createTreeView(TransformationResultsProvider.viewType, { + treeDataProvider: transformDataProvider, + }) + + let patchFiles: string[] = [] + let singlePatchFile: string = '' + + const reset = async () => { + await setContext('gumby.transformationProposalReviewInProgress', false) + await setContext('gumby.reviewState', TransformByQReviewStatus.NotStarted) + + // delete result archive after changes cleared; summary is under ResultArchiveFilePath + if (fs.existsSync(transformByQState.getResultArchiveFilePath())) { + fs.rmSync(transformByQState.getResultArchiveFilePath(), { recursive: true, force: true }) + } + if (fs.existsSync(transformByQState.getProjectCopyFilePath())) { + fs.rmSync(transformByQState.getProjectCopyFilePath(), { recursive: true, force: true }) + } + + diffModel.clearChanges() + // update summary path to where it is locally after user accepts changes, so that View Summary button works + transformByQState.setSummaryFilePath( + path.join(transformByQState.getProjectPath(), ExportResultArchiveStructure.PathToSummary) + ) + transformByQState.setProjectCopyFilePath('') + transformByQState.setResultArchiveFilePath('') + transformDataProvider.refresh() + } + + vscode.commands.registerCommand('aws.amazonq.transformationHub.reviewChanges.refresh', () => + transformDataProvider.refresh() + ) + + vscode.commands.registerCommand('aws.amazonq.transformationHub.reviewChanges.reset', async () => await reset()) + + vscode.commands.registerCommand('aws.amazonq.transformationHub.reviewChanges.reveal', async () => { + await setContext('gumby.transformationProposalReviewInProgress', true) + const root = diffModel.getRoot() + if (root) { + await this.changeViewer.reveal(root, { + expand: true, + }) + } + }) + + vscode.commands.registerCommand('aws.amazonq.transformationHub.summary.reveal', async () => { + if (fs.existsSync(transformByQState.getSummaryFilePath())) { + await vscode.commands.executeCommand( + 'markdown.showPreview', + vscode.Uri.file(transformByQState.getSummaryFilePath()) + ) + } + }) + + vscode.commands.registerCommand('aws.amazonq.transformationHub.reviewChanges.startReview', async () => { + await setContext('gumby.reviewState', TransformByQReviewStatus.PreparingReview) + + const pathToArchive = path.join( + ProposedTransformationExplorer.TmpDir, + transformByQState.getJobId(), + 'ExportResultsArchive.zip' + ) + let exportResultsArchiveSize = 0 + let downloadErrorMessage = undefined + + const cwStreamingClient = await createCodeWhispererChatStreamingClient() + try { + await telemetry.codeTransform_downloadArtifact.run(async () => { + telemetry.record({ + codeTransformArtifactType: 'ClientInstructions', + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + codeTransformJobId: transformByQState.getJobId(), + }) + + await downloadExportResultArchive( + cwStreamingClient, + { + exportId: transformByQState.getJobId(), + exportIntent: ExportIntent.TRANSFORMATION, + }, + pathToArchive, + AuthUtil.instance.regionProfileManager.activeRegionProfile + ) + + getLogger().info('CodeTransformation: downloaded results successfully') + // Update downloaded artifact size + exportResultsArchiveSize = (await fs.promises.stat(pathToArchive)).size + + telemetry.record({ codeTransformTotalByteSize: exportResultsArchiveSize }) + }) + } catch (e: any) { + // user can retry the download + downloadErrorMessage = (e as Error).message + if (downloadErrorMessage.includes('Encountered an unexpected error when processing the request')) { + downloadErrorMessage = CodeWhispererConstants.errorDownloadingExpiredDiff + } + void vscode.window.showErrorMessage( + `${CodeWhispererConstants.errorDownloadingDiffNotification} The download failed due to: ${downloadErrorMessage}` + ) + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: `${CodeWhispererConstants.errorDownloadingDiffChatMessage} The download failed due to: ${downloadErrorMessage}`, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + await setContext('gumby.reviewState', TransformByQReviewStatus.NotStarted) + getLogger().error(`CodeTransformation: ExportResultArchive error = ${downloadErrorMessage}`) + throw new Error('Error downloading diff') + } finally { + cwStreamingClient.destroy() + UserWrittenCodeTracker.instance.onQFeatureInvoked() + } + + let deserializeErrorMessage = undefined + let pathContainingArchive = '' + patchFiles = [] // reset patchFiles if there was a previous transformation + + try { + // Download and deserialize the zip + pathContainingArchive = path.dirname(pathToArchive) + const zip = new AdmZip(pathToArchive) + zip.extractAllTo(pathContainingArchive) + const files = fs.readdirSync(path.join(pathContainingArchive, ExportResultArchiveStructure.PathToPatch)) + singlePatchFile = path.join(pathContainingArchive, ExportResultArchiveStructure.PathToPatch, files[0]) + patchFiles.push(singlePatchFile) + diffModel.parseDiff(patchFiles[0], transformByQState.getProjectPath()) + + await setContext('gumby.reviewState', TransformByQReviewStatus.InReview) + transformDataProvider.refresh() + transformByQState.setSummaryFilePath( + path.join(pathContainingArchive, ExportResultArchiveStructure.PathToSummary) + ) + + await copyArtifacts(pathContainingArchive, transformByQState.getJobHistoryPath()) + + transformByQState.setResultArchiveFilePath(pathContainingArchive) + await setContext('gumby.isSummaryAvailable', true) + + // Do not await this so that the summary reveals without user needing to close this notification + void vscode.window.showInformationMessage(CodeWhispererConstants.viewProposedChangesNotification) + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: CodeWhispererConstants.viewProposedChangesChatMessage, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + await vscode.commands.executeCommand('aws.amazonq.transformationHub.summary.reveal') + } catch (e: any) { + deserializeErrorMessage = (e as Error).message + getLogger().error(`CodeTransformation: ParseDiff error = ${deserializeErrorMessage}`) + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: `${CodeWhispererConstants.errorDeserializingDiffChatMessage} ${deserializeErrorMessage}`, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + void vscode.window.showErrorMessage( + `${CodeWhispererConstants.errorDeserializingDiffNotification} ${deserializeErrorMessage}` + ) + } + + try { + const metricsPath = path.join(pathContainingArchive, ExportResultArchiveStructure.PathToMetrics) + const metricsData = JSON.parse(fs.readFileSync(metricsPath, 'utf8')) + + await codeWhisperer.codeWhispererClient.sendTelemetryEvent({ + telemetryEvent: { + transformEvent: { + jobId: transformByQState.getJobId(), + timestamp: new Date(), + ideCategory: 'VSCODE', + programmingLanguage: { + languageName: + transformByQState.getTransformationType() === TransformationType.LANGUAGE_UPGRADE + ? 'java' + : 'sql', + }, + linesOfCodeChanged: metricsData.linesOfCodeChanged, + charsOfCodeChanged: metricsData.charactersOfCodeChanged, + linesOfCodeSubmitted: transformByQState.getLinesOfCodeSubmitted(), // currently unavailable for SQL conversions + }, + }, + }) + } catch (err: any) { + // log error, but continue to show user diff.patch with results + getLogger().error(`CodeTransformation: SendTelemetryEvent error = ${err.message}`) + } + }) + + vscode.commands.registerCommand('aws.amazonq.transformationHub.reviewChanges.acceptChanges', async () => { + telemetry.codeTransform_submitSelection.run(() => { + getLogger().info('CodeTransformation: accepted changes') + diffModel.saveChanges() + telemetry.record({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + codeTransformJobId: transformByQState.getJobId(), + userChoice: 'acceptChanges', + }) + }) + void vscode.window.showInformationMessage(CodeWhispererConstants.changesAppliedNotificationOneDiff) + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: CodeWhispererConstants.changesAppliedChatMessageOneDiff, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + // reset after applying the patch + await reset() + }) + + vscode.commands.registerCommand('aws.amazonq.transformationHub.reviewChanges.rejectChanges', async () => { + await telemetry.codeTransform_submitSelection.run(async () => { + getLogger().info('CodeTransformation: rejected changes') + diffModel.rejectChanges() + await reset() + telemetry.record({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + codeTransformJobId: transformByQState.getJobId(), + userChoice: 'rejectChanges', + }) + }) + transformByQState.getChatControllers()?.transformationFinished.fire({ + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + }) + } +} diff --git a/packages/core/src/codewhisperer/tracker/codewhispererCodeCoverageTracker.ts b/packages/core/src/codewhisperer/tracker/codewhispererCodeCoverageTracker.ts new file mode 100644 index 00000000000..0989f022245 --- /dev/null +++ b/packages/core/src/codewhisperer/tracker/codewhispererCodeCoverageTracker.ts @@ -0,0 +1,319 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger } from '../../shared/logger/logger' +import * as CodeWhispererConstants from '../models/constants' +import globals from '../../shared/extensionGlobals' +import { vsCodeState } from '../models/model' +import { CodewhispererLanguage, telemetry } from '../../shared/telemetry/telemetry' +import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { TelemetryHelper } from '../util/telemetryHelper' +import { AuthUtil } from '../util/authUtil' +import { getSelectedCustomization } from '../util/customizationUtil' +import { codeWhispererClient as client } from '../client/codewhisperer' +import { isAwsError } from '../../shared/errors' +import { getUnmodifiedAcceptedTokens } from '../util/commonUtil' + +interface CodeWhispererToken { + range: vscode.Range + text: string + accepted: number +} + +const autoClosingKeystrokeInputs = ['[]', '{}', '()', '""', "''"] + +/** + * This singleton class is mainly used for calculating the code written by codeWhisperer + * TODO: Remove this tracker, uses user written code tracker instead. + * This is kept in codebase for server side backward compatibility until service fully switch to user written code + */ +export class CodeWhispererCodeCoverageTracker { + private _acceptedTokens: { [key: string]: CodeWhispererToken[] } + private _totalTokens: { [key: string]: number } + private _timer?: NodeJS.Timer + private _startTime: number + private _language: CodewhispererLanguage + private _serviceInvocationCount: number + + private constructor(language: CodewhispererLanguage) { + this._acceptedTokens = {} + this._totalTokens = {} + this._startTime = 0 + this._language = language + this._serviceInvocationCount = 0 + } + + public get serviceInvocationCount(): number { + return this._serviceInvocationCount + } + + public get acceptedTokens(): { [key: string]: CodeWhispererToken[] } { + return this._acceptedTokens + } + + public get totalTokens(): { [key: string]: number } { + return this._totalTokens + } + + public isActive(): boolean { + return TelemetryHelper.instance.isTelemetryEnabled() && AuthUtil.instance.isConnected() + } + + public incrementServiceInvocationCount() { + this._serviceInvocationCount += 1 + } + + public flush() { + if (!this.isActive()) { + this._totalTokens = {} + this._acceptedTokens = {} + this.closeTimer() + return + } + try { + this.emitCodeWhispererCodeContribution() + } catch (error) { + getLogger().error(`Encountered ${error} when emitting code contribution metric`) + } + } + + // TODO: Improve the range tracking of the accepted recommendation + // TODO: use the editor of the filename, not the current editor + public updateAcceptedTokensCount(editor: vscode.TextEditor) { + const filename = editor.document.fileName + if (filename in this._acceptedTokens) { + for (let i = 0; i < this._acceptedTokens[filename].length; i++) { + const oldText = this._acceptedTokens[filename][i].text + const newText = editor.document.getText(this._acceptedTokens[filename][i].range) + this._acceptedTokens[filename][i].accepted = getUnmodifiedAcceptedTokens(oldText, newText) + } + } + } + + public emitCodeWhispererCodeContribution() { + let totalTokens = 0 + for (const filename in this._totalTokens) { + totalTokens += this._totalTokens[filename] + } + if (vscode.window.activeTextEditor) { + this.updateAcceptedTokensCount(vscode.window.activeTextEditor) + } + // the accepted characters without counting user modification + let acceptedTokens = 0 + // the accepted characters after calculating user modification + let unmodifiedAcceptedTokens = 0 + for (const filename in this._acceptedTokens) { + for (const v of this._acceptedTokens[filename]) { + if (filename in this._totalTokens && this._totalTokens[filename] >= v.accepted) { + unmodifiedAcceptedTokens += v.accepted + acceptedTokens += v.text.length + } + } + } + const percentCount = ((acceptedTokens / totalTokens) * 100).toFixed(2) + const percentage = Math.round(parseInt(percentCount)) + const selectedCustomization = getSelectedCustomization() + if (this._serviceInvocationCount <= 0) { + getLogger().debug(`Skip emiting code contribution metric`) + return + } + telemetry.codewhisperer_codePercentage.emit({ + codewhispererTotalTokens: totalTokens, + codewhispererLanguage: this._language, + codewhispererAcceptedTokens: unmodifiedAcceptedTokens, + codewhispererSuggestedTokens: acceptedTokens, + codewhispererPercentage: percentage ? percentage : 0, + successCount: this._serviceInvocationCount, + codewhispererCustomizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, + credentialStartUrl: AuthUtil.instance.startUrl, + }) + + client + .sendTelemetryEvent({ + telemetryEvent: { + codeCoverageEvent: { + customizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(this._language), + }, + acceptedCharacterCount: acceptedTokens, + unmodifiedAcceptedCharacterCount: unmodifiedAcceptedTokens, + totalCharacterCount: totalTokens, + timestamp: new Date(Date.now()), + }, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + + getLogger().debug( + `Failed to sendTelemetryEvent to CodeWhisperer, requestId: ${requestId ?? ''}, message: ${ + error.message + }` + ) + }) + } + + private tryStartTimer() { + if (this._timer !== undefined) { + return + } + const currentDate = new globals.clock.Date() + this._startTime = currentDate.getTime() + this._timer = setTimeout(() => { + try { + const currentTime = new globals.clock.Date().getTime() + const delay: number = CodeWhispererConstants.defaultCheckPeriodMillis + const diffTime: number = this._startTime + delay + if (diffTime <= currentTime) { + let totalTokens = 0 + for (const filename in this._totalTokens) { + totalTokens += this._totalTokens[filename] + } + if (totalTokens > 0) { + this.flush() + } else { + getLogger().debug( + `CodeWhispererCodeCoverageTracker: skipped telemetry due to empty tokens array` + ) + } + } + } catch (e) { + getLogger().verbose(`Exception Thrown from CodeWhispererCodeCoverageTracker: ${e}`) + } finally { + this.resetTracker() + this.closeTimer() + } + }, CodeWhispererConstants.defaultCheckPeriodMillis) + } + + private resetTracker() { + this._totalTokens = {} + this._acceptedTokens = {} + this._startTime = 0 + this._serviceInvocationCount = 0 + } + + private closeTimer() { + if (this._timer !== undefined) { + clearTimeout(this._timer) + this._timer = undefined + } + } + + public addAcceptedTokens(filename: string, token: CodeWhispererToken) { + if (!(filename in this._acceptedTokens)) { + this._acceptedTokens[filename] = [] + } + this._acceptedTokens[filename].push(token) + } + + public addTotalTokens(filename: string, count: number) { + if (!(filename in this._totalTokens)) { + this._totalTokens[filename] = 0 + } + this._totalTokens[filename] += count + if (this._totalTokens[filename] < 0) { + this._totalTokens[filename] = 0 + } + } + + public countAcceptedTokens(range: vscode.Range, text: string, filename: string) { + if (!this.isActive()) { + return + } + // generate accepted recommendation token and stored in collection + this.addAcceptedTokens(filename, { range: range, text: text, accepted: text.length }) + this.addTotalTokens(filename, text.length) + } + + // For below 2 edge cases + // 1. newline character with indentation + // 2. 2 character insertion of closing brackets + public getCharacterCountFromComplexEvent(e: vscode.TextDocumentChangeEvent) { + function countChanges(cond: boolean, text: string): number { + if (!cond) { + return 0 + } + if ((text.startsWith('\n') || text.startsWith('\r\n')) && text.trim().length === 0) { + return 1 + } + if (autoClosingKeystrokeInputs.includes(text)) { + return 2 + } + return 0 + } + if (e.contentChanges.length === 2) { + const text1 = e.contentChanges[0].text + const text2 = e.contentChanges[1].text + const text2Count = countChanges(text1.length === 0, text2) + const text1Count = countChanges(text2.length === 0, text1) + return text2Count > 0 ? text2Count : text1Count + } else if (e.contentChanges.length === 1) { + return countChanges(true, e.contentChanges[0].text) + } + return 0 + } + + public isFromUserKeystroke(e: vscode.TextDocumentChangeEvent) { + return e.contentChanges.length === 1 && e.contentChanges[0].text.length === 1 + } + + public countTotalTokens(e: vscode.TextDocumentChangeEvent) { + // ignore no contentChanges. ignore contentChanges from other plugins (formatters) + // only include contentChanges from user keystroke input(one character input). + // Also ignore deletion events due to a known issue of tracking deleted CodeWhiperer tokens. + if (!runtimeLanguageContext.isLanguageSupported(e.document.languageId) || vsCodeState.isCodeWhispererEditing) { + return + } + // a user keystroke input can be + // 1. content change with 1 character insertion + // 2. newline character with indentation + // 3. 2 character insertion of closing brackets + if (this.isFromUserKeystroke(e)) { + this.tryStartTimer() + this.addTotalTokens(e.document.fileName, 1) + } else if (this.getCharacterCountFromComplexEvent(e) !== 0) { + this.tryStartTimer() + const characterIncrease = this.getCharacterCountFromComplexEvent(e) + this.addTotalTokens(e.document.fileName, characterIncrease) + } + // also include multi character input within 50 characters (not from CWSPR) + else if ( + e.contentChanges.length === 1 && + e.contentChanges[0].text.length > 1 && + TelemetryHelper.instance.lastSuggestionInDisplay !== e.contentChanges[0].text + ) { + const multiCharInputSize = e.contentChanges[0].text.length + + // select 50 as the cut-off threshold for counting user input. + // ignore all white space multi char input, this usually comes from reformat. + if (multiCharInputSize < 50 && e.contentChanges[0].text.trim().length > 0) { + this.addTotalTokens(e.document.fileName, multiCharInputSize) + } + } + } + + public static readonly instances = new Map() + + public static getTracker(language: string): CodeWhispererCodeCoverageTracker | undefined { + if (!runtimeLanguageContext.isLanguageSupported(language)) { + return undefined + } + const cwsprLanguage = runtimeLanguageContext.normalizeLanguage(language) + if (!cwsprLanguage) { + return undefined + } + const instance = this.instances.get(cwsprLanguage) ?? new this(cwsprLanguage) + this.instances.set(cwsprLanguage, instance) + return instance + } +} diff --git a/packages/core/src/codewhisperer/tracker/codewhispererTracker.ts b/packages/core/src/codewhisperer/tracker/codewhispererTracker.ts new file mode 100644 index 00000000000..ca19c87505f --- /dev/null +++ b/packages/core/src/codewhisperer/tracker/codewhispererTracker.ts @@ -0,0 +1,238 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import globals from '../../shared/extensionGlobals' +import { distance } from 'fastest-levenshtein' +import { AcceptedSuggestionEntry } from '../models/model' +import { getLogger } from '../../shared/logger/logger' +import { AmazonqModifyCode, telemetry } from '../../shared/telemetry/telemetry' +import { AuthUtil } from '../util/authUtil' +import { InsertedCode } from '../../codewhispererChat/controllers/chat/model' +import { codeWhispererClient } from '../client/codewhisperer' +import { logSendTelemetryEventFailure } from '../../codewhispererChat/controllers/chat/telemetryHelper' +import { Timeout } from '../../shared/utilities/timeoutUtils' +import { getSelectedCustomization } from '../util/customizationUtil' +import { isAwsError } from '../../shared/errors' +import { undefinedIfEmpty } from '../../shared/utilities/textUtilities' +import { getUnmodifiedAcceptedTokens } from '../util/commonUtil' + +/** + * This singleton class is mainly used for calculating the percentage of user modification. + * The current calculation method is (Levenshtein edit distance / acceptedSuggestion.length). + */ +export class CodeWhispererTracker { + private _eventQueue: (AcceptedSuggestionEntry | InsertedCode)[] + private _timer?: Timeout + private static instance: CodeWhispererTracker + + /** + * the interval of the background thread invocation, which is triggered by the timer + */ + private static readonly defaultCheckPeriodMillis = 1000 * 60 * 1 // 1 minute in milliseconds + /** + * modification should be recorded at least 5 minutes after accepted into the editor + */ + private static readonly defaultModificationIntervalMillis = 1000 * 60 * 5 // 5 minutes in milliseconds + + /** + * This is to avoid user overflowing the eventQueue by spamming accepted suggestions + */ + private static readonly defaultMaxQueueSize = 10000 + + private constructor() { + this._eventQueue = [] + } + + public enqueue(suggestion: AcceptedSuggestionEntry | InsertedCode) { + if (!globals.telemetry.telemetryEnabled) { + return + } + + if (this._eventQueue.length >= 0) { + this.startTimer().catch((e) => { + getLogger().error('startTimer failed: %s', (e as Error).message) + }) + } + + if (this._eventQueue.length >= CodeWhispererTracker.defaultMaxQueueSize) { + this._eventQueue.shift() + } + this._eventQueue.push(suggestion) + } + + public async flush() { + if (!globals.telemetry.telemetryEnabled) { + this._eventQueue = [] + this.closeTimer() + return + } + + const currentTime = new Date() + const newEventQueue: (AcceptedSuggestionEntry | InsertedCode)[] = [] + for (const suggestion of this._eventQueue) { + if ( + currentTime.getTime() - suggestion.time.getTime() > + CodeWhispererTracker.defaultModificationIntervalMillis + ) { + await this.emitTelemetryOnSuggestion(suggestion) + } else { + newEventQueue.push(suggestion) + } + } + + this._eventQueue = newEventQueue + if (this._eventQueue.length === 0) { + this.closeTimer() + } + } + + public async emitTelemetryOnSuggestion(suggestion: AcceptedSuggestionEntry | InsertedCode) { + let percentage = 1.0 + let currString = '' + const customizationArn = undefinedIfEmpty(getSelectedCustomization().arn) + + try { + if (suggestion.fileUrl?.scheme !== '') { + const document = await vscode.workspace.openTextDocument(suggestion.fileUrl) + if (document) { + currString = document.getText(new vscode.Range(suggestion.startPosition, suggestion.endPosition)) + percentage = this.checkDiff(currString, suggestion.originalString) + } + } + } catch (e) { + getLogger().verbose(`Exception Thrown from CodeWhispererTracker: ${e}`) + return + } finally { + if ('conversationID' in suggestion) { + const event: AmazonqModifyCode = { + cwsprChatConversationId: suggestion.conversationID, + cwsprChatMessageId: suggestion.messageID, + cwsprChatModificationPercentage: percentage ? percentage : 0, + credentialStartUrl: AuthUtil.instance.startUrl, + } + + telemetry.amazonq_modifyCode.emit(event) + + codeWhispererClient + .sendTelemetryEvent({ + telemetryEvent: { + chatUserModificationEvent: { + conversationId: event.cwsprChatConversationId, + messageId: event.cwsprChatMessageId, + modificationPercentage: event.cwsprChatModificationPercentage, + customizationArn: customizationArn, + }, + }, + }) + .then() + .catch(logSendTelemetryEventFailure) + } else { + telemetry.codewhisperer_userModification.emit({ + codewhispererRequestId: suggestion.requestId ? suggestion.requestId : 'undefined', + codewhispererSessionId: suggestion.sessionId ? suggestion.sessionId : undefined, + codewhispererTriggerType: suggestion.triggerType, + codewhispererSuggestionIndex: suggestion.index ? suggestion.index : 0, + codewhispererModificationPercentage: percentage ? percentage : 0, + codewhispererCompletionType: suggestion.completionType, + codewhispererLanguage: suggestion.language, + credentialStartUrl: AuthUtil.instance.startUrl, + codewhispererCharactersAccepted: suggestion.originalString.length, + codewhispererCharactersModified: 0, // TODO: currently we don't have an accurate number for this field with existing implementation + }) + + codeWhispererClient + .sendTelemetryEvent({ + telemetryEvent: { + userModificationEvent: { + sessionId: suggestion.sessionId, + requestId: suggestion.requestId, + programmingLanguage: { languageName: suggestion.language }, + // deprecated % value and should not be used by service side + modificationPercentage: percentage, + customizationArn: customizationArn, + timestamp: new Date(), + acceptedCharacterCount: suggestion.originalString.length, + unmodifiedAcceptedCharacterCount: getUnmodifiedAcceptedTokens( + suggestion.originalString, + currString + ), + }, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + + getLogger().debug( + `Failed to send UserModificationEvent to CodeWhisperer, requestId: ${requestId ?? ''}, message: ${ + error.message + }` + ) + }) + } + } + } + + /** + * This function calculates the Levenshtein edit distance of currString from original accepted String + * then return a percentage against the length of accepted string (capped by 1,0) + * @param currString the current string in the same location as the previously accepted suggestion + * @param acceptedString the accepted suggestion that was inserted into the editor + */ + public checkDiff(currString?: string, acceptedString?: string): number { + if (!currString || !acceptedString || currString.length === 0 || acceptedString.length === 0) { + return 1.0 + } + + const diff = distance(currString, acceptedString) + return Math.min(1.0, diff / acceptedString.length) + } + + public async startTimer() { + if (!this._timer) { + this._timer = new Timeout(CodeWhispererTracker.defaultCheckPeriodMillis) + this._timer.onCompletion(async () => { + try { + await this.flush() + } finally { + if (this._timer !== undefined) { + this._timer!.refresh() + } + } + }) + } + } + + public closeTimer() { + if (this._timer !== undefined) { + this._timer.cancel() + this._timer = undefined + } + } + + public async shutdown() { + this.closeTimer() + + if (globals.telemetry.telemetryEnabled) { + try { + await this.flush() + } finally { + this._eventQueue = [] + } + } + } + + public static getTracker(): CodeWhispererTracker { + if (!CodeWhispererTracker.instance) { + CodeWhispererTracker.instance = new CodeWhispererTracker() + } + return CodeWhispererTracker.instance + } +} diff --git a/packages/core/src/codewhisperer/tracker/lineTracker.ts b/packages/core/src/codewhisperer/tracker/lineTracker.ts new file mode 100644 index 00000000000..7fce5ef9534 --- /dev/null +++ b/packages/core/src/codewhisperer/tracker/lineTracker.ts @@ -0,0 +1,179 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { isTextEditor } from '../../shared/utilities/editorUtilities' +import { setContext } from '../../shared/vscode/setContext' + +export interface LineSelection { + anchor: number + active: number +} + +export interface LinesChangeEvent { + readonly editor: vscode.TextEditor | undefined + readonly selections: LineSelection[] | undefined + + readonly reason: 'editor' | 'selection' | 'content' +} + +/** + * This class providees a single interface to manage and access users' "line" selections + * Callers could use it by subscribing onDidChangeActiveLines to do UI updates or logic needed to be executed when line selections get changed + */ +export class LineTracker implements vscode.Disposable { + private _onDidChangeActiveLines = new vscode.EventEmitter() + get onDidChangeActiveLines(): vscode.Event { + return this._onDidChangeActiveLines.event + } + + private _editor: vscode.TextEditor | undefined + private _disposable: vscode.Disposable | undefined + + private _selections: LineSelection[] | undefined + get selections(): LineSelection[] | undefined { + return this._selections + } + + private _onReady: vscode.EventEmitter = new vscode.EventEmitter() + get onReady(): vscode.Event { + return this._onReady.event + } + + private _ready: boolean = false + get isReady() { + return this._ready + } + + constructor() { + this._disposable = vscode.Disposable.from( + vscode.window.onDidChangeActiveTextEditor(async (e) => { + await this.onActiveTextEditorChanged(e) + }), + vscode.window.onDidChangeTextEditorSelection(async (e) => { + await this.onTextEditorSelectionChanged(e) + }), + vscode.workspace.onDidChangeTextDocument((e) => { + this.onContentChanged(e) + }) + ) + + queueMicrotask(async () => await this.onActiveTextEditorChanged(vscode.window.activeTextEditor)) + } + + dispose() { + this._disposable?.dispose() + } + + ready() { + if (this._ready) { + throw new Error('Linetracker is already activated') + } + + this._ready = true + queueMicrotask(() => this._onReady.fire()) + } + + // @VisibleForTesting + async onActiveTextEditorChanged(editor: vscode.TextEditor | undefined) { + if (editor === this._editor) { + return + } + + this._editor = editor + this._selections = toLineSelections(editor?.selections) + if (this._selections && this._selections[0]) { + const s = this._selections.map((item) => item.active + 1) + await setContext('codewhisperer.activeLine', s) + } + + this.notifyLinesChanged('editor') + } + + // @VisibleForTesting + async onTextEditorSelectionChanged(e: vscode.TextEditorSelectionChangeEvent) { + // If this isn't for our cached editor and its not a real editor -- kick out + if (this._editor !== e.textEditor && !isTextEditor(e.textEditor)) { + return + } + + const selections = toLineSelections(e.selections) + if (this._editor === e.textEditor && this.includes(selections)) { + return + } + + this._editor = e.textEditor + this._selections = selections + if (this._selections && this._selections[0]) { + const s = this._selections.map((item) => item.active + 1) + await setContext('codewhisperer.activeLine', s) + } + + this.notifyLinesChanged('selection') + } + + // @VisibleForTesting + onContentChanged(e: vscode.TextDocumentChangeEvent) { + const editor = vscode.window.activeTextEditor + if (e.document === editor?.document && e.contentChanges.length > 0 && isTextEditor(editor)) { + this._editor = editor + this._selections = toLineSelections(this._editor?.selections) + + this.notifyLinesChanged('content') + } + } + + notifyLinesChanged(reason: 'editor' | 'selection' | 'content') { + const e: LinesChangeEvent = { editor: this._editor, selections: this.selections, reason: reason } + this._onDidChangeActiveLines.fire(e) + } + + includes(selections: LineSelection[]): boolean + includes(line: number, options?: { activeOnly: boolean }): boolean + includes(lineOrSelections: number | LineSelection[], options?: { activeOnly: boolean }): boolean { + if (typeof lineOrSelections !== 'number') { + return isIncluded(lineOrSelections, this._selections) + } + + if (this._selections === undefined || this._selections.length === 0) { + return false + } + + const line = lineOrSelections + const activeOnly = options?.activeOnly ?? true + + for (const selection of this._selections) { + if ( + line === selection.active || + (!activeOnly && + ((selection.anchor >= line && line >= selection.active) || + (selection.active >= line && line >= selection.anchor))) + ) { + return true + } + } + return false + } +} + +function isIncluded(selections: LineSelection[] | undefined, within: LineSelection[] | undefined): boolean { + if (selections === undefined && within === undefined) { + return true + } + if (selections === undefined || within === undefined || selections.length !== within.length) { + return false + } + + return selections.every((s, i) => { + const match = within[i] + return s.active === match.active && s.anchor === match.anchor + }) +} + +function toLineSelections(selections: readonly vscode.Selection[]): LineSelection[] +function toLineSelections(selections: readonly vscode.Selection[] | undefined): LineSelection[] | undefined +function toLineSelections(selections: readonly vscode.Selection[] | undefined) { + return selections?.map((s) => ({ active: s.active.line, anchor: s.anchor.line })) +} diff --git a/packages/core/src/codewhisperer/tracker/userWrittenCodeTracker.ts b/packages/core/src/codewhisperer/tracker/userWrittenCodeTracker.ts new file mode 100644 index 00000000000..7dfb14b5745 --- /dev/null +++ b/packages/core/src/codewhisperer/tracker/userWrittenCodeTracker.ts @@ -0,0 +1,196 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger } from '../../shared/logger/logger' +import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { AuthUtil } from '../util/authUtil' +import { getSelectedCustomization } from '../util/customizationUtil' +import { codeWhispererClient as client } from '../client/codewhisperer' +import { isAwsError } from '../../shared/errors' +import { undefinedIfEmpty } from '../../shared/utilities/textUtilities' +import { CodewhispererLanguage } from '../../shared/telemetry/telemetry' +import globals from '../../shared/extensionGlobals' + +/** + * This singleton class is mainly used for calculating the user written code + * for active Amazon Q users. + * It reports the user written code per 5 minutes when the user is coding and using Amazon Q features + */ +export class UserWrittenCodeTracker { + private _userWrittenNewCodeCharacterCount: Map + private _userWrittenNewCodeLineCount: Map + private _qIsMakingEdits: boolean + private _timer?: NodeJS.Timer + private _qUsageCount: number + private _lastQInvocationTime: number + + static #instance: UserWrittenCodeTracker + private static copySnippetThreshold = 50 + private static resetQIsEditingTimeoutMs = 2 * 60 * 1000 + private static defaultCheckPeriodMillis = 5 * 60 * 1000 + + private constructor() { + this._userWrittenNewCodeLineCount = new Map() + this._userWrittenNewCodeCharacterCount = new Map() + this._qUsageCount = 0 + this._qIsMakingEdits = false + this._timer = undefined + this._lastQInvocationTime = 0 + } + + public static get instance() { + return (this.#instance ??= new this()) + } + + public isActive(): boolean { + return globals.telemetry.telemetryEnabled && AuthUtil.instance.isConnected() + } + + // this should be invoked whenever there is a successful Q feature invocation + // for all Q features + public onQFeatureInvoked() { + this._qUsageCount += 1 + this._lastQInvocationTime = Date.now() + } + + public onQStartsMakingEdits() { + this._qIsMakingEdits = true + } + + public onQFinishesEdits() { + this._qIsMakingEdits = false + } + + public getUserWrittenCharacters(language: CodewhispererLanguage) { + return this._userWrittenNewCodeCharacterCount.get(language) || 0 + } + + public getUserWrittenLines(language: CodewhispererLanguage) { + return this._userWrittenNewCodeLineCount.get(language) || 0 + } + + public reset() { + this._userWrittenNewCodeLineCount = new Map() + this._userWrittenNewCodeCharacterCount = new Map() + this._qUsageCount = 0 + this._qIsMakingEdits = false + this._lastQInvocationTime = 0 + if (this._timer !== undefined) { + clearTimeout(this._timer) + this._timer = undefined + } + } + + public emitCodeContributions() { + const selectedCustomization = getSelectedCustomization() + + for (const [language, charCount] of this._userWrittenNewCodeCharacterCount) { + const lineCount = this.getUserWrittenLines(language) + if (charCount > 0) { + client + .sendTelemetryEvent({ + telemetryEvent: { + codeCoverageEvent: { + customizationArn: undefinedIfEmpty(selectedCustomization.arn), + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(language), + }, + acceptedCharacterCount: 0, + totalCharacterCount: 0, + timestamp: new Date(Date.now()), + userWrittenCodeCharacterCount: charCount, + userWrittenCodeLineCount: lineCount, + }, + }, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + getLogger().debug( + `Failed to sendTelemetryEvent, requestId: ${requestId ?? ''}, message: ${error.message}` + ) + }) + } + } + } + + private tryStartTimer() { + if (this._timer !== undefined) { + return + } + if (!this.isActive()) { + getLogger().debug(`Skip emiting code contribution metric. Telemetry disabled or not logged in. `) + this.reset() + return + } + const startTime = Date.now() + this._timer = setTimeout(() => { + try { + const currentTime = Date.now() + const delay: number = UserWrittenCodeTracker.defaultCheckPeriodMillis + const diffTime: number = startTime + delay + if (diffTime <= currentTime) { + if (this._qUsageCount <= 0) { + getLogger().debug(`Skip emiting code contribution metric. There is no active Amazon Q usage. `) + return + } + if (this._userWrittenNewCodeCharacterCount.size === 0) { + getLogger().debug(`Skip emiting code contribution metric. There is no new code added. `) + return + } + this.emitCodeContributions() + } + } catch (e) { + getLogger().verbose(`Exception Thrown from QCodeGenTracker: ${e}`) + } finally { + this.reset() + } + }, UserWrittenCodeTracker.defaultCheckPeriodMillis) + } + + private countNewLines(str: string) { + return str.split('\n').length - 1 + } + + public onTextDocumentChange(e: vscode.TextDocumentChangeEvent) { + // do not count code written by Q as user written code + if ( + !runtimeLanguageContext.isLanguageSupported(e.document.languageId) || + e.contentChanges.length === 0 || + this._qIsMakingEdits + ) { + // if the boolean of qIsMakingEdits was incorrectly set to true + // due to unhandled edge cases or early terminated code paths + // reset it back to false after a reasonable period of time + if (this._qIsMakingEdits) { + if (Date.now() - this._lastQInvocationTime > UserWrittenCodeTracker.resetQIsEditingTimeoutMs) { + getLogger().warn(`Reset Q is editing state to false.`) + this._qIsMakingEdits = false + } + } + return + } + const contentChange = e.contentChanges[0] + // if user copies code into the editor for more than 50 characters + // do not count this as total new code, this will skew the data, + // reporting highly inflated user written code + if (contentChange.text.length > UserWrittenCodeTracker.copySnippetThreshold) { + return + } + const language = runtimeLanguageContext.normalizeLanguage(e.document.languageId) + if (language) { + const charCount = this.getUserWrittenCharacters(language) + this._userWrittenNewCodeCharacterCount.set(language, charCount + contentChange.text.length) + const lineCount = this.getUserWrittenLines(language) + this._userWrittenNewCodeLineCount.set(language, lineCount + this.countNewLines(contentChange.text)) + // start 5 min data reporting once valid user input is detected + this.tryStartTimer() + } + } +} diff --git a/packages/core/src/codewhisperer/ui/codeWhispererNodes.ts b/packages/core/src/codewhisperer/ui/codeWhispererNodes.ts new file mode 100644 index 00000000000..c1934ec6a73 --- /dev/null +++ b/packages/core/src/codewhisperer/ui/codeWhispererNodes.ts @@ -0,0 +1,263 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import vscode from 'vscode' +import { addColor, codicon, getIcon } from '../../shared/icons' +import { DataQuickPickItem } from '../../shared/ui/pickerPrompter' +import { localize } from '../../shared/utilities/vsCodeUtils' +import { Commands, placeholder } from '../../shared/vscode/commands2' +import { + toggleCodeSuggestions, + showReferenceLog, + showLearnMore, + showFreeTierLimit, + reconnect, + selectCustomizationPrompt, + signoutCodeWhisperer, + showIntroduction, + toggleCodeScans, + selectRegionProfileCommand, +} from '../commands/basicCommands' +import { CodeWhispererCommandDeclarations } from '../commands/gettingStartedPageCommands' +import { CodeScansState, RegionProfile } from '../models/model' +import { getNewCustomizationsAvailable, getSelectedCustomization } from '../util/customizationUtil' +import { cwQuickPickSource } from '../commands/types' +import { AuthUtil } from '../util/authUtil' +import { submitFeedback } from '../../feedback/vue/submitFeedback' +import { focusAmazonQPanel } from '../../codewhispererChat/commands/registerCommands' +import { isWeb } from '../../shared/extensionGlobals' +import { getLogger } from '../../shared/logger/logger' + +export function createAutoSuggestions(running: boolean): DataQuickPickItem<'autoSuggestions'> { + const labelResume = localize('AWS.codewhisperer.resumeCodeWhispererNode.label', 'Resume Auto-Suggestions') + const iconResume = getIcon('vscode-debug-start') + const labelPause = localize('AWS.codewhisperer.pauseCodeWhispererNode.label', 'Pause Auto-Suggestions') + const iconPause = getIcon('vscode-debug-pause') + + return { + data: 'autoSuggestions', + label: running ? codicon`${iconPause} ${labelPause}` : codicon`${iconResume} ${labelResume}`, + description: running ? 'Currently RUNNING' : 'Currently PAUSED', + onClick: () => toggleCodeSuggestions.execute(placeholder, cwQuickPickSource), + } as DataQuickPickItem<'autoSuggestions'> +} + +export function createAutoScans(running: boolean): DataQuickPickItem<'autoScans'> { + const labelResume = localize('AWS.codewhisperer.resumeCodeWhispererNode.label', 'Resume Auto-Reviews') + const iconResume = getIcon('vscode-debug-alt') + const labelPause = localize('AWS.codewhisperer.pauseCodeWhispererNode.label', 'Pause Auto-Reviews') + const iconPause = getIcon('vscode-debug-pause') + const monthlyQuotaExceeded = CodeScansState.instance.isMonthlyQuotaExceeded() + + return { + data: 'autoScans', + label: running ? codicon`${iconPause} ${labelPause}` : codicon`${iconResume} ${labelResume}`, + description: monthlyQuotaExceeded ? 'Monthly quota exceeded' : running ? 'RUNNING' : 'PAUSED', + onClick: () => toggleCodeScans.execute(placeholder, cwQuickPickSource), + } as DataQuickPickItem<'autoScans'> +} + +export function createOpenReferenceLog(): DataQuickPickItem<'openReferenceLog'> { + const label = localize('AWS.codewhisperer.openReferenceLogNode.label', 'Open Code Reference Log') + const icon = getIcon('vscode-code') + + return { + data: 'openReferenceLog', + label: codicon`${icon} ${label}`, + onClick: () => showReferenceLog.execute(placeholder, cwQuickPickSource), + } as DataQuickPickItem<'openReferenceLog'> +} + +export function createReconnect(): DataQuickPickItem<'reconnect'> { + const label = localize('aws.amazonq.reconnectNode.label', 'Re-authenticate to connect') + const icon = addColor(getIcon('vscode-debug-disconnect'), 'notificationsErrorIcon.foreground') + + return { + data: 'reconnect', + label: codicon`${icon} ${label}`, + onClick: () => reconnect.execute(placeholder, cwQuickPickSource), + } +} + +export function createLearnMore(): DataQuickPickItem<'learnMore'> { + const label = localize('AWS.codewhisperer.learnMoreNode.label', 'Learn more about Amazon Q') + const icon = getIcon('vscode-question') + + return { + data: 'learnMore', + label: codicon`${icon} ${label}`, + onClick: () => showLearnMore.execute(cwQuickPickSource), + } as DataQuickPickItem<'learnMore'> +} + +export function createFreeTierLimitMet(): DataQuickPickItem<'freeTierLimitMet'> { + const label = localize('AWS.codewhisperer.freeTierLimitMetNode.label', 'Free Tier Limit Met') + const icon = getIcon('vscode-error') + + return { + data: 'freeTierLimitMet', + label: codicon`${icon} ${label}`, + onClick: () => showFreeTierLimit.execute(placeholder, cwQuickPickSource), + } +} + +export function createSelectCustomization(): DataQuickPickItem<'selectCustomization'> { + const selectedCustomization = getSelectedCustomization() + const newCustomizationsAmount = getNewCustomizationsAvailable() + + const label = localize('AWS.codewhisperer.selectCustomizationNode.label', 'Select Customization') + const icon = getIcon('vscode-settings') + const description = + newCustomizationsAmount > 0 ? `${newCustomizationsAmount} new available` : `Using ${selectedCustomization.name}` + + return { + data: 'selectCustomization', + label: codicon`${icon} ${label}`, + description: description, + onClick: () => selectCustomizationPrompt.execute(placeholder, cwQuickPickSource), + } as DataQuickPickItem<'selectCustomization'> +} + +export function createSelectRegionProfileNode( + profile: RegionProfile | undefined +): DataQuickPickItem<'selectRegionProfile'> { + const selectedRegionProfile = profile + + const label = profile ? 'Change Profile' : '(Required) Select Profile' + const icon = getIcon('vscode-arrow-swap') + const description = selectedRegionProfile + ? `Current profile: ${selectedRegionProfile.name}` + : 'A profile MUST be selected for features to work' + + return { + data: 'selectRegionProfile', + label: codicon`${icon} ${label}`, + onClick: async () => { + await selectRegionProfileCommand.execute(placeholder, cwQuickPickSource) + }, + description: description, + picked: profile === undefined, + } +} + +/* Opens the Learn CodeWhisperer Page */ +export function createGettingStarted(): DataQuickPickItem<'gettingStarted'> { + const label = localize('AWS.codewhisperer.gettingStartedNode.label', 'Try inline suggestion examples') + const icon = getIcon('vscode-rocket') + return { + data: 'gettingStarted', + label: codicon`${icon} ${label}`, + onClick: () => + CodeWhispererCommandDeclarations.instance.declared.showGettingStartedPage.execute( + placeholder, + cwQuickPickSource + ), + } as DataQuickPickItem<'gettingStarted'> +} + +export function createManageSubscription(): DataQuickPickItem<'manageSubscription'> { + const label = localize('AWS.command.manageSubscription', 'Manage Q Developer Pro Subscription') + // const kind = AuthUtil.instance.isBuilderIdInUse() ? 'AWS Builder ID' : 'IAM Identity Center' + + return { + data: 'manageSubscription', + label: label, + iconPath: getIcon('vscode-link-external'), + onClick: () => Commands.tryExecute('aws.amazonq.manageSubscription'), + } as DataQuickPickItem<'manageSubscription'> +} + +export function createSignout(): DataQuickPickItem<'signout'> { + const label = localize('AWS.codewhisperer.signoutNode.label', 'Sign Out') + const icon = getIcon('vscode-export') + const connection = AuthUtil.instance.isBuilderIdInUse() ? 'AWS Builder ID' : 'IAM Identity Center' + + return { + data: 'signout', + label: codicon`${icon} ${label}`, + description: `Connected with ${connection}`, + onClick: () => signoutCodeWhisperer.execute(placeholder, cwQuickPickSource), + } as DataQuickPickItem<'signout'> +} + +export function createSettingsNode(): DataQuickPickItem<'openCodeWhispererSettings'> { + return { + data: 'openCodeWhispererSettings', + label: 'Open Settings', + iconPath: getIcon('vscode-settings-gear'), + onClick: () => Commands.tryExecute('aws.amazonq.configure'), + } as DataQuickPickItem<'openCodeWhispererSettings'> +} + +export function createFeedbackNode(): DataQuickPickItem<'sendFeedback'> { + return { + data: 'sendFeedback', + label: 'Send Feedback', + iconPath: getIcon('vscode-thumbsup'), + onClick: () => submitFeedback(placeholder, 'Amazon Q'), + } as DataQuickPickItem<'sendFeedback'> +} + +export function createGitHubNode(): DataQuickPickItem<'visitGithub'> { + return { + data: 'visitGithub', + label: 'Connect with us on Github', + iconPath: getIcon('vscode-github-alt'), + onClick: () => Commands.tryExecute('aws.amazonq.github'), + } as DataQuickPickItem<'visitGithub'> +} + +/* Opens the AWS Docs for CodeWhisperer */ +export function createDocumentationNode(): DataQuickPickItem<'viewDocumentation'> { + return { + data: 'viewDocumentation', + label: 'View Documentation', + iconPath: getIcon('vscode-symbol-ruler'), + onClick: () => showIntroduction.execute(), + } as DataQuickPickItem<'viewDocumentation'> +} + +export function createSeparator(label: string = ''): DataQuickPickItem<'separator'> { + return { + kind: vscode.QuickPickItemKind.Separator, + data: 'separator', + label, + } +} + +export function switchToAmazonQNode(): DataQuickPickItem<'openChatPanel'> { + return { + data: 'openChatPanel', + label: 'Open Chat Panel', + iconPath: getIcon('vscode-comment'), + onClick: () => + focusAmazonQPanel.execute(placeholder, 'codewhispererQuickPick').catch((e) => { + getLogger().error('focusAmazonQPanel failed: %s', e) + }), + } +} + +export function createSignIn(): DataQuickPickItem<'signIn'> { + const label = localize('AWS.codewhisperer.signInNode.label', 'Sign in to get started') + const icon = getIcon('vscode-account') + + let onClick = () => { + focusAmazonQPanel.execute(placeholder, 'codewhispererQuickPick').catch((e) => { + getLogger().error('focusAmazonQPanel failed: %s', e) + }) + } + if (isWeb()) { + // TODO: nkomonen, call a Command instead + onClick = () => { + void AuthUtil.instance.connectToAwsBuilderId() + } + } + + return { + data: 'signIn', + label: codicon`${icon} ${label}`, + onClick, + } +} diff --git a/packages/core/src/codewhisperer/ui/prompters.ts b/packages/core/src/codewhisperer/ui/prompters.ts new file mode 100644 index 00000000000..95541d84a82 --- /dev/null +++ b/packages/core/src/codewhisperer/ui/prompters.ts @@ -0,0 +1,34 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + codeIssueGroupingStrategies, + CodeIssueGroupingStrategy, + codeIssueGroupingStrategyLabel, + CodeIssueGroupingStrategyState, +} from '../models/model' +import { createQuickPick, QuickPickPrompter } from '../../shared/ui/pickerPrompter' +import { localize } from '../../shared/utilities/vsCodeUtils' + +export function createCodeIssueGroupingStrategyPrompter(): QuickPickPrompter { + const groupingStrategy = CodeIssueGroupingStrategyState.instance.getState() + const prompter = createQuickPick( + codeIssueGroupingStrategies.map((strategy) => ({ + data: strategy, + label: codeIssueGroupingStrategyLabel[strategy], + })), + { + title: localize('AWS.amazonq.scans.groupIssues', 'Group Issues'), + placeholder: localize('AWS.amazonq.scans.groupIssues.placeholder', 'Select how to group code issues'), + } + ) + prompter.quickPick.activeItems = prompter.quickPick.items.filter((item) => item.data === groupingStrategy) + prompter.quickPick.onDidChangeSelection(async (items) => { + const [item] = items + await CodeIssueGroupingStrategyState.instance.setState(item.data) + prompter.quickPick.hide() + }) + return prompter +} diff --git a/packages/core/src/codewhisperer/ui/statusBarMenu.ts b/packages/core/src/codewhisperer/ui/statusBarMenu.ts new file mode 100644 index 00000000000..345ae641a78 --- /dev/null +++ b/packages/core/src/codewhisperer/ui/statusBarMenu.ts @@ -0,0 +1,136 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + createAutoSuggestions, + createOpenReferenceLog, + createLearnMore, + createFreeTierLimitMet, + createSelectCustomization, + createReconnect, + createGettingStarted, + createManageSubscription, + createSignout, + createSeparator, + createSettingsNode, + createFeedbackNode, + createGitHubNode, + createDocumentationNode, + createAutoScans, + createSignIn, + switchToAmazonQNode, + createSelectRegionProfileNode, +} from './codeWhispererNodes' +import { hasVendedIamCredentials, hasVendedCredentialsFromMetadata } from '../../auth/auth' +import { AuthUtil } from '../util/authUtil' +import { DataQuickPickItem, createQuickPick } from '../../shared/ui/pickerPrompter' +import { CodeScansState, CodeSuggestionsState, vsCodeState } from '../models/model' +import { Commands } from '../../shared/vscode/commands2' +import { createExitButton } from '../../shared/ui/buttons' +import { telemetry } from '../../shared/telemetry/telemetry' +import { getLogger } from '../../shared/logger/logger' + +function getAmazonQCodeWhispererNodes() { + const autoTriggerEnabled = CodeSuggestionsState.instance.isSuggestionsEnabled() + const autoScansEnabled = CodeScansState.instance.isScansEnabled() + if (AuthUtil.instance.isConnectionExpired()) { + return [createReconnect(), createLearnMore()] + } + + if (!AuthUtil.instance.isConnected()) { + return [createSignIn(), createLearnMore()] + } + + if (AuthUtil.instance.isConnected() && AuthUtil.instance.requireProfileSelection()) { + return [] + } + + if (vsCodeState.isFreeTierLimitReached) { + if (hasVendedIamCredentials()) { + return [createFreeTierLimitMet(), createOpenReferenceLog()] + } + return [createFreeTierLimitMet(), createOpenReferenceLog(), createSeparator('Other Features')] + } + + if (hasVendedIamCredentials()) { + return [createAutoSuggestions(autoTriggerEnabled), createOpenReferenceLog()] + } + + return [ + // CodeWhisperer + createSeparator('Inline Suggestions'), + createAutoSuggestions(autoTriggerEnabled), + createOpenReferenceLog(), + createGettingStarted(), // "Learn" node : opens Learn CodeWhisperer page + + // Security scans + createSeparator('Code Reviews'), + ...(AuthUtil.instance.isBuilderIdInUse() ? [] : [createAutoScans(autoScansEnabled)]), + + // Amazon Q + others + createSeparator('Other Features'), + ...(AuthUtil.instance.isValidEnterpriseSsoInUse() && AuthUtil.instance.isCustomizationFeatureEnabled + ? [createSelectCustomization()] + : []), + switchToAmazonQNode(), + ] +} + +export function getQuickPickItems(): DataQuickPickItem[] { + const isUsingEnterpriseSso = AuthUtil.instance.isValidEnterpriseSsoInUse() + const regionProfile = AuthUtil.instance.regionProfileManager.activeRegionProfile + + const children = [ + // If the user has signed in but not selected a region, we strongly indicate they need to select + // a profile, otherwise features will not work. + ...(isUsingEnterpriseSso && !regionProfile ? [createSelectRegionProfileNode(undefined)] : []), + + ...getAmazonQCodeWhispererNodes(), + + // Generic Nodes + createSeparator('Connect / Help'), + createFeedbackNode(), + createGitHubNode(), + createDocumentationNode(), + + // Add settings and signout + createSeparator(), + createSettingsNode(), + ...(isUsingEnterpriseSso && regionProfile ? [createSelectRegionProfileNode(regionProfile)] : []), + ...(AuthUtil.instance.isConnected() && !hasVendedIamCredentials() && !hasVendedCredentialsFromMetadata() + ? [...(AuthUtil.instance.isBuilderIdInUse() ? [createManageSubscription()] : []), createSignout()] + : []), + ] + + return children +} + +export const listCodeWhispererCommandsId = 'aws.amazonq.listCommands' +export const listCodeWhispererCommands = Commands.declare({ id: listCodeWhispererCommandsId }, () => () => { + telemetry.ui_click.emit({ elementId: 'cw_statusBarMenu' }) + Commands.tryExecute('aws.amazonq.refreshAnnotation', true) + .then() + .catch((e) => { + getLogger().debug( + `codewhisperer: running into error while executing command { refreshAnnotation } on user clicking statusbar: ${e}` + ) + }) + return createQuickPick(getQuickPickItems(), { + title: 'Amazon Q', + buttons: [createExitButton()], + ignoreFocusOut: false, + }).prompt() +}) + +/** + * Does what {@link listCodeWhispererCommands} does, must only be used by the walkthrough for telemetry + * purposes. + */ +export const listCodeWhispererCommandsWalkthrough = Commands.declare( + `_aws.amazonq.walkthrough.listCommands`, + () => async () => { + await listCodeWhispererCommands.execute() + } +) diff --git a/packages/core/src/codewhisperer/util/authUtil.ts b/packages/core/src/codewhisperer/util/authUtil.ts new file mode 100644 index 00000000000..e5177e7b578 --- /dev/null +++ b/packages/core/src/codewhisperer/util/authUtil.ts @@ -0,0 +1,587 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as localizedText from '../../shared/localizedText' +import { Auth } from '../../auth/auth' +import { ToolkitError, isNetworkError, tryRun } from '../../shared/errors' +import { getSecondaryAuth, setScopes } from '../../auth/secondaryAuth' +import { isSageMaker } from '../../shared/extensionUtilities' +import { AmazonQPromptSettings } from '../../shared/settings' +import { + scopesCodeWhispererCore, + createBuilderIdProfile, + hasScopes, + SsoConnection, + createSsoProfile, + Connection, + isIamConnection, + isSsoConnection, + isBuilderIdConnection, + scopesCodeWhispererChat, + scopesFeatureDev, + scopesGumby, + isIdcSsoConnection, + hasExactScopes, + getTelemetryMetadataForConn, + ProfileNotFoundError, +} from '../../auth/connection' +import { getLogger } from '../../shared/logger/logger' +import { Commands, placeholder } from '../../shared/vscode/commands2' +import { vsCodeState } from '../models/model' +import { onceChanged, once } from '../../shared/utilities/functionUtils' +import { indent } from '../../shared/utilities/textUtilities' +import { showReauthenticateMessage } from '../../shared/utilities/messages' +import { showAmazonQWalkthroughOnce } from '../../amazonq/onboardingPage/walkthrough' +import { setContext } from '../../shared/vscode/setContext' +import { isInDevEnv } from '../../shared/vscode/env' +import { openUrl } from '../../shared/utilities/vsCodeUtils' +import * as nls from 'vscode-nls' +const localize = nls.loadMessageBundle() +import { telemetry } from '../../shared/telemetry/telemetry' +import { asStringifiedStack } from '../../shared/telemetry/spans' +import { withTelemetryContext } from '../../shared/telemetry/util' +import { focusAmazonQPanel } from '../../codewhispererChat/commands/registerCommands' +import { throttle } from 'lodash' +import { RegionProfileManager } from '../region/regionProfileManager' + +/** Backwards compatibility for connections w pre-chat scopes */ +export const codeWhispererCoreScopes = [...scopesCodeWhispererCore] +export const codeWhispererChatScopes = [...codeWhispererCoreScopes, ...scopesCodeWhispererChat] +export const amazonQScopes = [...codeWhispererChatScopes, ...scopesGumby, ...scopesFeatureDev] + +/** + * "Core" are the CW scopes that existed before the addition of new scopes + * for Amazon Q. + */ +export const isValidCodeWhispererCoreConnection = (conn?: Connection): conn is Connection => { + return ( + (isSageMaker() && isIamConnection(conn)) || (isSsoConnection(conn) && hasScopes(conn, codeWhispererCoreScopes)) + ) +} +/** Superset that includes all of CodeWhisperer + Amazon Q */ +export const isValidAmazonQConnection = (conn?: Connection): conn is Connection => { + return ( + (isSageMaker() && isIamConnection(conn)) || + ((isSsoConnection(conn) || isBuilderIdConnection(conn)) && + isValidCodeWhispererCoreConnection(conn) && + hasScopes(conn, amazonQScopes)) + ) +} + +const authClassName = 'AuthQ' + +export class AuthUtil { + static #instance: AuthUtil + protected static readonly logIfChanged = onceChanged((s: string) => getLogger().info(s)) + + private reauthenticatePromptShown: boolean = false + private _isCustomizationFeatureEnabled: boolean = false + + // user should only see that screen once. + // TODO: move to memento + public hasAlreadySeenMigrationAuthScreen: boolean = false + + public get isCustomizationFeatureEnabled(): boolean { + return this._isCustomizationFeatureEnabled + } + + // This boolean controls whether the Select Customization node will be visible. A change to this value + // means that the old UX was wrong and must refresh the devTool tree. + public set isCustomizationFeatureEnabled(value: boolean) { + if (this._isCustomizationFeatureEnabled === value) { + return + } + this._isCustomizationFeatureEnabled = value + void Commands.tryExecute('aws.amazonq.refreshStatusBar') + } + + public readonly secondaryAuth = getSecondaryAuth( + this.auth, + 'codewhisperer', + 'Amazon Q', + isValidCodeWhispererCoreConnection + ) + public readonly restore = () => this.secondaryAuth.restoreConnection() + + public constructor( + public readonly auth = Auth.instance, + public readonly regionProfileManager = new RegionProfileManager(() => this.conn) + ) {} + + public initCodeWhispererHooks = once(() => { + this.auth.onDidChangeConnectionState(async (e) => { + getLogger().info(`codewhisperer: connection changed to ${e.state}: ${e.id}`) + if (e.state !== 'authenticating') { + await this.refreshCodeWhisperer() + } + + await this.setVscodeContextProps() + }) + + this.secondaryAuth.onDidChangeActiveConnection(async () => { + getLogger().info(`codewhisperer: active connection changed`) + if (this.isValidEnterpriseSsoInUse()) { + void vscode.commands.executeCommand('aws.amazonq.notifyNewCustomizations') + await this.regionProfileManager.restoreProfileSelection() + } + vsCodeState.isFreeTierLimitReached = false + await Promise.all([ + // onDidChangeActiveConnection may trigger before these modules are activated. + Commands.tryExecute('aws.amazonq.refreshStatusBar'), + Commands.tryExecute('aws.amazonq.updateReferenceLog'), + ]) + + await this.setVscodeContextProps() + + // To check valid connection + if (this.isValidEnterpriseSsoInUse() || (this.isBuilderIdInUse() && !this.isConnectionExpired())) { + await showAmazonQWalkthroughOnce() + } + + if (!this.isConnected()) { + await this.regionProfileManager.invalidateProfile(this.regionProfileManager.activeRegionProfile?.arn) + await this.regionProfileManager.clearCache() + } + }) + + this.regionProfileManager.onDidChangeRegionProfile(async () => { + await this.setVscodeContextProps() + }) + }) + + public async setVscodeContextProps() { + // if users are "pending profile selection", they're not fully connected and require profile selection for Q usage + // requireProfileSelection() always returns false for builderID users + await setContext('aws.codewhisperer.connected', this.isConnected() && !this.requireProfileSelection()) + const doShowAmazonQLoginView = + !this.isConnected() || this.isConnectionExpired() || this.requireProfileSelection() + await setContext('aws.amazonq.showLoginView', doShowAmazonQLoginView) + await setContext('aws.codewhisperer.connectionExpired', this.isConnectionExpired()) + await setContext('aws.amazonq.connectedSsoIdc', isIdcSsoConnection(this.conn)) + } + + public reformatStartUrl(startUrl: string | undefined) { + return !startUrl ? undefined : startUrl.replace(/[\/#]+$/g, '') + } + + // current active cwspr connection + public get conn() { + return this.secondaryAuth.activeConnection + } + + // TODO: move this to the shared auth.ts + public get startUrl(): string | undefined { + // Reformat the url to remove any trailing '/' and `#` + // e.g. https://view.awsapps.com/start/# will become https://view.awsapps.com/start + return isSsoConnection(this.conn) ? this.reformatStartUrl(this.conn?.startUrl) : undefined + } + + public get isUsingSavedConnection() { + return this.conn !== undefined && this.secondaryAuth.hasSavedConnection + } + + public isConnected(): boolean { + return this.conn !== undefined + } + + public isEnterpriseSsoInUse(): boolean { + const conn = this.conn + // we have an sso that isn't builder id, must be IdC by process of elimination + const isUsingEnterpriseSso = conn?.type === 'sso' && !isBuilderIdConnection(conn) + return conn !== undefined && isUsingEnterpriseSso + } + + // If there is an active SSO connection + public isValidEnterpriseSsoInUse(): boolean { + return this.isEnterpriseSsoInUse() && !this.isConnectionExpired() + } + + public isBuilderIdInUse(): boolean { + return this.conn !== undefined && isBuilderIdConnection(this.conn) + } + + @withTelemetryContext({ name: 'connectToAwsBuilderId', class: authClassName }) + public async connectToAwsBuilderId(): Promise { + let conn = (await this.auth.listConnections()).find(isBuilderIdConnection) + + if (!conn) { + conn = await this.auth.createConnection(createBuilderIdProfile(amazonQScopes)) + } else if (!isValidAmazonQConnection(conn)) { + conn = await this.secondaryAuth.addScopes(conn, amazonQScopes) + } + + if (this.auth.getConnectionState(conn) === 'invalid') { + conn = await this.auth.reauthenticate(conn) + } + + return (await this.secondaryAuth.useNewConnection(conn)) as SsoConnection + } + + @withTelemetryContext({ name: 'connectToEnterpriseSso', class: authClassName }) + public async connectToEnterpriseSso(startUrl: string, region: string): Promise { + let conn = (await this.auth.listConnections()).find( + (conn): conn is SsoConnection => + isSsoConnection(conn) && conn.startUrl.toLowerCase() === startUrl.toLowerCase() + ) + + if (!conn) { + conn = await this.auth.createConnection(createSsoProfile(startUrl, region, amazonQScopes)) + } else if (!isValidAmazonQConnection(conn)) { + conn = await this.secondaryAuth.addScopes(conn, amazonQScopes) + } + + if (this.auth.getConnectionState(conn) === 'invalid') { + conn = await this.auth.reauthenticate(conn) + } + + return (await this.secondaryAuth.useNewConnection(conn)) as SsoConnection + } + + public static get instance() { + if (this.#instance !== undefined) { + return this.#instance + } + + const self = (this.#instance = new this()) + return self + } + + @withTelemetryContext({ name: 'getBearerToken', class: authClassName }) + public async getBearerToken(): Promise { + await this.restore() + + if (this.conn === undefined) { + throw new ToolkitError('No connection found', { code: 'NoConnection' }) + } + + if (!isSsoConnection(this.conn)) { + throw new ToolkitError('Connection is not an SSO connection', { code: 'BadConnectionType' }) + } + + try { + const bearerToken = await this.conn.getToken() + return bearerToken.accessToken + } catch (err) { + if (err instanceof ProfileNotFoundError) { + // Expected that connection would be deleted by conn.getToken() + focusAmazonQPanel.execute(placeholder, 'profileNotFoundSignout').catch((e) => { + getLogger().error('focusAmazonQPanel failed: %s', e) + }) + } + throw err + } + } + + @withTelemetryContext({ name: 'getCredentials', class: authClassName }) + public async getCredentials() { + await this.restore() + + if (this.conn === undefined) { + throw new ToolkitError('No connection found', { code: 'NoConnection' }) + } + + if (!isIamConnection(this.conn)) { + throw new ToolkitError('Connection is not an IAM connection', { code: 'BadConnectionType' }) + } + + return this.conn.getCredentials() + } + + public isConnectionValid(log: boolean = true): boolean { + const connectionValid = this.conn !== undefined && !this.secondaryAuth.isConnectionExpired + + if (log) { + this.logConnection() + } + + return connectionValid + } + + public isConnectionExpired(log: boolean = true): boolean { + const connectionExpired = + this.secondaryAuth.isConnectionExpired && + this.conn !== undefined && + isValidCodeWhispererCoreConnection(this.conn) + + if (log) { + this.logConnection() + } + + return connectionExpired + } + + requireProfileSelection(): boolean { + if (isBuilderIdConnection(this.conn)) { + return false + } + return isIdcSsoConnection(this.conn) && this.regionProfileManager.activeRegionProfile === undefined + } + + private logConnection() { + const logStr = indent( + `codewhisperer: connection states + connection isValid=${this.isConnectionValid(false)}, + connection isValidCodewhispererCoreConnection=${isValidCodeWhispererCoreConnection(this.conn)}, + connection isExpired=${this.isConnectionExpired(false)}, + secondaryAuth isExpired=${this.secondaryAuth.isConnectionExpired}, + connection isUndefined=${this.conn === undefined}`, + 4, + true + ) + + AuthUtil.logIfChanged(logStr) + } + + @withTelemetryContext({ name: 'reauthenticate', class: authClassName }) + public async reauthenticate() { + try { + if (this.conn?.type !== 'sso') { + return + } + + if (!hasExactScopes(this.conn, amazonQScopes)) { + const conn = await setScopes(this.conn, amazonQScopes, this.auth) + await this.secondaryAuth.useNewConnection(conn) + } + + await this.auth.reauthenticate(this.conn) + } catch (err) { + throw ToolkitError.chain(err, 'Unable to authenticate connection') + } finally { + await this.setVscodeContextProps() + } + } + + public async refreshCodeWhisperer() { + vsCodeState.isFreeTierLimitReached = false + await Commands.tryExecute('aws.amazonq.refreshStatusBar') + } + + @withTelemetryContext({ name: 'showReauthenticatePrompt', class: authClassName }) + public async showReauthenticatePrompt(isAutoTrigger?: boolean) { + if (isAutoTrigger && this.reauthenticatePromptShown) { + return + } + + await showReauthenticateMessage({ + message: localizedText.connectionExpired('Amazon Q'), + connect: localizedText.reauthenticate, + suppressId: 'codeWhispererConnectionExpired', + settings: AmazonQPromptSettings.instance, + reauthFunc: async () => { + await this.reauthenticate() + }, + }) + + if (isAutoTrigger) { + this.reauthenticatePromptShown = true + } + } + + public async notifySessionConfiguration() { + const suppressId = 'amazonQSessionConfigurationMessage' + const settings = AmazonQPromptSettings.instance + const shouldShow = settings.isPromptEnabled(suppressId) + if (!shouldShow) { + return + } + + const message = localize( + 'aws.amazonq.sessionConfiguration.message', + 'Your maximum session length for Amazon Q can be extended to 90 days by your administrator. For more information, refer to How to extend the session duration for Amazon Q in the IDE in the IAM Identity Center User Guide.' + ) + + const learnMoreUrl = vscode.Uri.parse( + 'https://docs.aws.amazon.com/singlesignon/latest/userguide/configure-user-session.html#90-day-extended-session-duration' + ) + await telemetry.toolkit_showNotification.run(async () => { + telemetry.record({ id: 'sessionExtension' }) + void vscode.window.showInformationMessage(message, localizedText.learnMore).then(async (resp) => { + await telemetry.toolkit_invokeAction.run(async () => { + if (resp === localizedText.learnMore) { + telemetry.record({ action: 'learnMore' }) + await openUrl(learnMoreUrl) + } else { + telemetry.record({ action: 'dismissSessionExtensionNotification' }) + } + await settings.disablePrompt(suppressId) + }) + }) + }) + } + + @withTelemetryContext({ name: 'notifyReauthenticate', class: authClassName }) + public async notifyReauthenticate(isAutoTrigger?: boolean) { + void this.showReauthenticatePrompt(isAutoTrigger) + await this.setVscodeContextProps() + } + + public isValidCodeTransformationAuthUser(): boolean { + return (this.isEnterpriseSsoInUse() || this.isBuilderIdInUse()) && this.isConnectionValid() + } + + /** + * Asynchronously returns a snapshot of the overall auth state of CodeWhisperer + Chat features. + * It guarantees the latest state is correct at the risk of modifying connection state. + * If this guarantee is not required, use sync method getChatAuthStateSync() + * + * By default, network errors are ignored when determining auth state since they may be silently + * recoverable later. + * + * THROTTLE: This function is called in rapid succession by Amazon Q features and can lead to + * a barrage of disk access and/or token refreshes. We throttle to deal with this. + * + * Note we do an explicit cast of the return type due to Lodash types incorrectly indicating + * a FeatureAuthState or undefined can be returned. But since we set `leading: true` + * it will always return FeatureAuthState + */ + public getChatAuthState = throttle(() => this._getChatAuthState(), 2000, { + leading: true, + }) as () => Promise + /** + * IMPORTANT: Only use this if you do NOT want to swallow network errors, otherwise use {@link getChatAuthState()} + * @param ignoreNetErr swallows network errors + */ + @withTelemetryContext({ name: 'getChatAuthState', class: authClassName }) + public async _getChatAuthState(ignoreNetErr: boolean = true): Promise { + // The state of the connection may not have been properly validated + // and the current state we see may be stale, so refresh for latest state. + if (ignoreNetErr) { + await tryRun( + () => this.auth.refreshConnectionState(this.conn), + (err) => !isNetworkError(err), + 'getChatAuthState: Cannot refresh connection state due to network error: %s' + ) + } else { + await this.auth.refreshConnectionState(this.conn) + } + + return this.getChatAuthStateSync(this.conn) + } + + /** + * Synchronously returns a snapshot of the overall auth state of CodeWhisperer + Chat features without + * validating or modifying the connection state. It is possible that the connection + * is invalid/valid, but the current state displays something else. To guarantee the true state, + * use async method getChatAuthState() + */ + public getChatAuthStateSync(conn = this.conn): FeatureAuthState { + if (conn === undefined) { + return buildFeatureAuthState(AuthStates.disconnected) + } + + if (!isSsoConnection(conn) && !isSageMaker()) { + throw new ToolkitError(`Connection "${conn.id}" is not a valid type: ${conn.type}`) + } + + // default to expired to indicate reauth is needed if unmodified + const state: FeatureAuthState = buildFeatureAuthState(AuthStates.expired) + + if (this.isConnectionExpired()) { + return state + } + + if (isBuilderIdConnection(conn) || isIdcSsoConnection(conn) || isSageMaker()) { + // TODO: refactor + if (isValidCodeWhispererCoreConnection(conn)) { + if (this.requireProfileSelection()) { + state[Features.codewhispererCore] = AuthStates.pendingProfileSelection + } else { + state[Features.codewhispererCore] = AuthStates.connected + } + } + if (isValidAmazonQConnection(conn)) { + if (this.requireProfileSelection()) { + for (const v of Object.values(Features)) { + state[v as Feature] = AuthStates.pendingProfileSelection + } + } else { + for (const v of Object.values(Features)) { + state[v as Feature] = AuthStates.connected + } + } + } + } + + return state + } + + /** + * Edge Case: Due to a change in behaviour/functionality, there are potential extra + * auth connections that the Amazon Q extension has cached. We need to remove these + * as they are irrelevant to the Q extension and can cause issues. + */ + public async clearExtraConnections(): Promise { + const currentQConn = this.conn + // Q currently only maintains 1 connection at a time, so we assume everything else is extra. + // IMPORTANT: In the case Q starts to manage multiple connections, this implementation will need to be updated. + const allOtherConnections = (await this.auth.listConnections()).filter((c) => c.id !== currentQConn?.id) + for (const conn of allOtherConnections) { + getLogger().warn(`forgetting extra amazon q connection: %O`, conn) + await telemetry.auth_modifyConnection.run( + async () => { + telemetry.record({ + connectionState: Auth.instance.getConnectionState(conn) ?? 'undefined', + source: asStringifiedStack(telemetry.getFunctionStack()), + ...(await getTelemetryMetadataForConn(conn)), + }) + + if (isInDevEnv()) { + telemetry.record({ action: 'forget' }) + // in a Dev Env the connection may be used by code catalyst, so we forget instead of fully deleting + await this.auth.forgetConnection(conn) + } else { + telemetry.record({ action: 'delete' }) + await this.auth.deleteConnection(conn) + } + }, + { functionId: { name: 'clearExtraConnections', class: authClassName } } + ) + } + } +} + +export type FeatureAuthState = { [feature in Feature]: AuthState } +export type Feature = (typeof Features)[keyof typeof Features] +export type AuthState = (typeof AuthStates)[keyof typeof AuthStates] + +export const AuthStates = { + /** The current connection is working and supports this feature. */ + connected: 'connected', + /** No connection exists, so this feature cannot be used*/ + disconnected: 'disconnected', + /** + * The current connection exists, but needs to be reauthenticated for this feature to work + * + * Look to use {@link AuthUtil.reauthenticate()} + */ + expired: 'expired', + /** + * A connection exists, but does not support this feature. + * + * Eg: We are currently using Builder ID, but must use Identity Center. + */ + unsupported: 'unsupported', + /** + * The current connection exists and isn't expired, + * but fetching/refreshing the token resulted in a network error. + */ + connectedWithNetworkError: 'connectedWithNetworkError', + pendingProfileSelection: 'pendingProfileSelection', +} as const +const Features = { + codewhispererCore: 'codewhispererCore', + codewhispererChat: 'codewhispererChat', + amazonQ: 'amazonQ', +} as const + +function buildFeatureAuthState(state: AuthState): FeatureAuthState { + return { + codewhispererCore: state, + codewhispererChat: state, + amazonQ: state, + } +} diff --git a/packages/core/src/codewhisperer/util/closingBracketUtil.ts b/packages/core/src/codewhisperer/util/closingBracketUtil.ts new file mode 100644 index 00000000000..4892c5694b4 --- /dev/null +++ b/packages/core/src/codewhisperer/util/closingBracketUtil.ts @@ -0,0 +1,263 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * Reference: https://github.com/aws/aws-toolkit-vscode/blob/amazonq/v1.74.0/packages/core/src/codewhisperer/util/closingBracketUtil.ts + */ + +import * as vscode from 'vscode' +import * as CodeWhispererConstants from '../models/constants' + +interface bracketMapType { + [k: string]: string +} + +const quotes = ["'", '"', '`'] +const parenthesis = ['(', '[', '{', ')', ']', '}', '<', '>'] + +const closeToOpen: bracketMapType = { + ')': '(', + ']': '[', + '}': '{', + '>': '<', +} + +const openToClose: bracketMapType = { + '(': ')', + '[': ']', + '{': '}', + '<': '>', +} + +/** + * LeftContext | Recommendation | RightContext + * This function aims to resolve symbols which are redundant and need to be removed + * The high level logic is as followed + * 1. Pair non-paired closing symbols(parenthesis, brackets, quotes) existing in the "recommendation" with non-paired symbols existing in the "leftContext" + * 2. Remove non-paired closing symbols existing in the "rightContext" + * @param endPosition: end position of the effective recommendation written by CodeWhisperer + * @param startPosition: start position of the effective recommendation by CodeWhisperer + * + * for example given file context ('|' is where we trigger the service): + * anArray.pu| + * recommendation returned: "sh(element);" + * typeahead: "sh(" + * the effective recommendation written by CodeWhisperer: "element);" + */ +export async function handleExtraBrackets( + editor: vscode.TextEditor, + endPosition: vscode.Position, + startPosition: vscode.Position +) { + const recommendation = editor.document.getText(new vscode.Range(startPosition, endPosition)) + const endOffset = editor.document.offsetAt(endPosition) + const startOffset = editor.document.offsetAt(startPosition) + const leftContext = editor.document.getText( + new vscode.Range( + startPosition, + editor.document.positionAt(Math.max(startOffset - CodeWhispererConstants.charactersLimit, 0)) + ) + ) + + const rightContext = editor.document.getText( + new vscode.Range( + editor.document.positionAt(endOffset), + editor.document.positionAt(endOffset + CodeWhispererConstants.charactersLimit) + ) + ) + const bracketsToRemove = getBracketsToRemove( + editor, + recommendation, + leftContext, + rightContext, + endPosition, + startPosition + ) + + const quotesToRemove = getQuotesToRemove( + editor, + recommendation, + leftContext, + rightContext, + endPosition, + startPosition + ) + + const symbolsToRemove = [...bracketsToRemove, ...quotesToRemove] + + if (symbolsToRemove.length) { + await removeBracketsFromRightContext(editor, symbolsToRemove, endPosition) + } +} + +const removeBracketsFromRightContext = async ( + editor: vscode.TextEditor, + idxToRemove: number[], + endPosition: vscode.Position +) => { + const offset = editor.document.offsetAt(endPosition) + + await editor.edit( + (editBuilder) => { + for (const idx of idxToRemove) { + const range = new vscode.Range( + editor.document.positionAt(offset + idx), + editor.document.positionAt(offset + idx + 1) + ) + editBuilder.delete(range) + } + }, + { undoStopAfter: false, undoStopBefore: false } + ) +} + +function getBracketsToRemove( + editor: vscode.TextEditor, + recommendation: string, + leftContext: string, + rightContext: string, + end: vscode.Position, + start: vscode.Position +) { + const unpairedClosingsInReco = nonClosedClosingParen(recommendation) + const unpairedOpeningsInLeftContext = nonClosedOpneingParen(leftContext, unpairedClosingsInReco.length) + const unpairedClosingsInRightContext = nonClosedClosingParen(rightContext) + + const toRemove: number[] = [] + + let i = 0 + let j = 0 + let k = 0 + while (i < unpairedOpeningsInLeftContext.length && j < unpairedClosingsInReco.length) { + const opening = unpairedOpeningsInLeftContext[i] + const closing = unpairedClosingsInReco[j] + + const isPaired = closeToOpen[closing.char] === opening.char + const rightContextCharToDelete = unpairedClosingsInRightContext[k] + + if (isPaired) { + if (rightContextCharToDelete && rightContextCharToDelete.char === closing.char) { + const rightContextStart = editor.document.offsetAt(end) + 1 + const symbolPosition = editor.document.positionAt( + rightContextStart + rightContextCharToDelete.strOffset + ) + const lineCnt = recommendation.split('\n').length - 1 + const isSameline = symbolPosition.line - lineCnt === start.line + + if (isSameline) { + toRemove.push(rightContextCharToDelete.strOffset) + } + + k++ + } + } + + i++ + j++ + } + + return toRemove +} + +function getQuotesToRemove( + editor: vscode.TextEditor, + recommendation: string, + leftContext: string, + rightContext: string, + endPosition: vscode.Position, + startPosition: vscode.Position +) { + let leftQuote: string | undefined = undefined + let leftIndex: number | undefined = undefined + for (let i = leftContext.length - 1; i >= 0; i--) { + const char = leftContext[i] + if (quotes.includes(char)) { + leftQuote = char + leftIndex = leftContext.length - i + break + } + } + + let rightQuote: string | undefined = undefined + let rightIndex: number | undefined = undefined + for (let i = 0; i < rightContext.length; i++) { + const char = rightContext[i] + if (quotes.includes(char)) { + rightQuote = char + rightIndex = i + break + } + } + + let quoteCountInReco = 0 + if (leftQuote && rightQuote && leftQuote === rightQuote) { + for (const char of recommendation) { + if (quotes.includes(char) && char === leftQuote) { + quoteCountInReco++ + } + } + } + + if (leftIndex !== undefined && rightIndex !== undefined && quoteCountInReco % 2 !== 0) { + const p = editor.document.positionAt(editor.document.offsetAt(endPosition) + rightIndex) + + if (endPosition.line === startPosition.line && endPosition.line === p.line) { + return [rightIndex] + } + } + + return [] +} + +function nonClosedOpneingParen(str: string, cnt?: number): { char: string; strOffset: number }[] { + const resultSet: { char: string; strOffset: number }[] = [] + const stack: string[] = [] + + for (let i = str.length - 1; i >= 0; i--) { + const char = str[i] + if (char! in parenthesis) { + continue + } + + if (char in closeToOpen) { + stack.push(char) + if (cnt && cnt === resultSet.length) { + return resultSet + } + } else if (char in openToClose) { + if (stack.length !== 0 && stack[stack.length - 1] === openToClose[char]) { + stack.pop() + } else { + resultSet.push({ char: char, strOffset: i }) + } + } + } + + return resultSet +} + +function nonClosedClosingParen(str: string, cnt?: number): { char: string; strOffset: number }[] { + const resultSet: { char: string; strOffset: number }[] = [] + const stack: string[] = [] + + for (let i = 0; i < str.length; i++) { + const char = str[i] + if (char! in parenthesis) { + continue + } + + if (char in openToClose) { + stack.push(char) + if (cnt && cnt === resultSet.length) { + return resultSet + } + } else if (char in closeToOpen) { + if (stack.length !== 0 && stack[stack.length - 1] === closeToOpen[char]) { + stack.pop() + } else { + resultSet.push({ char: char, strOffset: i }) + } + } + } + + return resultSet +} diff --git a/packages/core/src/codewhisperer/util/codeWhispererApplication.ts b/packages/core/src/codewhisperer/util/codeWhispererApplication.ts new file mode 100644 index 00000000000..e9e4947d9e1 --- /dev/null +++ b/packages/core/src/codewhisperer/util/codeWhispererApplication.ts @@ -0,0 +1,19 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import vscode from 'vscode' + +class CodeWhispererApplication { + static #instance: CodeWhispererApplication + + readonly _clearCodeWhispererUIListener: vscode.EventEmitter = new vscode.EventEmitter() + public readonly clearCodeWhispererUIListener: vscode.Event = this._clearCodeWhispererUIListener.event + + public static get instance() { + return (this.#instance ??= new CodeWhispererApplication()) + } +} + +export const application = () => CodeWhispererApplication.instance diff --git a/packages/core/src/codewhisperer/util/codeWhispererSession.ts b/packages/core/src/codewhisperer/util/codeWhispererSession.ts new file mode 100644 index 00000000000..4a529941004 --- /dev/null +++ b/packages/core/src/codewhisperer/util/codeWhispererSession.ts @@ -0,0 +1,126 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { + CodewhispererCompletionType, + CodewhispererLanguage, + CodewhispererGettingStartedTask, + CodewhispererAutomatedTriggerType, + CodewhispererTriggerType, +} from '../../shared/telemetry/telemetry.gen' +import { GenerateRecommendationsRequest, ListRecommendationsRequest, Recommendation } from '../client/codewhisperer' +import { Position } from 'vscode' +import { CodeWhispererSupplementalContext, vsCodeState } from '../models/model' +import { FileDiagnostic, getDiagnosticsOfCurrentFile } from './diagnosticsUtil' + +class CodeWhispererSession { + static #instance: CodeWhispererSession + + // Per-session states + sessionId = '' + requestIdList: string[] = [] + startPos = new Position(0, 0) + startCursorOffset = 0 + leftContextOfCurrentLine = '' + requestContext: { + request: ListRecommendationsRequest | GenerateRecommendationsRequest + supplementalMetadata: CodeWhispererSupplementalContext | undefined + } = { request: {} as any, supplementalMetadata: {} as any } + language: CodewhispererLanguage = 'python' + taskType: CodewhispererGettingStartedTask | undefined + triggerType: CodewhispererTriggerType = 'OnDemand' + autoTriggerType: CodewhispererAutomatedTriggerType | undefined + + // Various states of recommendations + recommendations: Recommendation[] = [] + suggestionStates = new Map() + completionTypes = new Map() + + // Some other variables for client component latency + fetchCredentialStartTime = 0 + sdkApiCallStartTime = 0 + invokeSuggestionStartTime = 0 + preprocessEndTime = 0 + timeToFirstRecommendation = 0 + firstSuggestionShowTime = 0 + perceivedLatency = 0 + diagnosticsBeforeAccept: FileDiagnostic | undefined = undefined + + public static get instance() { + return (this.#instance ??= new CodeWhispererSession()) + } + + setFetchCredentialStart() { + if (this.fetchCredentialStartTime === 0 && this.invokeSuggestionStartTime !== 0) { + this.fetchCredentialStartTime = Date.now() + } + } + + setSdkApiCallStart() { + if (this.sdkApiCallStartTime === 0 && this.fetchCredentialStartTime !== 0) { + this.sdkApiCallStartTime = Date.now() + } + } + + setTimeToFirstRecommendation(timeToFirstRecommendation: number) { + if (this.invokeSuggestionStartTime) { + this.timeToFirstRecommendation = timeToFirstRecommendation - this.invokeSuggestionStartTime + } + this.diagnosticsBeforeAccept = getDiagnosticsOfCurrentFile() + } + + setSuggestionState(index: number, value: string) { + this.suggestionStates.set(index, value) + } + + getSuggestionState(index: number): string | undefined { + return this.suggestionStates.get(index) + } + + setCompletionType(index: number, recommendation: Recommendation) { + const nonBlankLines = recommendation.content.split('\n').filter((line) => line.trim() !== '').length + this.completionTypes.set(index, nonBlankLines > 1 ? 'Block' : 'Line') + } + + getCompletionType(index: number): CodewhispererCompletionType { + return this.completionTypes.get(index) || 'Line' + } + + getPerceivedLatency(triggerType: CodewhispererTriggerType) { + if (triggerType === 'OnDemand') { + return this.timeToFirstRecommendation + } else { + return session.firstSuggestionShowTime - vsCodeState.lastUserModificationTime + } + } + + setPerceivedLatency() { + if (this.perceivedLatency !== 0) { + return + } + if (this.triggerType === 'OnDemand') { + this.perceivedLatency = this.timeToFirstRecommendation + } else { + this.perceivedLatency = this.firstSuggestionShowTime - vsCodeState.lastUserModificationTime + } + } + + reset() { + this.sessionId = '' + this.requestContext = { request: {} as any, supplementalMetadata: {} as any } + this.requestIdList = [] + this.startPos = new Position(0, 0) + this.startCursorOffset = 0 + this.leftContextOfCurrentLine = '' + this.language = 'python' + this.triggerType = 'OnDemand' + this.recommendations = [] + this.suggestionStates.clear() + this.completionTypes.clear() + this.diagnosticsBeforeAccept = undefined + } +} + +// TODO: convert this to a function call +export const session = CodeWhispererSession.instance diff --git a/packages/core/src/codewhisperer/util/codewhispererSettings.ts b/packages/core/src/codewhisperer/util/codewhispererSettings.ts new file mode 100644 index 00000000000..80f5d1f2a0d --- /dev/null +++ b/packages/core/src/codewhisperer/util/codewhispererSettings.ts @@ -0,0 +1,102 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { ArrayConstructor } from '../../shared/utilities/typeConstructors' +import { fromExtensionManifest, migrateSetting } from '../../shared/settings' + +const description = { + showCodeWithReferences: Boolean, + importRecommendationForInlineCodeSuggestions: Boolean, // eslint-disable-line id-length + shareContentWithAWS: Boolean, + workspaceIndex: Boolean, + workspaceIndexWorkerThreads: Number, + workspaceIndexUseGPU: Boolean, + workspaceIndexMaxSize: Number, + workspaceIndexMaxFileSize: Number, + workspaceIndexCacheDirPath: String, + workspaceIndexIgnoreFilePatterns: ArrayConstructor(String), + allowFeatureDevelopmentToRunCodeAndTests: Object, + ignoredSecurityIssues: ArrayConstructor(String), +} + +export class CodeWhispererSettings extends fromExtensionManifest('amazonQ', description) { + // TODO: Remove after a few releases + public async importSettings() { + await migrateSetting( + { key: 'amazonQ.showInlineCodeSuggestionsWithCodeReferences', type: Boolean }, + { key: 'amazonQ.showCodeWithReferences' } + ) + } + public isSuggestionsWithCodeReferencesEnabled(): boolean { + return this.get(`showCodeWithReferences`, false) + } + public isImportRecommendationEnabled(): boolean { + return this.get(`importRecommendationForInlineCodeSuggestions`, false) + } + + public isOptoutEnabled(): boolean { + const value = this.get('shareContentWithAWS', true) + return !value + } + public isLocalIndexEnabled(): boolean { + return this.get('workspaceIndex', false) + } + + public async enableLocalIndex() { + await this.update('workspaceIndex', true) + } + + public isLocalIndexGPUEnabled(): boolean { + return this.get('workspaceIndexUseGPU', false) + } + + public getIndexWorkerThreads(): number { + // minimal 0 threads + return Math.max(this.get('workspaceIndexWorkerThreads', 0), 0) + } + + public getMaxIndexSize(): number { + // minimal 1MB + return Math.max(this.get('workspaceIndexMaxSize', 2048), 1) + } + + public getMaxIndexFileSize(): number { + // minimal 1MB + return Math.max(this.get('workspaceIndexMaxFileSize', 10), 1) + } + + public getIndexCacheDirPath(): string { + return this.get('workspaceIndexCacheDirPath', '') + } + + public getIndexIgnoreFilePatterns(): string[] { + return this.get('workspaceIndexIgnoreFilePatterns', []) + } + + public getAutoBuildSetting(): { [key: string]: boolean } { + return this.get('allowFeatureDevelopmentToRunCodeAndTests', {}) + } + + public async updateAutoBuildSetting(projectName: string, setting: boolean) { + const projects = this.getAutoBuildSetting() + + projects[projectName] = setting + + await this.update('allowFeatureDevelopmentToRunCodeAndTests', projects) + } + + public getIgnoredSecurityIssues(): string[] { + return this.get('ignoredSecurityIssues', []) + } + + public async addToIgnoredSecurityIssuesList(issueTitle: string) { + await this.update('ignoredSecurityIssues', [...this.getIgnoredSecurityIssues(), issueTitle]) + } + + static #instance: CodeWhispererSettings + + public static get instance() { + return (this.#instance ??= new this()) + } +} diff --git a/packages/core/src/codewhisperer/util/commonUtil.ts b/packages/core/src/codewhisperer/util/commonUtil.ts new file mode 100644 index 00000000000..d2df78f1369 --- /dev/null +++ b/packages/core/src/codewhisperer/util/commonUtil.ts @@ -0,0 +1,87 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as semver from 'semver' +import { distance } from 'fastest-levenshtein' +import { getInlineSuggestEnabled } from '../../shared/utilities/editorUtilities' +import { + AWSTemplateCaseInsensitiveKeyWords, + AWSTemplateKeyWords, + JsonConfigFileNamingConvention, +} from '../models/constants' + +export function getLocalDatetime() { + const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone + return new Date().toLocaleString([], { timeZone: timezone }) +} + +export function asyncCallWithTimeout(asyncPromise: Promise, message: string, timeLimit: number): Promise { + let timeoutHandle: NodeJS.Timeout + const timeoutPromise = new Promise((_resolve, reject) => { + timeoutHandle = setTimeout(() => reject(new Error(message)), timeLimit) + }) + return Promise.race([asyncPromise, timeoutPromise]).then((result) => { + clearTimeout(timeoutHandle) + return result as T + }) +} + +export function isInlineCompletionEnabled() { + return getInlineSuggestEnabled() +} + +// This is the VS Code version that started to have regressions in inline completion API +export function isVscHavingRegressionInlineCompletionApi() { + return semver.gte(vscode.version, '1.78.0') && getInlineSuggestEnabled() +} + +export function getFileExt(languageId: string) { + switch (languageId) { + case 'java': + return '.java' + case 'python': + return '.py' + default: + break + } + return undefined +} + +/** + * Returns the longest overlap between the Suffix of firstString and Prefix of second string + * getPrefixSuffixOverlap("adwg31", "31ggrs") = "31" + */ +export function getPrefixSuffixOverlap(firstString: string, secondString: string) { + let i = Math.min(firstString.length, secondString.length) + while (i > 0) { + if (secondString.slice(0, i) === firstString.slice(-i)) { + break + } + i-- + } + return secondString.slice(0, i) +} + +export function checkLeftContextKeywordsForJson(fileName: string, leftFileContent: string, language: string): boolean { + if ( + language === 'json' && + !AWSTemplateKeyWords.some((substring) => leftFileContent.includes(substring)) && + !AWSTemplateCaseInsensitiveKeyWords.some((substring) => leftFileContent.toLowerCase().includes(substring)) && + !JsonConfigFileNamingConvention.has(fileName.toLowerCase()) + ) { + return true + } + return false +} + +// With edit distance, complicate usermodification can be considered as simple edit(add, delete, replace), +// and thus the unmodified part of recommendation length can be deducted/approximated +// ex. (modified > original): originalRecom: foo -> modifiedRecom: fobarbarbaro, distance = 9, delta = 12 - 9 = 3 +// ex. (modified == original): originalRecom: helloworld -> modifiedRecom: HelloWorld, distance = 2, delta = 10 - 2 = 8 +// ex. (modified < original): originalRecom: CodeWhisperer -> modifiedRecom: CODE, distance = 12, delta = 13 - 12 = 1 +export function getUnmodifiedAcceptedTokens(origin: string, after: string) { + return Math.max(origin.length, after.length) - distance(origin, after) +} diff --git a/packages/core/src/codewhisperer/util/customizationUtil.ts b/packages/core/src/codewhisperer/util/customizationUtil.ts new file mode 100644 index 00000000000..600317c53e0 --- /dev/null +++ b/packages/core/src/codewhisperer/util/customizationUtil.ts @@ -0,0 +1,457 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import globals from '../../shared/extensionGlobals' +import { customLearnMoreUri, newCustomizationMessage } from '../models/constants' +import { localize, openUrl } from '../../shared/utilities/vsCodeUtils' +import { AuthUtil } from './authUtil' +import * as vscode from 'vscode' +import { createCommonButtons } from '../../shared/ui/buttons' +import { DataQuickPickItem, showQuickPick } from '../../shared/ui/pickerPrompter' +import CodeWhispererUserClient, { Customization, ResourceArn } from '../client/codewhispereruserclient' +import { codicon, getIcon } from '../../shared/icons' +import { getLogger } from '../../shared/logger/logger' +import { showMessageWithUrl } from '../../shared/utilities/messages' +import { parse } from '@aws-sdk/util-arn-parser' +import { Commands } from '../../shared/vscode/commands2' +import { RegionProfile, vsCodeState } from '../models/model' +import { pageableToCollection } from '../../shared/utilities/collectionUtils' +import { isAwsError } from '../../shared/errors' +import { ProfileChangedEvent } from '../region/regionProfileManager' + +export class CustomizationProvider { + readonly region: string + constructor( + private readonly client: CodeWhispererUserClient, + private readonly profile: RegionProfile + ) { + this.region = profile.region + } + + async listAvailableCustomizations(): Promise { + const requester = async (request: CodeWhispererUserClient.ListAvailableCustomizationsRequest) => + this.client.listAvailableCustomizations(request).promise() + + try { + const request = { profileArn: this.profile.arn } + const customizations = await pageableToCollection(requester, request, 'nextToken', 'customizations') + .flatten() + .promise() + + return customizations + } catch (e) { + const logMsg = isAwsError(e) ? `requestId=${e.requestId}; message=${e.message}` : (e as Error).message + getLogger().error(`failed to listAvailableCustomizations: ${logMsg}`) + return [] + } + } + + static async init(profile: RegionProfile): Promise { + const client = await AuthUtil.instance.regionProfileManager.createQClient(profile) + return new CustomizationProvider(client, profile) + } +} + +export const onProfileChangedListener: (event: ProfileChangedEvent) => any = async (event) => { + // Skip because customization means the following validation has been done + if (event.intent === 'customization') { + return + } + const logger = getLogger() + if (!event.profile) { + await setSelectedCustomization(baseCustomization) + return + } + + // Validate user still has access to the selected customization. + const selectedCustomization = getSelectedCustomization() + // No need to validate base customization which has empty arn. + if (selectedCustomization.arn.length > 0) { + const customizationProvider = await CustomizationProvider.init(event.profile) + const customizations = await customizationProvider.listAvailableCustomizations() + + const r = customizations.find((it) => it.arn === selectedCustomization.arn) + if (!r) { + logger.debug( + `profile ${event.profile.name} doesnt have access to customization ${selectedCustomization.name} but has access to ${customizations.map((it) => it.name)}` + ) + await switchToBaseCustomizationAndNotify() + } + } +} + +/** + * + * @param availableCustomizations + * @returns customization diff of availableCustomizations vs. persisted customizations + */ +export const getNewCustomizations = (availableCustomizations: Customization[]) => { + const persistedCustomizations = getPersistedCustomizations() + return availableCustomizations.filter((c) => !persistedCustomizations.map((p) => p.arn).includes(c.arn)) +} + +export async function notifyNewCustomizations() { + let availableCustomizations: Customization[] = [] + try { + availableCustomizations = await getAvailableCustomizationsList() + AuthUtil.instance.isCustomizationFeatureEnabled = true + } catch (error) { + // On receiving any error, we will disable the customization feature + AuthUtil.instance.isCustomizationFeatureEnabled = false + await setSelectedCustomization(baseCustomization) + getLogger().error(`Failed to fetch customizations: %O`, error) + return + } + + const selectedCustomization = getSelectedCustomization() + if (!isSelectedCustomizationAvailable(availableCustomizations, selectedCustomization)) { + await switchToBaseCustomizationAndNotify() + } + + const newCustomizations = getNewCustomizations(availableCustomizations) + await setPersistedCustomizations(availableCustomizations) + + if (newCustomizations.length === 0) { + return + } + + await setNewCustomizationsAvailable(newCustomizations.length) + + const select = localize( + 'AWS.codewhisperer.customization.notification.new_customizations.select', + 'Select Customization' + ) + const learnMore = localize( + 'AWS.codewhisperer.customization.notification.new_customizations.learn_more', + 'Learn More' + ) + void vscode.window.showInformationMessage(newCustomizationMessage, select, learnMore).then(async (resp) => { + if (resp === select) { + showCustomizationPrompt().catch((e) => { + getLogger().error('showCustomizationPrompt failed: %s', (e as Error).message) + }) + } else if (resp === learnMore) { + // TODO: figure out the right uri + void openUrl(vscode.Uri.parse(customLearnMoreUri)) + } + }) +} + +// Return true when either it's the default option or the selected one is in the ones we fetched from upstream. +export const isSelectedCustomizationAvailable = (available: Customization[], selected: Customization) => { + return selected.arn === '' || available.map((c) => c.arn).includes(selected.arn) +} + +export const baseCustomization = { + arn: '', + name: localize('AWS.codewhisperer.customization.base.label', 'Amazon Q foundation (Default)'), + description: localize( + 'AWS.codewhisperer.customization.base.detail', + 'Receive suggestions from Amazon Q base model' + ), +} + +/** + * @returns customization selected by users, `baseCustomization` if none is selected + */ +export const getSelectedCustomization = (): Customization => { + if ( + !AuthUtil.instance.isCustomizationFeatureEnabled || + !AuthUtil.instance.isValidEnterpriseSsoInUse() || + !AuthUtil.instance.conn + ) { + return baseCustomization + } + + const selectedCustomizationArr = globals.globalState.tryGet<{ [label: string]: Customization }>( + 'CODEWHISPERER_SELECTED_CUSTOMIZATION', + Object, + {} + ) + const selectedCustomization = selectedCustomizationArr[AuthUtil.instance.conn.label] + + if (selectedCustomization && selectedCustomization.name !== '') { + return selectedCustomization + } else { + return baseCustomization + } +} + +/** + * @param customization customization to select + * @param isOverride if the API call is made from us (Q) but not users' intent, set isOverride to TRUE + * Override happens when ALL following conditions are met + * 1. service returns non-empty override customization arn, refer to [featureConfig.ts] + * 2. the override customization arn is different from the previous override customization if any. The purpose is to only do override once on users' behalf. + */ +export const setSelectedCustomization = async (customization: Customization, isOverride: boolean = false) => { + if (!AuthUtil.instance.isValidEnterpriseSsoInUse() || !AuthUtil.instance.conn) { + return + } + if (isOverride) { + const previousOverride = globals.globalState.tryGet('aws.amazonq.customization.overrideV2', String) + if (customization.arn === previousOverride) { + return + } + } + const selectedCustomizationObj = globals.globalState.tryGet<{ [label: string]: Customization }>( + 'CODEWHISPERER_SELECTED_CUSTOMIZATION', + Object, + {} + ) + selectedCustomizationObj[AuthUtil.instance.conn.label] = customization + getLogger().debug(`Selected customization ${customization.name} for ${AuthUtil.instance.conn.label}`) + + await globals.globalState.update('CODEWHISPERER_SELECTED_CUSTOMIZATION', selectedCustomizationObj) + if (isOverride) { + await globals.globalState.update('aws.amazonq.customization.overrideV2', customization.arn) + } + vsCodeState.isFreeTierLimitReached = false + await Commands.tryExecute('aws.amazonq.refreshStatusBar') + + // hack: triggers amazon q to send the customizations back to flare + await Commands.tryExecute('aws.amazonq.updateCustomizations') +} + +export const getPersistedCustomizations = (): Customization[] => { + if (!AuthUtil.instance.isValidEnterpriseSsoInUse() || !AuthUtil.instance.conn) { + return [] + } + const persistedCustomizationsObj = globals.globalState.tryGet<{ [label: string]: Customization[] }>( + 'CODEWHISPERER_PERSISTED_CUSTOMIZATIONS', + Object, + {} + ) + return persistedCustomizationsObj[AuthUtil.instance.conn.label] || [] +} + +export const setPersistedCustomizations = async (customizations: Customization[]) => { + if (!AuthUtil.instance.isValidEnterpriseSsoInUse() || !AuthUtil.instance.conn) { + return + } + const persistedCustomizationsObj = globals.globalState.tryGet<{ [label: string]: Customization[] }>( + 'CODEWHISPERER_PERSISTED_CUSTOMIZATIONS', + Object, + {} + ) + persistedCustomizationsObj[AuthUtil.instance.conn.label] = customizations + await globals.globalState.update('CODEWHISPERER_PERSISTED_CUSTOMIZATIONS', persistedCustomizationsObj) +} + +export const getNewCustomizationsAvailable = () => { + return globals.globalState.tryGet('aws.amazonq.codewhisperer.newCustomizations', Number, 0) +} + +export const setNewCustomizationsAvailable = async (num: number) => { + await globals.globalState.update('aws.amazonq.codewhisperer.newCustomizations', num) + vsCodeState.isFreeTierLimitReached = false +} + +export async function showCustomizationPrompt() { + await setNewCustomizationsAvailable(0) + await showQuickPick(createCustomizationItems(), { + title: localize('AWS.codewhisperer.customization.quickPick.title', 'Select a Customization'), + placeholder: localize( + 'AWS.codewhisperer.customization.quickPick.placeholder', + 'You have access to the following customizations' + ), + buttons: createCommonButtons() as vscode.QuickInputButton[], + compare: (a, b) => { + if (a.invalidSelection) { + return -1 + } + if (b.invalidSelection) { + return 1 + } + return a.label < b.label ? -1 : 1 + }, + recentlyUsed: localize('AWS.codewhisperer.customization.selected', ' Connected'), + }) +} + +const createCustomizationItems = async () => { + const items = [] + const availableCustomizations = await getAvailableCustomizationsList() + + // Order matters + // 1. read the old snapshot of customizations + const persistedCustomizations = getPersistedCustomizations() + + // 2. update the customizations snapshot with the latest + await setPersistedCustomizations(availableCustomizations) + + const selectedCustomization = getSelectedCustomization() + if (!isSelectedCustomizationAvailable(availableCustomizations, selectedCustomization)) { + await switchToBaseCustomizationAndNotify() + } + + if (availableCustomizations.length === 0) { + items.push(createBaseCustomizationItem()) + + void showMessageWithUrl( + localize( + 'AWS.codewhisperer.customization.noCustomizations.description', + 'You dont have access to any Amazon Q customization. Contact your admin for access.' + ), + customLearnMoreUri, + localize('AWS.codewhisperer.customization.notification.new_customizations.learn_more', 'Learn More'), + 'info' + ) + return items + } + + const persistedArns = persistedCustomizations.map((c) => c.arn) + const customizationNameToCount = availableCustomizations.reduce((map, customization) => { + if (customization.name) { + map.set(customization.name, (map.get(customization.name) || 0) + 1) + } + + return map + }, new Map()) + + items.push(createBaseCustomizationItem()) + items.push( + ...availableCustomizations.map((c) => { + let shouldPrefixAccountId = false + if (c.name) { + const cnt = customizationNameToCount.get(c.name) || 0 + if (cnt > 1) { + shouldPrefixAccountId = true + } + } + + return createCustomizationItem(c, persistedArns, shouldPrefixAccountId) + }) + ) + return items +} + +const createBaseCustomizationItem = () => { + const label = codicon`${getIcon('vscode-circuit-board')} ${localize( + 'AWS.codewhisperer.customization.base.label', + 'Amazon Q foundation (Default)' + )}` + const selectedArn = getSelectedCustomization().arn + return { + label: label, + onClick: async () => { + await selectCustomization(baseCustomization) + }, + detail: localize( + 'AWS.codewhisperer.customization.base.description', + 'Receive suggestions from Amazon Q base model' + ), + description: renderDescriptionText(label), + recentlyUsed: selectedArn === baseCustomization.arn, + } as DataQuickPickItem +} + +/** + * When users click "select customizations", we're showing ALL customizations across different profiles. + * Thus If users select the customization, we also change the profile if the customization is accessible from a different profile. + */ +const createCustomizationItem = ( + customization: Customization & { profile: RegionProfile }, + persistedArns: (ResourceArn | undefined)[], + shouldPrefixAccountId: boolean +) => { + const accountId = parse(customization.arn).accountId + const displayedName = customization.name + ? shouldPrefixAccountId + ? accountId + ? `${customization.name} (${accountId})` + : `${customization.name} (${customization.profile.name})` + : `${customization.name} (${customization.profile.name})` + : 'unknown' + + const isNewCustomization = !persistedArns.includes(customization.arn) + const label = codicon`${getIcon('vscode-circuit-board')} ${displayedName}` + const selectedArn = getSelectedCustomization().arn + return { + label: label, + onClick: async () => { + const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile + if (profile && customization.profile.arn !== profile.arn) { + await AuthUtil.instance.regionProfileManager.switchRegionProfile(customization.profile, 'customization') + } + await selectCustomization(customization) + }, + detail: + customization.description !== '' + ? customization.description + : localize('AWS.codewhisperer.customization.no.description.text', 'No description provided'), + description: renderDescriptionText(label, isNewCustomization), + data: customization.arn, + recentlyUsed: selectedArn === customization.arn, + } as DataQuickPickItem +} + +export const selectCustomization = async (customization: Customization) => { + // If the newly selected customization is same as the old one, do nothing + const selectedCustomization = getSelectedCustomization() + if (selectedCustomization.arn === customization.arn) { + return + } + await setSelectedCustomization(customization) + const suffix = + customization.arn === baseCustomization.arn ? customization.name : `${customization.name} customization.` + void vscode.window.showInformationMessage( + localize( + 'AWS.codewhisperer.customization.selected.message', + 'Amazon Q suggestions are now coming from the {0}', + suffix + ) + ) +} + +// Return all customizations across different profiles and associate the customization with the source profile +export const getAvailableCustomizationsList = async () => { + const items: (Customization & { profile: RegionProfile })[] = [] + const profiles: RegionProfile[] = [] + try { + const r = await AuthUtil.instance.regionProfileManager.getProfiles() + profiles.push(...r) + } catch (e) { + getLogger().error(`Failed to list customizations because listAvailableProfiles failed %s`, (e as Error).message) + return [] + } + + for (const profile of profiles) { + const provider = await CustomizationProvider.init(profile) + const customizations = await provider.listAvailableCustomizations() + + for (const c of customizations) { + items.push({ + ...c, + profile: profile, + }) + } + } + + return items +} + +// show notification that selected customization is not available, switching back to base +export const switchToBaseCustomizationAndNotify = async () => { + await setSelectedCustomization(baseCustomization) + const selectCustomizationLabel = localize( + 'AWS.codewhisperer.customization.notification.selectCustomization', + 'Select Another Customization' + ) + const selection = await vscode.window.showWarningMessage( + localize( + 'AWS.codewhisperer.customization.notification.selected_customization_not_available', + 'Selected Amazon Q customization is not available. Contact your administrator. Your instance of Amazon Q is using the foundation model.' + ), + selectCustomizationLabel + ) + if (selection === selectCustomizationLabel) { + await showCustomizationPrompt() + } +} + +const renderDescriptionText = (label: string, isNewCustomization: boolean = false) => { + return isNewCustomization ? ' New' : '' +} diff --git a/packages/core/src/codewhisperer/util/diagnosticsUtil.ts b/packages/core/src/codewhisperer/util/diagnosticsUtil.ts new file mode 100644 index 00000000000..5a34c3e4430 --- /dev/null +++ b/packages/core/src/codewhisperer/util/diagnosticsUtil.ts @@ -0,0 +1,117 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import * as crypto from 'crypto' +import { IdeDiagnostic } from '../client/codewhispereruserclient' + +export function getDiagnosticsOfCurrentFile(): FileDiagnostic | undefined { + if (vscode.window.activeTextEditor) { + return { + diagnostics: vscode.languages.getDiagnostics(vscode.window.activeTextEditor.document.uri), + filepath: vscode.window.activeTextEditor.document.uri.fsPath, + } + } + return undefined +} + +export type FileDiagnostic = { + filepath: string + diagnostics: vscode.Diagnostic[] +} + +export function getDiagnosticsDifferences( + oldDiagnostics: FileDiagnostic | undefined, + newDiagnostics: FileDiagnostic | undefined +): { added: vscode.Diagnostic[]; removed: vscode.Diagnostic[] } { + const result: { added: vscode.Diagnostic[]; removed: vscode.Diagnostic[] } = { added: [], removed: [] } + if ( + oldDiagnostics === undefined || + newDiagnostics === undefined || + newDiagnostics.filepath !== oldDiagnostics.filepath + ) { + return result + } + + // Create maps using diagnostic key for uniqueness + const oldMap = new Map(oldDiagnostics.diagnostics.map((d) => [getDiagnosticKey(d), d])) + const newMap = new Map(newDiagnostics.diagnostics.map((d) => [getDiagnosticKey(d), d])) + + // Get added diagnostics (in new but not in old) + result.added = [...newMap.values()].filter((d) => !oldMap.has(getDiagnosticKey(d))) + + // Get removed diagnostics (in old but not in new) + result.removed = [...oldMap.values()].filter((d) => !newMap.has(getDiagnosticKey(d))) + + return result +} + +export function toIdeDiagnostics(diagnostic: vscode.Diagnostic): IdeDiagnostic { + const severity = + diagnostic.severity === vscode.DiagnosticSeverity.Error + ? 'ERROR' + : diagnostic.severity === vscode.DiagnosticSeverity.Warning + ? 'WARNING' + : diagnostic.severity === vscode.DiagnosticSeverity.Hint + ? 'HINT' + : 'INFORMATION' + + return { + ideDiagnosticType: getDiagnosticsType(diagnostic.message), + severity: severity, + source: diagnostic.source, + range: { + start: { + line: diagnostic.range.start.line, + character: diagnostic.range.start.character, + }, + end: { + line: diagnostic.range.end.line, + character: diagnostic.range.end.character, + }, + }, + } +} + +export function getDiagnosticsType(message: string): string { + const errorTypes = new Map([ + ['SYNTAX_ERROR', ['expected', 'indent', 'syntax']], + ['TYPE_ERROR', ['type', 'cast']], + ['REFERENCE_ERROR', ['undefined', 'not defined', 'undeclared', 'reference']], + ['BEST_PRACTICE', ['deprecated', 'unused', 'uninitialized', 'not initialized']], + ['SECURITY', ['security', 'vulnerability']], + ]) + + const lowercaseMessage = message.toLowerCase() + + for (const [errorType, keywords] of errorTypes) { + if (keywords.some((keyword) => lowercaseMessage.includes(keyword))) { + return errorType + } + } + + return 'OTHER' +} + +/** + * Generates a unique MD5 hash key for a VS Code diagnostic object. + * + * @param diagnostic - A VS Code Diagnostic object containing information about a code diagnostic + * @returns A 32-character hexadecimal MD5 hash string that uniquely identifies the diagnostic + * + * @description + * Creates a deterministic hash by combining the diagnostic's message, severity, code, and source. + * This hash can be used as a unique identifier for deduplication or tracking purposes. + * Note: range is not in the hashed string because a diagnostic can move and its range can change within the editor + */ +function getDiagnosticKey(diagnostic: vscode.Diagnostic): string { + const jsonStr = JSON.stringify({ + message: diagnostic.message, + severity: diagnostic.severity, + code: diagnostic.code, + source: diagnostic.source, + }) + + return crypto.createHash('md5').update(jsonStr).digest('hex') +} diff --git a/packages/core/src/codewhisperer/util/editorContext.ts b/packages/core/src/codewhisperer/util/editorContext.ts new file mode 100644 index 00000000000..9ea3020dba4 --- /dev/null +++ b/packages/core/src/codewhisperer/util/editorContext.ts @@ -0,0 +1,427 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as codewhispererClient from '../client/codewhisperer' +import * as path from 'path' +import * as CodeWhispererConstants from '../models/constants' +import { getTabSizeSetting } from '../../shared/utilities/editorUtilities' +import { truncate } from '../../shared/utilities/textUtilities' +import { getLogger } from '../../shared/logger/logger' +import { runtimeLanguageContext } from './runtimeLanguageContext' +import { fetchSupplementalContext } from './supplementalContext/supplementalContextUtil' +import { editorStateMaxLength, supplementalContextTimeoutInMs } from '../models/constants' +import { getSelectedCustomization } from './customizationUtil' +import { selectFrom } from '../../shared/utilities/tsUtils' +import { checkLeftContextKeywordsForJson } from './commonUtil' +import { CodeWhispererSupplementalContext } from '../models/model' +import { getOptOutPreference } from '../../shared/telemetry/util' +import { indent } from '../../shared/utilities/textUtilities' +import { isInDirectory } from '../../shared/filesystemUtilities' +import { AuthUtil } from './authUtil' +import { predictionTracker } from '../nextEditPrediction/activation' +import { BaseLanguageClient } from 'vscode-languageclient' + +let tabSize: number = getTabSizeSetting() + +function getEnclosingNotebook(editor: vscode.TextEditor): vscode.NotebookDocument | undefined { + // For notebook cells, find the existing notebook with a cell that matches the current editor. + return vscode.workspace.notebookDocuments.find( + (nb) => + nb.notebookType === 'jupyter-notebook' && nb.getCells().some((cell) => cell.document === editor.document) + ) +} + +export function getNotebookContext( + notebook: vscode.NotebookDocument, + editor: vscode.TextEditor, + languageName: string, + caretLeftFileContext: string, + caretRightFileContext: string +) { + // Expand the context for a cell inside of a noteboo with whatever text fits from the preceding and subsequent cells + const allCells = notebook.getCells() + const cellIndex = allCells.findIndex((cell) => cell.document === editor.document) + // Extract text from prior cells if there is enough room in left file context + if (caretLeftFileContext.length < CodeWhispererConstants.charactersLimit - 1) { + const leftCellsText = getNotebookCellsSliceContext( + allCells.slice(0, cellIndex), + CodeWhispererConstants.charactersLimit - (caretLeftFileContext.length + 1), + languageName, + true + ) + if (leftCellsText.length > 0) { + caretLeftFileContext = addNewlineIfMissing(leftCellsText) + caretLeftFileContext + } + } + // Extract text from subsequent cells if there is enough room in right file context + if (caretRightFileContext.length < CodeWhispererConstants.charactersLimit - 1) { + const rightCellsText = getNotebookCellsSliceContext( + allCells.slice(cellIndex + 1), + CodeWhispererConstants.charactersLimit - (caretRightFileContext.length + 1), + languageName, + false + ) + if (rightCellsText.length > 0) { + caretRightFileContext = addNewlineIfMissing(caretRightFileContext) + rightCellsText + } + } + return { caretLeftFileContext, caretRightFileContext } +} + +export function getNotebookCellContext(cell: vscode.NotebookCell, referenceLanguage?: string): string { + // Extract the text verbatim if the cell is code and the cell has the same language. + // Otherwise, add the correct comment string for the reference language + const cellText = cell.document.getText() + if ( + cell.kind === vscode.NotebookCellKind.Markup || + (runtimeLanguageContext.normalizeLanguage(cell.document.languageId) ?? cell.document.languageId) !== + referenceLanguage + ) { + const commentPrefix = runtimeLanguageContext.getSingleLineCommentPrefix(referenceLanguage) + if (commentPrefix === '') { + return cellText + } + return cell.document + .getText() + .split('\n') + .map((line) => `${commentPrefix}${line}`) + .join('\n') + } + return cellText +} + +export function getNotebookCellsSliceContext( + cells: vscode.NotebookCell[], + maxLength: number, + referenceLanguage: string, + fromStart: boolean +): string { + // Extract context from array of notebook cells that fits inside `maxLength` characters, + // from either the start or the end of the array. + let output: string[] = [] + if (!fromStart) { + cells = cells.reverse() + } + cells.some((cell) => { + const cellText = addNewlineIfMissing(getNotebookCellContext(cell, referenceLanguage)) + if (cellText.length > 0) { + if (cellText.length >= maxLength) { + if (fromStart) { + output.push(cellText.substring(0, maxLength)) + } else { + output.push(cellText.substring(cellText.length - maxLength)) + } + return true + } + output.push(cellText) + maxLength -= cellText.length + } + }) + if (!fromStart) { + output = output.reverse() + } + return output.join('') +} + +export function addNewlineIfMissing(text: string): string { + if (text.length > 0 && !text.endsWith('\n')) { + text += '\n' + } + return text +} + +export function extractContextForCodeWhisperer(editor: vscode.TextEditor): codewhispererClient.FileContext { + const document = editor.document + const curPos = editor.selection.active + const offset = document.offsetAt(curPos) + + let caretLeftFileContext = editor.document.getText( + new vscode.Range( + document.positionAt(offset - CodeWhispererConstants.charactersLimit), + document.positionAt(offset) + ) + ) + let caretRightFileContext = editor.document.getText( + new vscode.Range( + document.positionAt(offset), + document.positionAt(offset + CodeWhispererConstants.charactersLimit) + ) + ) + let languageName = 'plaintext' + if (!checkLeftContextKeywordsForJson(document.fileName, caretLeftFileContext, editor.document.languageId)) { + languageName = runtimeLanguageContext.resolveLang(editor.document) + } + if (editor.document.uri.scheme === 'vscode-notebook-cell') { + const notebook = getEnclosingNotebook(editor) + if (notebook) { + ;({ caretLeftFileContext, caretRightFileContext } = getNotebookContext( + notebook, + editor, + languageName, + caretLeftFileContext, + caretRightFileContext + )) + } + } + + return { + fileUri: editor.document.uri.toString().substring(0, CodeWhispererConstants.filenameCharsLimit), + filename: getFileRelativePath(editor), + programmingLanguage: { + languageName: languageName, + }, + leftFileContent: caretLeftFileContext, + rightFileContent: caretRightFileContext, + } as codewhispererClient.FileContext +} + +export function getFileName(editor: vscode.TextEditor): string { + const fileName = path.basename(editor.document.fileName) + return fileName.substring(0, CodeWhispererConstants.filenameCharsLimit) +} + +export function getFileRelativePath(editor: vscode.TextEditor): string { + const fileName = path.basename(editor.document.fileName) + let relativePath = '' + const workspaceFolder = vscode.workspace.getWorkspaceFolder(editor.document.uri) + if (!workspaceFolder) { + relativePath = fileName + } else { + const workspacePath = workspaceFolder.uri.fsPath + const filePath = editor.document.uri.fsPath + relativePath = path.relative(workspacePath, filePath) + } + // For notebook files, we want to use the programming language for each cell for the code suggestions, so change + // the filename sent in the request to reflect that language + if (relativePath.endsWith('.ipynb')) { + const fileExtension = runtimeLanguageContext.getLanguageExtensionForNotebook(editor.document.languageId) + if (fileExtension !== undefined) { + const filenameWithNewExtension = relativePath.substring(0, relativePath.length - 5) + fileExtension + return filenameWithNewExtension.substring(0, CodeWhispererConstants.filenameCharsLimit) + } + } + return relativePath.substring(0, CodeWhispererConstants.filenameCharsLimit) +} + +async function getWorkspaceId(editor: vscode.TextEditor): Promise { + try { + const workspaceIds: { workspaces: { workspaceRoot: string; workspaceId: string }[] } = + await vscode.commands.executeCommand('aws.amazonq.getWorkspaceId') + for (const item of workspaceIds.workspaces) { + const path = vscode.Uri.parse(item.workspaceRoot).fsPath + if (isInDirectory(path, editor.document.uri.fsPath)) { + return item.workspaceId + } + } + } catch (err) { + getLogger().warn(`No workspace id found ${err}`) + } + return undefined +} + +export async function buildListRecommendationRequest( + editor: vscode.TextEditor, + nextToken: string, + allowCodeWithReference: boolean, + languageClient?: BaseLanguageClient +): Promise<{ + request: codewhispererClient.ListRecommendationsRequest + supplementalMetadata: CodeWhispererSupplementalContext | undefined +}> { + const fileContext = extractContextForCodeWhisperer(editor) + + const tokenSource = new vscode.CancellationTokenSource() + setTimeout(() => { + tokenSource.cancel() + }, supplementalContextTimeoutInMs) + + const supplementalContexts = await fetchSupplementalContext(editor, tokenSource.token, languageClient) + + logSupplementalContext(supplementalContexts) + + // Get predictionSupplementalContext from PredictionTracker + let predictionSupplementalContext: codewhispererClient.SupplementalContext[] = [] + if (predictionTracker) { + predictionSupplementalContext = await predictionTracker.generatePredictionSupplementalContext() + } + + const selectedCustomization = getSelectedCustomization() + const completionSupplementalContext: codewhispererClient.SupplementalContext[] = supplementalContexts + ? supplementalContexts.supplementalContextItems.map((v) => { + return selectFrom(v, 'content', 'filePath') + }) + : [] + + const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile + + const editorState = getEditorState(editor, fileContext) + + // Combine inline and prediction supplemental contexts + const finalSupplementalContext = completionSupplementalContext.concat(predictionSupplementalContext) + return { + request: { + fileContext: fileContext, + nextToken: nextToken, + referenceTrackerConfiguration: { + recommendationsWithReferences: allowCodeWithReference ? 'ALLOW' : 'BLOCK', + }, + supplementalContexts: finalSupplementalContext, + editorState: editorState, + maxResults: CodeWhispererConstants.maxRecommendations, + customizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, + optOutPreference: getOptOutPreference(), + workspaceId: await getWorkspaceId(editor), + profileArn: profile?.arn, + }, + supplementalMetadata: supplementalContexts, + } +} + +export async function buildGenerateRecommendationRequest(editor: vscode.TextEditor): Promise<{ + request: codewhispererClient.GenerateRecommendationsRequest + supplementalMetadata: CodeWhispererSupplementalContext | undefined +}> { + const fileContext = extractContextForCodeWhisperer(editor) + + const tokenSource = new vscode.CancellationTokenSource() + // the supplement context fetch mechanisms each has a timeout of supplementalContextTimeoutInMs + // adding 10 ms for overall timeout as buffer + setTimeout(() => { + tokenSource.cancel() + }, supplementalContextTimeoutInMs + 10) + const supplementalContexts = await fetchSupplementalContext(editor, tokenSource.token) + + logSupplementalContext(supplementalContexts) + + return { + request: { + fileContext: fileContext, + maxResults: CodeWhispererConstants.maxRecommendations, + supplementalContexts: supplementalContexts?.supplementalContextItems ?? [], + }, + supplementalMetadata: supplementalContexts, + } +} + +export function validateRequest( + req: codewhispererClient.ListRecommendationsRequest | codewhispererClient.GenerateRecommendationsRequest +): boolean { + const isLanguageNameValid = + req.fileContext.programmingLanguage.languageName !== undefined && + req.fileContext.programmingLanguage.languageName.length >= 1 && + req.fileContext.programmingLanguage.languageName.length <= 128 && + (runtimeLanguageContext.isLanguageSupported(req.fileContext.programmingLanguage.languageName) || + runtimeLanguageContext.isFileFormatSupported( + req.fileContext.filename.substring(req.fileContext.filename.lastIndexOf('.') + 1) + )) + const isFileNameValid = !(req.fileContext.filename === undefined || req.fileContext.filename.length < 1) + const isFileContextValid = !( + req.fileContext.leftFileContent.length > CodeWhispererConstants.charactersLimit || + req.fileContext.rightFileContent.length > CodeWhispererConstants.charactersLimit + ) + if (isFileNameValid && isLanguageNameValid && isFileContextValid) { + return true + } + return false +} + +export function updateTabSize(val: number): void { + tabSize = val +} + +export function getTabSize(): number { + return tabSize +} + +export function getEditorState(editor: vscode.TextEditor, fileContext: codewhispererClient.FileContext): any { + try { + const cursorPosition = editor.selection.active + const cursorOffset = editor.document.offsetAt(cursorPosition) + const documentText = editor.document.getText() + + // Truncate if document content is too large (defined in constants.ts) + let fileText = documentText + if (documentText.length > editorStateMaxLength) { + const halfLength = Math.floor(editorStateMaxLength / 2) + + // Use truncate function to get the text around the cursor position + const leftPart = truncate(documentText.substring(0, cursorOffset), -halfLength, '') + const rightPart = truncate(documentText.substring(cursorOffset), halfLength, '') + + fileText = leftPart + rightPart + } + + return { + document: { + programmingLanguage: { + languageName: fileContext.programmingLanguage.languageName, + }, + relativeFilePath: fileContext.filename, + text: fileText, + }, + cursorState: { + position: { + line: editor.selection.active.line, + character: editor.selection.active.character, + }, + }, + } + } catch (error) { + getLogger().error(`Error generating editor state: ${error}`) + return undefined + } +} + +export function getLeftContext(editor: vscode.TextEditor, line: number): string { + let lineText = '' + try { + if (editor && editor.document.lineAt(line)) { + lineText = editor.document.lineAt(line).text + if (lineText.length > CodeWhispererConstants.contextPreviewLen) { + lineText = + '...' + + lineText.substring( + lineText.length - CodeWhispererConstants.contextPreviewLen - 1, + lineText.length - 1 + ) + } + } + } catch (error) { + getLogger().error(`Error when getting left context ${error}`) + } + + return lineText +} + +function logSupplementalContext(supplementalContext: CodeWhispererSupplementalContext | undefined) { + if (!supplementalContext) { + return + } + + let logString = indent( + `CodeWhispererSupplementalContext: + isUtg: ${supplementalContext.isUtg}, + isProcessTimeout: ${supplementalContext.isProcessTimeout}, + contentsLength: ${supplementalContext.contentsLength}, + latency: ${supplementalContext.latency} + strategy: ${supplementalContext.strategy}`, + 4, + true + ).trimStart() + + for (const [index, context] of supplementalContext.supplementalContextItems.entries()) { + logString += indent(`\nChunk ${index}:\n`, 4, true) + logString += indent( + `Path: ${context.filePath} + Length: ${context.content.length} + Score: ${context.score}`, + 8, + true + ) + } + + getLogger().debug(logString) +} diff --git a/packages/core/src/codewhisperer/util/getStartUrl.ts b/packages/core/src/codewhisperer/util/getStartUrl.ts new file mode 100644 index 00000000000..0d9ca7617ff --- /dev/null +++ b/packages/core/src/codewhisperer/util/getStartUrl.ts @@ -0,0 +1,43 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as CodeWhispererConstants from '../models/constants' +import { isValidResponse } from '../../shared/wizards/wizard' +import { AuthUtil, amazonQScopes } from './authUtil' +import { CancellationError } from '../../shared/utilities/timeoutUtils' +import { ToolkitError } from '../../shared/errors' +import { telemetry } from '../../shared/telemetry/telemetry' +import { createStartUrlPrompter, showRegionPrompter } from '../../auth/utils' +import { Region } from '../../shared/regions/endpoints' +import { Commands } from '../../shared/vscode/commands2' +import { vsCodeState } from '../models/model' + +export const getStartUrl = async () => { + const inputBox = await createStartUrlPrompter('IAM Identity Center', amazonQScopes) + const userInput = await inputBox.prompt() + if (!isValidResponse(userInput)) { + telemetry.ui_click.emit({ elementId: 'connection_optionescapecancel' }) + throw new CancellationError('user') + } + telemetry.ui_click.emit({ elementId: 'connection_startUrl' }) + const region = await showRegionPrompter() + telemetry.ui_click.emit({ elementId: 'connection_region' }) + return connectToEnterpriseSso(userInput, region.id) +} + +export async function connectToEnterpriseSso(startUrl: string, region: Region['id']) { + let conn + try { + conn = await AuthUtil.instance.connectToEnterpriseSso(startUrl, region) + } catch (e) { + throw ToolkitError.chain(e, CodeWhispererConstants.failedToConnectIamIdentityCenter, { + code: 'FailedToConnect', + }) + } + vsCodeState.isFreeTierLimitReached = false + await Commands.tryExecute('aws.amazonq.enableCodeSuggestions') + + return conn +} diff --git a/packages/core/src/codewhisperer/util/gitUtil.ts b/packages/core/src/codewhisperer/util/gitUtil.ts new file mode 100644 index 00000000000..09cd9a6116f --- /dev/null +++ b/packages/core/src/codewhisperer/util/gitUtil.ts @@ -0,0 +1,37 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { removeAnsi } from '../../shared/utilities/textUtilities' +import { getLogger } from '../../shared/logger/logger' +import { ChildProcess, ChildProcessOptions } from '../../shared/utilities/processUtils' +import { Uri } from 'vscode' + +export async function isGitRepo(folder: Uri): Promise { + const childProcess = new ChildProcess('git', ['rev-parse', '--is-inside-work-tree']) + + let output = '' + const runOptions: ChildProcessOptions = { + rejectOnError: true, + rejectOnErrorCode: true, + onStdout: (text) => { + output += text + getLogger().verbose(removeAnsi(text)) + }, + onStderr: (text) => { + getLogger().error(removeAnsi(text)) + }, + spawnOptions: { + cwd: folder.fsPath, + }, + } + + try { + await childProcess.run(runOptions) + return output.trim() === 'true' + } catch (err) { + getLogger().warn(`Failed to run command \`${childProcess.toString()}\`: ${err}`) + return false + } +} diff --git a/src/codewhisperer/util/globalStateUtil.ts b/packages/core/src/codewhisperer/util/globalStateUtil.ts similarity index 87% rename from src/codewhisperer/util/globalStateUtil.ts rename to packages/core/src/codewhisperer/util/globalStateUtil.ts index 641ade41264..55376a83546 100644 --- a/src/codewhisperer/util/globalStateUtil.ts +++ b/packages/core/src/codewhisperer/util/globalStateUtil.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/packages/core/src/codewhisperer/util/importAdderUtil.ts b/packages/core/src/codewhisperer/util/importAdderUtil.ts new file mode 100644 index 00000000000..fa53944def0 --- /dev/null +++ b/packages/core/src/codewhisperer/util/importAdderUtil.ts @@ -0,0 +1,89 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { Recommendation } from '../client/codewhisperer' +import { CodeWhispererSettings } from './codewhispererSettings' + +export function findLineToInsertImportStatement(editor: vscode.TextEditor, firstLineOfRecommendation: number): number { + let line = findLineOfLastImportStatement(editor, firstLineOfRecommendation) + if (line === -1) { + line = findLineOfFirstCode(editor, firstLineOfRecommendation) + } + return line +} + +export function findLineOfFirstCode(editor: vscode.TextEditor, firstLineOfRecommendation: number): number { + const lang = editor.document.languageId + for (let i = 0; i <= firstLineOfRecommendation; i++) { + const text = editor.document.lineAt(i).text + if (lang === 'python') { + // skip #, empty line + if (!text.match(/^\s*#/) && !text.match(/^\s*$/)) { + return i + } + } else if (lang === 'javascript' || lang === 'jsx') { + // skip //, /*, *, */, empty line + if ( + !text.match(/^\s*\/\//) && + !text.match(/\s*use\s+strict/) && + !text.match(/^\s*$/) && + !text.match(/^\s*\/\s*\*/) && + !text.match(/^\s*\*/) && + !text.match(/^\s*\*\s*\//) + ) { + return i + } + } else if (lang === 'java') { + // skip //, /*, *, */, package, empty line + if ( + !text.match(/^\s*\/\//) && + !text.match(/^\s*package\s+\S+/) && + !text.match(/^\s*$/) && + !text.match(/^\s*\/\s*\*/) && + !text.match(/^\s*\*/) && + !text.match(/^\s*\*\s*\//) + ) { + return i + } + } + } + return 0 +} + +export function findLineOfLastImportStatement(editor: vscode.TextEditor, firstLineOfRecommendation: number): number { + const lang = editor.document.languageId + for (let i = firstLineOfRecommendation; i >= 0; i--) { + const text = editor.document.lineAt(i).text + if (lang === 'python') { + if (text.match(/^\s*import\s+\S+/) || text.match(/^\s*from\s+\S+/)) { + return i + 1 + } + } else if (lang === 'javascript' || lang === 'jsx') { + if (text.match(/^\s*import\s+\S+/) || text.match(/=\s*require\s*\(\s*\S+\s*\)\s*;/)) { + return i + 1 + } + } else if (lang === 'java') { + if (text.match(/^\s*import\s+\S+\s*;/)) { + return i + 1 + } + } + } + return -1 +} + +/* Returns the number of imports in a recommendation + * return undefined if the API response field is missing or import is disabled + */ +export function getImportCount(recommendation: Recommendation): number | undefined { + if ( + 'mostRelevantMissingImports' in recommendation && + recommendation.mostRelevantMissingImports !== undefined && + CodeWhispererSettings.instance.isImportRecommendationEnabled() + ) { + return recommendation.mostRelevantMissingImports.length + } + return undefined +} diff --git a/src/codewhisperer/util/licenseUtil.ts b/packages/core/src/codewhisperer/util/licenseUtil.ts similarity index 98% rename from src/codewhisperer/util/licenseUtil.ts rename to packages/core/src/codewhisperer/util/licenseUtil.ts index 314d8142cb0..d21cd093e12 100644 --- a/src/codewhisperer/util/licenseUtil.ts +++ b/packages/core/src/codewhisperer/util/licenseUtil.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -463,7 +463,7 @@ export class LicenseUtil { } public static getLicenseHtml(licenseName: string | undefined): string { - if (licenseName != undefined && licenseName in this._licenseLookup) { + if (licenseName !== undefined && licenseName in this._licenseLookup) { return this._licenseLookup[licenseName] } return `https://spdx.org/licenses` @@ -471,11 +471,13 @@ export class LicenseUtil { public static getUniqueLicenseNames(references: References | undefined): Set { const n = new Set() - references?.forEach(r => { - if (r.licenseName) { - n.add(r.licenseName) + if (references) { + for (const r of references) { + if (r.licenseName) { + n.add(r.licenseName) + } } - }) + } return n } } diff --git a/packages/core/src/codewhisperer/util/runtimeLanguageContext.ts b/packages/core/src/codewhisperer/util/runtimeLanguageContext.ts new file mode 100644 index 00000000000..b87db0a65e8 --- /dev/null +++ b/packages/core/src/codewhisperer/util/runtimeLanguageContext.ts @@ -0,0 +1,362 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger } from '../../shared/logger/logger' +import { CodewhispererLanguage } from '../../shared/telemetry/telemetry.gen' +import { createConstantMap, ConstantMap } from '../../shared/utilities/tsUtils' +import * as codewhispererClient from '../client/codewhisperer' +import * as CodeWhispererConstants from '../models/constants' +import * as path from 'path' + +type RuntimeLanguage = Exclude | 'systemverilog' + +const runtimeLanguageSet: ReadonlySet = new Set([ + 'c', + 'cpp', + 'csharp', + 'go', + 'java', + 'javascript', + 'kotlin', + 'php', + 'python', + 'powershell', + 'r', + 'dart', + 'ruby', + 'rust', + 'scala', + 'shell', + 'sql', + 'swift', + 'lua', + 'vue', + 'typescript', + 'json', + 'yaml', + 'tf', + 'systemverilog', +]) + +export class RuntimeLanguageContext { + /** + * Key: Union set of CodewhispererLanguageId and PlatformLanguageId (VSC, C9 etc.) + * Value: CodeWhispererLanguageId + */ + private supportedLanguageMap: ConstantMap< + CodeWhispererConstants.PlatformLanguageId | CodewhispererLanguage, + CodewhispererLanguage + > + + /** + * A map storing CodeWhisperer supported programming language with key: language extension and value: vscLanguageId + * Key: language extension + * Value: vscLanguageId + */ + private supportedLanguageExtensionMap: ConstantMap + + /** + * A map storing single-line comment prefixes for different languages + * Key: CodewhispererLanguage + * Value: Comment prefix string + */ + private languageSingleLineCommentPrefixMap: ConstantMap + + constructor() { + this.supportedLanguageMap = createConstantMap< + Exclude, + CodewhispererLanguage + >({ + c: 'c', + cpp: 'cpp', + csharp: 'csharp', + c_cpp: 'cpp', + go: 'go', + golang: 'go', + hcl: 'tf', + java: 'java', + javascript: 'javascript', + javascriptreact: 'jsx', + json: 'json', + jsonc: 'json', + jsx: 'jsx', + kotlin: 'kotlin', + packer: 'tf', + php: 'php', + python: 'python', + ruby: 'ruby', + rust: 'rust', + scala: 'scala', + sh: 'shell', + shell: 'shell', + shellscript: 'shell', + sql: 'sql', + terraform: 'tf', + terragrunt: 'tf', + tf: 'tf', + tsx: 'tsx', + typescript: 'typescript', + typescriptreact: 'tsx', + yml: 'yaml', + yaml: 'yaml', + dart: 'dart', + lua: 'lua', + powershell: 'powershell', + r: 'r', + swift: 'swift', + systemVerilog: 'systemVerilog', + systemverilog: 'systemVerilog', + verilog: 'systemVerilog', + vue: 'vue', + abap: 'abap', + }) + this.supportedLanguageExtensionMap = createConstantMap({ + c: 'c', + h: 'c', + cpp: 'cpp', + cc: 'cpp', + 'c++': 'cpp', + cs: 'csharp', + go: 'go', + hcl: 'tf', + java: 'java', + js: 'javascript', + json: 'json', + jsonc: 'json', + jsx: 'jsx', + kt: 'kotlin', + txt: 'plaintext', + php: 'php', + py: 'python', + rb: 'ruby', + rs: 'rust', + scala: 'scala', + sh: 'shell', + sql: 'sql', + tf: 'tf', + tsx: 'tsx', + ts: 'typescript', + yaml: 'yaml', + yml: 'yaml', + sv: 'systemVerilog', + svh: 'systemVerilog', + vh: 'systemVerilog', + dart: 'dart', + lua: 'lua', + wlua: 'lua', + swift: 'swift', + vue: 'vue', + ps1: 'powershell', + psm1: 'powershell', + r: 'r', + abap: 'abap', + }) + this.languageSingleLineCommentPrefixMap = createConstantMap({ + c: '// ', + cpp: '// ', + csharp: '// ', + dart: '// ', + go: '// ', + hcl: '# ', + java: '// ', + javascript: '// ', + json: '// ', + jsonc: '// ', + jsx: '// ', + kotlin: '// ', + lua: '-- ', + php: '// ', + plaintext: '', + powershell: '# ', + python: '# ', + r: '# ', + ruby: '# ', + rust: '// ', + scala: '// ', + shell: '# ', + sql: '-- ', + swift: '// ', + systemVerilog: '// ', + tf: '# ', + tsx: '// ', + typescript: '// ', + vue: '', // vue lacks a single-line comment prefix + yaml: '# ', + yml: '# ', + abap: '', + }) + } + + public resolveLang(doc: vscode.TextDocument): CodewhispererLanguage { + return this.normalizeLanguage(doc.languageId) || this.byFileExt(doc) || 'plaintext' + } + + /** + * To add a new platform language id: + * 1. add new platform language ID constant in the file codewhisperer/constant.ts + * 2. add corresponding CodeWhispererLanguage mapping in the constructor of RuntimeLanguageContext + * @param languageId : vscode language id or codewhisperer language name + * @returns normalized language id of type CodewhispererLanguage if any, otherwise undefined + */ + public normalizeLanguage(languageId?: string): CodewhispererLanguage | undefined { + return this.supportedLanguageMap.get(languageId) + } + + /** + * Get the comment prefix for a given language + * @param language The language to get comment prefix for + * @returns The comment prefix string, or empty string if not found + */ + public getSingleLineCommentPrefix(language?: string): string { + const normalizedLanguage = this.normalizeLanguage(language) + return normalizedLanguage ? (this.languageSingleLineCommentPrefixMap.get(normalizedLanguage) ?? '') : '' + } + + /** + * Normalize client side language id to service aware language id (service is not aware of jsx/tsx) + * Only used when invoking CodeWhisperer service API, for client usage please use normalizeLanguage + * Client side CodewhispererLanguage is a superset of NormalizedLanguageId + */ + public toRuntimeLanguage(language: CodewhispererLanguage): RuntimeLanguage { + switch (language) { + case 'jsx': + return 'javascript' + + case 'tsx': + return 'typescript' + + case 'systemVerilog': + return 'systemverilog' + + default: + if (!runtimeLanguageSet.has(language)) { + getLogger().error(`codewhisperer: unknown runtime language ${language}`) + } + return language + } + } + + /** + * This is for notebook files map to a new filename with the corresponding language extension + * @param languageId : vscode language id or codewhisperer language name + * @returns corresponding language extension if any, otherwise undefined + */ + public getLanguageExtensionForNotebook(languageId?: string): string | undefined { + return [...this.supportedLanguageExtensionMap.entries()].find( + ([, language]) => language === this.normalizeLanguage(languageId) + )?.[0] + } + + /** + * @param languageId : vscode language id or codewhisperer language name, fileExtension: extension of the selected file + * @returns An object with a field language: CodewhispererLanguage, if no corresponding CodewhispererLanguage ID, plaintext is returned + */ + public getLanguageContext(languageId?: string, fileExtension?: string): { language: CodewhispererLanguage } { + const extensionToLanguageMap: Record = { + tf: 'tf', + hcl: 'tf', + json: 'json', + yaml: 'yaml', + yml: 'yaml', + sv: 'systemVerilog', + svh: 'systemVerilog', + vh: 'systemVerilog', + dart: 'dart', + lua: 'lua', + wlua: 'lua', + swift: 'swift', + vue: 'vue', + ps1: 'powershell', + psm1: 'powershell', + r: 'r', + // Add more mappings if needed + } + + if (languageId === 'plaintext' && fileExtension !== undefined) { + const languages = extensionToLanguageMap[fileExtension] + if (languages) { + return { language: languages } + } + } + return { language: this.normalizeLanguage(languageId) ?? 'plaintext' } + } + + /** + * Mapping the field ProgrammingLanguage of codewhisperer generateRecommendationRequest | listRecommendationRequest to + * its Codewhisperer runtime language e.g. jsx -> typescript, typescript -> typescript + * @param request : cwspr generateRecommendationRequest | ListRecommendationRequest + * @returns request with source language name mapped to cwspr runtime language + */ + public mapToRuntimeLanguage< + T extends codewhispererClient.ListRecommendationsRequest | codewhispererClient.GenerateRecommendationsRequest, + >(request: T): T { + const fileContext = request.fileContext + const runtimeLanguage: codewhispererClient.ProgrammingLanguage = { + languageName: this.toRuntimeLanguage( + runtimeLanguageContext.getLanguageContext( + request.fileContext.programmingLanguage.languageName, + request.fileContext.filename.substring(request.fileContext.filename.lastIndexOf('.') + 1) + ).language + ), + } + + return { + ...request, + fileContext: { + ...fileContext, + programmingLanguage: runtimeLanguage, + }, + } + } + + public isLanguageSupported(languageId: string): boolean + public isLanguageSupported(doc: vscode.TextDocument): boolean + public isLanguageSupported(arg: string | vscode.TextDocument): boolean { + if (typeof arg === 'string') { + const normalizedLanguageId = this.normalizeLanguage(arg) + const byLanguageId = !normalizedLanguageId || normalizedLanguageId === 'plaintext' ? false : true + + return byLanguageId + } else { + const normalizedLanguageId = this.normalizeLanguage(arg.languageId) + const byLanguageId = !normalizedLanguageId || normalizedLanguageId === 'plaintext' ? false : true + const byFileExtension = this.byFileExt(arg) !== undefined + + return byLanguageId || byFileExtension + } + } + + /** + * + * @param fileFormat : vscode editor filecontext filename extension + * @returns true if the fileformat is supported by CodeWhisperer otherwise false + */ + public isFileFormatSupported(fileFormat: string): boolean { + const language = this.supportedLanguageExtensionMap.get(fileFormat) + return language !== undefined && language !== 'plaintext' + } + + /** + * @param fileExtension: extension of the selected file + * @returns corresponding vscode language id if any, otherwise undefined + */ + public getLanguageFromFileExtension(fileExtension: string) { + return this.supportedLanguageExtensionMap.get(fileExtension) + } + + private byFileExt(doc: vscode.TextDocument): CodewhispererLanguage | undefined { + const extension = path.extname(doc.uri.fsPath) + const byExt = this.supportedLanguageExtensionMap.get(extension.substring(1)) + + if (byExt === 'plaintext') { + return undefined + } + + return byExt + } +} + +export const runtimeLanguageContext = new RuntimeLanguageContext() diff --git a/packages/core/src/codewhisperer/util/securityScanLanguageContext.ts b/packages/core/src/codewhisperer/util/securityScanLanguageContext.ts new file mode 100644 index 00000000000..100e837b947 --- /dev/null +++ b/packages/core/src/codewhisperer/util/securityScanLanguageContext.ts @@ -0,0 +1,64 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { CodewhispererLanguage } from '../../shared/telemetry/telemetry.gen' +import { ConstantMap, createConstantMap } from '../../shared/utilities/tsUtils' +import * as CodeWhispererConstants from '../models/constants' + +export class SecurityScanLanguageContext { + private supportedLanguageMap: ConstantMap + + constructor() { + this.supportedLanguageMap = createConstantMap< + CodeWhispererConstants.SecurityScanLanguageId, + CodewhispererLanguage + >({ + java: 'java', + python: 'python', + javascript: 'javascript', + javascriptreact: 'javascript', + typescript: 'typescript', + typescriptreact: 'typescript', + csharp: 'csharp', + go: 'go', + golang: 'go', + ruby: 'ruby', + json: 'json', + jsonc: 'json', + yaml: 'yaml', + tf: 'tf', + hcl: 'tf', + terraform: 'tf', + terragrunt: 'tf', + packer: 'tf', + plaintext: 'plaintext', + c: 'c', + cpp: 'cpp', + php: 'php', + xml: 'plaintext', // xml does not exist in CodewhispererLanguage + toml: 'plaintext', + 'pip-requirements': 'plaintext', + 'java-properties': 'plaintext', + 'go.mod': 'plaintext', + 'go.sum': 'plaintext', + kotlin: 'kotlin', + scala: 'scala', + sh: 'shell', + shell: 'shell', + shellscript: 'shell', + brazilPackageConfig: 'plaintext', + }) + } + + public normalizeLanguage(languageId?: string): CodewhispererLanguage | undefined { + return this.supportedLanguageMap.get(languageId) + } + + public isLanguageSupported(languageId: string): boolean { + const lang = this.normalizeLanguage(languageId) + return lang !== undefined && lang !== 'plaintext' + } +} + +export const securityScanLanguageContext = new SecurityScanLanguageContext() diff --git a/packages/core/src/codewhisperer/util/showSsoPrompt.ts b/packages/core/src/codewhisperer/util/showSsoPrompt.ts new file mode 100644 index 00000000000..13af4cf771f --- /dev/null +++ b/packages/core/src/codewhisperer/util/showSsoPrompt.ts @@ -0,0 +1,67 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger } from '../../shared/logger/logger' +import { getStartUrl } from './getStartUrl' +import { showQuickPick } from '../../shared/ui/pickerPrompter' +import { AuthUtil } from './authUtil' +import { failedToConnectAwsBuilderId } from '../models/constants' +import { isValidResponse } from '../../shared/wizards/wizard' +import { CancellationError } from '../../shared/utilities/timeoutUtils' +import { ToolkitError } from '../../shared/errors' +import { createCommonButtons } from '../../shared/ui/buttons' +import { telemetry } from '../../shared/telemetry/telemetry' +import { createBuilderIdItem, createSsoItem, createIamItem } from '../../auth/utils' +import { Commands } from '../../shared/vscode/commands2' +import { vsCodeState } from '../models/model' + +export const showCodeWhispererConnectionPrompt = async () => { + const items = [createBuilderIdItem(), createSsoItem(), createCodeWhispererIamItem()] + + const resp = await showQuickPick(items, { + title: 'Amazon Q: Add Connection to AWS', + placeholder: 'Select a connection option to start using Amazon Q', + buttons: createCommonButtons() as vscode.QuickInputButton[], + }) + + if (!isValidResponse(resp)) { + telemetry.ui_click.emit({ elementId: 'connection_optionescapecancel' }) + throw new CancellationError('user') + } + switch (resp) { + case 'iam': + throw new Error('IAM is not supported') + case 'sso': { + return await getStartUrl() + } + case 'builderId': { + return await awsIdSignIn() + } + } +} + +export async function awsIdSignIn() { + getLogger().info('selected AWS ID sign in') + let conn + try { + conn = await AuthUtil.instance.connectToAwsBuilderId() + } catch (e) { + throw ToolkitError.chain(e, failedToConnectAwsBuilderId, { code: 'FailedToConnect' }) + } + vsCodeState.isFreeTierLimitReached = false + await Commands.tryExecute('aws.amazonq.enableCodeSuggestions') + + return conn +} + +export const createCodeWhispererIamItem = () => { + const item = createIamItem() + item.detail = 'Not supported by Amazon Q' + item.description = 'not supported' + item.invalidSelection = true + + return item +} diff --git a/packages/core/src/codewhisperer/util/supplementalContext/codeParsingUtil.ts b/packages/core/src/codewhisperer/util/supplementalContext/codeParsingUtil.ts new file mode 100644 index 00000000000..c73a2eebaa4 --- /dev/null +++ b/packages/core/src/codewhisperer/util/supplementalContext/codeParsingUtil.ts @@ -0,0 +1,130 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import path = require('path') +import { normalize } from '../../../shared/utilities/pathUtils' + +// TODO: functionExtractionPattern, classExtractionPattern, imposrtStatementRegex are not scalable and we will deprecate and remove the usage in the near future +export interface utgLanguageConfig { + extension: string + testFilenamePattern: RegExp[] + functionExtractionPattern?: RegExp + classExtractionPattern?: RegExp + importStatementRegExp?: RegExp +} + +export const utgLanguageConfigs: Record = { + // Java regexes are not working efficiently for class or function extraction + java: { + extension: '.java', + testFilenamePattern: [/^(.+)Test(\.java)$/, /(.+)Tests(\.java)$/, /Test(.+)(\.java)$/], + functionExtractionPattern: + /(?:(?:public|private|protected)\s+)(?:static\s+)?(?:[\w<>]+\s+)?(\w+)\s*\([^)]*\)\s*(?:(?:throws\s+\w+)?\s*)[{;]/gm, // TODO: Doesn't work for generice T functions. + classExtractionPattern: /(?<=^|\n)\s*public\s+class\s+(\w+)/gm, // TODO: Verify these. + importStatementRegExp: /import .*\.([a-zA-Z0-9]+);/, + }, + python: { + extension: '.py', + testFilenamePattern: [/^test_(.+)(\.py)$/, /^(.+)_test(\.py)$/], + functionExtractionPattern: /def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/g, // Worked fine + classExtractionPattern: /^class\s+(\w+)\s*:/gm, + importStatementRegExp: /from (.*) import.*/, + }, + typescript: { + extension: '.ts', + testFilenamePattern: [/^(.+)\.test(\.ts|\.tsx)$/, /^(.+)\.spec(\.ts|\.tsx)$/], + }, + javascript: { + extension: '.js', + testFilenamePattern: [/^(.+)\.test(\.js|\.jsx)$/, /^(.+)\.spec(\.js|\.jsx)$/], + }, + typescriptreact: { + extension: '.tsx', + testFilenamePattern: [/^(.+)\.test(\.ts|\.tsx)$/, /^(.+)\.spec(\.ts|\.tsx)$/], + }, + javascriptreact: { + extension: '.jsx', + testFilenamePattern: [/^(.+)\.test(\.js|\.jsx)$/, /^(.+)\.spec(\.js|\.jsx)$/], + }, +} + +export function extractFunctions(fileContent: string, regex?: RegExp) { + if (!regex) { + return [] + } + const functionNames: string[] = [] + let match: RegExpExecArray | null + + while ((match = regex.exec(fileContent)) !== null) { + functionNames.push(match[1]) + } + return functionNames +} + +export function extractClasses(fileContent: string, regex?: RegExp) { + if (!regex) { + return [] + } + const classNames: string[] = [] + let match: RegExpExecArray | null + + while ((match = regex.exec(fileContent)) !== null) { + classNames.push(match[1]) + } + return classNames +} + +export function countSubstringMatches(arr1: string[], arr2: string[]): number { + let count = 0 + for (const str1 of arr1) { + for (const str2 of arr2) { + if (str2.toLowerCase().includes(str1.toLowerCase())) { + count++ + } + } + } + return count +} + +export async function isTestFile( + filePath: string, + languageConfig: { + languageId: vscode.TextDocument['languageId'] + fileContent?: string + } +): Promise { + const normalizedFilePath = normalize(filePath) + const pathContainsTest = + normalizedFilePath.includes('tests/') || + normalizedFilePath.includes('test/') || + normalizedFilePath.includes('tst/') + const fileNameMatchTestPatterns = isTestFileByName(normalizedFilePath, languageConfig.languageId) + + if (pathContainsTest || fileNameMatchTestPatterns) { + return true + } + + return false +} + +function isTestFileByName(filePath: string, language: vscode.TextDocument['languageId']): boolean { + const languageConfig = utgLanguageConfigs[language] + if (!languageConfig) { + // We have enabled the support only for python and Java for this check + // as we depend on Regex for this validation. + return false + } + const testFilenamePattern = languageConfig.testFilenamePattern + + const filename = path.basename(filePath) + for (const pattern of testFilenamePattern) { + if (pattern.test(filename)) { + return true + } + } + + return false +} diff --git a/packages/core/src/codewhisperer/util/supplementalContext/crossFileContextUtil.ts b/packages/core/src/codewhisperer/util/supplementalContext/crossFileContextUtil.ts new file mode 100644 index 00000000000..aa732a5d70a --- /dev/null +++ b/packages/core/src/codewhisperer/util/supplementalContext/crossFileContextUtil.ts @@ -0,0 +1,400 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import path = require('path') +import { BM25Document, BM25Okapi } from './rankBm25' +import { + crossFileContextConfig, + supplementalContextTimeoutInMs, + supplementalContextMaxTotalLength, +} from '../../models/constants' +import { isTestFile } from './codeParsingUtil' +import { getFileDistance } from '../../../shared/filesystemUtilities' +import { getOpenFilesInWindow } from '../../../shared/utilities/editorUtilities' +import { getLogger } from '../../../shared/logger/logger' +import { + CodeWhispererSupplementalContext, + CodeWhispererSupplementalContextItem, + SupplementalContextStrategy, +} from '../../models/model' +import { waitUntil } from '../../../shared/utilities/timeoutUtils' +import { FeatureConfigProvider } from '../../../shared/featureConfig' +import fs from '../../../shared/fs/fs' +import { BaseLanguageClient } from 'vscode-languageclient' + +import { GetSupplementalContextParams, getSupplementalContextRequestType } from '@aws/language-server-runtimes/protocol' +type CrossFileSupportedLanguage = + | 'java' + | 'python' + | 'javascript' + | 'typescript' + | 'javascriptreact' + | 'typescriptreact' + +// TODO: ugly, can we make it prettier? like we have to manually type 'java', 'javascriptreact' which is error prone +// TODO: Move to another config file or constants file +// Supported language to its corresponding file ext +const supportedLanguageToDialects: Readonly>> = { + java: new Set(['.java']), + python: new Set(['.py']), + javascript: new Set(['.js', '.jsx']), + javascriptreact: new Set(['.js', '.jsx']), + typescript: new Set(['.ts', '.tsx']), + typescriptreact: new Set(['.ts', '.tsx']), +} + +function isCrossFileSupported(languageId: string): languageId is CrossFileSupportedLanguage { + return Object.keys(supportedLanguageToDialects).includes(languageId) +} + +interface Chunk { + fileName: string + content: string + nextContent: string + score?: number +} + +/** + * `none`: supplementalContext is not supported + * `opentabs`: opentabs_BM25 + * `codemap`: repomap + opentabs BM25 + * `bm25`: global_BM25 + * `default`: repomap + global_BM25 + */ +type SupplementalContextConfig = 'none' | 'opentabs' | 'codemap' | 'bm25' | 'default' + +export async function fetchSupplementalContextForSrc( + editor: vscode.TextEditor, + cancellationToken: vscode.CancellationToken, + languageClient?: BaseLanguageClient +): Promise | undefined> { + const supplementalContextConfig = getSupplementalContextConfig(editor.document.languageId) + + // not supported case + if (supplementalContextConfig === 'none') { + return undefined + } + + // fallback to opentabs if projectContext timeout + const opentabsContextPromise = waitUntil( + async function () { + return await fetchOpentabsContext(editor, cancellationToken) + }, + { timeout: supplementalContextTimeoutInMs, interval: 5, truthy: false } + ) + + // opentabs context will use bm25 and users' open tabs to fetch supplemental context + if (supplementalContextConfig === 'opentabs') { + const supContext = (await opentabsContextPromise) ?? [] + return { + supplementalContextItems: supContext, + strategy: supContext.length === 0 ? 'empty' : 'opentabs', + } + } + + // codemap will use opentabs context plus repomap if it's present + if (supplementalContextConfig === 'codemap') { + let strategy: SupplementalContextStrategy = 'empty' + let hasCodemap: boolean = false + let hasOpentabs: boolean = false + const opentabsContextAndCodemap = await waitUntil( + async function () { + const result: CodeWhispererSupplementalContextItem[] = [] + const opentabsContext = await fetchOpentabsContext(editor, cancellationToken) + const codemap = await fetchProjectContext(editor, 'codemap', languageClient) + + function addToResult(items: CodeWhispererSupplementalContextItem[]) { + for (const item of items) { + const curLen = result.reduce((acc, i) => acc + i.content.length, 0) + if (curLen + item.content.length < supplementalContextMaxTotalLength) { + result.push(item) + } + } + } + + if (codemap && codemap.length > 0) { + addToResult(codemap) + hasCodemap = true + } + + if (opentabsContext && opentabsContext.length > 0) { + addToResult(opentabsContext) + hasOpentabs = true + } + + return result + }, + { timeout: supplementalContextTimeoutInMs, interval: 5, truthy: false } + ) + + if (hasCodemap) { + strategy = 'codemap' + } else if (hasOpentabs) { + strategy = 'opentabs' + } else { + strategy = 'empty' + } + + return { + supplementalContextItems: opentabsContextAndCodemap ?? [], + strategy: strategy, + } + } + + // global bm25 without repomap + if (supplementalContextConfig === 'bm25') { + const projectBM25Promise = waitUntil( + async function () { + return await fetchProjectContext(editor, 'bm25', languageClient) + }, + { timeout: supplementalContextTimeoutInMs, interval: 5, truthy: false } + ) + + const [projectContext, opentabsContext] = await Promise.all([projectBM25Promise, opentabsContextPromise]) + if (projectContext && projectContext.length > 0) { + return { + supplementalContextItems: projectContext, + strategy: 'bm25', + } + } + + const supContext = opentabsContext ?? [] + return { + supplementalContextItems: supContext, + strategy: supContext.length === 0 ? 'empty' : 'opentabs', + } + } + + // global bm25 with repomap + const projectContextAndCodemapPromise = waitUntil( + async function () { + return await fetchProjectContext(editor, 'default', languageClient) + }, + { timeout: supplementalContextTimeoutInMs, interval: 5, truthy: false } + ) + + const [projectContext, opentabsContext] = await Promise.all([ + projectContextAndCodemapPromise, + opentabsContextPromise, + ]) + if (projectContext && projectContext.length > 0) { + return { + supplementalContextItems: projectContext, + strategy: 'default', + } + } + + return { + supplementalContextItems: opentabsContext ?? [], + strategy: 'opentabs', + } +} + +export async function fetchProjectContext( + editor: vscode.TextEditor, + target: 'default' | 'codemap' | 'bm25', + languageclient?: BaseLanguageClient +): Promise { + try { + if (languageclient) { + const request: GetSupplementalContextParams = { + filePath: editor.document.uri.fsPath, + } + const response = await languageclient.sendRequest(getSupplementalContextRequestType.method, request) + return response as CodeWhispererSupplementalContextItem[] + } + } catch (error) { + return [] + } + return [] +} + +export async function fetchOpentabsContext( + editor: vscode.TextEditor, + cancellationToken: vscode.CancellationToken +): Promise { + const codeChunksCalculated = crossFileContextConfig.numberOfChunkToFetch + + // Step 1: Get relevant cross files to refer + const relevantCrossFilePaths = await getCrossFileCandidates(editor) + + // Step 2: Split files to chunks with upper bound on chunkCount + // We restrict the total number of chunks to improve on latency. + // Chunk linking is required as we want to pass the next chunk value for matched chunk. + let chunkList: Chunk[] = [] + for (const relevantFile of relevantCrossFilePaths) { + const chunks: Chunk[] = await splitFileToChunks(relevantFile, crossFileContextConfig.numberOfLinesEachChunk) + const linkedChunks = linkChunks(chunks) + chunkList.push(...linkedChunks) + if (chunkList.length >= codeChunksCalculated) { + break + } + } + + // it's required since chunkList.push(...) is likely giving us a list of size > 60 + chunkList = chunkList.slice(0, codeChunksCalculated) + + // Step 3: Generate Input chunk (10 lines left of cursor position) + // and Find Best K chunks w.r.t input chunk using BM25 + const inputChunk: Chunk = getInputChunk(editor) + const bestChunks: Chunk[] = findBestKChunkMatches(inputChunk, chunkList, crossFileContextConfig.topK) + + // Step 4: Transform best chunks to supplemental contexts + const supplementalContexts: CodeWhispererSupplementalContextItem[] = [] + let totalLength = 0 + for (const chunk of bestChunks) { + totalLength += chunk.nextContent.length + + if (totalLength > crossFileContextConfig.maximumTotalLength) { + break + } + + supplementalContexts.push({ + filePath: chunk.fileName, + content: chunk.nextContent, + score: chunk.score, + }) + } + + // DO NOT send code chunk with empty content + getLogger().debug(`CodeWhisperer finished fetching crossfile context out of ${relevantCrossFilePaths.length} files`) + return supplementalContexts +} + +function findBestKChunkMatches(chunkInput: Chunk, chunkReferences: Chunk[], k: number): Chunk[] { + const chunkContentList = chunkReferences.map((chunk) => chunk.content) + + // performBM25Scoring returns the output in a sorted order (descending of scores) + const top3: BM25Document[] = new BM25Okapi(chunkContentList).topN(chunkInput.content, crossFileContextConfig.topK) + + return top3.map((doc) => { + // reference to the original metadata since BM25.top3 will sort the result + const chunkIndex = doc.index + const chunkReference = chunkReferences[chunkIndex] + return { + content: chunkReference.content, + fileName: chunkReference.fileName, + nextContent: chunkReference.nextContent, + score: doc.score, + } + }) +} + +/* This extract 10 lines to the left of the cursor from trigger file. + * This will be the inputquery to bm25 matching against list of cross-file chunks + */ +function getInputChunk(editor: vscode.TextEditor) { + const chunkSize = crossFileContextConfig.numberOfLinesEachChunk + const cursorPosition = editor.selection.active + const startLine = Math.max(cursorPosition.line - chunkSize, 0) + const endLine = Math.max(cursorPosition.line - 1, 0) + const inputChunkContent = editor.document.getText( + new vscode.Range(startLine, 0, endLine, editor.document.lineAt(endLine).text.length) + ) + const inputChunk: Chunk = { fileName: editor.document.fileName, content: inputChunkContent, nextContent: '' } + return inputChunk +} + +/** + * Util to decide if we need to fetch crossfile context since CodeWhisperer CrossFile Context feature is gated by userGroup and language level + * @param languageId: VSCode language Identifier + * @returns specifically returning undefined if the langueage is not supported, + * otherwise true/false depending on if the language is fully supported or not belonging to the user group + */ +function getSupplementalContextConfig(languageId: vscode.TextDocument['languageId']): SupplementalContextConfig { + if (!isCrossFileSupported(languageId)) { + return 'none' + } + + const group = FeatureConfigProvider.instance.getProjectContextGroup() + switch (group) { + default: + return 'codemap' + } +} + +/** + * This linking is required from science experimentations to pass the next contnet chunk + * when a given chunk context passes the match in BM25. + * Special handling is needed for last(its next points to its own) and first chunk + */ +export function linkChunks(chunks: Chunk[]) { + const updatedChunks: Chunk[] = [] + + // This additional chunk is needed to create a next pointer to chunk 0. + const firstChunk = chunks[0] + const firstChunkSubContent = firstChunk.content.split('\n').slice(0, 3).join('\n').trimEnd() + const newFirstChunk = { + fileName: firstChunk.fileName, + content: firstChunkSubContent, + nextContent: firstChunk.content, + } + updatedChunks.push(newFirstChunk) + + const n = chunks.length + for (let i = 0; i < n; i++) { + const chunk = chunks[i] + const nextChunk = i < n - 1 ? chunks[i + 1] : chunk + + chunk.nextContent = nextChunk.content + updatedChunks.push(chunk) + } + + return updatedChunks +} + +export async function splitFileToChunks(filePath: string, chunkSize: number): Promise { + const chunks: Chunk[] = [] + + const fileContent = (await fs.readFileText(filePath)).trimEnd() + const lines = fileContent.split('\n') + + for (let i = 0; i < lines.length; i += chunkSize) { + const chunkContent = lines.slice(i, Math.min(i + chunkSize, lines.length)).join('\n') + const chunk = { fileName: filePath, content: chunkContent.trimEnd(), nextContent: '' } + chunks.push(chunk) + } + return chunks +} + +/** + * This function will return relevant cross files sorted by file distance for the given editor file + * by referencing open files, imported files and same package files. + */ +export async function getCrossFileCandidates(editor: vscode.TextEditor): Promise { + const targetFile = editor.document.uri.fsPath + const language = editor.document.languageId as CrossFileSupportedLanguage + const dialects = supportedLanguageToDialects[language] + + /** + * Consider a file which + * 1. is different from the target + * 2. has the same file extension or it's one of the dialect of target file (e.g .js vs. .jsx) + * 3. is not a test file + */ + const unsortedCandidates = await getOpenFilesInWindow(async (candidateFile) => { + return ( + targetFile !== candidateFile && + (path.extname(targetFile) === path.extname(candidateFile) || + (dialects && dialects.has(path.extname(candidateFile)))) && + !(await isTestFile(candidateFile, { languageId: language })) + ) + }) + + return unsortedCandidates + .map((candidate) => { + return { + file: candidate, + fileDistance: getFileDistance(targetFile, candidate), + } + }) + .sort((file1, file2) => { + return file1.fileDistance - file2.fileDistance + }) + .map((fileToDistance) => { + return fileToDistance.file + }) +} diff --git a/packages/core/src/codewhisperer/util/supplementalContext/rankBm25.ts b/packages/core/src/codewhisperer/util/supplementalContext/rankBm25.ts new file mode 100644 index 00000000000..a2c77e0b10f --- /dev/null +++ b/packages/core/src/codewhisperer/util/supplementalContext/rankBm25.ts @@ -0,0 +1,137 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Implementation inspired by https://github.com/dorianbrown/rank_bm25/blob/990470ebbe6b28c18216fd1a8b18fe7446237dd6/rank_bm25.py#L52 + +export interface BM25Document { + content: string + /** The score that the document receives. */ + score: number + + index: number +} + +export abstract class BM25 { + protected readonly corpusSize: number + protected readonly avgdl: number + protected readonly idf: Map = new Map() + protected readonly docLen: number[] = [] + protected readonly docFreqs: Map[] = [] + protected readonly nd: Map = new Map() + + constructor( + protected readonly corpus: string[], + protected readonly tokenizer: (str: string) => string[] = defaultTokenizer, + protected readonly k1: number, + protected readonly b: number, + protected readonly epsilon: number + ) { + this.corpusSize = corpus.length + + let numDoc = 0 + for (const document of corpus.map((document) => { + return tokenizer(document) + })) { + this.docLen.push(document.length) + numDoc += document.length + + const frequencies = new Map() + for (const word of document) { + frequencies.set(word, (frequencies.get(word) || 0) + 1) + } + this.docFreqs.push(frequencies) + + for (const [word, _] of frequencies.entries()) { + this.nd.set(word, (this.nd.get(word) || 0) + 1) + } + } + + this.avgdl = numDoc / this.corpusSize + + this.calIdf(this.nd) + } + + abstract calIdf(nd: Map): void + + abstract score(query: string): BM25Document[] + + topN(query: string, n: number): BM25Document[] { + const notSorted = this.score(query) + const sorted = notSorted.sort((a, b) => b.score - a.score) + return sorted.slice(0, Math.min(n, sorted.length)) + } +} + +export class BM25Okapi extends BM25 { + constructor(corpus: string[], tokenizer: (str: string) => string[] = defaultTokenizer) { + super(corpus, tokenizer, 1.5, 0.75, 0.25) + } + + calIdf(nd: Map): void { + let idfSum = 0 + + const negativeIdfs: string[] = [] + for (const [word, freq] of nd) { + const idf = Math.log(this.corpusSize - freq + 0.5) - Math.log(freq + 0.5) + this.idf.set(word, idf) + idfSum += idf + + if (idf < 0) { + negativeIdfs.push(word) + } + } + + const averageIdf = idfSum / this.idf.size + const eps = this.epsilon * averageIdf + for (const word of negativeIdfs) { + this.idf.set(word, eps) + } + } + + score(query: string): BM25Document[] { + const queryWords = defaultTokenizer(query) + return this.docFreqs.map((docFreq, index) => { + let score = 0 + for (const [_, queryWord] of queryWords.entries()) { + const queryWordFreqForDocument = docFreq.get(queryWord) || 0 + const numerator = (this.idf.get(queryWord) || 0.0) * queryWordFreqForDocument * (this.k1 + 1) + const denominator = + queryWordFreqForDocument + this.k1 * (1 - this.b + (this.b * this.docLen[index]) / this.avgdl) + + score += numerator / denominator + } + + return { + content: this.corpus[index], + score: score, + index: index, + } + }) + } +} + +// TODO: This is a very simple tokenizer, we want to replace this by more sophisticated one. +function defaultTokenizer(content: string): string[] { + const regex = /\w+/g + const words = content.split(' ') + const result = [] + for (const word of words) { + const wordList = findAll(word, regex) + result.push(...wordList) + } + + return result +} + +function findAll(str: string, re: RegExp): string[] { + let match: RegExpExecArray | null + const matches: string[] = [] + + while ((match = re.exec(str)) !== null) { + matches.push(match[0]) + } + + return matches +} diff --git a/packages/core/src/codewhisperer/util/supplementalContext/supplementalContextUtil.ts b/packages/core/src/codewhisperer/util/supplementalContext/supplementalContextUtil.ts new file mode 100644 index 00000000000..3a8d66b8b42 --- /dev/null +++ b/packages/core/src/codewhisperer/util/supplementalContext/supplementalContextUtil.ts @@ -0,0 +1,139 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { fetchSupplementalContextForTest } from './utgUtils' +import { fetchSupplementalContextForSrc } from './crossFileContextUtil' +import { isTestFile } from './codeParsingUtil' +import * as vscode from 'vscode' +import { CancellationError } from '../../../shared/utilities/timeoutUtils' +import { ToolkitError } from '../../../shared/errors' +import { getLogger } from '../../../shared/logger/logger' +import { CodeWhispererSupplementalContext } from '../../models/model' +import * as os from 'os' +import { crossFileContextConfig } from '../../models/constants' +import { BaseLanguageClient } from 'vscode-languageclient' + +export async function fetchSupplementalContext( + editor: vscode.TextEditor, + cancellationToken: vscode.CancellationToken, + languageClient?: BaseLanguageClient +): Promise { + const timesBeforeFetching = Date.now() + + const isUtg = await isTestFile(editor.document.uri.fsPath, { + languageId: editor.document.languageId, + fileContent: editor.document.getText(), + }) + + let supplementalContextPromise: Promise< + Pick | undefined + > + + if (isUtg) { + supplementalContextPromise = fetchSupplementalContextForTest(editor, cancellationToken) + } else { + supplementalContextPromise = fetchSupplementalContextForSrc(editor, cancellationToken, languageClient) + } + + return supplementalContextPromise + .then((value) => { + if (value) { + const resBeforeTruncation = { + isUtg: isUtg, + isProcessTimeout: false, + supplementalContextItems: value.supplementalContextItems.filter( + (item) => item.content.trim().length !== 0 + ), + contentsLength: value.supplementalContextItems.reduce((acc, curr) => acc + curr.content.length, 0), + latency: Date.now() - timesBeforeFetching, + strategy: value.strategy, + } + + return truncateSuppelementalContext(resBeforeTruncation) + } else { + return undefined + } + }) + .catch((err) => { + if (err instanceof ToolkitError && err.cause instanceof CancellationError) { + return { + isUtg: isUtg, + isProcessTimeout: true, + supplementalContextItems: [], + contentsLength: 0, + latency: Date.now() - timesBeforeFetching, + strategy: 'empty', + } + } else { + getLogger().error( + `Fail to fetch supplemental context for target file ${editor.document.fileName}: ${err}` + ) + return undefined + } + }) +} + +/** + * Requirement + * - Maximum 5 supplemental context. + * - Each chunk can't exceed 10240 characters + * - Sum of all chunks can't exceed 20480 characters + */ +export function truncateSuppelementalContext( + context: CodeWhispererSupplementalContext +): CodeWhispererSupplementalContext { + let c = context.supplementalContextItems.map((item) => { + if (item.content.length > crossFileContextConfig.maxLengthEachChunk) { + return { + ...item, + content: truncateLineByLine(item.content, crossFileContextConfig.maxLengthEachChunk), + } + } else { + return item + } + }) + + if (c.length > crossFileContextConfig.maxContextCount) { + c = c.slice(0, crossFileContextConfig.maxContextCount) + } + + let curTotalLength = c.reduce((acc, cur) => { + return acc + cur.content.length + }, 0) + while (curTotalLength >= 20480 && c.length - 1 >= 0) { + const last = c[c.length - 1] + c = c.slice(0, -1) + curTotalLength -= last.content.length + } + + return { + ...context, + supplementalContextItems: c, + contentsLength: curTotalLength, + } +} + +export function truncateLineByLine(input: string, l: number): string { + const maxLength = l > 0 ? l : -1 * l + if (input.length === 0) { + return '' + } + + const shouldAddNewLineBack = input.endsWith(os.EOL) + let lines = input.trim().split(os.EOL) + let curLen = input.length + while (curLen > maxLength && lines.length - 1 >= 0) { + const last = lines[lines.length - 1] + lines = lines.slice(0, -1) + curLen -= last.length + 1 + } + + const r = lines.join(os.EOL) + if (shouldAddNewLineBack) { + return r + os.EOL + } else { + return r + } +} diff --git a/packages/core/src/codewhisperer/util/supplementalContext/utgUtils.ts b/packages/core/src/codewhisperer/util/supplementalContext/utgUtils.ts new file mode 100644 index 00000000000..0d33969773e --- /dev/null +++ b/packages/core/src/codewhisperer/util/supplementalContext/utgUtils.ts @@ -0,0 +1,229 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as path from 'path' +import { fs } from '../../../shared/fs/fs' +import * as vscode from 'vscode' +import { + countSubstringMatches, + extractClasses, + extractFunctions, + isTestFile, + utgLanguageConfig, + utgLanguageConfigs, +} from './codeParsingUtil' +import { ToolkitError } from '../../../shared/errors' +import { supplemetalContextFetchingTimeoutMsg } from '../../models/constants' +import { CancellationError } from '../../../shared/utilities/timeoutUtils' +import { utgConfig } from '../../models/constants' +import { getOpenFilesInWindow } from '../../../shared/utilities/editorUtilities' +import { getLogger } from '../../../shared/logger/logger' +import { CodeWhispererSupplementalContext, CodeWhispererSupplementalContextItem, UtgStrategy } from '../../models/model' + +const utgSupportedLanguages: vscode.TextDocument['languageId'][] = ['java', 'python'] + +type UtgSupportedLanguage = (typeof utgSupportedLanguages)[number] + +function isUtgSupportedLanguage(languageId: vscode.TextDocument['languageId']): languageId is UtgSupportedLanguage { + return utgSupportedLanguages.includes(languageId) +} + +export function shouldFetchUtgContext(languageId: vscode.TextDocument['languageId']): boolean | undefined { + if (!isUtgSupportedLanguage(languageId)) { + return undefined + } + + return languageId === 'java' +} + +/** + * This function attempts to find a focal file for the given trigger file. + * Attempt 1: If naming patterns followed correctly, source file can be found by name referencing. + * Attempt 2: Compare the function and class names of trigger file and all other open files in editor + * to find the closest match. + * Once found the focal file, we split it into multiple pieces as supplementalContext. + * @param editor + * @returns + */ +export async function fetchSupplementalContextForTest( + editor: vscode.TextEditor, + cancellationToken: vscode.CancellationToken +): Promise | undefined> { + const shouldProceed = shouldFetchUtgContext(editor.document.languageId) + + if (!shouldProceed) { + return shouldProceed === undefined ? undefined : { supplementalContextItems: [], strategy: 'empty' } + } + + const languageConfig = utgLanguageConfigs[editor.document.languageId] + + // TODO (Metrics): 1. Total number of calls to fetchSupplementalContextForTest + throwIfCancelled(cancellationToken) + + let crossSourceFile = await findSourceFileByName(editor, languageConfig, cancellationToken) + if (crossSourceFile) { + // TODO (Metrics): 2. Success count for fetchSourceFileByName (find source file by name) + getLogger().debug(`CodeWhisperer finished fetching utg context by file name`) + return { + supplementalContextItems: await generateSupplementalContextFromFocalFile( + crossSourceFile, + 'byName', + cancellationToken + ), + strategy: 'byName', + } + } + throwIfCancelled(cancellationToken) + + crossSourceFile = await findSourceFileByContent(editor, languageConfig, cancellationToken) + if (crossSourceFile) { + // TODO (Metrics): 3. Success count for fetchSourceFileByContent (find source file by content) + getLogger().debug(`CodeWhisperer finished fetching utg context by file content`) + return { + supplementalContextItems: await generateSupplementalContextFromFocalFile( + crossSourceFile, + 'byContent', + cancellationToken + ), + strategy: 'byContent', + } + } + + // TODO (Metrics): 4. Failure count - when unable to find focal file (supplemental context empty) + getLogger().debug(`CodeWhisperer failed to fetch utg context`) + return { + supplementalContextItems: [], + strategy: 'empty', + } +} + +async function generateSupplementalContextFromFocalFile( + filePath: string, + strategy: UtgStrategy, + cancellationToken: vscode.CancellationToken +): Promise { + const fileContent = await fs.readFileText(vscode.Uri.parse(filePath!).fsPath) + + // DO NOT send code chunk with empty content + if (fileContent.trim().length === 0) { + return [] + } + + return [ + { + filePath: filePath, + content: 'UTG\n' + fileContent.slice(0, Math.min(fileContent.length, utgConfig.maxSegmentSize)), + }, + ] +} + +async function findSourceFileByContent( + editor: vscode.TextEditor, + languageConfig: utgLanguageConfig, + cancellationToken: vscode.CancellationToken +): Promise { + const testFileContent = await fs.readFileText(editor.document.fileName) + const testElementList = extractFunctions(testFileContent, languageConfig.functionExtractionPattern) + + throwIfCancelled(cancellationToken) + + testElementList.push(...extractClasses(testFileContent, languageConfig.classExtractionPattern)) + + throwIfCancelled(cancellationToken) + + let sourceFilePath: string | undefined = undefined + let maxMatchCount = 0 + + if (testElementList.length === 0) { + // TODO: Add metrics here, as unable to parse test file using Regex. + return sourceFilePath + } + + const relevantFilePaths = await getRelevantUtgFiles(editor) + + throwIfCancelled(cancellationToken) + + // TODO (Metrics):Add metrics for relevantFilePaths length + for (const filePath of relevantFilePaths) { + throwIfCancelled(cancellationToken) + + const fileContent = await fs.readFileText(filePath) + const elementList = extractFunctions(fileContent, languageConfig.functionExtractionPattern) + elementList.push(...extractClasses(fileContent, languageConfig.classExtractionPattern)) + const matchCount = countSubstringMatches(elementList, testElementList) + if (matchCount > maxMatchCount) { + maxMatchCount = matchCount + sourceFilePath = filePath + } + } + return sourceFilePath +} + +async function getRelevantUtgFiles(editor: vscode.TextEditor): Promise { + const targetFile = editor.document.uri.fsPath + const language = editor.document.languageId + + return await getOpenFilesInWindow(async (candidateFile) => { + return ( + targetFile !== candidateFile && + path.extname(targetFile) === path.extname(candidateFile) && + !(await isTestFile(candidateFile, { languageId: language })) + ) + }) +} + +export function guessSrcFileName( + testFileName: string, + languageId: vscode.TextDocument['languageId'] +): string | undefined { + const languageConfig = utgLanguageConfigs[languageId] + if (!languageConfig) { + return undefined + } + + for (const pattern of languageConfig.testFilenamePattern) { + try { + const match = testFileName.match(pattern) + if (match) { + return match[1] + match[2] + } + } catch (err) { + if (err instanceof Error) { + getLogger().error( + `codewhisperer: error while guessing source file name from file ${testFileName} and pattern ${pattern}: ${err.message}` + ) + } + } + } + + return undefined +} + +async function findSourceFileByName( + editor: vscode.TextEditor, + languageConfig: utgLanguageConfig, + cancellationToken: vscode.CancellationToken +): Promise { + const testFileName = path.basename(editor.document.fileName) + const assumedSrcFileName = guessSrcFileName(testFileName, editor.document.languageId) + if (!assumedSrcFileName) { + return undefined + } + + const sourceFiles = await vscode.workspace.findFiles(`**/${assumedSrcFileName}`) + + throwIfCancelled(cancellationToken) + + if (sourceFiles.length > 0) { + return sourceFiles[0].toString() + } + return undefined +} + +function throwIfCancelled(token: vscode.CancellationToken): void | never { + if (token.isCancellationRequested) { + throw new ToolkitError(supplemetalContextFetchingTimeoutMsg, { cause: new CancellationError('timeout') }) + } +} diff --git a/packages/core/src/codewhisperer/util/telemetryHelper.ts b/packages/core/src/codewhisperer/util/telemetryHelper.ts new file mode 100644 index 00000000000..72f88ab9dc2 --- /dev/null +++ b/packages/core/src/codewhisperer/util/telemetryHelper.ts @@ -0,0 +1,881 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import globals from '../../shared/extensionGlobals' + +import { runtimeLanguageContext } from './runtimeLanguageContext' +import { codeWhispererClient as client, RecommendationsList } from '../client/codewhisperer' +import { LicenseUtil } from './licenseUtil' +import { + CodewhispererGettingStartedTask, + CodewhispererLanguage, + CodewhispererPreviousSuggestionState, + CodewhispererUserDecision, + CodewhispererUserTriggerDecision, + telemetry, +} from '../../shared/telemetry/telemetry' +import { CodewhispererCompletionType, CodewhispererSuggestionState } from '../../shared/telemetry/telemetry' +import { getImportCount } from './importAdderUtil' +import { CodeWhispererSettings } from './codewhispererSettings' +import { getSelectedCustomization } from './customizationUtil' +import { AuthUtil } from './authUtil' +import { isAwsError } from '../../shared/errors' +import { getLogger } from '../../shared/logger/logger' +import { session } from './codeWhispererSession' +import { CodeWhispererSupplementalContext } from '../models/model' +import { FeatureConfigProvider } from '../../shared/featureConfig' +import CodeWhispererUserClient, { CodeScanRemediationsEventType } from '../client/codewhispereruserclient' +import { CodeAnalysisScope as CodeAnalysisScopeClientSide } from '../models/constants' +import { sleep } from '../../shared/utilities/timeoutUtils' +import { getDiagnosticsDifferences, getDiagnosticsOfCurrentFile, toIdeDiagnostics } from './diagnosticsUtil' +import { Auth } from '../../auth/auth' + +export class TelemetryHelper { + // Some variables for client component latency + private _sdkApiCallEndTime = 0 + get sdkApiCallEndTime(): number { + return this._sdkApiCallEndTime + } + private _allPaginationEndTime = 0 + get allPaginationEndTime(): number { + return this._allPaginationEndTime + } + private _firstResponseRequestId = '' + get firstResponseRequestId(): string { + return this._firstResponseRequestId + } + // variables for user trigger decision + // these will be cleared after a invocation session + private sessionDecisions: CodewhispererUserTriggerDecision[] = [] + private triggerChar?: string = undefined + private prevTriggerDecision?: CodewhispererPreviousSuggestionState + private typeAheadLength = 0 + private timeSinceLastModification = 0 + private lastTriggerDecisionTime = 0 + private classifierResult?: number = undefined + private classifierThreshold?: number = undefined + // variables for tracking end to end sessions + public traceId: string = 'notSet' + + // use this to distinguish DocumentChangeEvent from CWSPR or from other sources + public lastSuggestionInDisplay = '' + + constructor() {} + + static #instance: TelemetryHelper + + public static get instance() { + return (this.#instance ??= new this()) + } + + public recordServiceInvocationTelemetry( + requestId: string, + sessionId: string, + lastSuggestionIndex: number, + result: 'Succeeded' | 'Failed', + duration: number | undefined, + language: CodewhispererLanguage, + taskType: CodewhispererGettingStartedTask | undefined, + reason: string, + supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined + ) { + const event = { + codewhispererAutomatedTriggerType: session.autoTriggerType, + codewhispererCursorOffset: session.startCursorOffset, + codewhispererCustomizationArn: getSelectedCustomization().arn, + CodewhispererGettingStartedTask: taskType, + codewhispererImportRecommendationEnabled: CodeWhispererSettings.instance.isImportRecommendationEnabled(), + codewhispererLastSuggestionIndex: lastSuggestionIndex, + codewhispererLanguage: language, + codewhispererLineNumber: session.startPos.line, + codewhispererRequestId: requestId ? requestId : undefined, + codewhispererSessionId: sessionId ? sessionId : undefined, + codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, + codewhispererSupplementalContextLatency: supplementalContextMetadata?.latency, + codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, + codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, + codewhispererTriggerType: session.triggerType, + credentialStartUrl: AuthUtil.instance.startUrl, + duration: duration || 0, + reason: reason ? reason.substring(0, 200) : undefined, + result, + traceId: this.traceId, + } + telemetry.codewhisperer_serviceInvocation.emit(event) + } + + public recordUserDecisionTelemetryForEmptyList( + requestIdList: string[], + sessionId: string, + paginationIndex: number, + language: CodewhispererLanguage, + supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined + ) { + const selectedCustomization = getSelectedCustomization() + const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile + + telemetry.codewhisperer_userTriggerDecision.emit({ + codewhispererAutomatedTriggerType: session.autoTriggerType, + codewhispererClassifierResult: this.classifierResult, + codewhispererClassifierThreshold: this.classifierThreshold, + codewhispererCompletionType: 'Line', + codewhispererCursorOffset: session.startCursorOffset, + codewhispererCustomizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, + codewhispererFeatureEvaluations: FeatureConfigProvider.instance.getFeatureConfigsTelemetry(), + codewhispererFirstRequestId: requestIdList[0], + codewhispererGettingStartedTask: session.taskType, + codewhispererLanguage: language, + codewhispererLineNumber: session.startPos.line, + codewhispererPreviousSuggestionState: this.prevTriggerDecision, + codewhispererSessionId: sessionId, + codewhispererSuggestionCount: 0, + codewhispererSuggestionImportCount: 0, + codewhispererSuggestionState: 'Empty', + codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, + codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, + // eslint-disable-next-line id-length + codewhispererSupplementalContextStrategyId: supplementalContextMetadata?.strategy, + codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, + codewhispererTimeSinceLastDocumentChange: this.timeSinceLastModification + ? this.timeSinceLastModification + : undefined, + codewhispererTimeSinceLastUserDecision: this.lastTriggerDecisionTime + ? Date.now() - this.lastTriggerDecisionTime + : undefined, + codewhispererTimeToFirstRecommendation: session.timeToFirstRecommendation, + codewhispererTriggerType: session.triggerType, + codewhispererTypeaheadLength: this.typeAheadLength, + credentialStartUrl: AuthUtil.instance.startUrl, + traceId: this.traceId, + }) + + client + .sendTelemetryEvent({ + telemetryEvent: { + userTriggerDecisionEvent: { + sessionId: sessionId, + requestId: requestIdList[0], + customizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(language), + }, + completionType: 'LINE', + suggestionState: 'EMPTY', + recommendationLatencyMilliseconds: 0, + triggerToResponseLatencyMilliseconds: session.timeToFirstRecommendation, + perceivedLatencyMilliseconds: session.perceivedLatency, + timestamp: new Date(Date.now()), + suggestionReferenceCount: 0, + generatedLine: 0, + numberOfRecommendations: 0, + acceptedCharacterCount: 0, + }, + }, + profileArn: profile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + + getLogger().error(`Failed to invoke sendTelemetryEvent, requestId: ${requestId ?? ''}`) + }) + } + + /** + * This function is to record the user decision on each of the suggestion in the list of CodeWhisperer recommendations. + * @param recommendations the recommendations + * @param acceptIndex the index of the accepted suggestion in the corresponding list of CodeWhisperer response. + * If this function is not called on acceptance, then acceptIndex == -1 + * @param languageId the language ID of the current document in current active editor + * @param paginationIndex the index of pagination calls + * @param recommendationSuggestionState the key-value mapping from index to suggestion state + */ + + public recordUserDecisionTelemetry( + requestIdList: string[], + sessionId: string, + recommendations: RecommendationsList, + acceptIndex: number, + paginationIndex: number, + completionTypes: Map, + recommendationSuggestionState?: Map, + supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined + ) { + const events: CodewhispererUserDecision[] = [] + // emit user decision telemetry + for (const [i, _elem] of recommendations.entries()) { + let uniqueSuggestionReferences: string | undefined = undefined + const uniqueLicenseSet = LicenseUtil.getUniqueLicenseNames(_elem.references) + if (uniqueLicenseSet.size > 0) { + uniqueSuggestionReferences = JSON.stringify(Array.from(uniqueLicenseSet)) + } + if (_elem.content.length === 0) { + recommendationSuggestionState?.set(i, 'Empty') + } + const event: CodewhispererUserDecision = { + // TODO: maintain a list of RecommendationContexts with both recommendation and requestId in it, instead of two separate list items. + codewhispererCompletionType: this.getCompletionType(i, completionTypes), + codewhispererGettingStartedTask: session.taskType, + codewhispererLanguage: session.language, + codewhispererPaginationProgress: paginationIndex, + codewhispererRequestId: requestIdList[i], + codewhispererSessionId: sessionId ? sessionId : undefined, + codewhispererSuggestionImportCount: getImportCount(_elem), + codewhispererSuggestionIndex: i, + codewhispererSuggestionState: this.getSuggestionState(i, acceptIndex, recommendationSuggestionState), + codewhispererSuggestionReferenceCount: _elem.references ? _elem.references.length : 0, + codewhispererSuggestionReferences: uniqueSuggestionReferences, + codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, + codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, + codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, + codewhispererTriggerType: session.triggerType, + credentialStartUrl: AuthUtil.instance.startUrl, + traceId: this.traceId, + } + events.push(event) + } + + // aggregate suggestion references count + const referenceCount = this.getAggregatedSuggestionReferenceCount(events) + + // aggregate user decision events at requestId level + const aggregatedEvent = this.aggregateUserDecisionByRequest(events, requestIdList[0], sessionId) + if (aggregatedEvent) { + this.sessionDecisions.push(aggregatedEvent) + } + + // TODO: use a ternary for this + let acceptedRecommendationContent + if (acceptIndex !== -1 && recommendations[acceptIndex] !== undefined) { + acceptedRecommendationContent = recommendations[acceptIndex].content + } else { + acceptedRecommendationContent = '' + } + + // after we have all request level user decisions, aggregate them at session level and send + this.sendUserTriggerDecisionTelemetry( + sessionId, + acceptedRecommendationContent, + referenceCount, + supplementalContextMetadata + ) + } + + public aggregateUserDecisionByRequest( + events: CodewhispererUserDecision[], + requestId: string, + sessionId: string, + supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined + ) { + // the request level user decision will contain information from both the service_invocation event + // and the user_decision events for recommendations within that request + if (!events.length) { + return + } + const aggregated: CodewhispererUserTriggerDecision = { + codewhispererAutomatedTriggerType: session.autoTriggerType, + codewhispererCompletionType: events[0].codewhispererCompletionType, + codewhispererCursorOffset: session.startCursorOffset, + codewhispererFirstRequestId: requestId, + codewhispererGettingStartedTask: session.taskType, + codewhispererLanguage: events[0].codewhispererLanguage, + codewhispererLineNumber: session.startPos.line, + codewhispererSessionId: sessionId, + codewhispererSuggestionCount: events.length, + codewhispererSuggestionImportCount: events + .map((e) => e.codewhispererSuggestionImportCount || 0) + .reduce((a, b) => a + b, 0), + codewhispererSuggestionState: this.getAggregatedSuggestionState(events), + codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, + codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, + codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, + codewhispererTriggerType: events[0].codewhispererTriggerType, + codewhispererTypeaheadLength: 0, + credentialStartUrl: events[0].credentialStartUrl, + traceId: this.traceId, + } + return aggregated + } + + public sendUserTriggerDecisionTelemetry( + sessionId: string, + acceptedRecommendationContent: string, + referenceCount: number, + supplementalContextMetadata?: CodeWhispererSupplementalContext | undefined + ) { + // the user trigger decision will aggregate information from request level user decisions within one session + // and add additional session level insights + if (!this.sessionDecisions.length) { + return + } + + // TODO: add partial acceptance related metrics + const autoTriggerType = this.sessionDecisions[0].codewhispererAutomatedTriggerType + const language = this.sessionDecisions[0].codewhispererLanguage + const aggregatedCompletionType = this.sessionDecisions[0].codewhispererCompletionType + const aggregatedSuggestionState = this.getAggregatedSuggestionState(this.sessionDecisions) + const selectedCustomization = getSelectedCustomization() + const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile + const generatedLines = + acceptedRecommendationContent.trim() === '' ? 0 : acceptedRecommendationContent.split('\n').length + const suggestionCount = this.sessionDecisions + .map((e) => e.codewhispererSuggestionCount) + .reduce((a, b) => a + b, 0) + + const aggregated: CodewhispererUserTriggerDecision = { + codewhispererAutomatedTriggerType: autoTriggerType, + codewhispererCharactersAccepted: acceptedRecommendationContent.length, + codewhispererClassifierResult: this.classifierResult, + codewhispererClassifierThreshold: this.classifierThreshold, + codewhispererCompletionType: aggregatedCompletionType, + codewhispererCursorOffset: this.sessionDecisions[0].codewhispererCursorOffset, + codewhispererCustomizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, + codewhispererFeatureEvaluations: FeatureConfigProvider.instance.getFeatureConfigsTelemetry(), + codewhispererFirstRequestId: this.sessionDecisions[0].codewhispererFirstRequestId, + codewhispererGettingStartedTask: session.taskType, + codewhispererLanguage: language, + codewhispererLineNumber: this.sessionDecisions[0].codewhispererLineNumber, + codewhispererPreviousSuggestionState: this.prevTriggerDecision, + codewhispererSessionId: this.sessionDecisions[0].codewhispererSessionId, + codewhispererSuggestionCount: suggestionCount, + codewhispererSuggestionImportCount: this.sessionDecisions + .map((e) => e.codewhispererSuggestionImportCount || 0) + .reduce((a, b) => a + b, 0), + codewhispererSuggestionState: aggregatedSuggestionState, + codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg, + codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength, + // eslint-disable-next-line id-length + codewhispererSupplementalContextStrategyId: supplementalContextMetadata?.strategy, + codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout, + codewhispererTimeSinceLastDocumentChange: this.timeSinceLastModification + ? this.timeSinceLastModification + : undefined, + codewhispererTimeSinceLastUserDecision: this.lastTriggerDecisionTime + ? Date.now() - this.lastTriggerDecisionTime + : undefined, + codewhispererTimeToFirstRecommendation: session.timeToFirstRecommendation, + codewhispererTriggerCharacter: autoTriggerType === 'SpecialCharacters' ? this.triggerChar : undefined, + codewhispererTriggerType: this.sessionDecisions[0].codewhispererTriggerType, + codewhispererTypeaheadLength: this.typeAheadLength, + credentialStartUrl: this.sessionDecisions[0].credentialStartUrl, + traceId: this.traceId, + } + telemetry.codewhisperer_userTriggerDecision.emit(aggregated) + this.prevTriggerDecision = this.getAggregatedSuggestionState(this.sessionDecisions) + this.lastTriggerDecisionTime = Date.now() + + // When we send a userTriggerDecision for neither Accept nor Reject, service side should not use this value + // and client side will set this value to 0.0. + let e2eLatency = session.firstSuggestionShowTime - session.invokeSuggestionStartTime + if (aggregatedSuggestionState !== 'Reject' && aggregatedSuggestionState !== 'Accept') { + e2eLatency = 0.0 + } + + const userTriggerDecisionEvent: CodeWhispererUserClient.UserTriggerDecisionEvent = { + sessionId: sessionId, + requestId: this.sessionDecisions[0].codewhispererFirstRequestId, + customizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(this.sessionDecisions[0].codewhispererLanguage), + }, + completionType: this.getSendTelemetryCompletionType(aggregatedCompletionType), + suggestionState: this.getSendTelemetrySuggestionState(aggregatedSuggestionState), + recommendationLatencyMilliseconds: e2eLatency, + triggerToResponseLatencyMilliseconds: session.timeToFirstRecommendation, + perceivedLatencyMilliseconds: session.perceivedLatency, + timestamp: new Date(Date.now()), + suggestionReferenceCount: referenceCount, + generatedLine: generatedLines, + numberOfRecommendations: suggestionCount, + acceptedCharacterCount: acceptedRecommendationContent.length, + suggestionType: 'COMPLETIONS', + } + this.resetUserTriggerDecisionTelemetry() + + const sendEvent = () => + client + .sendTelemetryEvent({ + telemetryEvent: { userTriggerDecisionEvent: userTriggerDecisionEvent }, + profileArn: profile?.arn, + }) + .catch((error) => { + const requestId = isAwsError(error) ? error.requestId : undefined + getLogger().debug( + `Failed to sendTelemetryEvent to CodeWhisperer, requestId: ${requestId ?? ''}, message: ${error.message}` + ) + }) + + if (userTriggerDecisionEvent.suggestionState === 'ACCEPT' && Auth.instance.isInternalAmazonUser()) { + // wait 1 seconds for the user installed 3rd party LSP + // to update its diagnostics. + void sleep(1000).then(() => { + const diagnosticDiff = getDiagnosticsDifferences( + session.diagnosticsBeforeAccept, + getDiagnosticsOfCurrentFile() + ) + userTriggerDecisionEvent.addedIdeDiagnostics = diagnosticDiff.added.map((it) => toIdeDiagnostics(it)) + userTriggerDecisionEvent.removedIdeDiagnostics = diagnosticDiff.removed.map((it) => + toIdeDiagnostics(it) + ) + void sendEvent() + }) + } else { + void sendEvent() + } + } + + public getLastTriggerDecisionForClassifier() { + if (this.lastTriggerDecisionTime && Date.now() - this.lastTriggerDecisionTime <= 2 * 60 * 1000) { + return this.prevTriggerDecision + } + } + + public setClassifierResult(classifierResult: number) { + this.classifierResult = classifierResult + } + + public setClassifierThreshold(classifierThreshold: number) { + this.classifierThreshold = classifierThreshold + } + + public setTriggerCharForUserTriggerDecision(triggerChar: string) { + this.triggerChar = triggerChar + } + + public setTypeAheadLength(typeAheadLength: number) { + this.typeAheadLength = typeAheadLength + } + + public setTimeSinceLastModification(timeSinceLastModification: number) { + this.timeSinceLastModification = timeSinceLastModification + } + + public setTraceId(traceId: string) { + this.traceId = traceId + } + + private resetUserTriggerDecisionTelemetry() { + this.sessionDecisions = [] + this.triggerChar = '' + this.typeAheadLength = 0 + this.timeSinceLastModification = 0 + session.timeToFirstRecommendation = 0 + session.perceivedLatency = 0 + this.classifierResult = undefined + this.classifierThreshold = undefined + } + + private getSendTelemetryCompletionType(completionType: CodewhispererCompletionType) { + return completionType === 'Block' ? 'BLOCK' : 'LINE' + } + + private getAggregatedSuggestionState( + // if there is any Accept within the session, mark the session as Accept + // if there is any Reject within the session, mark the session as Reject + // if all recommendations within the session are empty, mark the session as Empty + // otherwise mark the session as Discard + events: CodewhispererUserDecision[] | CodewhispererUserTriggerDecision[] + ): CodewhispererPreviousSuggestionState { + let isEmpty = true + for (const event of events) { + if (event.codewhispererSuggestionState === 'Accept') { + return 'Accept' + } else if (event.codewhispererSuggestionState === 'Reject') { + return 'Reject' + } else if (event.codewhispererSuggestionState !== 'Empty') { + isEmpty = false + } + } + return isEmpty ? 'Empty' : 'Discard' + } + + private getSendTelemetrySuggestionState(state: CodewhispererPreviousSuggestionState) { + if (state === 'Accept') { + return 'ACCEPT' + } else if (state === 'Reject') { + return 'REJECT' + } else if (state === 'Discard') { + return 'DISCARD' + } + return 'EMPTY' + } + + private getAggregatedSuggestionReferenceCount( + events: CodewhispererUserDecision[] + // if there is reference for accepted recommendation within the session, mark the reference number + // as 1, otherwise mark the session as 0 + ) { + for (const event of events) { + if (event.codewhispererSuggestionState === 'Accept' && event.codewhispererSuggestionReferenceCount !== 0) { + return 1 + } + } + return 0 + } + + public getSuggestionState( + i: number, + acceptIndex: number, + recommendationSuggestionState?: Map + ): CodewhispererSuggestionState { + const state = recommendationSuggestionState?.get(i) + if (state && ['Empty', 'Filter', 'Discard'].includes(state)) { + return state as CodewhispererSuggestionState + } else if (recommendationSuggestionState !== undefined && recommendationSuggestionState.get(i) !== 'Showed') { + return 'Unseen' + } + if (acceptIndex === -1) { + return 'Reject' + } + return i === acceptIndex ? 'Accept' : 'Ignore' + } + + public getCompletionType(i: number, completionTypes: Map) { + return completionTypes.get(i) || 'Line' + } + + public isTelemetryEnabled(): boolean { + return globals.telemetry.telemetryEnabled + } + + public resetClientComponentLatencyTime() { + session.invokeSuggestionStartTime = 0 + session.preprocessEndTime = 0 + session.sdkApiCallStartTime = 0 + this._sdkApiCallEndTime = 0 + session.fetchCredentialStartTime = 0 + session.firstSuggestionShowTime = 0 + this._allPaginationEndTime = 0 + this._firstResponseRequestId = '' + } + + public setPreprocessEndTime() { + if (session.preprocessEndTime !== 0) { + getLogger().warn(`inline completion preprocessEndTime has been set and not reset correctly`) + } + session.preprocessEndTime = Date.now() + } + + /** This method is assumed to be invoked first at the start of execution **/ + public setInvokeSuggestionStartTime() { + this.resetClientComponentLatencyTime() + session.invokeSuggestionStartTime = Date.now() + } + + public setSdkApiCallEndTime() { + if (this._sdkApiCallEndTime === 0 && session.sdkApiCallStartTime !== 0) { + this._sdkApiCallEndTime = Date.now() + } + } + + public setAllPaginationEndTime() { + if (this._allPaginationEndTime === 0 && this._sdkApiCallEndTime !== 0) { + this._allPaginationEndTime = Date.now() + } + } + + public setFirstSuggestionShowTime() { + if (session.firstSuggestionShowTime === 0 && this._sdkApiCallEndTime !== 0) { + session.firstSuggestionShowTime = Date.now() + } + } + + public setFirstResponseRequestId(requestId: string) { + if (this._firstResponseRequestId === '') { + this._firstResponseRequestId = requestId + } + } + + // report client component latency after all pagination call finish + // and at least one suggestion is shown to the user + public tryRecordClientComponentLatency() { + if (session.firstSuggestionShowTime === 0 || this._allPaginationEndTime === 0) { + return + } + telemetry.codewhisperer_clientComponentLatency.emit({ + codewhispererAllCompletionsLatency: this._allPaginationEndTime - session.sdkApiCallStartTime, + codewhispererCompletionType: 'Line', + codewhispererCredentialFetchingLatency: session.sdkApiCallStartTime - session.fetchCredentialStartTime, + codewhispererCustomizationArn: getSelectedCustomization().arn, + codewhispererEndToEndLatency: session.firstSuggestionShowTime - session.invokeSuggestionStartTime, + codewhispererFirstCompletionLatency: this._sdkApiCallEndTime - session.sdkApiCallStartTime, + codewhispererLanguage: session.language, + codewhispererPostprocessingLatency: session.firstSuggestionShowTime - this._sdkApiCallEndTime, + codewhispererPreprocessingLatency: session.preprocessEndTime - session.invokeSuggestionStartTime, + codewhispererRequestId: this._firstResponseRequestId, + codewhispererSessionId: session.sessionId, + codewhispererTriggerType: session.triggerType, + credentialStartUrl: AuthUtil.instance.startUrl, + }) + } + public sendCodeScanEvent(languageId: string, jobId: string) { + getLogger().debug(`start sendCodeScanEvent: jobId: "${jobId}", languageId: "${languageId}"`) + + client + .sendTelemetryEvent({ + telemetryEvent: { + codeScanEvent: { + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(languageId as CodewhispererLanguage), + }, + codeScanJobId: jobId, + timestamp: new Date(Date.now()), + }, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + + getLogger().debug( + `Failed to sendCodeScanEvent to CodeWhisperer, requestId: ${requestId ?? ''}, message: ${ + error.message + }` + ) + }) + } + + public sendCodeScanSucceededEvent( + language: string, + jobId: string, + numberOfFindings: number, + scope: CodeAnalysisScopeClientSide + ) { + client + .sendTelemetryEvent({ + telemetryEvent: { + codeScanSucceededEvent: { + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), + }, + codeScanJobId: jobId, + numberOfFindings: numberOfFindings, + timestamp: new Date(Date.now()), + codeAnalysisScope: scope === CodeAnalysisScopeClientSide.FILE_AUTO ? 'FILE' : 'PROJECT', + }, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + + getLogger().debug( + `Failed to sendTelemetryEvent for code scan success, requestId: ${requestId ?? ''}, message: ${ + error.message + }` + ) + }) + } + + public sendCodeScanFailedEvent(language: string, jobId: string, scope: CodeAnalysisScopeClientSide) { + client + .sendTelemetryEvent({ + telemetryEvent: { + codeScanFailedEvent: { + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), + }, + codeScanJobId: jobId, + codeAnalysisScope: scope === CodeAnalysisScopeClientSide.FILE_AUTO ? 'FILE' : 'PROJECT', + timestamp: new Date(Date.now()), + }, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + getLogger().debug( + `Failed to sendTelemetryEvent for code scan failure, requestId: ${requestId ?? ''}, message: ${ + error.message + }` + ) + }) + } + + public sendCodeFixGenerationEvent( + jobId: string, + language?: string, + ruleId?: string, + detectorId?: string, + linesOfCodeGenerated?: number, + charsOfCodeGenerated?: number + ) { + client + .sendTelemetryEvent({ + telemetryEvent: { + codeFixGenerationEvent: { + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), + }, + jobId, + ruleId, + detectorId, + linesOfCodeGenerated, + charsOfCodeGenerated, + }, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + getLogger().debug( + `Failed to sendTelemetryEvent for code fix generation, requestId: ${requestId ?? ''}, message: ${ + error.message + }` + ) + }) + } + + public sendCodeFixAcceptanceEvent( + jobId: string, + language?: string, + ruleId?: string, + detectorId?: string, + linesOfCodeAccepted?: number, + charsOfCodeAccepted?: number + ) { + client + .sendTelemetryEvent({ + telemetryEvent: { + codeFixAcceptanceEvent: { + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), + }, + jobId, + ruleId, + detectorId, + linesOfCodeAccepted, + charsOfCodeAccepted, + }, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + getLogger().debug( + `Failed to sendTelemetryEvent for code fix acceptance, requestId: ${requestId ?? ''}, message: ${ + error.message + }` + ) + }) + } + + public sendTestGenerationEvent( + groupName: string, + jobId: string, + language?: string, + numberOfUnitTestCasesGenerated?: number, + numberOfUnitTestCasesAccepted?: number, + linesOfCodeGenerated?: number, + linesOfCodeAccepted?: number, + charsOfCodeGenerated?: number, + charsOfCodeAccepted?: number + ) { + client + .sendTelemetryEvent({ + telemetryEvent: { + testGenerationEvent: { + programmingLanguage: { + languageName: runtimeLanguageContext.toRuntimeLanguage(language as CodewhispererLanguage), + }, + jobId, + groupName, + ideCategory: 'VSCODE', + numberOfUnitTestCasesGenerated, + numberOfUnitTestCasesAccepted, + linesOfCodeGenerated, + linesOfCodeAccepted, + charsOfCodeGenerated, + charsOfCodeAccepted, + timestamp: new Date(Date.now()), + }, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + getLogger().debug( + `Failed to sendTelemetryEvent for test generation, requestId: ${requestId ?? ''}, message: ${ + error.message + }` + ) + }) + } + + public sendCodeScanRemediationsEvent( + languageId?: string, + codeScanRemediationEventType?: CodeScanRemediationsEventType, + detectorId?: string, + findingId?: string, + ruleId?: string, + component?: string, + reason?: string, + result?: string, + includesFix?: boolean + ) { + client + .sendTelemetryEvent({ + telemetryEvent: { + codeScanRemediationsEvent: { + programmingLanguage: languageId + ? { + languageName: runtimeLanguageContext.toRuntimeLanguage( + languageId as CodewhispererLanguage + ), + } + : undefined, + CodeScanRemediationsEventType: codeScanRemediationEventType, + detectorId: detectorId, + findingId: findingId, + ruleId: ruleId, + component: component, + reason: reason, + result: result, + includesFix: includesFix, + timestamp: new Date(Date.now()), + }, + }, + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + .then() + .catch((error) => { + let requestId: string | undefined + if (isAwsError(error)) { + requestId = error.requestId + } + getLogger().debug( + `Failed to sendCodeScanRemediationsEvent to CodeWhisperer, requestId: ${ + requestId ?? '' + }, message: ${error.message}` + ) + }) + } +} diff --git a/packages/core/src/codewhisperer/util/userGroupUtil.ts b/packages/core/src/codewhisperer/util/userGroupUtil.ts new file mode 100644 index 00000000000..e704e084ef8 --- /dev/null +++ b/packages/core/src/codewhisperer/util/userGroupUtil.ts @@ -0,0 +1,73 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { UserGroup } from '../models/constants' +import globals from '../../shared/extensionGlobals' +import { extensionVersion } from '../../shared/vscode/env' + +export class CodeWhispererUserGroupSettings { + private _userGroup: UserGroup | undefined + private _version: string | undefined + + public get userGroup(): UserGroup { + if (!this._userGroup) { + return this.determineUserGroupIfNeeded() + } else { + return this._userGroup + } + } + + // Visible for testing, DO NOT use on production + public set userGroup(userGroup: UserGroup) { + this._userGroup = userGroup + } + + public get version(): string | undefined { + return this._version + } + + // for testing purpose + public reset() { + this._userGroup = undefined + this._version = undefined + } + + private determineUserGroupIfNeeded(): UserGroup { + const userGroupMetadata = globals.globalState.get<{ group: UserGroup; version: string }>( + 'CODEWHISPERER_USER_GROUP' + ) + // use the same userGroup setting if and only if they are the same version of plugin + if (userGroupMetadata && userGroupMetadata.version && userGroupMetadata.version === extensionVersion) { + this._userGroup = userGroupMetadata.group + this._version = userGroupMetadata.version + return userGroupMetadata.group + } + + // otherwise, reassign group and reset the version + this._version = extensionVersion + this._userGroup = this.guessUserGroup() + + globals.globalState.tryUpdate('CODEWHISPERER_USER_GROUP', { + group: this._userGroup, + version: this._version, + }) + + return this._userGroup + } + + private guessUserGroup(): UserGroup { + return UserGroup.Control + } + + static #instance: CodeWhispererUserGroupSettings | undefined + + public static get instance() { + return (this.#instance ??= new CodeWhispererUserGroupSettings()) + } + + public static getUserGroup() { + return CodeWhispererUserGroupSettings.instance.userGroup + } +} diff --git a/packages/core/src/codewhisperer/util/zipUtil.ts b/packages/core/src/codewhisperer/util/zipUtil.ts new file mode 100644 index 00000000000..719116efdc7 --- /dev/null +++ b/packages/core/src/codewhisperer/util/zipUtil.ts @@ -0,0 +1,580 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import path from 'path' +import { tempDirPath } from '../../shared/filesystemUtilities' +import { getLogger } from '../../shared/logger/logger' +import * as CodeWhispererConstants from '../models/constants' +import { ToolkitError } from '../../shared/errors' +import { fs } from '../../shared/fs/fs' +import { getLoggerForScope } from '../service/securityScanHandler' +import { runtimeLanguageContext } from './runtimeLanguageContext' +import { CodewhispererLanguage } from '../../shared/telemetry/telemetry.gen' +import { CurrentWsFolders, collectFiles, defaultExcludePatterns } from '../../shared/utilities/workspaceUtils' +import { + FileSizeExceededError, + NoActiveFileError, + NoSourceFilesError, + ProjectSizeExceededError, +} from '../models/errors' +import { FeatureUseCase } from '../models/constants' +import { ChildProcess, ChildProcessOptions } from '../../shared/utilities/processUtils' +import { removeAnsi } from '../../shared/utilities/textUtilities' +import { normalize } from '../../shared/utilities/pathUtils' +import { ZipStream } from '../../shared/utilities/zipStream' + +export interface ZipMetadata { + rootDir: string + zipFilePath: string + scannedFiles: Set + srcPayloadSizeInBytes: number + buildPayloadSizeInBytes: number + zipFileSizeInBytes: number + lines: number + language: CodewhispererLanguage | undefined +} + +export const ZipConstants = { + newlineRegex: /\r?\n/, + gitignoreFilename: '.gitignore', + knownBinaryFileExts: ['.class'], + codeDiffFilePath: 'codeDiff/code.diff', +} + +interface GitDiffOptions { + projectPath: string + projectName: string + filePath?: string + scope?: CodeWhispererConstants.CodeAnalysisScope +} + +export class ZipUtil { + protected _pickedSourceFiles: Set = new Set() + protected _pickedBuildFiles: Set = new Set() + protected _totalSize: number = 0 + protected _totalBuildSize: number = 0 + protected _tmpDir: string = tempDirPath + protected _zipDir: string = '' + protected _totalLines: number = 0 + protected _fetchedDirs: Set = new Set() + protected _language: CodewhispererLanguage | undefined + protected _timestamp: string = Date.now().toString() + constructor() {} + + getFileScanPayloadSizeLimitInBytes(): number { + return CodeWhispererConstants.fileScanPayloadSizeLimitBytes + } + + getProjectScanPayloadSizeLimitInBytes(): number { + return CodeWhispererConstants.projectScanPayloadSizeLimitBytes + } + + public getProjectPaths() { + const workspaceFolders = vscode.workspace.workspaceFolders + return workspaceFolders?.map((folder) => folder.uri.fsPath) ?? [] + } + + public getProjectPath(filePath: string) { + const fileUri = vscode.Uri.file(filePath) + const workspaceFolder = vscode.workspace.getWorkspaceFolder(fileUri) + return workspaceFolder?.uri.fsPath + } + + protected async getTextContent(uri: vscode.Uri) { + const document = await vscode.workspace.openTextDocument(uri) + const content = document.getText() + return content + } + + public reachSizeLimit(size: number, scope: CodeWhispererConstants.CodeAnalysisScope): boolean { + if ( + scope === CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO || + scope === CodeWhispererConstants.CodeAnalysisScope.FILE_ON_DEMAND + ) { + return size > this.getFileScanPayloadSizeLimitInBytes() + } else { + return size > this.getProjectScanPayloadSizeLimitInBytes() + } + } + + public willReachSizeLimit(current: number, adding: number): boolean { + const willReachLimit = current + adding > this.getProjectScanPayloadSizeLimitInBytes() + return willReachLimit + } + + protected async zipFile(uri: vscode.Uri | undefined, scope: CodeWhispererConstants.CodeAnalysisScope) { + if (!uri) { + throw new NoActiveFileError() + } + const zip = new ZipStream() + + const content = await this.getTextContent(uri) + + const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri) + if (workspaceFolder) { + // Note: workspaceFolder.name is not the same as the file system folder name, + // use the fsPath value instead + const projectName = path.basename(workspaceFolder.uri.fsPath) + // Set includeWorkspaceFolder to false because we are already manually prepending the projectName + const relativePath = vscode.workspace.asRelativePath(uri, false) + const zipEntryPath = this.getZipEntryPath(projectName, relativePath) + zip.writeString(content, zipEntryPath) + + if (scope === CodeWhispererConstants.CodeAnalysisScope.FILE_ON_DEMAND) { + const gitDiffContent = `+++ b/${normalize(zipEntryPath)}` // Sending file path in payload for LLM code review + zip.writeString(gitDiffContent, ZipConstants.codeDiffFilePath) + } + } else { + zip.writeString(content, uri.fsPath) + } + + this._pickedSourceFiles.add(uri.fsPath) + this._totalSize += (await fs.stat(uri.fsPath)).size + this._totalLines += content.split(ZipConstants.newlineRegex).length + + if (this.reachSizeLimit(this._totalSize, scope)) { + throw new FileSizeExceededError() + } + const zipFilePath = this.getZipDirPath(FeatureUseCase.CODE_SCAN) + CodeWhispererConstants.codeScanZipExt + await zip.finalizeToFile(zipFilePath) + return zipFilePath + } + + protected getZipEntryPath(projectName: string, relativePath: string) { + return path.join(projectName, relativePath) + } + + /** + * Processes a directory and adds its contents to a zip archive while preserving the directory structure. + * + * @param zip - The AdmZip instance to add files and directories to + * @param metadataDir - The absolute path to the directory to process + * + * @remarks + * This function: + * - Creates empty directory entries in the zip for each directory + * - Recursively processes all subdirectories + * - Adds all files to the zip while maintaining relative paths + * - Handles errors for individual file operations without stopping the overall process + * + * The files in the zip will be stored under a root directory named after the input directory's basename. + * + * @throws May throw errors from filesystem operations or zip creation + * + * @example + * ```typescript + * const zip = new AdmZip(); + * await processMetadataDir(zip, '/path/to/directory'); + * ``` + */ + protected async processMetadataDir(zip: ZipStream, metadataDir: string) { + const metadataDirName = path.basename(metadataDir) + // Helper function to add empty directory to zip + const addEmptyDirectory = (dirPath: string) => { + const relativePath = path.relative(metadataDir, dirPath) + const pathWithMetadata = path.join(metadataDirName, relativePath, '/') + zip.writeString('', pathWithMetadata) + } + + // Recursive function to process directories + const processDirectory = async (dirPath: string) => { + const entries = await vscode.workspace.fs.readDirectory(vscode.Uri.file(dirPath)) + addEmptyDirectory(dirPath) + + for (const [fileName, fileType] of entries) { + const filePath = path.join(dirPath, fileName) + + if (fileType === vscode.FileType.File) { + try { + const fileUri = vscode.Uri.file(filePath) + const relativePath = path.relative(metadataDir, filePath) + const pathWithMetadata = path.join(metadataDirName, relativePath) + zip.writeFile(fileUri.fsPath, pathWithMetadata) + } catch (error) { + getLogger().error(`Failed to add file ${filePath} to zip: ${error}`) + } + } else if (fileType === vscode.FileType.Directory) { + // Recursively process subdirectory + await processDirectory(filePath) + } + } + } + await processDirectory(metadataDir) + } + + protected async zipProject(useCase: FeatureUseCase, projectPath?: string, metadataDir?: string) { + const zip = new ZipStream() + let projectPaths = [] + if (useCase === FeatureUseCase.TEST_GENERATION && projectPath) { + projectPaths.push(projectPath) + } else { + projectPaths = this.getProjectPaths() + } + if (useCase === FeatureUseCase.CODE_SCAN) { + await this.processCombinedGitDiff(zip, projectPaths, '', CodeWhispererConstants.CodeAnalysisScope.PROJECT) + } + const languageCount = new Map() + + await this.processSourceFiles(zip, languageCount, projectPaths, useCase) + if (metadataDir) { + await this.processMetadataDir(zip, metadataDir) + } + if (useCase !== FeatureUseCase.TEST_GENERATION) { + this.processOtherFiles(zip, languageCount) + } + + if (languageCount.size === 0) { + throw new NoSourceFilesError() + } + this._language = [...languageCount.entries()].reduce((a, b) => (b[1] > a[1] ? b : a))[0] + const zipFilePath = this.getZipDirPath(useCase) + CodeWhispererConstants.codeScanZipExt + await zip.finalizeToFile(zipFilePath) + return zipFilePath + } + + protected async processCombinedGitDiff( + zip: ZipStream, + projectPaths: string[], + filePath?: string, + scope?: CodeWhispererConstants.CodeAnalysisScope + ) { + let gitDiffContent = '' + for (const projectPath of projectPaths) { + const projectName = path.basename(projectPath) + // Get diff content + gitDiffContent += await this.executeGitDiff({ + projectPath, + projectName, + filePath, + scope, + }) + } + if (gitDiffContent) { + zip.writeString(gitDiffContent, ZipConstants.codeDiffFilePath) + } + } + + private async getGitUntrackedFiles(projectPath: string): Promise { + const checkNewFileArgs = ['ls-files', '--others', '--exclude-standard'] + const checkProcess = new ChildProcess('git', checkNewFileArgs) + + try { + let output = '' + await checkProcess.run({ + rejectOnError: true, + rejectOnErrorCode: true, + onStdout: (text) => { + output += text + }, + spawnOptions: { + cwd: projectPath, + }, + }) + return output + } catch (err) { + getLogger().warn(`Failed to check if file is new: ${err}`) + return undefined + } + } + + private async generateNewFileDiff(projectPath: string, projectName: string, relativePath: string): Promise { + let diffContent = '' + + const gitArgs = [ + 'diff', + '--no-index', + `--src-prefix=a/${projectName}/`, + `--dst-prefix=b/${projectName}/`, + '/dev/null', // Use /dev/null as the old file + relativePath, + ] + + const childProcess = new ChildProcess('git', gitArgs) + const runOptions: ChildProcessOptions = { + rejectOnError: false, + rejectOnErrorCode: false, + onStdout: (text) => { + diffContent += text + getLogger().verbose(removeAnsi(text)) + }, + onStderr: (text) => { + getLogger().error(removeAnsi(text)) + }, + spawnOptions: { + cwd: projectPath, + }, + } + + try { + await childProcess.run(runOptions) + return diffContent + } catch (err) { + getLogger().warn(`Failed to run diff command: ${err}`) + return '' + } + } + + private async generateHeadDiff(projectPath: string, projectName: string, relativePath?: string): Promise { + let diffContent = '' + + const gitArgs = [ + 'diff', + 'HEAD', + `--src-prefix=a/${projectName}/`, + `--dst-prefix=b/${projectName}/`, + ...(relativePath ? [relativePath] : []), + ] + + const childProcess = new ChildProcess('git', gitArgs) + + const runOptions: ChildProcessOptions = { + rejectOnError: true, + rejectOnErrorCode: true, + onStdout: (text) => { + diffContent += text + getLogger().verbose(removeAnsi(text)) + }, + onStderr: (text) => { + getLogger().error(removeAnsi(text)) + }, + spawnOptions: { + cwd: projectPath, + }, + } + + try { + await childProcess.run(runOptions) + return diffContent + } catch (err) { + getLogger().warn(`Failed to run command \`${childProcess.toString()}\`: ${err}`) + return '' + } + } + + private async executeGitDiff(options: GitDiffOptions): Promise { + const { projectPath, projectName, filePath, scope } = options + const isProjectScope = scope === CodeWhispererConstants.CodeAnalysisScope.PROJECT + + const untrackedFilesString = await this.getGitUntrackedFiles(projectPath) + const untrackedFilesArray = untrackedFilesString?.trim()?.split('\n')?.filter(Boolean) + + if (isProjectScope && untrackedFilesArray && !untrackedFilesArray.length) { + return await this.generateHeadDiff(projectPath, projectName) + } + + let diffContent = '' + + if (isProjectScope) { + diffContent = await this.generateHeadDiff(projectPath, projectName) + + if (untrackedFilesArray) { + const untrackedDiffs = await Promise.all( + untrackedFilesArray.map((file) => this.generateNewFileDiff(projectPath, projectName, file)) + ) + diffContent += untrackedDiffs.join('') + } + } else if (!isProjectScope && filePath) { + const relativeFilePath = path.relative(projectPath, filePath) + + const newFileDiff = await this.generateNewFileDiff(projectPath, projectName, relativeFilePath) + diffContent = this.rewriteDiff(newFileDiff) + } + return diffContent + } + + private rewriteDiff(inputStr: string): string { + const lines = inputStr.split('\n') + const rewrittenLines = lines.slice(0, 5).map((line) => { + line = line.replace(/\\\\/g, '/') + line = line.replace(/("a\/[^"]*)/g, (match, p1) => p1) + line = line.replace(/("b\/[^"]*)/g, (match, p1) => p1) + line = line.replace(/"/g, '') + + return line + }) + const outputLines = [...rewrittenLines, ...lines.slice(5)] + const outputStr = outputLines.join('\n') + + return outputStr + } + + protected async processSourceFiles( + zip: ZipStream, + languageCount: Map, + projectPaths: string[] | undefined, + useCase: FeatureUseCase + ) { + if (!projectPaths || projectPaths.length === 0) { + return + } + + const sourceFiles = await collectFiles( + projectPaths, + (useCase === FeatureUseCase.TEST_GENERATION + ? [...(vscode.workspace.workspaceFolders ?? [])].sort( + (a, b) => b.uri.fsPath.length - a.uri.fsPath.length + ) + : vscode.workspace.workspaceFolders) as CurrentWsFolders, + { + maxTotalSizeBytes: this.getProjectScanPayloadSizeLimitInBytes(), + excludePatterns: + useCase === FeatureUseCase.TEST_GENERATION + ? [...CodeWhispererConstants.testGenExcludePatterns, ...defaultExcludePatterns] + : defaultExcludePatterns, + } + ) + for (const file of sourceFiles) { + const projectName = path.basename(file.workspaceFolder.uri.fsPath) + const zipEntryPath = this.getZipEntryPath(projectName, file.relativeFilePath) + + if (ZipConstants.knownBinaryFileExts.includes(path.extname(file.fileUri.fsPath))) { + if (useCase === FeatureUseCase.TEST_GENERATION) { + continue + } + await this.processBinaryFile(zip, file.fileUri, zipEntryPath) + } else { + const isFileOpenAndDirty = this.isFileOpenAndDirty(file.fileUri) + const fileContent = isFileOpenAndDirty ? await this.getTextContent(file.fileUri) : file.fileContent + this.processTextFile(zip, file.fileUri, fileContent, languageCount, zipEntryPath) + } + } + } + + protected processOtherFiles(zip: ZipStream, languageCount: Map) { + for (const document of vscode.workspace.textDocuments + .filter((document) => document.uri.scheme === 'file') + .filter((document) => vscode.workspace.getWorkspaceFolder(document.uri) === undefined)) { + this.processTextFile(zip, document.uri, document.getText(), languageCount, document.uri.fsPath) + } + } + + protected async processTestCoverageFiles(targetPath: string) { + // TODO: will be removed post release + const coverageFilePatterns = ['**/coverage.xml', '**/coverage.json', '**/coverage.txt'] + let files: vscode.Uri[] = [] + + for (const pattern of coverageFilePatterns) { + files = await vscode.workspace.findFiles(pattern) + if (files.length > 0) { + break + } + } + + await Promise.all( + files.map(async (file) => { + const fileName = path.basename(file.path) + const targetFilePath = path.join(targetPath, fileName) + await fs.copy(file.path, targetFilePath) + }) + ) + } + + protected processTextFile( + zip: ZipStream, + uri: vscode.Uri, + fileContent: string, + languageCount: Map, + zipEntryPath: string + ) { + const fileSize = Buffer.from(fileContent).length + + if ( + this.reachSizeLimit(this._totalSize, CodeWhispererConstants.CodeAnalysisScope.PROJECT) || + this.willReachSizeLimit(this._totalSize, fileSize) + ) { + throw new ProjectSizeExceededError() + } + this._pickedSourceFiles.add(uri.fsPath) + this._totalSize += fileSize + this._totalLines += fileContent.split(ZipConstants.newlineRegex).length + + this.incrementCountForLanguage(uri, languageCount) + zip.writeString(fileContent, zipEntryPath) + } + + protected async processBinaryFile(zip: ZipStream, uri: vscode.Uri, zipEntryPath: string) { + const fileSize = (await fs.stat(uri.fsPath)).size + + if ( + this.reachSizeLimit(this._totalSize, CodeWhispererConstants.CodeAnalysisScope.PROJECT) || + this.willReachSizeLimit(this._totalSize, fileSize) + ) { + throw new ProjectSizeExceededError() + } + this._pickedSourceFiles.add(uri.fsPath) + this._totalSize += fileSize + + zip.writeFile(uri.fsPath, path.dirname(zipEntryPath)) + } + + protected incrementCountForLanguage(uri: vscode.Uri, languageCount: Map) { + const fileExtension = path.extname(uri.fsPath).slice(1) + const language = runtimeLanguageContext.getLanguageFromFileExtension(fileExtension) + if (language && language !== 'plaintext') { + languageCount.set(language, (languageCount.get(language) || 0) + 1) + } + } + + protected isFileOpenAndDirty(uri: vscode.Uri) { + return vscode.workspace.textDocuments.some((document) => document.uri.fsPath === uri.fsPath && document.isDirty) + } + + public getZipDirPath(useCase: FeatureUseCase): string { + if (this._zipDir === '') { + const prefix = + useCase === FeatureUseCase.TEST_GENERATION + ? CodeWhispererConstants.TestGenerationTruncDirPrefix + : CodeWhispererConstants.codeScanTruncDirPrefix + + this._zipDir = path.join(this._tmpDir, `${prefix}_${this._timestamp}`) + } + return this._zipDir + } + + public async generateZip( + uri: vscode.Uri | undefined, + scope: CodeWhispererConstants.CodeAnalysisScope + ): Promise { + try { + const zipDirPath = this.getZipDirPath(FeatureUseCase.CODE_SCAN) + let zipFilePath: string + if ( + scope === CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO || + scope === CodeWhispererConstants.CodeAnalysisScope.FILE_ON_DEMAND + ) { + zipFilePath = await this.zipFile(uri, scope) + } else if (scope === CodeWhispererConstants.CodeAnalysisScope.PROJECT) { + zipFilePath = await this.zipProject(FeatureUseCase.CODE_SCAN) + } else { + throw new ToolkitError(`Unknown code analysis scope: ${scope}`) + } + + getLoggerForScope(scope).debug(`Picked source files: [${[...this._pickedSourceFiles].join(', ')}]`) + const zipFileSize = (await fs.stat(zipFilePath)).size + return { + rootDir: zipDirPath, + zipFilePath: zipFilePath, + srcPayloadSizeInBytes: this._totalSize, + scannedFiles: new Set([...this._pickedSourceFiles, ...this._pickedBuildFiles]), + zipFileSizeInBytes: zipFileSize, + buildPayloadSizeInBytes: this._totalBuildSize, + lines: this._totalLines, + language: this._language, + } + } catch (error) { + getLogger().error('Zip error caused by: %O', error) + throw error + } + } + + // TODO: Refactor this + public async removeTmpFiles(zipMetadata: ZipMetadata, scope?: CodeWhispererConstants.CodeAnalysisScope) { + const logger = getLoggerForScope(scope) + logger.verbose(`Cleaning up temporary files...`) + await fs.delete(zipMetadata.zipFilePath, { force: true }) + await fs.delete(zipMetadata.rootDir, { recursive: true, force: true }) + logger.verbose(`Complete cleaning up temporary files.`) + } +} diff --git a/packages/core/src/codewhisperer/views/activeStateController.ts b/packages/core/src/codewhisperer/views/activeStateController.ts new file mode 100644 index 00000000000..614003d02ff --- /dev/null +++ b/packages/core/src/codewhisperer/views/activeStateController.ts @@ -0,0 +1,126 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { LineSelection, LinesChangeEvent } from '../tracker/lineTracker' +import { isTextEditor } from '../../shared/utilities/editorUtilities' +import { subscribeOnce } from '../../shared/utilities/vsCodeUtils' +import { Container } from '../service/serviceContainer' +import { cancellableDebounce } from '../../shared/utilities/functionUtils' + +export class ActiveStateController implements vscode.Disposable { + private readonly _disposable: vscode.Disposable + private _editor: vscode.TextEditor | undefined + + private readonly cwLineHintDecoration: vscode.TextEditorDecorationType = + vscode.window.createTextEditorDecorationType({ + after: { + margin: '0 0 0 3em', + contentText: 'Amazon Q is generating...', + textDecoration: 'none', + fontWeight: 'normal', + fontStyle: 'normal', + color: 'var(--vscode-editorCodeLens-foreground)', + }, + rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen, + isWholeLine: true, + }) + + constructor(private readonly container: Container) { + this._disposable = vscode.Disposable.from( + this.container.lineTracker.onDidChangeActiveLines(async (e) => { + await this.onActiveLinesChanged(e) + }), + subscribeOnce(this.container.lineTracker.onReady)(async (_) => { + await this.onReady() + }), + this.container.auth.auth.onDidChangeConnectionState(async (e) => { + if (e.state !== 'authenticating') { + await this._refresh(vscode.window.activeTextEditor) + } + }), + this.container.auth.secondaryAuth.onDidChangeActiveConnection(async () => { + await this._refresh(vscode.window.activeTextEditor) + }) + ) + } + + dispose() { + this._disposable.dispose() + } + + private _isReady: boolean = false + + private async onReady(): Promise { + this._isReady = true + await this._refresh(vscode.window.activeTextEditor) + } + + private async onActiveLinesChanged(e: LinesChangeEvent) { + if (!this._isReady) { + return + } + + await this.refreshDebounced.promise(e.editor) + } + + clear(editor: vscode.TextEditor | undefined) { + if (this._editor && this._editor !== editor) { + this._editor.setDecorations(this.cwLineHintDecoration, []) + } + + editor?.setDecorations(this.cwLineHintDecoration, []) + } + + readonly refreshDebounced = cancellableDebounce(async (editor: vscode.TextEditor | undefined) => { + await this._refresh(editor) + }, 1000) + + private async _refresh(editor: vscode.TextEditor | undefined, shouldDisplay?: boolean) { + if (!editor && !this._editor) { + return + } + + const selections = this.container.lineTracker.selections + if (!editor || !selections || !isTextEditor(editor)) { + this.clear(this._editor) + return + } + + if (this._editor !== editor) { + // Clear any annotations on the previously active editor + this.clear(this._editor) + this._editor = editor + } + + // Make sure the editor hasn't died since the await above and that we are still on the same line(s) + if (!editor.document || !this.container.lineTracker.includes(selections)) { + return + } + + if (!this.container.auth.isConnectionValid()) { + this.clear(this._editor) + return + } + + if (shouldDisplay !== undefined) { + await this.updateDecorations(editor, selections, shouldDisplay) + } else { + await this.updateDecorations(editor, selections, true) + } + } + + async updateDecorations(editor: vscode.TextEditor, lines: LineSelection[], shouldDisplay: boolean) { + const range = editor.document.validateRange( + new vscode.Range(lines[0].active, lines[0].active, lines[0].active, lines[0].active) + ) + + if (shouldDisplay) { + editor.setDecorations(this.cwLineHintDecoration, [range]) + } else { + editor.setDecorations(this.cwLineHintDecoration, []) + } + } +} diff --git a/src/codewhisperer/views/css/codewhispererReferenceLog.css b/packages/core/src/codewhisperer/views/css/codewhispererReferenceLog.css similarity index 100% rename from src/codewhisperer/views/css/codewhispererReferenceLog.css rename to packages/core/src/codewhisperer/views/css/codewhispererReferenceLog.css diff --git a/src/codewhisperer/views/css/securityPanel.css b/packages/core/src/codewhisperer/views/css/securityPanel.css similarity index 100% rename from src/codewhisperer/views/css/securityPanel.css rename to packages/core/src/codewhisperer/views/css/securityPanel.css diff --git a/packages/core/src/codewhisperer/views/lineAnnotationController.ts b/packages/core/src/codewhisperer/views/lineAnnotationController.ts new file mode 100644 index 00000000000..c449f5ab1d9 --- /dev/null +++ b/packages/core/src/codewhisperer/views/lineAnnotationController.ts @@ -0,0 +1,486 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as os from 'os' +import { LineSelection, LinesChangeEvent } from '../tracker/lineTracker' +import { isTextEditor } from '../../shared/utilities/editorUtilities' +import { cancellableDebounce } from '../../shared/utilities/functionUtils' +import { subscribeOnce } from '../../shared/utilities/vsCodeUtils' +import { AnnotationChangeSource, inlinehintKey } from '../models/constants' +import globals from '../../shared/extensionGlobals' +import { Container } from '../service/serviceContainer' +import { telemetry } from '../../shared/telemetry/telemetry' +import { getLogger } from '../../shared/logger/logger' +import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { setContext } from '../../shared/vscode/setContext' + +const case3TimeWindow = 30000 // 30 seconds + +const maxSmallIntegerV8 = 2 ** 30 // Max number that can be stored in V8's smis (small integers) + +function fromId(id: string | undefined): AnnotationState | undefined { + switch (id) { + case AutotriggerState.id: + return new AutotriggerState() + case PressTabState.id: + return new AutotriggerState() + case ManualtriggerState.id: + return new ManualtriggerState() + case TryMoreExState.id: + return new TryMoreExState() + case EndState.id: + return new EndState() + case InlineChatState.id: + return new InlineChatState() + default: + return undefined + } +} + +interface AnnotationState { + id: string + suppressWhileRunning: boolean + decorationRenderOptions?: vscode.ThemableDecorationAttachmentRenderOptions + + text: () => string + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState | undefined + isNextState(state: AnnotationState | undefined): boolean +} + +/** + * case 1: How Cwspr triggers + * Trigger Criteria: + * User opens an editor file && + * CW is not providing a suggestion && + * User has not accepted any suggestion + * + * Exit criteria: + * User accepts 1 suggestion + * + */ +export class AutotriggerState implements AnnotationState { + static id = 'codewhisperer_learnmore_case_1' + id = AutotriggerState.id + + suppressWhileRunning = true + text = () => 'Amazon Q Tip 1/3: Start typing to get suggestions ([ESC] to exit)' + static acceptedCount = 0 + + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState | undefined { + return undefined + } + + isNextState(state: AnnotationState | undefined): boolean { + return state instanceof ManualtriggerState + } +} + +/** + * case 1-a: Tab to accept + * Trigger Criteria: + * Case 1 && + * Inline suggestion is being shown + * + * Exit criteria: + * User accepts 1 suggestion + */ +export class PressTabState implements AnnotationState { + static id = 'codewhisperer_learnmore_case_1a' + id = PressTabState.id + + suppressWhileRunning = false + + text = () => 'Amazon Q Tip 1/3: Press [TAB] to accept ([ESC] to exit)' + + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState | undefined { + return new AutotriggerState().updateState(changeSource, force) + } + + isNextState(state: AnnotationState | undefined): boolean { + return state instanceof ManualtriggerState + } +} + +/** + * case 2: Manual trigger + * Trigger Criteria: + * User exists case 1 && + * User navigates to a new line + * + * Exit criteria: + * User inokes manual trigger shortcut + */ +export class ManualtriggerState implements AnnotationState { + static id = 'codewhisperer_learnmore_case_2' + id = ManualtriggerState.id + + suppressWhileRunning = true + + text = () => { + if (os.platform() === 'win32') { + return 'Amazon Q Tip 2/3: Invoke suggestions with [Alt] + [C] ([ESC] to exit)' + } + + return 'Amazon Q Tip 2/3: Invoke suggestions with [Option] + [C] ([ESC] to exit)' + } + hasManualTrigger: boolean = false + hasValidResponse: boolean = false + + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState | undefined { + if (this.hasManualTrigger && this.hasValidResponse) { + if (changeSource !== 'codewhisperer') { + return new TryMoreExState() + } else { + return undefined + } + } else { + return this + } + } + + isNextState(state: AnnotationState | undefined): boolean { + return state instanceof TryMoreExState + } +} + +/** + * case 3: Learn more + * Trigger Criteria: + * User exists case 2 && + * User navigates to a new line + * + * Exit criteria: + * User accepts or rejects the suggestion + */ +export class TryMoreExState implements AnnotationState { + static id = 'codewhisperer_learnmore_case_3' + id = TryMoreExState.id + + suppressWhileRunning = true + + text = () => 'Amazon Q Tip 3/3: For settings, open the Amazon Q menu from the status bar ([ESC] to exit)' + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState { + if (force) { + return new EndState() + } + return this + } + + isNextState(state: AnnotationState | undefined): boolean { + return state instanceof EndState + } + + static triggerCount: number = 0 + static learnmoeCount: number = 0 +} + +export class EndState implements AnnotationState { + static id = 'codewhisperer_learnmore_end' + id = EndState.id + + suppressWhileRunning = true + text = () => '' + updateState(changeSource: AnnotationChangeSource, force: boolean): AnnotationState { + return this + } + isNextState(state: AnnotationState): boolean { + return false + } +} + +export class InlineChatState implements AnnotationState { + static id = 'amazonq_annotation_inline_chat' + id = InlineChatState.id + suppressWhileRunning = false + + text = () => { + if (os.platform() === 'darwin') { + return 'Amazon Q: Edit \u2318I' + } + return 'Amazon Q: Edit (Ctrl+I)' + } + updateState(_changeSource: AnnotationChangeSource, _force: boolean): AnnotationState { + return this + } + isNextState(_state: AnnotationState | undefined): boolean { + return false + } +} + +/** + * There are + * - existing users + * - new users + * -- new users who has not seen tutorial + * -- new users who has seen tutorial + * + * "existing users" should have the context key "CODEWHISPERER_AUTO_TRIGGER_ENABLED" + * "new users who has seen tutorial" should have the context key "inlineKey" and "CODEWHISPERER_AUTO_TRIGGER_ENABLED" + * the remaining grouop of users should belong to "new users who has not seen tutorial" + */ +export class LineAnnotationController implements vscode.Disposable { + private readonly _disposable: vscode.Disposable + private _editor: vscode.TextEditor | undefined + + private _currentState: AnnotationState + + private readonly cwLineHintDecoration: vscode.TextEditorDecorationType = + vscode.window.createTextEditorDecorationType({ + after: { + margin: '0 0 0 3em', + // "borderRadius" and "padding" are not available on "after" type of decoration, this is a hack to inject these css prop to "after" content. Refer to https://github.com/microsoft/vscode/issues/68845 + textDecoration: ';border-radius:0.25rem;padding:0rem 0.5rem;', + width: 'fit-content', + }, + rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen, + }) + + constructor(private readonly container: Container) { + const cachedState = fromId(globals.globalState.get(inlinehintKey)) + const cachedAutotriggerEnabled = globals.globalState.get('CODEWHISPERER_AUTO_TRIGGER_ENABLED') + + // new users (has or has not seen tutorial) + if (cachedAutotriggerEnabled === undefined || cachedState !== undefined) { + this._currentState = cachedState ?? new AutotriggerState() + getLogger().debug( + `codewhisperer: new user login, activating inline tutorial. (autotriggerEnabled=${cachedAutotriggerEnabled}; inlineState=${cachedState?.id})` + ) + } else { + this._currentState = new EndState() + getLogger().debug(`codewhisperer: existing user login, disabling inline tutorial.`) + } + + this._disposable = vscode.Disposable.from( + subscribeOnce(this.container.lineTracker.onReady)(async (_) => { + await this.onReady() + }), + this.container.lineTracker.onDidChangeActiveLines(async (e) => { + await this.onActiveLinesChanged(e) + }), + this.container.auth.auth.onDidChangeConnectionState(async (e) => { + if (e.state !== 'authenticating') { + await this.refresh(vscode.window.activeTextEditor, 'editor') + } + }), + this.container.auth.secondaryAuth.onDidChangeActiveConnection(async () => { + await this.refresh(vscode.window.activeTextEditor, 'editor') + }) + ) + } + + dispose() { + this._disposable.dispose() + } + + private _isReady: boolean = false + + private async onReady(): Promise { + this._isReady = !(this._currentState instanceof EndState) + await this._refresh(vscode.window.activeTextEditor, 'editor') + } + + isTutorialDone(): boolean { + return this._currentState.id === new EndState().id + } + + isInlineChatHint(): boolean { + return this._currentState.id === new InlineChatState().id + } + + async dismissTutorial() { + this._currentState = new EndState() + await setContext('aws.codewhisperer.tutorial.workInProgress', false) + await globals.globalState.update(inlinehintKey, this._currentState.id) + } + + /** + * Trys to show the inline hint, if the tutorial is not finished it will not be shown + */ + async tryShowInlineHint(): Promise { + if (this.isTutorialDone()) { + this._isReady = true + this._currentState = new InlineChatState() + return true + } + return false + } + + async tryHideInlineHint(): Promise { + if (this._currentState instanceof InlineChatState) { + this._currentState = new EndState() + return true + } + return false + } + + private async onActiveLinesChanged(e: LinesChangeEvent) { + if (!this._isReady) { + return + } + + this.clear() + + await this.refresh(e.editor, e.reason) + } + + clear() { + this._editor?.setDecorations(this.cwLineHintDecoration, []) + } + + async refresh(editor: vscode.TextEditor | undefined, source: AnnotationChangeSource, force?: boolean) { + if (force) { + this.refreshDebounced.cancel() + await this._refresh(editor, source, true) + } else { + await this.refreshDebounced.promise(editor, source) + } + } + + private readonly refreshDebounced = cancellableDebounce( + async (editor: vscode.TextEditor | undefined, source: AnnotationChangeSource, force?: boolean) => { + await this._refresh(editor, source, force) + }, + 250 + ) + + private async _refresh(editor: vscode.TextEditor | undefined, source: AnnotationChangeSource, force?: boolean) { + if (!this._isReady) { + this.clear() + return + } + + if (this.isTutorialDone()) { + this.clear() + return + } + + if (editor === undefined && this._editor === undefined) { + this.clear() + return + } + + const selections = this.container.lineTracker.selections + if (editor === undefined || selections === undefined || !isTextEditor(editor)) { + this.clear() + return + } + + if (this._editor !== editor) { + // Clear any annotations on the previously active editor + this.clear() + this._editor = editor + } + + // Make sure the editor hasn't died since the await above and that we are still on the same line(s) + if (editor.document === undefined || !this.container.lineTracker.includes(selections)) { + this.clear() + return + } + + if (!this.container.auth.isConnectionValid()) { + this.clear() + return + } + + // Disable Tips when language is not supported by Amazon Q. + if (!runtimeLanguageContext.isLanguageSupported(editor.document)) { + return + } + + await this.updateDecorations(editor, selections, source, force) + } + + private async updateDecorations( + editor: vscode.TextEditor, + lines: LineSelection[], + source: AnnotationChangeSource, + force?: boolean + ) { + const range = editor.document.validateRange( + new vscode.Range(lines[0].active, maxSmallIntegerV8, lines[0].active, maxSmallIntegerV8) + ) + + const decorationOptions = this.getInlineDecoration(editor, lines, source, force) as + | vscode.DecorationOptions + | undefined + + if (decorationOptions === undefined) { + this.clear() + await setContext('aws.codewhisperer.tutorial.workInProgress', false) + return + } else if (this.isTutorialDone()) { + // special case + // Endstate is meaningless and doesnt need to be rendered + this.clear() + await this.dismissTutorial() + return + } else if (decorationOptions.renderOptions?.after?.contentText === new TryMoreExState().text()) { + // special case + // case 3 exit criteria is to fade away in 30s + setTimeout(async () => { + await this.refresh(editor, source, true) + }, case3TimeWindow) + } + + decorationOptions.range = range + + await globals.globalState.update(inlinehintKey, this._currentState.id) + if (!this.isInlineChatHint()) { + await setContext('aws.codewhisperer.tutorial.workInProgress', true) + } + editor.setDecorations(this.cwLineHintDecoration, [decorationOptions]) + } + + getInlineDecoration( + editor: vscode.TextEditor, + lines: LineSelection[], + source: AnnotationChangeSource, + force?: boolean + ): Partial | undefined { + const isCWRunning = true + + const textOptions: vscode.ThemableDecorationAttachmentRenderOptions = { + contentText: '', + fontWeight: 'normal', + fontStyle: 'normal', + textDecoration: 'none', + color: 'var(--vscode-editor-background)', + backgroundColor: 'var(--vscode-foreground)', + } + + if (isCWRunning && this._currentState.suppressWhileRunning) { + return undefined + } + + const updatedState: AnnotationState | undefined = this._currentState.updateState(source, force ?? false) + + if (updatedState === undefined) { + return undefined + } + + if (this._currentState.isNextState(updatedState)) { + // special case because PressTabState is part of case_1 (1a) which possibly jumps directly from case_1a to case_2 and miss case_1 + if (this._currentState instanceof PressTabState) { + telemetry.ui_click.emit({ elementId: AutotriggerState.id, passive: true }) + } + telemetry.ui_click.emit({ elementId: this._currentState.id, passive: true }) + } + + // update state + this._currentState = updatedState + + // take snapshot of accepted session so that we can compre if there is delta -> users accept 1 suggestion after seeing this state + AutotriggerState.acceptedCount = 0 + // take snapshot of total trigger count so that we can compare if there is delta -> users accept/reject suggestions after seeing this state + TryMoreExState.triggerCount = 0 + + textOptions.contentText = this._currentState.text() + + return { + renderOptions: { after: textOptions }, + } + } +} diff --git a/packages/core/src/codewhisperer/views/securityIssue/securityIssueWebview.ts b/packages/core/src/codewhisperer/views/securityIssue/securityIssueWebview.ts new file mode 100644 index 00000000000..632283215ab --- /dev/null +++ b/packages/core/src/codewhisperer/views/securityIssue/securityIssueWebview.ts @@ -0,0 +1,287 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { VueWebview } from '../../../webviews/main' +import { CodeScanIssue } from '../../models/model' +import { + CodeFixAction, + CodewhispererCodeScanIssueApplyFix, + Component, + telemetry, +} from '../../../shared/telemetry/telemetry' +import { copyToClipboard } from '../../../shared/utilities/messages' +import { EditorContentController } from '../../../amazonq/commons/controllers/contentController' +import { SecurityIssueProvider } from '../../service/securityIssueProvider' +import { getPatchedCode, previewDiff } from '../../../shared/utilities/diffUtils' +import { amazonqCodeIssueDetailsTabTitle } from '../../models/constants' +import { AuthUtil } from '../../util/authUtil' +import { Mutable } from '../../../shared/utilities/tsUtils' +import { ExtContext } from '../../../shared/extensions' + +export class SecurityIssueWebview extends VueWebview { + public static readonly sourcePath: string = 'src/codewhisperer/views/securityIssue/vue/index.js' + public readonly id = 'aws.codeWhisperer.securityIssue' + public readonly onChangeIssue = new vscode.EventEmitter() + public readonly onChangeFilePath = new vscode.EventEmitter() + public readonly onChangeGenerateFixLoading = new vscode.EventEmitter() + public readonly onChangeGenerateFixError = new vscode.EventEmitter() + + private issue: CodeScanIssue | undefined + private filePath: string | undefined + private isGenerateFixLoading: boolean = false + private generateFixError: string | null | undefined = undefined + + public constructor() { + super(SecurityIssueWebview.sourcePath) + } + + public getIssue() { + return this.issue + } + + public setIssue(issue: CodeScanIssue) { + this.issue = issue + this.onChangeIssue.fire(issue) + } + + public setFilePath(filePath: string) { + this.filePath = filePath + this.onChangeFilePath.fire(filePath) + } + + public applyFix() { + const args: [CodeScanIssue | undefined, string | undefined, Component] = [this.issue, this.filePath, 'webview'] + void vscode.commands.executeCommand('aws.amazonq.applySecurityFix', ...args) + } + + public explainWithQ() { + const args = [this.issue] + void this.navigateToFile()?.then(() => { + void vscode.commands.executeCommand('aws.amazonq.explainIssue', ...args) + }) + } + + public getRelativePath() { + if (this.filePath) { + return vscode.workspace.asRelativePath(this.filePath) + } + return '' + } + + public navigateToFile(showRange = true) { + if (this.issue && this.filePath) { + const range = new vscode.Range(this.issue.startLine, 0, this.issue.endLine, 0) + return vscode.workspace.openTextDocument(this.filePath).then((doc) => { + void vscode.window.showTextDocument(doc, { + selection: showRange ? range : undefined, + viewColumn: vscode.ViewColumn.One, + preview: true, + }) + }) + } + } + + public closeWebview(findingId: string) { + if (this.issue?.findingId === findingId) { + this.dispose() + } + } + + public getIsGenerateFixLoading() { + return this.isGenerateFixLoading + } + + public setIsGenerateFixLoading(isGenerateFixLoading: boolean) { + this.isGenerateFixLoading = isGenerateFixLoading + this.onChangeGenerateFixLoading.fire(isGenerateFixLoading) + } + + public getGenerateFixError() { + return this.generateFixError + } + + public setGenerateFixError(generateFixError: string | null | undefined) { + this.generateFixError = generateFixError + this.onChangeGenerateFixError.fire(generateFixError) + } + + public generateFix() { + const args = [this.issue] + void this.navigateToFile()?.then(() => { + void vscode.commands.executeCommand('aws.amazonq.generateFix', ...args) + }) + } + + public regenerateFix() { + void vscode.commands.executeCommand('aws.amazonq.security.regenerateFix', this.issue, this.filePath, 'webview') + } + + public rejectFix() { + void vscode.commands.executeCommand('aws.amazonq.security.rejectFix', this.issue, this.filePath) + } + + public ignoreIssue() { + void vscode.commands.executeCommand('aws.amazonq.security.ignore', this.issue, this.filePath, 'webview') + } + + public ignoreAllIssues() { + void vscode.commands.executeCommand('aws.amazonq.security.ignoreAll', this.issue, 'webview') + } + + createApplyFixTelemetryEntry(fixAction: CodeFixAction): Mutable { + return { + detectorId: this.issue!.detectorId, + findingId: this.issue!.findingId, + ruleId: this.issue!.ruleId, + component: 'webview', + result: 'Succeeded', + credentialStartUrl: AuthUtil.instance.startUrl, + codeFixAction: fixAction, + } + } + + public async copyFixedCode() { + telemetry.ui_click.emit({ elementId: 'codeReviewGeneratedFix_copyCodeFix' }) + const fixedCode = await this.getFixedCode() + if (!fixedCode || fixedCode.length === 0) { + return + } + void copyToClipboard(fixedCode, 'suggested code fix') + const copyFixedCodeTelemetryEntry = this.createApplyFixTelemetryEntry('copyDiff') + telemetry.codewhisperer_codeScanIssueApplyFix.emit(copyFixedCodeTelemetryEntry) + } + + public async insertAtCursor() { + telemetry.ui_click.emit({ elementId: 'codeReviewGeneratedFix_insertCodeFixAtCursor' }) + const fixedCode = await this.getFixedCode() + if (!fixedCode || fixedCode.length === 0) { + return + } + const controller = new EditorContentController() + await this.navigateToFile(false) + controller.insertTextAtCursorPosition(fixedCode, () => {}) + const copyFixedCodeTelemetryEntry = this.createApplyFixTelemetryEntry('insertAtCursor') + telemetry.codewhisperer_codeScanIssueApplyFix.emit(copyFixedCodeTelemetryEntry) + } + + public async openDiff() { + telemetry.ui_click.emit({ elementId: 'codeReviewGeneratedFix_openCodeFixDiff' }) + const [suggestedFix] = this.issue?.suggestedFixes ?? [] + if (!this.filePath || !suggestedFix || !suggestedFix.code) { + return + } + await previewDiff(this.filePath, suggestedFix.code) + const copyFixedCodeTelemetryEntry = this.createApplyFixTelemetryEntry('openDiff') + telemetry.codewhisperer_codeScanIssueApplyFix.emit(copyFixedCodeTelemetryEntry) + } + + public async getLanguageId() { + if (!this.filePath) { + return + } + const document = await vscode.workspace.openTextDocument(this.filePath) + return document.languageId + } + + public async getFixedCode(snippetMode = true) { + const [suggestedFix] = this.issue?.suggestedFixes ?? [] + if (!this.filePath || !suggestedFix || !suggestedFix.code || !this.issue) { + return '' + } + const patchedCode = await getPatchedCode(this.filePath, suggestedFix.code, snippetMode) + return patchedCode + } +} + +const Panel = VueWebview.compilePanel(SecurityIssueWebview) +let activePanel: InstanceType | undefined + +export async function showSecurityIssueWebview(ctx: vscode.ExtensionContext, issue: CodeScanIssue, filePath: string) { + activePanel ??= new Panel(ctx) + activePanel.server.setIssue(issue) + activePanel.server.setFilePath(filePath) + activePanel.server.setIsGenerateFixLoading(false) + activePanel.server.setGenerateFixError(undefined) + + const webviewPanel = await activePanel.show({ + title: amazonqCodeIssueDetailsTabTitle, + viewColumn: vscode.ViewColumn.Beside, + cssFiles: ['securityIssue.css'], + }) + webviewPanel.iconPath = { + light: vscode.Uri.joinPath(ctx.extensionUri, 'resources/icons/aws/amazonq/q-squid-ink.svg'), + dark: vscode.Uri.joinPath(ctx.extensionUri, 'resources/icons/aws/amazonq/q-white.svg'), + } + + webviewPanel.onDidDispose(() => (activePanel = undefined)) +} + +export function isSecurityIssueWebviewOpen() { + return activePanel !== undefined +} + +export async function closeSecurityIssueWebview(findingId: string) { + activePanel?.server.closeWebview(findingId) +} + +export async function syncSecurityIssueWebview(context: ExtContext) { + const activeIssueId = activePanel?.server.getIssue()?.findingId + if (!activeIssueId) { + return + } + const updatedIssue = SecurityIssueProvider.instance.issues + .flatMap(({ issues }) => issues) + .find((issue) => issue.findingId === activeIssueId) + await updateSecurityIssueWebview({ + issue: updatedIssue, + context: context.extensionContext, + shouldRefreshView: false, + }) +} + +export async function getWebviewActiveIssueId() { + return activePanel?.server.getIssue()?.findingId +} + +type WebviewParams = { + issue?: CodeScanIssue + filePath?: string + isGenerateFixLoading?: boolean + generateFixError?: string | null + shouldRefreshView: boolean + context: vscode.ExtensionContext +} +export async function updateSecurityIssueWebview({ + issue, + filePath, + isGenerateFixLoading, + generateFixError, + shouldRefreshView, + context, +}: WebviewParams): Promise { + if (!activePanel) { + return + } + if (issue) { + activePanel.server.setIssue(issue) + } + if (filePath) { + activePanel.server.setFilePath(filePath) + } + if (isGenerateFixLoading !== undefined) { + activePanel.server.setIsGenerateFixLoading(isGenerateFixLoading) + } + if (generateFixError !== undefined) { + activePanel.server.setGenerateFixError(generateFixError) + } + if (shouldRefreshView && filePath && issue) { + await showSecurityIssueWebview(context, issue, filePath) + } +} + +export function getIsGenerateFixLoading() { + return activePanel?.server.getIsGenerateFixLoading() +} diff --git a/packages/core/src/codewhisperer/views/securityIssue/vue/index.ts b/packages/core/src/codewhisperer/views/securityIssue/vue/index.ts new file mode 100644 index 00000000000..47e135649ac --- /dev/null +++ b/packages/core/src/codewhisperer/views/securityIssue/vue/index.ts @@ -0,0 +1,18 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * This module is run within the webview, and will mount the Vue app. + */ + +import { createApp } from 'vue' +import component from './root.vue' + +const create = () => createApp(component) +const app = create() + +app.mount('#vue-app') +window.addEventListener('remount', () => { + app.unmount() + create().mount('#vue-app') +}) diff --git a/packages/core/src/codewhisperer/views/securityIssue/vue/root.vue b/packages/core/src/codewhisperer/views/securityIssue/vue/root.vue new file mode 100644 index 00000000000..0bd4cce4d0c --- /dev/null +++ b/packages/core/src/codewhisperer/views/securityIssue/vue/root.vue @@ -0,0 +1,378 @@ +/*! * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ + + + + diff --git a/src/codewhisperer/views/securityPanelViewProvider.ts b/packages/core/src/codewhisperer/views/securityPanelViewProvider.ts similarity index 88% rename from src/codewhisperer/views/securityPanelViewProvider.ts rename to packages/core/src/codewhisperer/views/securityPanelViewProvider.ts index 1da6d0843a5..5f3b6cece70 100644 --- a/src/codewhisperer/views/securityPanelViewProvider.ts +++ b/packages/core/src/codewhisperer/views/securityPanelViewProvider.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -8,19 +8,17 @@ import { SecurityPanelSet, SecurityPanelItem, AggregatedCodeScanIssue } from '.. import { getLocalDatetime } from '../util/commonUtil' function makeUri(...args: Parameters): vscode.Uri { - return vscode.Uri.parse(`command:aws.codeWhisperer.openEditorAtRange?${encodeURIComponent(JSON.stringify(args))}`) + return vscode.Uri.parse(`command:aws.amazonq.openEditorAtRange?${encodeURIComponent(JSON.stringify(args))}`) } -async function openEditorAtRange(path: string, startLine: number, endLine: number) { +export async function openEditorAtRange(path: string, startLine: number, endLine: number) { const uri = vscode.Uri.parse(path) - await vscode.window.showTextDocument(uri, { preview: false, preserveFocus: true }).then(e => { + await vscode.window.showTextDocument(uri, { preview: false, preserveFocus: true }).then((e) => { e.selection = new vscode.Selection(startLine, 0, endLine, 0) e.revealRange(new vscode.Range(startLine, 0, endLine, 0), vscode.TextEditorRevealType.InCenterIfOutsideViewport) }) } -vscode.commands.registerCommand('aws.codeWhisperer.openEditorAtRange', openEditorAtRange) - export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { public static readonly viewType = 'aws.codeWhisperer.securityPanel' private view?: vscode.WebviewView @@ -90,9 +88,9 @@ export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { this.packageName } found ${total} issues

` ) - this.panelSets.forEach((panelSet, index) => { + for (const [index, panelSet] of this.panelSets.entries()) { this.addLine(panelSet, index) - }) + } this.update() if (editor) { this.setDecoration(editor, editor.document.uri) @@ -109,40 +107,40 @@ export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { private addLine(panelSet: SecurityPanelSet, index: number) { const filePath = panelSet.path const fileName = filePath.substring(Number(filePath.lastIndexOf('/')) + 1) - const handleId = 'handle'.concat(new Date().getTime().toString()).concat(index.toString()) + const handleId = 'handle'.concat(Date.now().toString()).concat(index.toString()) this.dynamicLog.push( `
` ) - panelSet.items.forEach(item => { + for (const item of panelSet.items) { if (item.severity === vscode.DiagnosticSeverity.Warning) { this.dynamicLog.push(`${this.addClickableWarningItem(item)}`) } else { this.dynamicLog.push(`${this.addClickableInfoItem(item)}`) } - }) + } this.dynamicLog.push(`
`) } private persistLines() { - this.panelSets.forEach((panelSet, index) => { + for (const [index, panelSet] of this.panelSets.entries()) { this.persistLine(panelSet, index) - }) + } } private persistLine(panelSet: SecurityPanelSet, index: number) { const filePath = panelSet.path const fileName = filePath.substring(Number(filePath.lastIndexOf('/')) + 1) - const handleId = 'handle'.concat(new Date().getTime().toString()).concat(index.toString()) + const handleId = 'handle'.concat(Date.now().toString()).concat(index.toString()) this.persistLog.push( `
` ) - panelSet.items.forEach(item => { + for (const item of panelSet.items) { if (item.severity === vscode.DiagnosticSeverity.Warning) { this.persistLog.push(`${this.addUnclickableWarningItem(item)}`) } else { this.persistLog.push(`${this.addUnclickableInfoItem(item)}`) } - }) + } this.persistLog.push(`
`) } @@ -173,13 +171,13 @@ export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { } private createPanelSets(securityRecommendationCollection: AggregatedCodeScanIssue[]) { - securityRecommendationCollection.forEach(securityRecommendation => { + for (const securityRecommendation of securityRecommendationCollection) { const panelSet: SecurityPanelSet = { path: securityRecommendation.filePath, uri: vscode.Uri.parse(securityRecommendation.filePath), items: [], } - securityRecommendation.issues.forEach(issue => { + for (const issue of securityRecommendation.issues) { panelSet.items.push({ path: securityRecommendation.filePath, range: new vscode.Range(issue.startLine, 0, issue.endLine, 0), @@ -191,9 +189,9 @@ export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { hoverMessage: issue.comment, }, }) - }) + } this.panelSets.push(panelSet) - }) + } } private getHtml(webview: vscode.Webview): string { @@ -215,8 +213,8 @@ export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { } private getHtmlContent(): string { - if (this.persistLog.length == 0) { - return 'No security issues have been detected in the workspace.' + if (this.persistLog.length === 0) { + return 'No code issues have been detected in the workspace.' } return this.persistLog.join('') + this.dynamicLog.join('') } @@ -234,15 +232,15 @@ export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { public setDecoration(editor: vscode.TextEditor, uri: vscode.Uri) { editor.setDecorations(this.getDecorator(), []) const rangesToRend: vscode.DecorationOptions[] = [] - this.panelSets.forEach(panelSet => { + for (const panelSet of this.panelSets) { if (panelSet.uri.fsPath === uri.fsPath) { - panelSet.items.forEach(item => { + for (const item of panelSet.items) { if (item.severity === vscode.DiagnosticSeverity.Warning) { rangesToRend.push(item.decoration) } - }) + } } - }) + } if (rangesToRend.length > 0) { editor.setDecorations(this.getDecorator(), rangesToRend) } @@ -253,7 +251,7 @@ export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { if (this.panelSets.length === 0) { return } - const index = this.panelSets.findIndex(panelSet => panelSet.uri.fsPath === uri.fsPath) + const index = this.panelSets.findIndex((panelSet) => panelSet.uri.fsPath === uri.fsPath) if (index === -1) { return } @@ -263,6 +261,7 @@ export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { const changedText = event.contentChanges[0].text const lineOffset = this.getLineOffset(changedRange, changedText) + // eslint-disable-next-line unicorn/no-array-for-each currentPanelSet.items.forEach((item, index, items) => { const intersection = changedRange.intersection(item.range) if ( @@ -284,9 +283,9 @@ export class SecurityPanelViewProvider implements vscode.WebviewViewProvider { }) this.panelSets[index] = currentPanelSet this.dynamicLog = [] - this.panelSets.forEach((panelSet, index) => { + for (const [index, panelSet] of this.panelSets.entries()) { this.addLine(panelSet, index) - }) + } this.update() if (editor) { this.setDecoration(editor, editor.document.uri) diff --git a/packages/core/src/codewhisperer/vue/backend.ts b/packages/core/src/codewhisperer/vue/backend.ts new file mode 100644 index 00000000000..0f4701fb745 --- /dev/null +++ b/packages/core/src/codewhisperer/vue/backend.ts @@ -0,0 +1,169 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as nls from 'vscode-nls' +const localize = nls.loadMessageBundle() +import * as os from 'os' +import * as vscode from 'vscode' +import * as path from 'path' +import { VueWebview } from '../../webviews/main' +import globals from '../../shared/extensionGlobals' +import { telemetry, CodewhispererLanguage, CodewhispererGettingStartedTask } from '../../shared/telemetry/telemetry' +import { fs } from '../../shared/fs/fs' +import { getLogger } from '../../shared/logger/logger' +import { AmazonQPromptSettings } from '../../shared/settings' +import { CodeWhispererSource } from '../commands/types' +import { submitFeedback } from '../../feedback/vue/submitFeedback' +import { placeholder } from '../../shared/vscode/commands2' + +export type OSType = 'Mac' | 'RestOfOS' +export class CodeWhispererWebview extends VueWebview { + public static readonly sourcePath: string = 'src/codewhisperer/vue/index.js' + public readonly id = 'CodeWhispererWebview' + + public constructor() { + super(CodeWhispererWebview.sourcePath) + } + + private isFileSaved: boolean = false + private getLocalFilePath(fileName: string): string { + // This will store the files in the global storage path of VSCode + return path.join(globals.context.globalStorageUri.fsPath, fileName) + } + + // This function opens TypeScript/JavaScript/Python/Java/C# file in the editor. + async openFile(name: [fileName: string, fileContent: string]): Promise { + const fileName = name[0] + const fileContent = name[1] + + const localFilePath = this.getLocalFilePath(fileName) + if ((await fs.existsFile(localFilePath)) && this.isFileSaved) { + const fileUri = vscode.Uri.file(localFilePath) + await vscode.workspace.openTextDocument(fileUri).then(async (doc) => { + await vscode.window.showTextDocument(doc, vscode.ViewColumn.Active).then((editor) => { + const endOfDocument = new vscode.Position( + doc.lineCount - 1, + doc.lineAt(doc.lineCount - 1).text.length + ) + editor.selection = new vscode.Selection(endOfDocument, endOfDocument) + }) + }) + } else { + await this.saveFileLocally(localFilePath, fileContent) + } + } + + // This function saves and open the file in the editor. + private async saveFileLocally(localFilePath: string, fileContent: string): Promise { + try { + await fs.writeFile(localFilePath, fileContent) + this.isFileSaved = true + // Opening the text document + await vscode.workspace.openTextDocument(localFilePath).then(async (doc) => { + await vscode.window.showTextDocument(doc, vscode.ViewColumn.Active).then((editor) => { + // Set the selection to the end of the document + const endOfDocument = new vscode.Position( + doc.lineCount - 1, + doc.lineAt(doc.lineCount - 1).text.length + ) + editor.selection = new vscode.Selection(endOfDocument, endOfDocument) + }) + }) + } catch (error) { + void vscode.window.showErrorMessage( + localize( + 'AWS.message.error.codewhispererLearnPage.saveFileLocally', + 'There was an error in saving the file, check log for details.' + ) + ) + } + } + + // This function returns the OS type of the machine used in Shortcuts and Generate Suggestion Sections + public getOSType(): OSType { + return os.platform() === 'darwin' ? 'Mac' : 'RestOfOS' + } + + // This function opens the Keyboard shortcuts in VSCode + async openShortCuts(): Promise { + await vscode.commands.executeCommand('workbench.action.openGlobalKeybindings', 'codewhisperer') + } + + // This function opens the Feedback CodeWhisperer page in the webview + async openFeedBack(): Promise { + return submitFeedback(placeholder, 'Amazon Q') + } + + // ------Telemetry------ + /** This represents the cause for the webview to open, whether a certain button was clicked or it opened automatically */ + #codeWhispererSource?: CodeWhispererSource + + setSource(source: CodeWhispererSource | undefined) { + if (this.#codeWhispererSource) { + return + } + this.#codeWhispererSource = source + } + + emitUiClick(id: CodeWhispererUiClick) { + telemetry.ui_click.emit({ + elementId: id, + passive: true, + }) + } + // Telemetry for CodeWhisperer Try Example with two params Language and Task Type + emitTryExampleClick(languageSelected: CodewhispererLanguage, taskType: CodewhispererGettingStartedTask) { + telemetry.codewhisperer_onboardingClick.emit({ + codewhispererLanguage: languageSelected, + codewhispererGettingStartedTask: taskType, + }) + } +} +// List of all events that are emitted from the webview of CodeWhisperer +export type CodeWhispererUiClick = + | 'codewhisperer_Resources_Documentation' + | 'codewhisperer_Resources_Feedback' + | 'codewhisperer_Prompt_Eng' + | 'codewhisperer_Commands_KeyboardShortcutsEditor' + | 'codewhisperer_ScanCode_LearnMore' + | 'codewhisperer_GenerateSuggestions_LearnMore' + | 'codewhisperer_Learn_PageOpen' + +const Panel = VueWebview.compilePanel(CodeWhispererWebview) +let activePanel: InstanceType | undefined +let subscriptions: vscode.Disposable[] | undefined + +// This function is called when the extension is activated : Webview of CodeWhisperer +export async function showCodeWhispererWebview( + ctx: vscode.ExtensionContext, + source: CodeWhispererSource | undefined +): Promise { + activePanel ??= new Panel(ctx) + activePanel.server.setSource(source) + if (activePanel === undefined) { + getLogger().error(`codewhisperer: failed to load Learn CodeWhisperer Page`) + return + } + const webview = await activePanel!.show({ + title: localize('AWS.view.gettingStartedPage.title', `Learn Amazon Q`), + viewColumn: vscode.ViewColumn.Active, + }) + + if (!subscriptions) { + subscriptions = [ + webview.onDidDispose(() => { + vscode.Disposable.from(...(subscriptions ?? [])).dispose() + activePanel = undefined + subscriptions = undefined + }), + ] + const prompts = AmazonQPromptSettings.instance + // To check the condition If the user has already seen the welcome message + if (prompts.isPromptEnabled('codeWhispererNewWelcomeMessage')) { + telemetry.ui_click.emit({ elementId: 'codewhisperer_Learn_PageOpen', passive: true }) + } else { + telemetry.ui_click.emit({ elementId: 'codewhisperer_Learn_PageOpen', passive: false }) + } + } +} diff --git a/packages/core/src/codewhisperer/vue/genSuggestionTab.vue b/packages/core/src/codewhisperer/vue/genSuggestionTab.vue new file mode 100644 index 00000000000..b4191e9e73b --- /dev/null +++ b/packages/core/src/codewhisperer/vue/genSuggestionTab.vue @@ -0,0 +1,228 @@ + + + + diff --git a/packages/core/src/codewhisperer/vue/index.ts b/packages/core/src/codewhisperer/vue/index.ts new file mode 100644 index 00000000000..47e135649ac --- /dev/null +++ b/packages/core/src/codewhisperer/vue/index.ts @@ -0,0 +1,18 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * This module is run within the webview, and will mount the Vue app. + */ + +import { createApp } from 'vue' +import component from './root.vue' + +const create = () => createApp(component) +const app = create() + +app.mount('#vue-app') +window.addEventListener('remount', () => { + app.unmount() + create().mount('#vue-app') +}) diff --git a/packages/core/src/codewhisperer/vue/root.vue b/packages/core/src/codewhisperer/vue/root.vue new file mode 100644 index 00000000000..61bc72737f8 --- /dev/null +++ b/packages/core/src/codewhisperer/vue/root.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/packages/core/src/codewhisperer/vue/telemetry.vue b/packages/core/src/codewhisperer/vue/telemetry.vue new file mode 100644 index 00000000000..4714f5f1825 --- /dev/null +++ b/packages/core/src/codewhisperer/vue/telemetry.vue @@ -0,0 +1,22 @@ + + diff --git a/packages/core/src/codewhispererChat/app.ts b/packages/core/src/codewhispererChat/app.ts new file mode 100644 index 00000000000..3916e571956 --- /dev/null +++ b/packages/core/src/codewhispererChat/app.ts @@ -0,0 +1,240 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EventEmitter } from 'vscode' +import { ChatController as CwChatController } from '../codewhispererChat/controllers/chat/controller' +import { UIMessageListener } from './view/messages/messageListener' +import { AmazonQAppInitContext } from '../amazonq/apps/initContext' +import { MessageListener } from '../amazonq/messages/messageListener' +import { MessagePublisher } from '../amazonq/messages/messagePublisher' +import { + ViewDiff, + ChatItemFeedbackMessage, + ChatItemVotedMessage, + CopyCodeToClipboard, + FooterInfoLinkClick, + InsertCodeAtCursorPosition, + PromptMessage, + ResponseBodyLinkClickMessage, + SourceLinkClickMessage, + StopResponseMessage, + TabChangedMessage, + TabClosedMessage, + TabCreatedMessage, + TriggerTabIDReceived, + UIFocusMessage, + AcceptDiff, + QuickCommandGroupActionClick, + FileClick, + TabBarButtonClick, + SaveChatMessage, +} from './controllers/chat/model' +import { EditorContextCommand, registerCommands } from './commands/registerCommands' +import { + ContextSelectedMessage, + CustomFormActionMessage, + DetailedListActionClickMessage, + DetailedListFilterChangeMessage, + DetailedListItemSelectMessage, +} from './view/connector/connector' + +export function init(appContext: AmazonQAppInitContext) { + const cwChatControllerEventEmitters = { + processPromptChatMessage: new EventEmitter(), + processTabCreatedMessage: new EventEmitter(), + processTabClosedMessage: new EventEmitter(), + processTabChangedMessage: new EventEmitter(), + processInsertCodeAtCursorPosition: new EventEmitter(), + processAcceptDiff: new EventEmitter(), + processViewDiff: new EventEmitter(), + processCopyCodeToClipboard: new EventEmitter(), + processContextMenuCommand: new EventEmitter(), + processTriggerTabIDReceived: new EventEmitter(), + processStopResponseMessage: new EventEmitter(), + processChatItemVotedMessage: new EventEmitter(), + processChatItemFeedbackMessage: new EventEmitter(), + processUIFocusMessage: new EventEmitter(), + processSourceLinkClick: new EventEmitter(), + processResponseBodyLinkClick: new EventEmitter(), + processFooterInfoLinkClick: new EventEmitter(), + processContextCommandUpdateMessage: new EventEmitter(), + processQuickCommandGroupActionClicked: new EventEmitter(), + processCustomFormAction: new EventEmitter(), + processContextSelected: new EventEmitter(), + processFileClick: new EventEmitter(), + processTabBarButtonClick: new EventEmitter(), + processSaveChat: new EventEmitter(), + processDetailedListFilterChangeMessage: new EventEmitter(), + processDetailedListItemSelectMessage: new EventEmitter(), + processDetailedListActionClickMessage: new EventEmitter(), + } + + const cwChatControllerMessageListeners = { + processPromptChatMessage: new MessageListener( + cwChatControllerEventEmitters.processPromptChatMessage + ), + processTabCreatedMessage: new MessageListener( + cwChatControllerEventEmitters.processTabCreatedMessage + ), + processTabClosedMessage: new MessageListener( + cwChatControllerEventEmitters.processTabClosedMessage + ), + processTabChangedMessage: new MessageListener( + cwChatControllerEventEmitters.processTabChangedMessage + ), + processInsertCodeAtCursorPosition: new MessageListener( + cwChatControllerEventEmitters.processInsertCodeAtCursorPosition + ), + processAcceptDiff: new MessageListener(cwChatControllerEventEmitters.processAcceptDiff), + processViewDiff: new MessageListener(cwChatControllerEventEmitters.processViewDiff), + processCopyCodeToClipboard: new MessageListener( + cwChatControllerEventEmitters.processCopyCodeToClipboard + ), + processContextMenuCommand: new MessageListener( + cwChatControllerEventEmitters.processContextMenuCommand + ), + processTriggerTabIDReceived: new MessageListener( + cwChatControllerEventEmitters.processTriggerTabIDReceived + ), + processStopResponseMessage: new MessageListener( + cwChatControllerEventEmitters.processStopResponseMessage + ), + processChatItemVotedMessage: new MessageListener( + cwChatControllerEventEmitters.processChatItemVotedMessage + ), + processChatItemFeedbackMessage: new MessageListener( + cwChatControllerEventEmitters.processChatItemFeedbackMessage + ), + processUIFocusMessage: new MessageListener(cwChatControllerEventEmitters.processUIFocusMessage), + processSourceLinkClick: new MessageListener( + cwChatControllerEventEmitters.processSourceLinkClick + ), + processResponseBodyLinkClick: new MessageListener( + cwChatControllerEventEmitters.processResponseBodyLinkClick + ), + processFooterInfoLinkClick: new MessageListener( + cwChatControllerEventEmitters.processFooterInfoLinkClick + ), + processContextCommandUpdateMessage: new MessageListener( + cwChatControllerEventEmitters.processContextCommandUpdateMessage + ), + processQuickCommandGroupActionClicked: new MessageListener( + cwChatControllerEventEmitters.processQuickCommandGroupActionClicked + ), + processCustomFormAction: new MessageListener( + cwChatControllerEventEmitters.processCustomFormAction + ), + processContextSelected: new MessageListener( + cwChatControllerEventEmitters.processContextSelected + ), + processFileClick: new MessageListener(cwChatControllerEventEmitters.processFileClick), + processTabBarButtonClick: new MessageListener( + cwChatControllerEventEmitters.processTabBarButtonClick + ), + processSaveChat: new MessageListener(cwChatControllerEventEmitters.processSaveChat), + processDetailedListFilterChangeMessage: new MessageListener( + cwChatControllerEventEmitters.processDetailedListFilterChangeMessage + ), + processDetailedListItemSelectMessage: new MessageListener( + cwChatControllerEventEmitters.processDetailedListItemSelectMessage + ), + processDetailedListActionClickMessage: new MessageListener( + cwChatControllerEventEmitters.processDetailedListActionClickMessage + ), + } + + const cwChatControllerMessagePublishers = { + processPromptChatMessage: new MessagePublisher( + cwChatControllerEventEmitters.processPromptChatMessage + ), + processTabCreatedMessage: new MessagePublisher( + cwChatControllerEventEmitters.processTabCreatedMessage + ), + processTabClosedMessage: new MessagePublisher( + cwChatControllerEventEmitters.processTabClosedMessage + ), + processTabChangedMessage: new MessagePublisher( + cwChatControllerEventEmitters.processTabChangedMessage + ), + processInsertCodeAtCursorPosition: new MessagePublisher( + cwChatControllerEventEmitters.processInsertCodeAtCursorPosition + ), + processAcceptDiff: new MessagePublisher(cwChatControllerEventEmitters.processAcceptDiff), + processViewDiff: new MessagePublisher(cwChatControllerEventEmitters.processViewDiff), + processCopyCodeToClipboard: new MessagePublisher( + cwChatControllerEventEmitters.processCopyCodeToClipboard + ), + processContextMenuCommand: new MessagePublisher( + cwChatControllerEventEmitters.processContextMenuCommand + ), + processTriggerTabIDReceived: new MessagePublisher( + cwChatControllerEventEmitters.processTriggerTabIDReceived + ), + processStopResponseMessage: new MessagePublisher( + cwChatControllerEventEmitters.processStopResponseMessage + ), + processChatItemVotedMessage: new MessagePublisher( + cwChatControllerEventEmitters.processChatItemVotedMessage + ), + processChatItemFeedbackMessage: new MessagePublisher( + cwChatControllerEventEmitters.processChatItemFeedbackMessage + ), + processUIFocusMessage: new MessagePublisher( + cwChatControllerEventEmitters.processUIFocusMessage + ), + processSourceLinkClick: new MessagePublisher( + cwChatControllerEventEmitters.processSourceLinkClick + ), + processResponseBodyLinkClick: new MessagePublisher( + cwChatControllerEventEmitters.processResponseBodyLinkClick + ), + processFooterInfoLinkClick: new MessagePublisher( + cwChatControllerEventEmitters.processFooterInfoLinkClick + ), + processContextCommandUpdateMessage: new MessagePublisher( + cwChatControllerEventEmitters.processContextCommandUpdateMessage + ), + processQuickCommandGroupActionClicked: new MessagePublisher( + cwChatControllerEventEmitters.processQuickCommandGroupActionClicked + ), + processCustomFormAction: new MessagePublisher( + cwChatControllerEventEmitters.processCustomFormAction + ), + processContextSelected: new MessagePublisher( + cwChatControllerEventEmitters.processContextSelected + ), + processFileClick: new MessagePublisher(cwChatControllerEventEmitters.processFileClick), + processTabBarButtonClick: new MessagePublisher( + cwChatControllerEventEmitters.processTabBarButtonClick + ), + processSaveChat: new MessagePublisher(cwChatControllerEventEmitters.processSaveChat), + processDetailedListActionClickMessage: new MessagePublisher( + cwChatControllerEventEmitters.processDetailedListActionClickMessage + ), + processDetailedListFilterChangeMessage: new MessagePublisher( + cwChatControllerEventEmitters.processDetailedListFilterChangeMessage + ), + processDetailedListItemSelectMessage: new MessagePublisher( + cwChatControllerEventEmitters.processDetailedListItemSelectMessage + ), + } + + new CwChatController( + cwChatControllerMessageListeners, + appContext.getAppsToWebViewMessagePublisher(), + appContext.onDidChangeAmazonQVisibility.event + ) + + const cwChatUIInputEventEmitter = new EventEmitter() + + new UIMessageListener({ + chatControllerMessagePublishers: cwChatControllerMessagePublishers, + webViewMessageListener: new MessageListener(cwChatUIInputEventEmitter), + }) + + appContext.registerWebViewToAppMessagePublisher(new MessagePublisher(cwChatUIInputEventEmitter), 'cwc') + + registerCommands() +} diff --git a/packages/core/src/codewhispererChat/clients/chat/v0/chat.ts b/packages/core/src/codewhispererChat/clients/chat/v0/chat.ts new file mode 100644 index 00000000000..c32f67cdac5 --- /dev/null +++ b/packages/core/src/codewhispererChat/clients/chat/v0/chat.ts @@ -0,0 +1,84 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SendMessageCommandOutput, SendMessageRequest } from '@amzn/amazon-q-developer-streaming-client' +import { GenerateAssistantResponseCommandOutput, GenerateAssistantResponseRequest } from '@amzn/codewhisperer-streaming' +import * as vscode from 'vscode' +import { ToolkitError } from '../../../../shared/errors' +import { createCodeWhispererChatStreamingClient } from '../../../../shared/clients/codewhispererChatClient' +import { createQDeveloperStreamingClient } from '../../../../shared/clients/qDeveloperChatClient' +import { UserWrittenCodeTracker } from '../../../../codewhisperer/tracker/userWrittenCodeTracker' + +export class ChatSession { + private sessionId?: string + /** + * True if messages from local history have been sent to session. + */ + localHistoryHydrated: boolean = false + + contexts: Map = new Map() + // TODO: doesn't handle the edge case when two files share the same relativePath string but from different root + // e.g. root_a/file1 vs root_b/file1 + relativePathToWorkspaceRoot: Map = new Map() + public get sessionIdentifier(): string | undefined { + return this.sessionId + } + + public tokenSource!: vscode.CancellationTokenSource + + constructor() { + this.createNewTokenSource() + } + + createNewTokenSource() { + this.tokenSource = new vscode.CancellationTokenSource() + } + + public setSessionID(id?: string) { + this.sessionId = id + } + async chatIam(chatRequest: SendMessageRequest): Promise { + const client = await createQDeveloperStreamingClient() + + const response = await client.sendMessage(chatRequest) + if (!response.sendMessageResponse) { + throw new ToolkitError( + `Empty chat response. Session id: ${this.sessionId} Request ID: ${response.$metadata.requestId}` + ) + } + + const responseStream = response.sendMessageResponse + for await (const event of responseStream) { + if ('messageMetadataEvent' in event) { + this.sessionId = event.messageMetadataEvent?.conversationId + break + } + } + + UserWrittenCodeTracker.instance.onQFeatureInvoked() + return response + } + + async chatSso(chatRequest: GenerateAssistantResponseRequest): Promise { + const client = await createCodeWhispererChatStreamingClient() + + if (this.sessionId !== undefined && chatRequest.conversationState !== undefined) { + chatRequest.conversationState.conversationId = this.sessionId + } + + const response = await client.generateAssistantResponse(chatRequest) + if (!response.generateAssistantResponseResponse) { + throw new ToolkitError( + `Empty chat response. Session id: ${this.sessionId} Request ID: ${response.$metadata.requestId}` + ) + } + + this.sessionId = response.conversationId + + UserWrittenCodeTracker.instance.onQFeatureInvoked() + + return response + } +} diff --git a/packages/core/src/codewhispererChat/clients/chat/v0/model.ts b/packages/core/src/codewhispererChat/clients/chat/v0/model.ts new file mode 100644 index 00000000000..60a0cc64367 --- /dev/null +++ b/packages/core/src/codewhispererChat/clients/chat/v0/model.ts @@ -0,0 +1,155 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +export type ContextKey = string +export type ContextKeys = ContextKey[] + +export interface MatchPolicy { + readonly should?: ContextKeys + readonly must?: ContextKeys + readonly mustNot?: ContextKeys +} + +export interface Context { + readonly matchPolicy?: MatchPolicy +} + +export interface FullyQualifiedName { + readonly source?: SourceIdentifier + readonly symbol?: SymbolIdentifier +} + +export type FullyQualifiedNamesUsages = FullyQualifiedName[] + +export type Name = string + +export type SymbolIdentifier = Name[] + +export type SourceIdentifier = Name[] +export type FullyQualifiedNames = FullyQualifiedName[] +export interface FullyQualifiedNamesDeclarationsUsages { + readonly used?: FullyQualifiedNamesUsages +} + +export type SimpleName = string +export type SimpleNames = SimpleName[] + +export interface CodeQuery { + readonly simpleNames?: SimpleNames + readonly fullyQualifiedNames?: FullyQualifiedNamesDeclarationsUsages +} + +export interface EditorContext { + readonly fileContent?: string + readonly language?: string + readonly query?: string + readonly code?: string + readonly context?: Context + readonly codeQuery?: CodeQuery +} + +export enum ApiDocsType { + RequestMatchedFqn, + RelatedFrequentlyUsedFqn, +} + +class ApiDocsMetadata { + constructor(public canonicalExample?: CanonicalExample) {} +} + +class CanonicalExample { + constructor( + public url: string, + public body: string + ) {} +} + +export interface ChatApiDocsSuggestion { + readonly url?: string + readonly title?: string + readonly body?: string + readonly ancestor?: string | null + readonly type: ApiDocsType + readonly metadata?: ApiDocsMetadata +} + +export interface ChatSuggestion { + readonly url: string + readonly title: string + readonly body: string + readonly context: string[] + readonly metadata: SuggestionMetadata + readonly type: string +} + +interface StackExchangeMetadata { + readonly answerCount: number + readonly isAccepted: boolean + readonly score: number + readonly lastActivityDate: number +} + +interface SuggestionMetadata { + readonly StackOverflow?: StackExchangeMetadata + readonly StackExchange?: StackExchangeMetadata +} + +export interface FollowUp { + readonly type: FollowUpType + readonly message?: string + readonly attachedSuggestions?: ChatSuggestion[] + readonly attachedApiDocsSuggestions?: ChatApiDocsSuggestion[] +} + +export interface ChatRequest { + readonly message: string + readonly editorContext: EditorContext + readonly attachedSuggestions: ChatSuggestion[] + readonly attachedApiDocsSuggestions: ChatApiDocsSuggestion[] +} +export interface FollowUpRequest { + readonly followUp: FollowUp + readonly editorContext?: EditorContext +} + +export interface IdeTriggerRequest { + readonly trigger: string + readonly editorContext?: EditorContext +} + +// @ts-ignore +export interface ChatEvent { + readonly messageId: string + readonly header?: Header + readonly token?: string + readonly followUps?: SuggestedFollowUp[] + readonly suggestions?: ChatSuggestion[] + readonly query?: string +} + +interface Header { + readonly sender: string + readonly responseTo: string + readonly sequenceId: string +} + +export enum FollowUpType { + Alternatives, + CommonPractices, + Improvements, + MoreExamples, + CiteSources, + LineByLine, + ExplainInDetail, + Generated, +} + +interface SuggestedFollowUp { + readonly type: FollowUpType + readonly pillText?: string + readonly prompt?: string + readonly message?: string + readonly attachedSuggestions?: ChatSuggestion[] + readonly attachedApiDocsSuggestions?: ChatApiDocsSuggestion[] +} diff --git a/packages/core/src/codewhispererChat/commands/registerCommands.ts b/packages/core/src/codewhispererChat/commands/registerCommands.ts new file mode 100644 index 00000000000..8ec1f7ac759 --- /dev/null +++ b/packages/core/src/codewhispererChat/commands/registerCommands.ts @@ -0,0 +1,71 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { commandPalette } from '../../codewhisperer/commands/types' +import { CodeScanIssue } from '../../codewhisperer/models/model' +import { Commands, VsCodeCommandArg, placeholder } from '../../shared/vscode/commands2' + +/** + * Opens the Amazon Q panel, showing the correct View that should + * be shown in it. + */ +export const focusAmazonQPanel = Commands.declare( + { id: `aws.amazonq.focusChat`, compositeKey: { 1: 'source' } }, + () => async (_: VsCodeCommandArg, source: string) => { + /** + * The Amazon Q panel is the window that opens when you click the Q icon + * on the sidebar. Within this panel we can render different Views. + * + * The logic for determining which view is show is currently determined by + * the value of the context `aws.amazonq.showLoginView`. + * So when we try to focus the following Views, only one will show depending + * on the context. + */ + await Commands.tryExecute('aws.amazonq.AmazonQChatView.focus') + await Commands.tryExecute('aws.amazonq.AmazonCommonAuth.focus') + } +) + +/** + * {@link focusAmazonQPanel} but only used for the keybinding since we cannot + * explicitly set the `source` in the package.json definition + */ +export const focusAmazonQPanelKeybinding = Commands.declare('_aws.amazonq.focusChat.keybinding', () => async () => { + await focusAmazonQPanel.execute(placeholder, 'keybinding') +}) + +export function registerCommands() { + /** + * make these no-ops, since theres still callers that need to be deprecated + */ + Commands.register('aws.amazonq.updateContextCommandItems', () => {}) +} + +export type EditorContextBaseCommandType = + | 'aws.amazonq.explainCode' + | 'aws.amazonq.refactorCode' + | 'aws.amazonq.fixCode' + | 'aws.amazonq.optimizeCode' + | 'aws.amazonq.sendToPrompt' + | 'aws.amazonq.generateUnitTests' + +export type CodeScanIssueCommandType = 'aws.amazonq.explainIssue' + +export type EditorContextCommandType = EditorContextBaseCommandType | CodeScanIssueCommandType + +export type EditorContextCommandTriggerType = 'contextMenu' | 'keybinding' | typeof commandPalette | 'click' + +export interface EditorContextCommandBase { + type: EditorContextBaseCommandType + triggerType: EditorContextCommandTriggerType +} + +export interface EditorContextCommandWithIssue { + type: CodeScanIssueCommandType + triggerType: EditorContextCommandTriggerType + issue: CodeScanIssue +} + +export type EditorContextCommand = EditorContextCommandBase | EditorContextCommandWithIssue diff --git a/packages/core/src/codewhispererChat/constants.ts b/packages/core/src/codewhispererChat/constants.ts new file mode 100644 index 00000000000..84dd2dae292 --- /dev/null +++ b/packages/core/src/codewhispererChat/constants.ts @@ -0,0 +1,42 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as path from 'path' +import fs from '../shared/fs/fs' +import { ContextLengths } from './controllers/chat/model' + +export const promptFileExtension = '.md' + +// limit for each entry of @prompt, @rules, @files and @folder +export const additionalContentInnerContextLimit = 8192 + +export const aditionalContentNameLimit = 1024 + +// limit for each chunk of @workspace +export const workspaceChunkMaxSize = 40_960 + +export const getUserPromptsDirectory = () => { + return path.join(fs.getUserHomeDir(), '.aws', 'amazonq', 'prompts') +} + +export const createSavedPromptCommandId = 'create-saved-prompt' + +export const defaultContextLengths: ContextLengths = { + additionalContextLengths: { + fileContextLength: 0, + promptContextLength: 0, + ruleContextLength: 0, + }, + truncatedAdditionalContextLengths: { + fileContextLength: 0, + promptContextLength: 0, + ruleContextLength: 0, + }, + workspaceContextLength: 0, + truncatedWorkspaceContextLength: 0, + userInputContextLength: 0, + truncatedUserInputContextLength: 0, + focusFileContextLength: 0, + truncatedFocusFileContextLength: 0, +} diff --git a/packages/core/src/codewhispererChat/controllers/chat/chatRequest/converter.ts b/packages/core/src/codewhispererChat/controllers/chat/chatRequest/converter.ts new file mode 100644 index 00000000000..b4b17f35a64 --- /dev/null +++ b/packages/core/src/codewhispererChat/controllers/chat/chatRequest/converter.ts @@ -0,0 +1,330 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ConversationState, CursorState, DocumentSymbol, SymbolType, TextDocument } from '@amzn/codewhisperer-streaming' +import { AdditionalContentEntryAddition, ChatTriggerType, RelevantTextDocumentAddition, TriggerPayload } from '../model' +import { undefinedIfEmpty } from '../../../../shared/utilities/textUtilities' +import { getLogger } from '../../../../shared/logger/logger' +import { messageToChatMessage } from '../../../../shared/db/chatDb/util' + +const fqnNameSizeDownLimit = 1 +const fqnNameSizeUpLimit = 256 +export const supportedLanguagesList = [ + 'python', + 'javascript', + 'java', + 'csharp', + 'typescript', + 'c', + 'cpp', + 'go', + 'kotlin', + 'php', + 'ruby', + 'rust', + 'scala', + 'shell', + 'sql', +] + +export const filePathSizeLimit = 4_000 + +export function triggerPayloadToChatRequest(triggerPayload: TriggerPayload): { + conversationState: ConversationState + profileArn?: string +} { + // Flexible truncation logic + const remainingPayloadSize = 100_000 + + // Type A context: Preserving user input as much as possible + const userInputTruncationInfo = preserveContexts(triggerPayload, remainingPayloadSize, ChatContextType.UserInput) + + // Type B1(prompts) context: Preserving @prompt as much as possible + const userSpecificPromptsTruncationInfo = preserveContexts( + triggerPayload, + userInputTruncationInfo.remainingPayloadSize, + ChatContextType.UserSpecificPrompts + ) + + // Type C context: Preserving current file context as much as possible + const currentFileTruncationInfo = preserveContexts( + triggerPayload, + userSpecificPromptsTruncationInfo.remainingPayloadSize, + ChatContextType.CurrentFile + ) + + // Type B1(rules) context: Preserving rules as much as possible + const userSpecificRulesTruncationInfo = preserveContexts( + triggerPayload, + currentFileTruncationInfo.remainingPayloadSize, + ChatContextType.UserSpecificRules + ) + + // Type B2(explicit @files) context: Preserving files as much as possible + const userSpecificFilesTruncationInfo = preserveContexts( + triggerPayload, + userSpecificRulesTruncationInfo.remainingPayloadSize, + ChatContextType.UserSpecificFiles + ) + + // Type B3 @workspace context: Preserving workspace as much as possible + const workspaceTruncationInfo = preserveContexts( + triggerPayload, + userSpecificFilesTruncationInfo.remainingPayloadSize, + ChatContextType.Workspace + ) + + getLogger().debug( + `current request total payload size: ${userInputTruncationInfo.sizeAfter + currentFileTruncationInfo.sizeAfter + userSpecificRulesTruncationInfo.sizeAfter + userSpecificFilesTruncationInfo.sizeAfter + workspaceTruncationInfo.sizeAfter}` + ) + + // Filter out empty innerContext from additionalContents + if (triggerPayload.additionalContents !== undefined) { + triggerPayload.additionalContents = triggerPayload.additionalContents.filter( + (content) => content.innerContext !== undefined && content.innerContext !== '' + ) + } + + // Filter out empty text from relevantTextDocuments + triggerPayload.relevantTextDocuments = triggerPayload.relevantTextDocuments.filter( + (doc) => doc.text !== undefined && doc.text !== '' + ) + + let document: TextDocument | undefined = undefined + let cursorState: CursorState | undefined = undefined + + if (triggerPayload.filePath !== undefined && triggerPayload.filePath !== '') { + const documentSymbolFqns: DocumentSymbol[] = [] + if (triggerPayload.codeQuery?.fullyQualifiedNames?.used) { + for (const fqn of triggerPayload.codeQuery.fullyQualifiedNames.used) { + const elem = { + name: fqn.symbol?.join('.') ?? '', + type: SymbolType.USAGE, + source: fqn.source?.join('.'), + } + + if ( + elem.name.length >= fqnNameSizeDownLimit && + elem.name.length < fqnNameSizeUpLimit && + (elem.source === undefined || + (elem.source.length >= fqnNameSizeDownLimit && elem.source.length < fqnNameSizeUpLimit)) + ) { + documentSymbolFqns.push(elem) + } + } + } + + let programmingLanguage + if ( + triggerPayload.fileLanguage !== undefined && + triggerPayload.fileLanguage !== '' && + supportedLanguagesList.includes(triggerPayload.fileLanguage) + ) { + programmingLanguage = { languageName: triggerPayload.fileLanguage } + } + + document = { + relativeFilePath: triggerPayload.filePath ? triggerPayload.filePath.substring(0, filePathSizeLimit) : '', + text: triggerPayload.fileText, + programmingLanguage: programmingLanguage, + documentSymbols: documentSymbolFqns, + } + + if (triggerPayload.codeSelection?.start) { + cursorState = { + range: { + start: { + line: triggerPayload.codeSelection.start.line, + character: triggerPayload.codeSelection.start.character, + }, + end: { + line: triggerPayload.codeSelection.end.line, + character: triggerPayload.codeSelection.end.character, + }, + }, + } + } + } + + // service will throw validation exception if string is empty + const customizationArn: string | undefined = undefinedIfEmpty(triggerPayload.customization.arn) + const chatTriggerType = triggerPayload.trigger === ChatTriggerType.InlineChatMessage ? 'INLINE_CHAT' : 'MANUAL' + const history = + triggerPayload.history && + triggerPayload.history.length > 0 && + triggerPayload.history.map((chat) => messageToChatMessage(chat)) + + return { + conversationState: { + currentMessage: { + userInputMessage: { + content: triggerPayload.message, + userInputMessageContext: { + editorState: { + document, + cursorState, + relevantDocuments: triggerPayload.relevantTextDocuments, + useRelevantDocuments: triggerPayload.useRelevantDocuments, + }, + additionalContext: triggerPayload.additionalContents, + }, + userIntent: triggerPayload.userIntent, + }, + }, + chatTriggerType, + customizationArn: customizationArn, + history: history || undefined, + }, + profileArn: triggerPayload.profile?.arn, + } +} + +function preserveContexts( + triggerPayload: TriggerPayload, + remainingPayloadSize: number, + contextType: ChatContextType +): FlexibleTruncationInfo { + const typeToContextMap = new Map< + ChatContextType, + string | AdditionalContentEntryAddition[] | RelevantTextDocumentAddition[] + >([ + [ChatContextType.UserInput, triggerPayload.message], + [ChatContextType.CurrentFile, triggerPayload.fileText], + [ChatContextType.UserSpecificPrompts, triggerPayload.additionalContents], + [ChatContextType.UserSpecificRules, triggerPayload.additionalContents], + [ChatContextType.UserSpecificFiles, triggerPayload.additionalContents], + [ChatContextType.Workspace, triggerPayload.relevantTextDocuments], + ]) + + let truncationInfo = { + remainingPayloadSize: remainingPayloadSize, + sizeBefore: 0, + sizeAfter: 0, + textAfter: '', + } + + const contexts = typeToContextMap.get(contextType) + switch (contextType) { + case ChatContextType.UserInput: + truncationInfo = truncate(contexts as string, truncationInfo) + triggerPayload.message = truncationInfo.textAfter + triggerPayload.contextLengths.truncatedUserInputContextLength = truncationInfo.sizeAfter + break + case ChatContextType.CurrentFile: + truncationInfo = truncate(contexts as string, truncationInfo) + triggerPayload.fileText = truncationInfo.textAfter + triggerPayload.contextLengths.truncatedFocusFileContextLength = truncationInfo.sizeAfter + break + case ChatContextType.UserSpecificPrompts: + truncationInfo = truncateUserSpecificContexts( + contexts as AdditionalContentEntryAddition[], + truncationInfo, + 'prompt' + ) + triggerPayload.contextLengths.truncatedAdditionalContextLengths.promptContextLength = + truncationInfo.sizeAfter + break + case ChatContextType.UserSpecificRules: + truncationInfo = truncateUserSpecificContexts( + contexts as AdditionalContentEntryAddition[], + truncationInfo, + 'rule' + ) + triggerPayload.contextLengths.truncatedAdditionalContextLengths.ruleContextLength = truncationInfo.sizeAfter + break + case ChatContextType.UserSpecificFiles: + truncationInfo = truncateUserSpecificContexts( + contexts as AdditionalContentEntryAddition[], + truncationInfo, + 'file' + ) + triggerPayload.contextLengths.truncatedAdditionalContextLengths.fileContextLength = truncationInfo.sizeAfter + break + case ChatContextType.Workspace: + truncationInfo = truncateWorkspaceContexts(contexts as RelevantTextDocumentAddition[], truncationInfo) + triggerPayload.contextLengths.truncatedWorkspaceContextLength = truncationInfo.sizeAfter + break + default: + getLogger().warn(`Unexpected context type: ${contextType}`) + return truncationInfo + } + + getLogger().debug( + `Current request context size: type: ${contextType}, before: ${truncationInfo.sizeBefore}, after: ${truncationInfo.sizeAfter}` + ) + return truncationInfo +} + +function truncateUserSpecificContexts( + contexts: AdditionalContentEntryAddition[], + truncationInfo: FlexibleTruncationInfo, + type: string +): FlexibleTruncationInfo { + for (const context of contexts) { + if (context.type !== type || !context.innerContext) { + continue + } + truncationInfo = truncate(context.innerContext, truncationInfo) + context.innerContext = truncationInfo.textAfter + } + return truncationInfo +} + +function truncateWorkspaceContexts( + contexts: RelevantTextDocumentAddition[], + truncationInfo: FlexibleTruncationInfo +): FlexibleTruncationInfo { + for (const context of contexts) { + if (!context.text) { + continue + } + truncationInfo = truncate(context.text, truncationInfo) + context.text = truncationInfo.textAfter + } + return truncationInfo +} + +function truncate( + textBefore: string, + truncationInfo: FlexibleTruncationInfo, + isCurrentFile: boolean = false +): FlexibleTruncationInfo { + const sizeBefore = truncationInfo.sizeBefore + textBefore.length + + // for all other types of contexts, we simply truncate the tail, + // for current file context, since it's expanded from the middle context, we truncate head and tail to preserve middle context + const middle = Math.floor(textBefore.length / 2) + const halfRemaining = Math.floor(truncationInfo.remainingPayloadSize / 2) + const startPos = isCurrentFile ? middle - halfRemaining : 0 + const endPos = isCurrentFile + ? middle + (truncationInfo.remainingPayloadSize - halfRemaining) + : Math.min(textBefore.length, truncationInfo.remainingPayloadSize) + const textAfter = textBefore.substring(startPos, endPos) + + const sizeAfter = truncationInfo.sizeAfter + textAfter.length + const remainingPayloadSize = truncationInfo.remainingPayloadSize - textAfter.length + return { + remainingPayloadSize, + sizeBefore, + sizeAfter, + textAfter, + } +} + +type FlexibleTruncationInfo = { + readonly remainingPayloadSize: number + readonly sizeBefore: number + readonly sizeAfter: number + readonly textAfter: string +} + +export enum ChatContextType { + UserInput = 'userInput', + CurrentFile = 'currentFile', + UserSpecificPrompts = 'userSpecificPrompts', + UserSpecificRules = 'userSpecificRules', + UserSpecificFiles = 'userSpecificFiles', + Workspace = 'workspace', +} diff --git a/packages/core/src/codewhispererChat/controllers/chat/controller.ts b/packages/core/src/codewhispererChat/controllers/chat/controller.ts new file mode 100644 index 00000000000..1be0c0332f5 --- /dev/null +++ b/packages/core/src/codewhispererChat/controllers/chat/controller.ts @@ -0,0 +1,1171 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as path from 'path' +import * as vscode from 'vscode' +import { Event as VSCodeEvent, Uri, workspace, window, ViewColumn, Position, Selection } from 'vscode' +import { EditorContextExtractor } from '../../editor/context/extractor' +import { ChatSessionStorage } from '../../storages/chatSession' +import { Messenger, MessengerResponseType, StaticTextResponseType } from './messenger/messenger' +import { + PromptMessage, + ChatTriggerType, + TriggerPayload, + TabClosedMessage, + InsertCodeAtCursorPosition, + TriggerTabIDReceived, + StopResponseMessage, + CopyCodeToClipboard, + ChatItemVotedMessage, + ChatItemFeedbackMessage, + TabCreatedMessage, + TabChangedMessage, + UIFocusMessage, + SourceLinkClickMessage, + ResponseBodyLinkClickMessage, + ChatPromptCommandType, + FooterInfoLinkClick, + ViewDiff, + AcceptDiff, + QuickCommandGroupActionClick, + DocumentReference, + FileClick, + RelevantTextDocumentAddition, + TabBarButtonClick, + SaveChatMessage, +} from './model' +import { + AppToWebViewMessageDispatcher, + ContextSelectedMessage, + CustomFormActionMessage, + DetailedListActionClickMessage, + DetailedListFilterChangeMessage, + DetailedListItemSelectMessage, +} from '../../view/connector/connector' +import { MessagePublisher } from '../../../amazonq/messages/messagePublisher' +import { MessageListener } from '../../../amazonq/messages/messageListener' +import { EditorContentController } from '../../../amazonq/commons/controllers/contentController' +import { EditorContextCommand } from '../../commands/registerCommands' +import { PromptsGenerator } from './prompts/promptsGenerator' +import { TriggerEventsStorage } from '../../storages/triggerEvents' +import { SendMessageRequest } from '@amzn/amazon-q-developer-streaming-client' +import { CodeWhispererStreamingServiceException } from '@amzn/codewhisperer-streaming' +import { UserIntentRecognizer } from './userIntent/userIntentRecognizer' +import { CWCTelemetryHelper, recordTelemetryChatRunCommand } from './telemetryHelper' +import { CodeWhispererTracker } from '../../../codewhisperer/tracker/codewhispererTracker' +import { getLogger } from '../../../shared/logger/logger' +import { triggerPayloadToChatRequest } from './chatRequest/converter' +import { AuthUtil } from '../../../codewhisperer/util/authUtil' +import { openUrl } from '../../../shared/utilities/vsCodeUtils' +import { randomUUID } from '../../../shared/crypto' +import { CodeWhispererSettings } from '../../../codewhisperer/util/codewhispererSettings' +import { getSelectedCustomization } from '../../../codewhisperer/util/customizationUtil' +import { getHttpStatusCode, AwsClientResponseError } from '../../../shared/errors' +import { uiEventRecorder } from '../../../amazonq/util/eventRecorder' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { isSsoConnection } from '../../../auth/connection' +import { inspect } from '../../../shared/utilities/collectionUtils' +import { DefaultAmazonQAppInitContext } from '../../../amazonq/apps/initContext' +import globals from '../../../shared/extensionGlobals' +import { MynahIconsType, MynahUIDataModel, QuickActionCommand } from '@aws/mynah-ui' +import { workspaceCommand } from '../../../amazonq/webview/ui/tabs/constants' +import fs from '../../../shared/fs/fs' +import { FeatureConfigProvider, Features } from '../../../shared/featureConfig' +import { i18n } from '../../../shared/i18n-helper' +import { + getUserPromptsDirectory, + promptFileExtension, + createSavedPromptCommandId, + defaultContextLengths, +} from '../../constants' +import { ChatSession } from '../../clients/chat/v0/chat' +import { Database } from '../../../shared/db/chatDb/chatDb' +import { TabBarController } from './tabBarController' + +export interface ChatControllerMessagePublishers { + readonly processPromptChatMessage: MessagePublisher + readonly processTabCreatedMessage: MessagePublisher + readonly processTabClosedMessage: MessagePublisher + readonly processTabChangedMessage: MessagePublisher + readonly processInsertCodeAtCursorPosition: MessagePublisher + readonly processAcceptDiff: MessagePublisher + readonly processViewDiff: MessagePublisher + readonly processCopyCodeToClipboard: MessagePublisher + readonly processContextMenuCommand: MessagePublisher + readonly processTriggerTabIDReceived: MessagePublisher + readonly processStopResponseMessage: MessagePublisher + readonly processChatItemVotedMessage: MessagePublisher + readonly processChatItemFeedbackMessage: MessagePublisher + readonly processUIFocusMessage: MessagePublisher + readonly processSourceLinkClick: MessagePublisher + readonly processResponseBodyLinkClick: MessagePublisher + readonly processFooterInfoLinkClick: MessagePublisher + readonly processContextCommandUpdateMessage: MessagePublisher + readonly processQuickCommandGroupActionClicked: MessagePublisher + readonly processCustomFormAction: MessagePublisher + readonly processContextSelected: MessagePublisher + readonly processFileClick: MessagePublisher + readonly processTabBarButtonClick: MessagePublisher + readonly processSaveChat: MessagePublisher + readonly processDetailedListFilterChangeMessage: MessagePublisher + readonly processDetailedListItemSelectMessage: MessagePublisher + readonly processDetailedListActionClickMessage: MessagePublisher +} + +export interface ChatControllerMessageListeners { + readonly processPromptChatMessage: MessageListener + readonly processTabCreatedMessage: MessageListener + readonly processTabClosedMessage: MessageListener + readonly processTabChangedMessage: MessageListener + readonly processInsertCodeAtCursorPosition: MessageListener + readonly processAcceptDiff: MessageListener + readonly processViewDiff: MessageListener + readonly processCopyCodeToClipboard: MessageListener + readonly processContextMenuCommand: MessageListener + readonly processTriggerTabIDReceived: MessageListener + readonly processStopResponseMessage: MessageListener + readonly processChatItemVotedMessage: MessageListener + readonly processChatItemFeedbackMessage: MessageListener + readonly processUIFocusMessage: MessageListener + readonly processSourceLinkClick: MessageListener + readonly processResponseBodyLinkClick: MessageListener + readonly processFooterInfoLinkClick: MessageListener + readonly processContextCommandUpdateMessage: MessageListener + readonly processQuickCommandGroupActionClicked: MessageListener + readonly processCustomFormAction: MessageListener + readonly processContextSelected: MessageListener + readonly processFileClick: MessageListener + readonly processTabBarButtonClick: MessageListener + readonly processSaveChat: MessageListener + readonly processDetailedListFilterChangeMessage: MessageListener + readonly processDetailedListItemSelectMessage: MessageListener + readonly processDetailedListActionClickMessage: MessageListener +} + +export class ChatController { + private readonly sessionStorage: ChatSessionStorage + private readonly triggerEventsStorage: TriggerEventsStorage + private readonly messenger: Messenger + private readonly editorContextExtractor: EditorContextExtractor + private readonly editorContentController: EditorContentController + private readonly tabBarController: TabBarController + private readonly promptGenerator: PromptsGenerator + private readonly userIntentRecognizer: UserIntentRecognizer + private readonly telemetryHelper: CWCTelemetryHelper + private userPromptsWatcher: vscode.FileSystemWatcher | undefined + private chatHistoryDb = Database.getInstance() + private cancelTokenSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource() + + public constructor( + private readonly chatControllerMessageListeners: ChatControllerMessageListeners, + appsToWebViewMessagePublisher: MessagePublisher, + onDidChangeAmazonQVisibility: VSCodeEvent + ) { + this.sessionStorage = new ChatSessionStorage() + this.triggerEventsStorage = new TriggerEventsStorage() + this.telemetryHelper = CWCTelemetryHelper.init(this.sessionStorage, this.triggerEventsStorage) + this.messenger = new Messenger( + new AppToWebViewMessageDispatcher(appsToWebViewMessagePublisher), + this.telemetryHelper + ) + this.editorContextExtractor = new EditorContextExtractor() + this.editorContentController = new EditorContentController() + this.promptGenerator = new PromptsGenerator() + this.userIntentRecognizer = new UserIntentRecognizer() + this.tabBarController = new TabBarController(this.messenger) + + onDidChangeAmazonQVisibility((visible) => { + if (visible) { + this.telemetryHelper.recordOpenChat() + } else { + this.telemetryHelper.recordCloseChat() + } + }) + + AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { + this.cancelTokenSource.cancel() + }) + + this.chatControllerMessageListeners.processPromptChatMessage.onMessage((data) => { + const uiEvents = uiEventRecorder.get(data.tabID) + if (uiEvents) { + uiEventRecorder.set(data.tabID, { + events: { + featureReceivedMessage: globals.clock.Date.now(), + }, + }) + } + /** + * traceId is only instrumented for chat-prompt but not for things + * like follow-up-was-clicked. In those cases we fallback to a different + * uuid + **/ + return telemetry.withTraceId(() => { + return this.processPromptChatMessage(data) + }, uiEvents?.traceId ?? randomUUID()) + }) + + this.chatControllerMessageListeners.processTabCreatedMessage.onMessage((data) => { + return this.processTabCreateMessage(data) + }) + + this.chatControllerMessageListeners.processTabClosedMessage.onMessage((data) => { + return this.processTabCloseMessage(data) + }) + + this.chatControllerMessageListeners.processTabChangedMessage.onMessage((data) => { + return this.processTabChangedMessage(data) + }) + + this.chatControllerMessageListeners.processInsertCodeAtCursorPosition.onMessage((data) => { + return this.processInsertCodeAtCursorPosition(data) + }) + + this.chatControllerMessageListeners.processAcceptDiff.onMessage((data) => { + return this.processAcceptDiff(data) + }) + + this.chatControllerMessageListeners.processViewDiff.onMessage((data) => { + return this.processViewDiff(data) + }) + + this.chatControllerMessageListeners.processCopyCodeToClipboard.onMessage((data) => { + return this.processCopyCodeToClipboard(data) + }) + + this.chatControllerMessageListeners.processContextMenuCommand.onMessage((data) => { + return this.processContextMenuCommand(data) + }) + + this.chatControllerMessageListeners.processTriggerTabIDReceived.onMessage((data) => { + return this.processTriggerTabIDReceived(data) + }) + + this.chatControllerMessageListeners.processStopResponseMessage.onMessage((data) => { + return this.processStopResponseMessage(data) + }) + + this.chatControllerMessageListeners.processChatItemVotedMessage.onMessage((data) => { + return this.processChatItemVotedMessage(data) + }) + + this.chatControllerMessageListeners.processChatItemFeedbackMessage.onMessage((data) => { + return this.processChatItemFeedbackMessage(data) + }) + + this.chatControllerMessageListeners.processUIFocusMessage.onMessage((data) => { + return this.processUIFocusMessage(data) + }) + + this.chatControllerMessageListeners.processSourceLinkClick.onMessage((data) => { + return this.processSourceLinkClick(data) + }) + this.chatControllerMessageListeners.processResponseBodyLinkClick.onMessage((data) => { + return this.processResponseBodyLinkClick(data) + }) + this.chatControllerMessageListeners.processFooterInfoLinkClick.onMessage((data) => { + return this.processFooterInfoLinkClick(data) + }) + this.chatControllerMessageListeners.processContextCommandUpdateMessage.onMessage(() => { + return this.processContextCommandUpdateMessage() + }) + this.chatControllerMessageListeners.processQuickCommandGroupActionClicked.onMessage((data) => { + return this.processQuickCommandGroupActionClicked(data) + }) + this.chatControllerMessageListeners.processCustomFormAction.onMessage((data) => { + return this.processCustomFormAction(data) + }) + this.chatControllerMessageListeners.processContextSelected.onMessage((data) => { + return this.processContextSelected(data) + }) + this.chatControllerMessageListeners.processFileClick.onMessage((data) => { + return this.processFileClickMessage(data) + }) + this.chatControllerMessageListeners.processTabBarButtonClick.onMessage((data) => { + return this.tabBarController.processTabBarButtonClick(data) + }) + this.chatControllerMessageListeners.processSaveChat.onMessage((data) => { + return this.tabBarController.processSaveChat(data) + }) + this.chatControllerMessageListeners.processDetailedListActionClickMessage.onMessage((data) => { + return this.tabBarController.processActionClickMessage(data) + }) + this.chatControllerMessageListeners.processDetailedListFilterChangeMessage.onMessage((data) => { + return this.tabBarController.processFilterChangeMessage(data) + }) + this.chatControllerMessageListeners.processDetailedListItemSelectMessage.onMessage((data) => { + return this.tabBarController.processItemSelectMessage(data) + }) + AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { + this.sessionStorage.deleteAllSessions() + }) + } + + private registerUserPromptsWatcher() { + if (this.userPromptsWatcher) { + return + } + this.userPromptsWatcher = vscode.workspace.createFileSystemWatcher( + new vscode.RelativePattern(vscode.Uri.file(getUserPromptsDirectory()), `*${promptFileExtension}`), + false, + true, + false + ) + this.userPromptsWatcher.onDidCreate(() => this.processContextCommandUpdateMessage()) + this.userPromptsWatcher.onDidDelete(() => this.processContextCommandUpdateMessage()) + globals.context.subscriptions.push(this.userPromptsWatcher) + } + + private processFooterInfoLinkClick(click: FooterInfoLinkClick) { + this.openLinkInExternalBrowser(click) + } + + private openLinkInExternalBrowser( + click: ResponseBodyLinkClickMessage | SourceLinkClickMessage | FooterInfoLinkClick + ) { + this.telemetryHelper.recordInteractWithMessage(click) + void openUrl(Uri.parse(click.link)) + } + + private processResponseBodyLinkClick(click: ResponseBodyLinkClickMessage) { + this.openLinkInExternalBrowser(click) + } + + private processSourceLinkClick(click: SourceLinkClickMessage) { + this.openLinkInExternalBrowser(click) + } + + private processQuickActionCommand(message: PromptMessage) { + this.editorContextExtractor + .extractContextForTrigger('QuickAction') + .then((context) => { + const triggerID = randomUUID() + + const quickActionCommand = message.command as ChatPromptCommandType + + this.messenger.sendQuickActionMessage(quickActionCommand, triggerID) + + this.triggerEventsStorage.addTriggerEvent({ + id: triggerID, + tabID: message.tabID, + message: undefined, + type: 'quick_action', + quickAction: quickActionCommand, + context, + }) + + if (quickActionCommand === 'help') { + void this.generateStaticTextResponse('quick-action-help', triggerID) + recordTelemetryChatRunCommand('help') + return + } + }) + .catch((e) => { + this.processException(e, '') + }) + } + + private async processChatItemFeedbackMessage(message: ChatItemFeedbackMessage) { + await this.telemetryHelper.recordFeedback(message) + } + + private async processChatItemVotedMessage(message: ChatItemVotedMessage) { + this.telemetryHelper.recordInteractWithMessage(message) + } + + private async processStopResponseMessage(message: StopResponseMessage) { + const session = this.sessionStorage.getSession(message.tabID) + session.tokenSource.cancel() + } + + private async processTriggerTabIDReceived(message: TriggerTabIDReceived) { + this.triggerEventsStorage.updateTriggerEventTabIDFromUnknown(message.triggerID, message.tabID) + } + + private async processInsertCodeAtCursorPosition(message: InsertCodeAtCursorPosition) { + this.editorContentController.insertTextAtCursorPosition(message.code, (editor, cursorStart) => { + CodeWhispererTracker.getTracker().enqueue({ + conversationID: this.telemetryHelper.getConversationId(message.tabID) ?? '', + messageID: message.messageId, + userIntent: message.userIntent, + time: new Date(), + fileUrl: editor.document.uri, + startPosition: cursorStart, + endPosition: editor.selection.active, + originalString: message.code, + }) + }) + this.telemetryHelper.recordInteractWithMessage(message) + } + + private async processAcceptDiff(message: AcceptDiff) { + const context = this.triggerEventsStorage.getTriggerEvent((message.data as any)?.triggerID) || '' + this.editorContentController + .acceptDiff({ ...message, ...context }) + .then(() => { + this.telemetryHelper.recordInteractWithMessage(message) + }) + .catch((error) => { + this.telemetryHelper.recordInteractWithMessage(message, { result: 'Failed' }) + }) + } + + private async processViewDiff(message: ViewDiff) { + const context = this.triggerEventsStorage.getTriggerEvent((message.data as any)?.triggerID) || '' + this.editorContentController + .viewDiff({ ...message, ...context }) + .then(() => { + this.telemetryHelper.recordInteractWithMessage(message) + }) + .catch((error) => { + this.telemetryHelper.recordInteractWithMessage(message, { result: 'Failed' }) + }) + } + + private async processCopyCodeToClipboard(message: CopyCodeToClipboard) { + this.telemetryHelper.recordInteractWithMessage(message) + } + + private async processTabCreateMessage(message: TabCreatedMessage) { + // this.telemetryHelper.recordOpenChat(message.tabOpenInteractionType) + } + + private async processTabCloseMessage(message: TabClosedMessage) { + this.sessionStorage.deleteSession(message.tabID) + this.triggerEventsStorage.removeTabEvents(message.tabID) + // this.telemetryHelper.recordCloseChat(message.tabID) + this.chatHistoryDb.updateTabOpenState(message.tabID, false) + } + + private async processTabChangedMessage(message: TabChangedMessage) { + if (message.prevTabID) { + this.telemetryHelper.recordExitFocusConversation(message.prevTabID) + } + this.telemetryHelper.recordEnterFocusConversation(message.tabID) + } + + private async processUIFocusMessage(message: UIFocusMessage) { + switch (message.type) { + case 'focus': + this.telemetryHelper.recordEnterFocusChat() + break + case 'blur': + this.telemetryHelper.recordExitFocusChat() + break + } + } + + private async processContextCommandUpdateMessage() { + // when UI is ready, refresh the context commands + this.tabBarController.loadChats() + this.registerUserPromptsWatcher() + const contextCommand: MynahUIDataModel['contextCommands'] = [ + { + commands: [ + ...workspaceCommand.commands, + { + command: i18n('AWS.amazonq.context.folders.title'), + children: [ + { + groupName: i18n('AWS.amazonq.context.folders.title'), + commands: [], + }, + ], + description: i18n('AWS.amazonq.context.folders.description'), + icon: 'folder' as MynahIconsType, + }, + { + command: i18n('AWS.amazonq.context.files.title'), + children: [ + { + groupName: i18n('AWS.amazonq.context.files.title'), + commands: [], + }, + ], + description: i18n('AWS.amazonq.context.files.description'), + icon: 'file' as MynahIconsType, + }, + { + command: i18n('AWS.amazonq.context.code.title'), + children: [ + { + groupName: i18n('AWS.amazonq.context.code.title'), + commands: [], + }, + ], + description: i18n('AWS.amazonq.context.code.description'), + icon: 'code-block' as MynahIconsType, + }, + { + command: i18n('AWS.amazonq.context.prompts.title'), + children: [ + { + groupName: i18n('AWS.amazonq.context.prompts.title'), + commands: [], + }, + ], + description: i18n('AWS.amazonq.context.prompts.description'), + icon: 'magic' as MynahIconsType, + }, + ], + }, + ] + + const feature = FeatureConfigProvider.getFeature(Features.highlightCommand) + const commandName = feature?.value.stringValue + if (commandName) { + const commandDescription = feature.variation + contextCommand.push({ + groupName: 'Additional Commands', + commands: [{ command: commandName, description: commandDescription }], + }) + } + const promptsCmd: QuickActionCommand = contextCommand[0].commands?.[4] + + // Check for user prompts + try { + const userPromptsDirectory = getUserPromptsDirectory() + const directoryExists = await fs.exists(userPromptsDirectory) + if (directoryExists) { + const systemPromptFiles = await fs.readdir(userPromptsDirectory) + promptsCmd.children?.[0].commands.push( + ...systemPromptFiles + .filter(([name]) => name.endsWith(promptFileExtension)) + .map(([name]) => ({ + command: path.basename(name, promptFileExtension), + icon: 'magic' as MynahIconsType, + id: 'prompt', + // label: 'file' as ContextCommandItemType, + route: [userPromptsDirectory, name], + })) + ) + } + } catch (e) { + getLogger().verbose(`Could not read prompts from ~/.aws/prompts: ${e}`) + } + + // Add create prompt button to the bottom of the prompts list + promptsCmd.children?.[0].commands.push({ + command: i18n('AWS.amazonq.savedPrompts.action'), + id: createSavedPromptCommandId, + icon: 'list-add' as MynahIconsType, + }) + + this.messenger.sendContextCommandData(contextCommand) + } + + private handlePromptCreate(tabID: string) { + this.messenger.showCustomForm( + tabID, + [ + { + id: 'prompt-name', + type: 'textinput', + mandatory: true, + autoFocus: true, + title: i18n('AWS.amazonq.savedPrompts.title'), + placeholder: i18n('AWS.amazonq.savedPrompts.placeholder'), + description: i18n('AWS.amazonq.savedPrompts.description'), + }, + ], + [ + { id: 'cancel-create-prompt', text: i18n('AWS.generic.cancel'), status: 'clear' }, + { id: 'submit-create-prompt', text: i18n('AWS.amazonq.savedPrompts.create'), status: 'main' }, + ], + `Create a saved prompt` + ) + } + + private processQuickCommandGroupActionClicked(message: QuickCommandGroupActionClick) { + if (message.actionId === createSavedPromptCommandId) { + this.handlePromptCreate(message.tabID) + } + } + + private async processCustomFormAction(message: CustomFormActionMessage) { + if (message.action.id === 'submit-create-prompt') { + const userPromptsDirectory = getUserPromptsDirectory() + + const title = message.action.formItemValues?.['prompt-name'] + const newFilePath = path.join( + userPromptsDirectory, + title ? `${title}${promptFileExtension}` : `default${promptFileExtension}` + ) + const newFileContent = new Uint8Array(Buffer.from('')) + await fs.writeFile(newFilePath, newFileContent, { mode: 0o600 }) + const newFileDoc = await vscode.workspace.openTextDocument(newFilePath) + await vscode.window.showTextDocument(newFileDoc) + telemetry.ui_click.emit({ elementId: 'amazonq_createSavedPrompt' }) + } + } + + private async processContextSelected(message: ContextSelectedMessage) { + if (message.tabID && message.contextItem.id === createSavedPromptCommandId) { + this.handlePromptCreate(message.tabID) + } + } + private async processFileClickMessage(message: FileClick) { + const session = this.sessionStorage.getSession(message.tabID) + const lineRanges = session.contexts.get(message.filePath) + + if (!lineRanges) { + return + } + + // Check if clicked file is in a different workspace root + const projectRoot = + session.relativePathToWorkspaceRoot.get(message.filePath) || workspace.workspaceFolders?.[0]?.uri.fsPath + if (!projectRoot) { + return + } + let absoluteFilePath = path.join(projectRoot, message.filePath) + + // Handle clicking on a user prompt outside the workspace + if (message.filePath.endsWith(promptFileExtension)) { + try { + await vscode.workspace.fs.stat(vscode.Uri.file(absoluteFilePath)) + } catch { + absoluteFilePath = path.join(getUserPromptsDirectory(), message.filePath) + } + } + + try { + // Open the file in VSCode + const document = await workspace.openTextDocument(absoluteFilePath) + const editor = await window.showTextDocument(document, ViewColumn.Active) + + // Create multiple selections based on line ranges + const selections: Selection[] = lineRanges + .filter(({ first, second }) => first !== -1 && second !== -1) + .map(({ first, second }) => { + const startPosition = new Position(first - 1, 0) // Convert 1-based to 0-based + const endPosition = new Position(second - 1, document.lineAt(second - 1).range.end.character) + return new Selection( + startPosition.line, + startPosition.character, + endPosition.line, + endPosition.character + ) + }) + + // Apply multiple selections to the editor + if (selections.length > 0) { + editor.selection = selections[0] // Set the first selection as active + editor.selections = selections // Apply multiple selections + editor.revealRange(selections[0], vscode.TextEditorRevealType.InCenter) + } + } catch (error) {} + } + + private processException(e: any, tabID: string) { + let errorMessage = '' + let requestID = undefined + const defaultMessage = 'Failed to get response' + if (typeof e === 'string') { + errorMessage = e.toUpperCase() + } else if (e instanceof SyntaxError) { + // Workaround to handle case when LB returns web-page with error and our client doesn't return proper exception + errorMessage = AwsClientResponseError.tryExtractReasonFromSyntaxError(e) ?? defaultMessage + } else if (e instanceof CodeWhispererStreamingServiceException) { + errorMessage = e.message + requestID = e.$metadata.requestId + } else if (e instanceof Error) { + errorMessage = e.message + } + + this.messenger.sendErrorMessage(errorMessage, tabID, requestID) + getLogger().error(`error: ${errorMessage} tabID: ${tabID} requestID: ${requestID}`) + + this.sessionStorage.deleteSession(tabID) + } + + private async processContextMenuCommand(command: EditorContextCommand) { + // Just open the chat panel in this case + if (!this.editorContextExtractor.isCodeBlockSelected() && command.type === 'aws.amazonq.sendToPrompt') { + return + } + + this.editorContextExtractor + .extractContextForTrigger('ContextMenu') + .then(async (context) => { + const triggerID = randomUUID() + if (command.type === 'aws.amazonq.generateUnitTests') { + DefaultAmazonQAppInitContext.instance.getAppsToWebViewMessagePublisher().publish({ + sender: 'testChat', + command: 'test', + type: 'chatMessage', + }) + // For non-supported languages, we'll just open the standard chat. + return + } + + if (context?.focusAreaContext?.codeBlock === undefined) { + throw 'Sorry, I cannot help with the selected language code snippet' + } + + const prompt = this.promptGenerator.generateForContextMenuCommand(command) + + if (command.type === 'aws.amazonq.explainIssue') { + this.messenger.sendEditorContextCommandMessage( + command.type, + context.activeFileContext?.fileText + ?.split('\n') + .slice(command.issue.startLine, command.issue.endLine) + .join('') ?? '', + triggerID, + command.issue + ) + } else { + this.messenger.sendEditorContextCommandMessage( + command.type, + context?.focusAreaContext?.codeBlock ?? '', + triggerID + ) + } + + if (command.type === 'aws.amazonq.sendToPrompt') { + // No need for response if send the code to prompt + return + } + + this.triggerEventsStorage.addTriggerEvent({ + id: triggerID, + tabID: undefined, + message: prompt, + type: 'editor_context_command', + context, + command, + }) + + return this.generateResponse( + { + message: prompt, + trigger: ChatTriggerType.ChatMessage, + query: undefined, + codeSelection: context?.focusAreaContext?.selectionInsideExtendedCodeBlock, + fileText: context?.focusAreaContext?.extendedCodeBlock ?? '', + fileLanguage: context?.activeFileContext?.fileLanguage, + filePath: context?.activeFileContext?.filePath, + matchPolicy: context?.activeFileContext?.matchPolicy, + codeQuery: context?.focusAreaContext?.names, + userIntent: this.userIntentRecognizer.getFromContextMenuCommand(command), + customization: getSelectedCustomization(), + profile: AuthUtil.instance.regionProfileManager.activeRegionProfile, + additionalContents: [], + relevantTextDocuments: [], + documentReferences: [], + useRelevantDocuments: false, + contextLengths: { + ...defaultContextLengths, + }, + context: [], + }, + triggerID + ) + }) + .catch((e) => { + this.processException(e, '') + }) + } + + private async processPromptChatMessage(message: PromptMessage) { + if (message.message === undefined) { + this.messenger.sendErrorMessage('chatMessage should be set', message.tabID, undefined) + return + } + try { + switch (message.command) { + case 'follow-up-was-clicked': + await this.processFollowUp(message) + this.telemetryHelper.recordInteractWithMessage(message) + break + case 'onboarding-page-cwc-button-clicked': + case 'chat-prompt': + await this.processPromptMessageAsNewThread(message) + break + default: + await this.processCommandMessage(message) + } + } catch (e) { + this.processException(e, message.tabID) + } + } + + private async processCommandMessage(message: PromptMessage) { + if (message.command === undefined) { + return + } + switch (message.command) { + case 'clear': + this.sessionStorage.deleteSession(message.tabID) + this.triggerEventsStorage.removeTabEvents(message.tabID) + recordTelemetryChatRunCommand('clear') + this.chatHistoryDb.clearTab(message.tabID) + return + default: + this.processQuickActionCommand(message) + } + } + + private async processFollowUp(message: PromptMessage) { + try { + const lastTriggerEvent = this.triggerEventsStorage.getLastTriggerEventByTabID(message.tabID) + + if (lastTriggerEvent === undefined) { + throw "It's impossible to ask follow-ups on empty tabs" + } + + const triggerID = randomUUID() + this.triggerEventsStorage.addTriggerEvent({ + id: triggerID, + tabID: message.tabID, + message: message.message, + type: 'follow_up', + context: lastTriggerEvent.context, + }) + + return this.generateResponse( + { + message: message.message ?? '', + trigger: ChatTriggerType.ChatMessage, + query: message.message, + codeSelection: lastTriggerEvent.context?.focusAreaContext?.selectionInsideExtendedCodeBlock, + fileText: lastTriggerEvent.context?.focusAreaContext?.extendedCodeBlock ?? '', + fileLanguage: lastTriggerEvent.context?.activeFileContext?.fileLanguage, + filePath: lastTriggerEvent.context?.activeFileContext?.filePath, + matchPolicy: lastTriggerEvent.context?.activeFileContext?.matchPolicy, + codeQuery: lastTriggerEvent.context?.focusAreaContext?.names, + userIntent: message.userIntent, + customization: getSelectedCustomization(), + profile: AuthUtil.instance.regionProfileManager.activeRegionProfile, + contextLengths: { + ...defaultContextLengths, + }, + relevantTextDocuments: [], + additionalContents: [], + documentReferences: [], + useRelevantDocuments: false, + context: [], + }, + triggerID + ) + } catch (e) { + this.processException(e, message.tabID) + } + } + + private async processPromptMessageAsNewThread(message: PromptMessage) { + this.editorContextExtractor + .extractContextForTrigger('ChatMessage') + .then((context) => { + const triggerID = randomUUID() + this.triggerEventsStorage.addTriggerEvent({ + id: triggerID, + tabID: message.tabID, + message: message.message, + type: 'chat_message', + context, + }) + return this.generateResponse( + { + message: message.message ?? '', + trigger: ChatTriggerType.ChatMessage, + query: message.message, + codeSelection: context?.focusAreaContext?.selectionInsideExtendedCodeBlock, + fileText: context?.focusAreaContext?.extendedCodeBlock ?? '', + fileLanguage: context?.activeFileContext?.fileLanguage, + filePath: context?.activeFileContext?.filePath, + matchPolicy: context?.activeFileContext?.matchPolicy, + codeQuery: context?.focusAreaContext?.names, + userIntent: this.userIntentRecognizer.getFromPromptChatMessage(message), + customization: getSelectedCustomization(), + profile: AuthUtil.instance.regionProfileManager.activeRegionProfile, + context: message.context ?? [], + relevantTextDocuments: [], + additionalContents: [], + documentReferences: [], + useRelevantDocuments: false, + contextLengths: { + ...defaultContextLengths, + }, + }, + triggerID + ) + }) + .catch((e) => { + this.processException(e, message.tabID) + }) + } + + private async generateStaticTextResponse(responseType: StaticTextResponseType, triggerID: string) { + // Loop while we waiting for tabID to be set + const triggerEvent = this.triggerEventsStorage.getTriggerEvent(triggerID) + if (triggerEvent === undefined) { + return + } + + if (triggerEvent.tabID === 'no-available-tabs') { + return + } + + if (triggerEvent.tabID === undefined) { + setTimeout(() => { + this.generateStaticTextResponse(responseType, triggerID).catch((e) => { + getLogger().error('generateStaticTextResponse failed: %s', (e as Error).message) + }) + }, 20) + return + } + + const tabID = triggerEvent.tabID + + const credentialsState = await AuthUtil.instance.getChatAuthState() + + if (credentialsState.codewhispererChat !== 'connected' && credentialsState.codewhispererCore !== 'connected') { + await this.messenger.sendAuthNeededExceptionMessage(credentialsState, tabID, triggerID) + return + } + + this.messenger.sendStaticTextResponse(responseType, triggerID, tabID) + } + + /** + * @returns A Uri array of prompt files in each workspace root's .amazonq/rules directory + */ + private async collectWorkspaceRules(): Promise { + const rulesFiles: string[] = [] + + if (!vscode.workspace.workspaceFolders) { + return rulesFiles + } + + for (const folder of vscode.workspace.workspaceFolders) { + const rulesPath = path.join(folder.uri.fsPath, '.amazonq', 'rules') + const folderExists = await fs.exists(rulesPath) + + if (folderExists) { + const entries = await fs.readdir(rulesPath) + + for (const [name, type] of entries) { + if (type === vscode.FileType.File && name.endsWith(promptFileExtension)) { + rulesFiles.push(path.join(rulesPath, name)) + } + } + } + } + + return rulesFiles + } + + private async resolveContextCommandPayload(triggerPayload: TriggerPayload, session: ChatSession) { + const contextCommands: any[] = [] + + // Check for workspace rules to add to context + const workspaceRules = await this.collectWorkspaceRules() + if (workspaceRules.length > 0) { + contextCommands.push( + ...workspaceRules.map((rule) => { + const workspaceFolderPath = + vscode.workspace.getWorkspaceFolder(vscode.Uri.parse(rule))?.uri?.path || '' + return { + workspaceFolder: workspaceFolderPath, + type: 'file' as any, + relativePath: path.relative(workspaceFolderPath, rule), + } + }) + ) + } + triggerPayload.workspaceRulesCount = workspaceRules.length + + for (const context of triggerPayload.context) { + if (typeof context !== 'string' && context.route && context.route.length === 2) { + contextCommands.push({ + workspaceFolder: context.route[0] || '', + type: (context.label || '') as any, + relativePath: context.route[1] || '', + id: context.id, + }) + } + } + + if (contextCommands.length === 0) { + return [] + } + const workspaceFolders = (vscode.workspace.workspaceFolders ?? []).map((folder) => folder.uri.fsPath) + if (!workspaceFolders) { + return [] + } + workspaceFolders.sort() + } + + private async generateResponse( + triggerPayload: TriggerPayload & { projectContextQueryLatencyMs?: number }, + triggerID: string + ) { + const triggerEvent = this.triggerEventsStorage.getTriggerEvent(triggerID) + if (triggerEvent === undefined) { + return + } + + if (triggerEvent.tabID === 'no-available-tabs') { + return + } + + if (triggerEvent.tabID === undefined) { + setTimeout(() => { + this.generateResponse(triggerPayload, triggerID).catch((e) => { + getLogger().error('generateResponse failed: %s', (e as Error).message) + }) + }, 20) + return + } + + const tabID = triggerEvent.tabID + + const credentialsState = await AuthUtil.instance.getChatAuthState() + + if ( + !(credentialsState.codewhispererChat === 'connected' && credentialsState.codewhispererCore === 'connected') + ) { + await this.messenger.sendAuthNeededExceptionMessage(credentialsState, tabID, triggerID) + return + } + + const session = this.sessionStorage.getSession(tabID) + if (!session.localHistoryHydrated) { + triggerPayload.history = this.chatHistoryDb.getMessages(triggerEvent.tabID, 10) + session.localHistoryHydrated = true + } + await this.resolveContextCommandPayload(triggerPayload, session) + triggerPayload.useRelevantDocuments = triggerPayload.context.some( + (context) => typeof context !== 'string' && context.command === '@workspace' + ) + if (triggerPayload.useRelevantDocuments) { + triggerPayload.message = triggerPayload.message.replace(/@workspace/, '') + if (CodeWhispererSettings.instance.isLocalIndexEnabled()) { + } else { + this.messenger.sendOpenSettingsMessage(triggerID, tabID) + return + } + } + + triggerPayload.contextLengths.userInputContextLength = triggerPayload.message.length + triggerPayload.contextLengths.focusFileContextLength = triggerPayload.fileText.length + const request = triggerPayloadToChatRequest(triggerPayload) + triggerPayload.documentReferences = this.mergeRelevantTextDocuments(triggerPayload.relevantTextDocuments) + + // Update context transparency after it's truncated dynamically to show users only the context sent. + const relativePathsOfMergedRelevantDocuments = triggerPayload.documentReferences.map( + (doc) => doc.relativeFilePath + ) + const seen: string[] = [] + for (const additionalContent of triggerPayload.additionalContents) { + const relativePath = additionalContent.relativePath + if (!relativePathsOfMergedRelevantDocuments.includes(relativePath) && !seen.includes(relativePath)) { + triggerPayload.documentReferences.push({ + relativeFilePath: relativePath, + lineRanges: + additionalContent.name === 'symbol' + ? [{ first: additionalContent.startLine, second: additionalContent.endLine }] + : [{ first: -1, second: -1 }], + }) + seen.push(relativePath) + } + } + for (const doc of triggerPayload.documentReferences) { + session.contexts.set(doc.relativeFilePath, doc.lineRanges) + } + + getLogger().debug( + `request from tab: ${tabID} conversationID: ${session.sessionIdentifier} request: ${inspect(request, { + depth: 12, + })}` + ) + let response: MessengerResponseType | undefined = undefined + session.createNewTokenSource() + // TODO: onProfileChanged, abort previous response? + try { + this.messenger.sendInitalStream(tabID, triggerID, triggerPayload.documentReferences) + this.telemetryHelper.setConversationStreamStartTime(tabID) + if (isSsoConnection(AuthUtil.instance.conn)) { + const { $metadata, generateAssistantResponseResponse } = await session.chatSso(request) + response = { + $metadata: $metadata, + message: generateAssistantResponseResponse, + } + } else { + const { $metadata, sendMessageResponse } = await session.chatIam(request as SendMessageRequest) + response = { + $metadata: $metadata, + message: sendMessageResponse, + } + } + this.telemetryHelper.recordEnterFocusConversation(triggerEvent.tabID) + this.telemetryHelper.recordStartConversation(triggerEvent, triggerPayload) + if (session.sessionIdentifier) { + this.chatHistoryDb.addMessage(tabID, 'cwc', session.sessionIdentifier, { + body: triggerPayload.message, + type: 'prompt' as any, + }) + } + + getLogger().info( + `response to tab: ${tabID} conversationID: ${session.sessionIdentifier} requestID: ${ + response.$metadata.requestId + } metadata: ${inspect(response.$metadata, { depth: 12 })}` + ) + this.cancelTokenSource = new vscode.CancellationTokenSource() + await this.messenger.sendAIResponse( + response, + session, + tabID, + triggerID, + triggerPayload, + this.cancelTokenSource.token + ) + } catch (e: any) { + this.telemetryHelper.recordMessageResponseError(triggerPayload, tabID, getHttpStatusCode(e) ?? 0) + // clears session, record telemetry before this call + this.processException(e, tabID) + } + } + + private mergeRelevantTextDocuments(documents: RelevantTextDocumentAddition[]): DocumentReference[] { + if (documents.length === 0) { + return [] + } + return Object.entries( + documents.reduce>((acc, doc) => { + if (!doc.relativeFilePath || doc.startLine === undefined || doc.endLine === undefined) { + return acc // Skip invalid documents + } + + if (!acc[doc.relativeFilePath]) { + acc[doc.relativeFilePath] = [] + } + acc[doc.relativeFilePath].push({ first: doc.startLine, second: doc.endLine }) + return acc + }, {}) + ).map(([filePath, ranges]) => { + // Sort by startLine + const sortedRanges = ranges.sort((a, b) => a.first - b.first) + + const mergedRanges: { first: number; second: number }[] = [] + for (const { first, second } of sortedRanges) { + if (mergedRanges.length === 0 || mergedRanges[mergedRanges.length - 1].second < first - 1) { + // If no overlap, add new range + mergedRanges.push({ first, second }) + } else { + // Merge overlapping or consecutive ranges + mergedRanges[mergedRanges.length - 1].second = Math.max( + mergedRanges[mergedRanges.length - 1].second, + second + ) + } + } + + return { relativeFilePath: filePath, lineRanges: mergedRanges } + }) + } +} diff --git a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts new file mode 100644 index 00000000000..ab059ecb22d --- /dev/null +++ b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts @@ -0,0 +1,567 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { waitUntil } from '../../../../shared/utilities/timeoutUtils' +import { + AppToWebViewMessageDispatcher, + AuthNeededException, + CodeReference, + ContextCommandData, + EditorContextCommandMessage, + ExportChatMessage, + OpenSettingsMessage, + OpenDetailedListMessage, + QuickActionMessage, + RestoreTabMessage, + ShowCustomFormMessage, + UpdateDetailedListMessage, + CloseDetailedListMessage, + SelectTabMessage, +} from '../../../view/connector/connector' +import { EditorContextCommandType } from '../../../commands/registerCommands' +import { ChatResponseStream as qdevChatResponseStream } from '@amzn/amazon-q-developer-streaming-client' +import { + ChatResponseStream as cwChatResponseStream, + CodeWhispererStreamingServiceException, + SupplementaryWebLink, +} from '@amzn/codewhisperer-streaming' +import { ChatMessage, ErrorMessage, FollowUp, Suggestion } from '../../../view/connector/connector' +import { ChatSession } from '../../../clients/chat/v0/chat' +import { ChatException } from './model' +import { CWCTelemetryHelper } from '../telemetryHelper' +import { ChatPromptCommandType, DocumentReference, TriggerPayload } from '../model' +import { getHttpStatusCode, getRequestId, ToolkitError } from '../../../../shared/errors' +import { keys } from '../../../../shared/utilities/tsUtils' +import { getLogger } from '../../../../shared/logger/logger' +import { FeatureAuthState } from '../../../../codewhisperer/util/authUtil' +import { CodeScanIssue } from '../../../../codewhisperer/models/model' +import { marked } from 'marked' +import { JSDOM } from 'jsdom' +import { extractCodeBlockLanguage } from '../../../../shared/markdown' +import { extractAuthFollowUp } from '../../../../amazonq/util/authUtils' +import { helpMessage } from '../../../../amazonq/webview/ui/texts/constants' +import { ChatItem, ChatItemButton, ChatItemFormItem, DetailedList, MynahUIDataModel } from '@aws/mynah-ui' +import { Database } from '../../../../shared/db/chatDb/chatDb' +import { TabType } from '../../../../amazonq/webview/ui/storages/tabsStorage' + +export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help' + +export type MessengerResponseType = { + $metadata: { requestId?: string; httpStatusCode?: number } + message?: AsyncIterable +} + +export class Messenger { + chatHistoryDb = Database.getInstance() + + public constructor( + private readonly dispatcher: AppToWebViewMessageDispatcher, + private readonly telemetryHelper: CWCTelemetryHelper + ) {} + + public async sendAuthNeededExceptionMessage(credentialState: FeatureAuthState, tabID: string, triggerID: string) { + const { message, authType } = extractAuthFollowUp(credentialState) + this.dispatcher.sendAuthNeededExceptionMessage( + new AuthNeededException( + { + message, + authType, + triggerID, + }, + tabID + ) + ) + } + + public sendInitalStream( + tabID: string, + triggerID: string, + mergedRelevantDocuments: DocumentReference[] | undefined + ) { + this.dispatcher.sendChatMessage( + new ChatMessage( + { + message: '', + messageType: 'answer-stream', + followUps: undefined, + followUpsHeader: undefined, + relatedSuggestions: undefined, + triggerID, + messageID: triggerID, + userIntent: undefined, + codeBlockLanguage: undefined, + contextList: mergedRelevantDocuments, + }, + tabID + ) + ) + } + /** + * Tries to calculate the total number of code blocks. + * NOTES: + * - Not correct on all examples. Some may cause it to return 0 unexpectedly. + * - Plans in place (as of 4/22/2024) to move this server side. + * - See original pr: https://github.com/aws/aws-toolkit-vscode/pull/4761 for more details. + * @param message raw message response from codewhisperer client. + * @returns count of multi-line code blocks in response. + */ + public async countTotalNumberOfCodeBlocks(message: string): Promise { + // TODO: remove this when moved to server-side. + if (message === undefined) { + return 0 + } + + // To Convert Markdown text to HTML using marked library + const html = await marked(message) + + const dom = new JSDOM(html) + const document = dom.window.document + + // Search for
 elements containing  elements
+        const codeBlocks = document.querySelectorAll('pre > code')
+
+        return codeBlocks.length
+    }
+
+    public async sendAIResponse(
+        response: MessengerResponseType,
+        session: ChatSession,
+        tabID: string,
+        triggerID: string,
+        triggerPayload: TriggerPayload,
+        cancelToken: vscode.CancellationToken
+    ) {
+        let message = ''
+        const messageID = response.$metadata.requestId ?? ''
+        let codeReference: CodeReference[] = []
+        let followUps: FollowUp[] = []
+        let relatedSuggestions: Suggestion[] = []
+        let codeBlockLanguage: string = 'plaintext'
+
+        if (response.message === undefined) {
+            throw new ToolkitError(
+                `Empty response from CodeWhisperer Streaming service. Request ID: ${response.$metadata.requestId}`
+            )
+        }
+        this.telemetryHelper.setResponseStreamStartTime(tabID)
+
+        let cwsprChatHasProjectContext = false
+        if (
+            triggerPayload.relevantTextDocuments &&
+            triggerPayload.relevantTextDocuments.length > 0 &&
+            triggerPayload.useRelevantDocuments === true
+        ) {
+            cwsprChatHasProjectContext = true
+        }
+        const additionalCounts = this.telemetryHelper.getAdditionalContextCounts(triggerPayload)
+
+        this.telemetryHelper.setResponseFromAdditionalContext(messageID, {
+            cwsprChatHasProjectContext,
+            cwsprChatRuleContextCount: triggerPayload.workspaceRulesCount,
+            cwsprChatFileContextCount: additionalCounts.fileContextCount,
+            cwsprChatFolderContextCount: additionalCounts.folderContextCount,
+            cwsprChatPromptContextCount: additionalCounts.promptContextCount,
+        })
+
+        const eventCounts = new Map()
+        waitUntil(
+            async () => {
+                for await (const chatEvent of response.message!) {
+                    if (cancelToken.isCancellationRequested) {
+                        return
+                    }
+                    for (const key of keys(chatEvent)) {
+                        if ((chatEvent[key] as any) !== undefined) {
+                            eventCounts.set(key, (eventCounts.get(key) ?? 0) + 1)
+                        }
+                    }
+
+                    if (session.tokenSource.token.isCancellationRequested) {
+                        return true
+                    }
+
+                    if (
+                        chatEvent.codeReferenceEvent?.references !== undefined &&
+                        chatEvent.codeReferenceEvent.references.length > 0
+                    ) {
+                        codeReference = [
+                            ...codeReference,
+                            ...chatEvent.codeReferenceEvent.references.map((reference) => ({
+                                ...reference,
+                                recommendationContentSpan: {
+                                    start: reference.recommendationContentSpan?.start ?? 0,
+                                    end: reference.recommendationContentSpan?.end ?? 0,
+                                },
+                                information: `Reference code under **${reference.licenseName}** license from repository \`${reference.repository}\``,
+                            })),
+                        ]
+                    }
+
+                    if (
+                        chatEvent.assistantResponseEvent?.content !== undefined &&
+                        chatEvent.assistantResponseEvent.content.length > 0
+                    ) {
+                        message += chatEvent.assistantResponseEvent.content
+                        if (codeBlockLanguage === 'plaintext') {
+                            codeBlockLanguage = extractCodeBlockLanguage(message)
+                        }
+                        this.dispatcher.sendChatMessage(
+                            new ChatMessage(
+                                {
+                                    message: message,
+                                    messageType: 'answer-part',
+                                    followUps: undefined,
+                                    followUpsHeader: undefined,
+                                    relatedSuggestions: undefined,
+                                    codeReference,
+                                    triggerID,
+                                    messageID,
+                                    userIntent: triggerPayload.userIntent,
+                                    codeBlockLanguage: codeBlockLanguage,
+                                    contextList: undefined,
+                                },
+                                tabID
+                            )
+                        )
+                        this.telemetryHelper.setResponseStreamTimeForChunks(tabID)
+                    }
+
+                    if (chatEvent.supplementaryWebLinksEvent?.supplementaryWebLinks !== undefined) {
+                        let suggestionIndex = 0
+                        const newSuggestions: Suggestion[] =
+                            chatEvent.supplementaryWebLinksEvent.supplementaryWebLinks.map(
+                                (s: SupplementaryWebLink) =>
+                                    new Suggestion({
+                                        title: s.title ?? '',
+                                        url: s.url ?? '',
+                                        body: s.snippet ?? '',
+                                        id: suggestionIndex++,
+                                        context: [],
+                                    })
+                            )
+                        relatedSuggestions.push(...newSuggestions)
+                    }
+
+                    if (chatEvent.followupPromptEvent?.followupPrompt !== undefined) {
+                        const followUp = chatEvent.followupPromptEvent.followupPrompt
+                        followUps.push({
+                            type: followUp.userIntent ?? '',
+                            pillText: followUp.content ?? '',
+                            prompt: followUp.content ?? '',
+                        })
+                    }
+                }
+                return true
+            },
+            { timeout: 60000, truthy: true }
+        )
+            .catch((error: any) => {
+                let errorMessage = 'Error reading chat stream.'
+                let statusCode = undefined
+                let requestID = undefined
+
+                if (error instanceof CodeWhispererStreamingServiceException) {
+                    errorMessage = error.message
+                    statusCode = getHttpStatusCode(error) ?? 0
+                    requestID = getRequestId(error)
+                }
+
+                this.showChatExceptionMessage(
+                    { errorMessage, statusCode: statusCode?.toString(), sessionID: undefined },
+                    tabID,
+                    requestID
+                )
+                getLogger().error(`error: ${errorMessage} tabID: ${tabID} requestID: ${requestID}`)
+
+                followUps = []
+                relatedSuggestions = []
+                this.telemetryHelper.recordMessageResponseError(triggerPayload, tabID, statusCode ?? 0)
+            })
+            .finally(async () => {
+                if (session.sessionIdentifier) {
+                    this.chatHistoryDb.addMessage(tabID, 'cwc', session.sessionIdentifier, {
+                        body: message,
+                        type: 'answer' as any,
+                        codeReference: codeReference as any,
+                        relatedContent: { title: 'Sources', content: relatedSuggestions as any },
+                    })
+                }
+                if (triggerPayload.relevantTextDocuments && triggerPayload.relevantTextDocuments.length > 0) {
+                    this.dispatcher.sendChatMessage(
+                        new ChatMessage(
+                            {
+                                message:
+                                    message +
+                                    ` \n\nBy the way, I'm still indexing this project for full context from your workspace. I may have a better response in a few minutes when it's complete if you'd like to try again then.`,
+                                messageType: 'answer-part',
+                                followUps: undefined,
+                                followUpsHeader: undefined,
+                                relatedSuggestions: undefined,
+                                triggerID,
+                                messageID,
+                                userIntent: triggerPayload.userIntent,
+                                codeBlockLanguage: codeBlockLanguage,
+                                contextList: undefined,
+                            },
+                            tabID
+                        )
+                    )
+                }
+
+                if (relatedSuggestions.length !== 0) {
+                    this.dispatcher.sendChatMessage(
+                        new ChatMessage(
+                            {
+                                message: undefined,
+                                messageType: 'answer-part',
+                                followUpsHeader: undefined,
+                                followUps: undefined,
+                                relatedSuggestions,
+                                triggerID,
+                                messageID,
+                                userIntent: triggerPayload.userIntent,
+                                codeBlockLanguage: undefined,
+                                contextList: undefined,
+                            },
+                            tabID
+                        )
+                    )
+                }
+
+                this.dispatcher.sendChatMessage(
+                    new ChatMessage(
+                        {
+                            message: undefined,
+                            messageType: 'answer',
+                            followUps: followUps,
+                            followUpsHeader: undefined,
+                            relatedSuggestions: undefined,
+                            triggerID,
+                            messageID,
+                            userIntent: triggerPayload.userIntent,
+                            codeBlockLanguage: undefined,
+                            contextList: undefined,
+                        },
+                        tabID
+                    )
+                )
+
+                getLogger().info(
+                    `All events received. requestId=%s counts=%s`,
+                    response.$metadata.requestId,
+                    Object.fromEntries(eventCounts)
+                )
+
+                this.telemetryHelper.setResponseStreamTotalTime(tabID)
+
+                const responseCode = response?.$metadata.httpStatusCode ?? 0
+                this.telemetryHelper.recordAddMessage(triggerPayload, {
+                    followUpCount: followUps.length,
+                    suggestionCount: relatedSuggestions.length,
+                    tabID: tabID,
+                    messageLength: message.length,
+                    messageID,
+                    responseCode,
+                    codeReferenceCount: codeReference.length,
+                    totalNumberOfCodeBlocksInResponse: await this.countTotalNumberOfCodeBlocks(message),
+                })
+            })
+    }
+
+    public sendErrorMessage(errorMessage: string | undefined, tabID: string, requestID: string | undefined) {
+        this.showChatExceptionMessage(
+            {
+                errorMessage: errorMessage,
+                sessionID: undefined,
+                statusCode: undefined,
+            },
+            tabID,
+            requestID
+        )
+    }
+
+    private editorContextMenuCommandVerbs: Map = new Map([
+        ['aws.amazonq.explainCode', 'Explain'],
+        ['aws.amazonq.explainIssue', 'Explain'],
+        ['aws.amazonq.refactorCode', 'Refactor'],
+        ['aws.amazonq.fixCode', 'Fix'],
+        ['aws.amazonq.optimizeCode', 'Optimize'],
+        ['aws.amazonq.sendToPrompt', 'Send to prompt'],
+        ['aws.amazonq.generateUnitTests', 'Generate unit tests for'],
+    ])
+
+    public sendStaticTextResponse(type: StaticTextResponseType, triggerID: string, tabID: string) {
+        let message
+        let followUps
+        let followUpsHeader
+        switch (type) {
+            case 'quick-action-help':
+                message = helpMessage
+                break
+            case 'onboarding-help':
+                message = `### What I can do:
+                \n\n- Answer questions about AWS
+                \n\n- Answer questions about general programming concepts
+                \n\n- Explain what a line of code or code function does
+                \n\n- Write unit tests and code
+                \n\n- Debug and fix code
+                \n\n- Refactor code`
+                followUps = [
+                    {
+                        type: '',
+                        pillText: 'Should I use AWS Lambda or EC2 for a scalable web application backend?',
+                        prompt: 'Should I use AWS Lambda or EC2 for a scalable web application backend?',
+                    },
+                    {
+                        type: '',
+                        pillText: 'What is the syntax of declaring a variable in TypeScript?',
+                        prompt: 'What is the syntax of declaring a variable in TypeScript?',
+                    },
+                    {
+                        type: '',
+                        pillText: 'Write code for uploading a file to an s3 bucket in typescript',
+                        prompt: 'Write code for uploading a file to an s3 bucket in typescript',
+                    },
+                ]
+                followUpsHeader = 'Try Examples:'
+                break
+        }
+
+        this.dispatcher.sendChatMessage(
+            new ChatMessage(
+                {
+                    message,
+                    messageType: 'answer',
+                    followUpsHeader,
+                    followUps,
+                    relatedSuggestions: undefined,
+                    triggerID,
+                    messageID: 'static_message_' + triggerID,
+                    userIntent: undefined,
+                    codeBlockLanguage: undefined,
+                    contextList: undefined,
+                },
+                tabID
+            )
+        )
+    }
+
+    public sendQuickActionMessage(quickAction: ChatPromptCommandType, triggerID: string) {
+        let message = ''
+        switch (quickAction) {
+            case 'help':
+                message = 'How can Amazon Q help me?'
+                break
+        }
+
+        this.dispatcher.sendQuickActionMessage(
+            new QuickActionMessage({
+                message,
+                triggerID,
+            })
+        )
+    }
+
+    public sendEditorContextCommandMessage(
+        command: EditorContextCommandType,
+        selectedCode: string,
+        triggerID: string,
+        issue?: CodeScanIssue
+    ) {
+        // Remove newlines and spaces before and after the code
+        const trimmedCode = selectedCode.trimStart().trimEnd()
+
+        let message
+        if (command === 'aws.amazonq.sendToPrompt') {
+            message = ['\n```\n', trimmedCode, '\n```'].join('')
+        } else if (command === 'aws.amazonq.explainIssue' && issue) {
+            message = [
+                this.editorContextMenuCommandVerbs.get(command),
+                ` the "${issue.title}" issue in the following code:`,
+                '\n```\n',
+                trimmedCode,
+                '\n```',
+            ].join('')
+        } else {
+            message = [
+                this.editorContextMenuCommandVerbs.get(command),
+                ' the following part of my code:',
+                '\n```\n',
+                trimmedCode,
+                '\n```',
+            ].join('')
+        }
+
+        this.dispatcher.sendEditorContextCommandMessage(
+            new EditorContextCommandMessage({ message, triggerID, command })
+        )
+    }
+
+    private showChatExceptionMessage(e: ChatException, tabID: string, requestID: string | undefined) {
+        let message = 'This error is reported to the team automatically. We will attempt to fix it as soon as possible.'
+        if (e.errorMessage !== undefined) {
+            message += `\n\nDetails: ${e.errorMessage}`
+        }
+
+        if (e.statusCode !== undefined) {
+            message += `\n\nStatus Code: ${e.statusCode}`
+        }
+        if (e.sessionID !== undefined) {
+            message += `\n\nSession ID: ${e.sessionID}`
+        }
+        if (requestID !== undefined) {
+            message += `\n\nRequest ID: ${requestID}`
+        }
+
+        this.dispatcher.sendErrorMessage(
+            new ErrorMessage('An error occurred while processing your request.', message.trimEnd().trimStart(), tabID)
+        )
+    }
+
+    public sendOpenSettingsMessage(triggerId: string, tabID: string) {
+        this.dispatcher.sendOpenSettingsMessage(new OpenSettingsMessage(tabID))
+    }
+
+    public sendRestoreTabMessage(historyId: string, tabType: TabType, chats: ChatItem[], exportTab?: boolean) {
+        this.dispatcher.sendRestoreTabMessage(new RestoreTabMessage(historyId, tabType, chats, exportTab))
+    }
+
+    public sendOpenDetailedListMessage(tabId: string, listType: string, data: DetailedList) {
+        this.dispatcher.sendOpenDetailedListMessage(new OpenDetailedListMessage(tabId, listType, data))
+    }
+
+    public sendUpdateDetailedListMessage(listType: string, data: DetailedList) {
+        this.dispatcher.sendUpdateDetailedListMessage(new UpdateDetailedListMessage(listType, data))
+    }
+
+    public sendCloseDetailedListMessage(listType: string) {
+        this.dispatcher.sendCloseDetailedListMessage(new CloseDetailedListMessage(listType))
+    }
+
+    public sendSerializeTabMessage(tabId: string, uri: string, format: 'html' | 'markdown') {
+        this.dispatcher.sendSerializeTabMessage(new ExportChatMessage(tabId, format, uri))
+    }
+
+    public sendSelectTabMessage(tabId: string, eventID?: string) {
+        this.dispatcher.sendSelectTabMessage(new SelectTabMessage(tabId, eventID))
+    }
+
+    public sendContextCommandData(contextCommands: MynahUIDataModel['contextCommands']) {
+        this.dispatcher.sendContextCommandData(new ContextCommandData(contextCommands))
+    }
+
+    public showCustomForm(
+        tabID: string,
+        formItems?: ChatItemFormItem[],
+        buttons?: ChatItemButton[],
+        title?: string,
+        description?: string
+    ) {
+        this.dispatcher.sendShowCustomFormMessage(
+            new ShowCustomFormMessage(tabID, formItems, buttons, title, description)
+        )
+    }
+}
diff --git a/packages/core/src/codewhispererChat/controllers/chat/messenger/model.ts b/packages/core/src/codewhispererChat/controllers/chat/messenger/model.ts
new file mode 100644
index 00000000000..c5d5eb92dd2
--- /dev/null
+++ b/packages/core/src/codewhispererChat/controllers/chat/messenger/model.ts
@@ -0,0 +1,10 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export interface ChatException {
+    readonly errorMessage: string | undefined
+    readonly sessionID: string | undefined
+    readonly statusCode: string | undefined
+}
diff --git a/packages/core/src/codewhispererChat/controllers/chat/model.ts b/packages/core/src/codewhispererChat/controllers/chat/model.ts
new file mode 100644
index 00000000000..04da124aecc
--- /dev/null
+++ b/packages/core/src/codewhispererChat/controllers/chat/model.ts
@@ -0,0 +1,261 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { AdditionalContentEntry, RelevantTextDocument, UserIntent } from '@amzn/codewhisperer-streaming'
+import { MatchPolicy, CodeQuery } from '../../clients/chat/v0/model'
+import { Selection } from 'vscode'
+import { TabOpenType } from '../../../amazonq/webview/ui/storages/tabsStorage'
+import { CodeReference } from '../../view/connector/connector'
+import { Customization } from '../../../codewhisperer/client/codewhispereruserclient'
+import { QuickActionCommand } from '@aws/mynah-ui'
+import { Message } from '../../../shared/db/chatDb/util'
+import { RegionProfile } from '../../../codewhisperer/models/model'
+
+export interface TriggerTabIDReceived {
+    tabID: string
+    triggerID: string
+}
+
+export interface TabCreatedMessage {
+    tabID: string
+    tabOpenInteractionType: TabOpenType
+}
+
+export interface TabClosedMessage {
+    tabID: string
+}
+
+export interface TabChangedMessage {
+    tabID: string
+    prevTabID?: string
+}
+
+export interface UIFocusMessage {
+    command: string
+    type: 'focus' | 'blur'
+}
+
+export interface InsertCodeAtCursorPosition {
+    command: string | undefined
+    tabID: string
+    messageId: string
+    userIntent: UserIntent | undefined
+    code: string
+    insertionTargetType: string | undefined
+    codeReference: CodeReference[] | undefined
+    eventId: string
+    codeBlockIndex: number
+    totalCodeBlocks: number
+    codeBlockLanguage: string
+}
+
+export interface CopyCodeToClipboard {
+    command: string | undefined
+    tabID: string
+    messageId: string
+    userIntent: UserIntent | undefined
+    code: string
+    insertionTargetType: string | undefined
+    codeReference: CodeReference[] | undefined
+    eventId: string
+    codeBlockIndex: number
+    totalCodeBlocks: number
+    codeBlockLanguage: string
+}
+
+export interface AcceptDiff {
+    command: string | undefined
+    tabID: string // rename tabId
+    messageId: string
+    actionId: string
+    data: string
+    code: string
+    referenceTrackerInformation?: CodeReference[]
+    eventId: string
+    codeBlockIndex?: number
+    totalCodeBlocks?: number
+}
+export interface ViewDiff {
+    command: string | undefined
+    tabID: string // rename tabId
+    messageId: string
+    actionId: string
+    data: string
+    code: string
+    referenceTrackerInformation?: CodeReference[]
+    eventId: string
+    codeBlockIndex?: number
+    totalCodeBlocks?: number
+}
+
+export type ChatPromptCommandType =
+    | 'help'
+    | 'clear'
+    | 'follow-up-was-clicked'
+    | 'onboarding-page-cwc-button-clicked'
+    | 'chat-prompt'
+    | 'transform'
+
+export interface PromptMessage {
+    message: string | undefined
+    messageId: string
+    command: ChatPromptCommandType | undefined
+    userIntent: UserIntent | undefined
+    tabID: string
+    context?: string[] | QuickActionCommand[]
+}
+
+export interface PromptAnswer {
+    messageLength: number
+    tabID: string
+    suggestionCount: number
+    followUpCount: number
+    messageID: string
+    responseCode: number
+    codeReferenceCount: number
+    totalNumberOfCodeBlocksInResponse: number
+}
+
+export interface StopResponseMessage {
+    tabID: string
+}
+
+export interface SourceLinkClickMessage {
+    command: string | undefined
+    tabID: string
+    messageId: string
+    link: string
+}
+
+export interface ResponseBodyLinkClickMessage {
+    command: string | undefined
+    tabID: string
+    messageId: string
+    link: string
+}
+
+export interface FooterInfoLinkClick {
+    command: string
+    tabID: string
+    link: string
+}
+
+export interface TabBarButtonClick {
+    tabID: string
+    buttonId: string
+}
+
+export interface SaveChatMessage {
+    serializedChat: string
+    uri: string
+}
+
+export interface QuickCommandGroupActionClick {
+    command: string
+    actionId: string
+    tabID: string
+}
+
+export interface FileClick {
+    command: string
+    tabID: string
+    messageId: string
+    filePath: string
+}
+
+export interface ChatItemVotedMessage {
+    tabID: string
+    command: string
+    vote: 'upvote' | 'downvote'
+    messageId: string
+}
+
+export interface ChatItemFeedbackMessage {
+    messageId: string
+    tabID: string
+    command: string
+    selectedOption: string
+    comment?: string
+}
+
+export enum ChatTriggerType {
+    ChatMessage = 'ChatMessage',
+    InlineChatMessage = 'InlineChatMessage',
+}
+
+export interface TriggerPayload {
+    readonly query: string | undefined
+    readonly codeSelection: Selection | undefined
+    readonly trigger: ChatTriggerType
+    fileText: string
+    readonly fileLanguage: string | undefined
+    readonly filePath: string | undefined
+    message: string
+    readonly matchPolicy: MatchPolicy | undefined
+    readonly codeQuery: CodeQuery | undefined
+    readonly userIntent: UserIntent | undefined
+    readonly customization: Customization
+    readonly profile: RegionProfile | undefined
+    readonly context: string[] | QuickActionCommand[]
+    relevantTextDocuments: RelevantTextDocumentAddition[]
+    additionalContents: AdditionalContentEntryAddition[]
+    // a reference to all documents used in chat payload
+    // for providing better context transparency
+    documentReferences: DocumentReference[]
+    useRelevantDocuments: boolean
+    traceId?: string
+    contextLengths: ContextLengths
+    workspaceRulesCount?: number
+    history?: Message[]
+}
+
+export type ContextLengths = {
+    additionalContextLengths: AdditionalContextLengths
+    truncatedAdditionalContextLengths: AdditionalContextLengths
+    workspaceContextLength: number
+    truncatedWorkspaceContextLength: number
+    userInputContextLength: number
+    truncatedUserInputContextLength: number
+    focusFileContextLength: number
+    truncatedFocusFileContextLength: number
+}
+
+export type AdditionalContextLengths = {
+    fileContextLength: number
+    promptContextLength: number
+    ruleContextLength: number
+}
+
+export type AdditionalContextInfo = {
+    cwsprChatFileContextCount?: number
+    cwsprChatFolderContextCount?: number
+    cwsprChatPromptContextCount?: number
+    cwsprChatRuleContextCount?: number
+    cwsprChatHasProjectContext?: boolean
+}
+
+export type LineInfo = { startLine: number; endLine: number }
+
+// TODO move this to API definition (or just use this across the codebase)
+export type RelevantTextDocumentAddition = RelevantTextDocument & LineInfo
+
+export type AdditionalContentEntryAddition = AdditionalContentEntry & { type: string; relativePath: string } & LineInfo
+
+export interface DocumentReference {
+    readonly relativeFilePath: string
+    readonly lineRanges: Array<{ first: number; second: number }>
+}
+
+export interface InsertedCode {
+    readonly conversationID: string
+    readonly messageID: string
+    readonly userIntent: UserIntent | undefined
+    readonly time: Date
+    readonly fileUrl: vscode.Uri
+    readonly startPosition: vscode.Position
+    readonly endPosition: vscode.Position
+    readonly originalString: string
+}
diff --git a/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts b/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts
new file mode 100644
index 00000000000..6abecda2f8c
--- /dev/null
+++ b/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts
@@ -0,0 +1,27 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { EditorContextBaseCommandType, EditorContextCommand } from '../../../commands/registerCommands'
+
+// TODO: It's a workaround for the demo, we need to remove it after backend will be ready
+
+export class PromptsGenerator {
+    private editorContextMenuCommandVerbs: Map = new Map([
+        ['aws.amazonq.explainCode', 'Explain'],
+        ['aws.amazonq.refactorCode', 'Refactor'],
+        ['aws.amazonq.fixCode', 'Fix'],
+        ['aws.amazonq.optimizeCode', 'Optimize'],
+        ['aws.amazonq.sendToPrompt', 'Send to prompt'],
+    ])
+
+    public generateForContextMenuCommand(command: EditorContextCommand): string {
+        if (command.type === 'aws.amazonq.explainIssue') {
+            return `Explain the issue "${command.issue.title}" (${JSON.stringify(
+                command.issue
+            )}) and generate code demonstrating the fix`
+        }
+        return [this.editorContextMenuCommandVerbs.get(command.type), ' the selected codeblock'].join('')
+    }
+}
diff --git a/packages/core/src/codewhispererChat/controllers/chat/tabBarController.ts b/packages/core/src/codewhispererChat/controllers/chat/tabBarController.ts
new file mode 100644
index 00000000000..13088e096f8
--- /dev/null
+++ b/packages/core/src/codewhispererChat/controllers/chat/tabBarController.ts
@@ -0,0 +1,181 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import fs from '../../../shared/fs/fs'
+import {
+    DetailedListActionClickMessage,
+    DetailedListFilterChangeMessage,
+    DetailedListItemSelectMessage,
+} from '../../view/connector/connector'
+import * as vscode from 'vscode'
+import { Messenger } from './messenger/messenger'
+import { Database } from '../../../shared/db/chatDb/chatDb'
+import { TabBarButtonClick, SaveChatMessage } from './model'
+import { Conversation, messageToChatItem, Tab } from '../../../shared/db/chatDb/util'
+import { DetailedListItemGroup, MynahIconsType } from '@aws/mynah-ui'
+
+export class TabBarController {
+    private readonly messenger: Messenger
+    private chatHistoryDb = Database.getInstance()
+    private loadedChats: boolean = false
+    private searchTimeout: NodeJS.Timeout | undefined = undefined
+    private readonly DebounceTime = 300 // milliseconds
+
+    constructor(messenger: Messenger) {
+        this.messenger = messenger
+    }
+    async processActionClickMessage(msg: DetailedListActionClickMessage) {
+        if (msg.listType === 'history') {
+            if (msg.action.text === 'Delete') {
+                this.chatHistoryDb.deleteHistory(msg.action.id)
+                this.messenger.sendUpdateDetailedListMessage('history', { list: this.generateHistoryList() })
+            } else if (msg.action.text === 'Export') {
+                // If conversation is already open, export it
+                const openTabId = this.chatHistoryDb.getOpenTabId(msg.action.id)
+                if (openTabId) {
+                    await this.exportChatButtonClicked({ tabID: openTabId, buttonId: 'export_chat' })
+                } // If conversation is not open, restore it before exporting
+                else {
+                    const selectedTab = this.chatHistoryDb.getTab(msg.action.id)
+                    this.restoreTab(selectedTab, true)
+                }
+            }
+        }
+    }
+
+    async processFilterChangeMessage(msg: DetailedListFilterChangeMessage) {
+        if (msg.listType === 'history') {
+            const searchFilter = msg.filterValues['search']
+            if (typeof searchFilter !== 'string') {
+                return
+            }
+
+            // Clear any pending search
+            if (this.searchTimeout) {
+                clearTimeout(this.searchTimeout)
+            }
+
+            // Set new timeout for this search
+            this.searchTimeout = setTimeout(() => {
+                const searchResults = this.chatHistoryDb.searchMessages(searchFilter)
+                this.messenger.sendUpdateDetailedListMessage('history', { list: searchResults })
+            }, this.DebounceTime)
+        }
+    }
+
+    // If selected is conversation is already open, select that tab. Else, open new tab with conversation.
+    processItemSelectMessage(msg: DetailedListItemSelectMessage) {
+        if (msg.listType === 'history') {
+            const historyID = msg.item.id
+            if (historyID) {
+                const openTabID = this.chatHistoryDb.getOpenTabId(historyID)
+                if (!openTabID) {
+                    const selectedTab = this.chatHistoryDb.getTab(historyID)
+                    this.restoreTab(selectedTab)
+                } else {
+                    this.messenger.sendSelectTabMessage(openTabID, historyID)
+                }
+                this.messenger.sendCloseDetailedListMessage('history')
+            }
+        }
+    }
+
+    restoreTab(selectedTab?: Tab | null, exportTab?: boolean) {
+        if (selectedTab) {
+            this.messenger.sendRestoreTabMessage(
+                selectedTab.historyId,
+                selectedTab.tabType,
+                selectedTab.conversations.flatMap((conv: Conversation) =>
+                    conv.messages.map((message) => messageToChatItem(message))
+                ),
+                exportTab
+            )
+        }
+    }
+
+    loadChats() {
+        if (this.loadedChats) {
+            return
+        }
+        this.loadedChats = true
+        const openConversations = this.chatHistoryDb.getOpenTabs()
+        if (openConversations) {
+            for (const conversation of openConversations) {
+                if (conversation.conversations && conversation.conversations.length > 0) {
+                    this.restoreTab(conversation)
+                }
+            }
+        }
+    }
+
+    async historyButtonClicked(message: TabBarButtonClick) {
+        this.messenger.sendOpenDetailedListMessage(message.tabID, 'history', {
+            header: { title: 'Chat history' },
+            filterOptions: [
+                {
+                    type: 'textinput',
+                    icon: 'search' as MynahIconsType,
+                    id: 'search',
+                    placeholder: 'Search...',
+                    autoFocus: true,
+                },
+            ],
+            list: this.generateHistoryList(),
+        })
+    }
+
+    generateHistoryList(): DetailedListItemGroup[] {
+        const historyItems = this.chatHistoryDb.getHistory()
+        return historyItems.length > 0 ? historyItems : [{ children: [{ description: 'No chat history found' }] }]
+    }
+
+    async processSaveChat(message: SaveChatMessage) {
+        try {
+            await fs.writeFile(message.uri, message.serializedChat)
+        } catch (error) {
+            void vscode.window.showErrorMessage('An error occurred while exporting your chat.')
+        }
+    }
+
+    async processTabBarButtonClick(message: TabBarButtonClick) {
+        switch (message.buttonId) {
+            case 'history_sheet':
+                await this.historyButtonClicked(message)
+                break
+            case 'export_chat':
+                await this.exportChatButtonClicked(message)
+                break
+        }
+    }
+
+    private async exportChatButtonClicked(message: TabBarButtonClick) {
+        const defaultFileName = `q-dev-chat-${new Date().toISOString().split('T')[0]}.md`
+        const workspaceFolders = vscode.workspace.workspaceFolders
+        let defaultUri
+
+        if (workspaceFolders && workspaceFolders.length > 0) {
+            // Use the first workspace folder as root
+            defaultUri = vscode.Uri.joinPath(workspaceFolders[0].uri, defaultFileName)
+        } else {
+            // Fallback if no workspace is open
+            defaultUri = vscode.Uri.file(defaultFileName)
+        }
+
+        const saveUri = await vscode.window.showSaveDialog({
+            filters: {
+                Markdown: ['md'],
+                HTML: ['html'],
+            },
+            defaultUri,
+            title: 'Export chat',
+        })
+
+        if (saveUri) {
+            // Determine format from file extension
+            const format = saveUri.fsPath.endsWith('.md') ? 'markdown' : 'html'
+            this.messenger.sendSerializeTabMessage(message.tabID, saveUri.fsPath, format)
+        }
+    }
+}
diff --git a/packages/core/src/codewhispererChat/controllers/chat/telemetryHelper.ts b/packages/core/src/codewhispererChat/controllers/chat/telemetryHelper.ts
new file mode 100644
index 00000000000..ac914e77b6b
--- /dev/null
+++ b/packages/core/src/codewhispererChat/controllers/chat/telemetryHelper.ts
@@ -0,0 +1,716 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { UserIntent } from '@amzn/codewhisperer-streaming'
+import {
+    AmazonqAddMessage,
+    AmazonqInteractWithMessage,
+    CwsprChatCommandType,
+    CwsprChatInteractionType,
+    CwsprChatTriggerInteraction,
+    CwsprChatUserIntent,
+    Result,
+    telemetry,
+} from '../../../shared/telemetry/telemetry'
+import { ChatSessionStorage } from '../../storages/chatSession'
+import {
+    AcceptDiff,
+    ViewDiff,
+    ChatItemFeedbackMessage,
+    ChatItemVotedMessage,
+    CopyCodeToClipboard,
+    FooterInfoLinkClick,
+    InsertCodeAtCursorPosition,
+    PromptAnswer,
+    PromptMessage,
+    ResponseBodyLinkClickMessage,
+    SourceLinkClickMessage,
+    TriggerPayload,
+    AdditionalContextInfo,
+} from './model'
+import { TriggerEvent, TriggerEventsStorage } from '../../storages/triggerEvents'
+import globals from '../../../shared/extensionGlobals'
+import { getLogger } from '../../../shared/logger/logger'
+import { codeWhispererClient } from '../../../codewhisperer/client/codewhisperer'
+import { isAwsError } from '../../../shared/errors'
+import CodeWhispererUserClient, {
+    ChatMessageInteractionType,
+} from '../../../codewhisperer/client/codewhispereruserclient'
+import { supportedLanguagesList } from '../chat/chatRequest/converter'
+import { AuthUtil } from '../../../codewhisperer/util/authUtil'
+import { getSelectedCustomization } from '../../../codewhisperer/util/customizationUtil'
+import { undefinedIfEmpty } from '../../../shared/utilities/textUtilities'
+import { sleep } from '../../../shared/utilities/timeoutUtils'
+import {
+    FileDiagnostic,
+    getDiagnosticsDifferences,
+    getDiagnosticsOfCurrentFile,
+    toIdeDiagnostics,
+} from '../../../codewhisperer/util/diagnosticsUtil'
+import { Auth } from '../../../auth/auth'
+
+export function logSendTelemetryEventFailure(error: any) {
+    let requestId: string | undefined
+    if (isAwsError(error)) {
+        requestId = error.requestId
+    }
+
+    getLogger().debug(
+        `Failed to sendTelemetryEvent to CodeWhisperer, requestId: ${requestId ?? ''}, message: ${error.message}`
+    )
+}
+
+export function recordTelemetryChatRunCommand(type: CwsprChatCommandType, command?: string) {
+    telemetry.amazonq_runCommand.emit({
+        result: 'Succeeded',
+        cwsprChatCommandType: type,
+        cwsprChatCommandName: command,
+        credentialStartUrl: AuthUtil.instance.startUrl,
+    })
+}
+
+export class CWCTelemetryHelper {
+    static instance: CWCTelemetryHelper
+    private sessionStorage: ChatSessionStorage
+    private triggerEventsStorage: TriggerEventsStorage
+    private responseStreamStartTime: Map = new Map()
+    private responseStreamTotalTime: Map = new Map()
+    private conversationStreamStartTime: Map = new Map()
+    private conversationStreamTotalTime: Map = new Map()
+    private responseStreamTimeForChunks: Map = new Map()
+    private responseWithContextInfo: Map = new Map()
+
+    // Keeps track of when chunks of data were displayed in a tab
+    private displayTimeForChunks: Map = new Map()
+
+    /**
+     * Stores payload information about a message response until
+     * the full round trip time finishes and addMessage telemetry
+     * is sent
+     */
+    private messageStorage: Map<
+        string,
+        {
+            triggerPayload: TriggerPayload
+            message: PromptAnswer
+        }
+    > = new Map()
+
+    private documentDiagnostics: FileDiagnostic | undefined = undefined
+
+    constructor(sessionStorage: ChatSessionStorage, triggerEventsStorage: TriggerEventsStorage) {
+        this.sessionStorage = sessionStorage
+        this.triggerEventsStorage = triggerEventsStorage
+    }
+
+    public setDocumentDiagnostics() {
+        this.documentDiagnostics = getDiagnosticsOfCurrentFile()
+    }
+
+    public static init(sessionStorage: ChatSessionStorage, triggerEventsStorage: TriggerEventsStorage) {
+        const lastInstance = CWCTelemetryHelper.instance
+        if (lastInstance !== undefined) {
+            return lastInstance
+        }
+
+        getLogger().debug('CWCTelemetryHelper: Initialized new telemetry helper')
+        const instance = new CWCTelemetryHelper(sessionStorage, triggerEventsStorage)
+        CWCTelemetryHelper.instance = instance
+        return instance
+    }
+
+    private getUserIntentForTelemetry(userIntent: UserIntent | undefined): CwsprChatUserIntent | undefined {
+        switch (userIntent) {
+            case UserIntent.EXPLAIN_CODE_SELECTION:
+                return 'explainCodeSelection'
+            case UserIntent.SUGGEST_ALTERNATE_IMPLEMENTATION:
+                return 'suggestAlternateImplementation'
+            case UserIntent.APPLY_COMMON_BEST_PRACTICES:
+                return 'applyCommonBestPractices'
+            case UserIntent.IMPROVE_CODE:
+                return 'improveCode'
+            case UserIntent.CITE_SOURCES:
+                return 'citeSources'
+            case UserIntent.EXPLAIN_LINE_BY_LINE:
+                return 'explainLineByLine'
+            case UserIntent.SHOW_EXAMPLES:
+                return 'showExample'
+            case UserIntent.GENERATE_UNIT_TESTS:
+                return 'generateUnitTests'
+            default:
+                return undefined
+        }
+    }
+
+    public recordOpenChat() {
+        telemetry.amazonq_openChat.emit({ result: 'Succeeded', passive: true })
+    }
+
+    public recordCloseChat() {
+        telemetry.amazonq_closeChat.emit({ result: 'Succeeded', passive: true })
+    }
+
+    public recordEnterFocusChat() {
+        telemetry.amazonq_enterFocusChat.emit({ result: 'Succeeded', passive: true })
+    }
+
+    public recordExitFocusChat() {
+        telemetry.amazonq_exitFocusChat.emit({ result: 'Succeeded', passive: true })
+    }
+
+    public async recordFeedback(message: ChatItemFeedbackMessage) {
+        const logger = getLogger()
+        try {
+            await globals.telemetry.postFeedback({
+                comment: JSON.stringify({
+                    type: 'codewhisperer-chat-answer-feedback',
+                    conversationId: this.getConversationId(message.tabID) ?? '',
+                    messageId: message.messageId,
+                    reason: message.selectedOption,
+                    userComment: message.comment,
+                }),
+                sentiment: 'Negative',
+            })
+        } catch (err) {
+            const errorMessage = (err as Error).message || 'Failed to submit feedback'
+            logger.error(`CodeWhispererChat answer feedback failed: "Negative": ${errorMessage}`)
+
+            this.recordFeedbackResult('Failed')
+
+            return errorMessage
+        }
+
+        logger.info(`CodeWhispererChat answer feedback sent: "Negative"`)
+
+        this.recordFeedbackResult('Succeeded')
+    }
+
+    private recordFeedbackResult(feedbackResult: Result) {
+        telemetry.feedback_result.emit({ result: feedbackResult })
+    }
+
+    public recordInteractWithMessage(
+        message:
+            | AcceptDiff
+            | InsertCodeAtCursorPosition
+            | CopyCodeToClipboard
+            | PromptMessage
+            | ChatItemVotedMessage
+            | SourceLinkClickMessage
+            | ResponseBodyLinkClickMessage
+            | FooterInfoLinkClick
+            | ViewDiff,
+        { result }: { result: Result } = { result: 'Succeeded' }
+    ) {
+        const conversationId = this.getConversationId(message.tabID)
+        let event: AmazonqInteractWithMessage | undefined
+        let additionalContextInfo = undefined
+        const messageId = (message as any).messageId
+        if (messageId) {
+            additionalContextInfo = this.responseWithContextInfo.get(messageId)
+        }
+        switch (message.command) {
+            case 'insert_code_at_cursor_position':
+                message = message as InsertCodeAtCursorPosition
+                event = {
+                    result,
+                    cwsprChatConversationId: conversationId ?? '',
+                    credentialStartUrl: AuthUtil.instance.startUrl,
+                    cwsprChatMessageId: message.messageId,
+                    cwsprChatUserIntent: this.getUserIntentForTelemetry(message.userIntent),
+                    cwsprChatInteractionType: 'insertAtCursor',
+                    cwsprChatAcceptedCharactersLength: message.code.length,
+                    cwsprChatAcceptedNumberOfLines: message.code.split('\n').length,
+                    cwsprChatInteractionTarget: message.insertionTargetType,
+                    cwsprChatHasReference: message.codeReference && message.codeReference.length > 0,
+                    cwsprChatCodeBlockIndex: message.codeBlockIndex,
+                    cwsprChatTotalCodeBlocks: message.totalCodeBlocks,
+                    cwsprChatProgrammingLanguage: message.codeBlockLanguage,
+                }
+                break
+            case 'code_was_copied_to_clipboard':
+                message = message as CopyCodeToClipboard
+                event = {
+                    result,
+                    cwsprChatConversationId: conversationId ?? '',
+                    credentialStartUrl: AuthUtil.instance.startUrl,
+                    cwsprChatMessageId: message.messageId,
+                    cwsprChatUserIntent: this.getUserIntentForTelemetry(message.userIntent),
+                    cwsprChatInteractionType: 'copySnippet',
+                    cwsprChatAcceptedCharactersLength: message.code.length,
+                    cwsprChatInteractionTarget: message.insertionTargetType,
+                    cwsprChatHasReference: message.codeReference && message.codeReference.length > 0,
+                    cwsprChatCodeBlockIndex: message.codeBlockIndex,
+                    cwsprChatTotalCodeBlocks: message.totalCodeBlocks,
+                    cwsprChatProgrammingLanguage: message.codeBlockLanguage,
+                }
+                break
+            case 'accept_diff':
+                message = message as AcceptDiff
+                event = {
+                    result,
+                    cwsprChatConversationId: conversationId ?? '',
+                    cwsprChatMessageId: message.messageId,
+                    cwsprChatInteractionType: 'acceptDiff',
+                    credentialStartUrl: AuthUtil.instance.startUrl,
+                    cwsprChatAcceptedCharactersLength: message.code.length,
+                    cwsprChatHasReference:
+                        message.referenceTrackerInformation && message.referenceTrackerInformation.length > 0,
+                    cwsprChatCodeBlockIndex: message.codeBlockIndex,
+                    cwsprChatTotalCodeBlocks: message.totalCodeBlocks,
+                }
+                break
+            case 'view_diff':
+                message = message as ViewDiff
+                event = {
+                    result,
+                    cwsprChatConversationId: conversationId ?? '',
+                    cwsprChatMessageId: message.messageId,
+                    cwsprChatInteractionType: 'viewDiff',
+                    credentialStartUrl: AuthUtil.instance.startUrl,
+                    cwsprChatAcceptedCharactersLength: message.code.length,
+                    cwsprChatHasReference:
+                        message.referenceTrackerInformation && message.referenceTrackerInformation.length > 0,
+                    cwsprChatCodeBlockIndex: message.codeBlockIndex,
+                    cwsprChatTotalCodeBlocks: message.totalCodeBlocks,
+                }
+                break
+            case 'follow-up-was-clicked':
+                message = message as PromptMessage
+                event = {
+                    result,
+                    cwsprChatConversationId: conversationId ?? '',
+                    credentialStartUrl: AuthUtil.instance.startUrl,
+                    cwsprChatMessageId: message.messageId,
+                    cwsprChatInteractionType: 'clickFollowUp',
+                }
+                break
+            case 'chat-item-voted':
+                message = message as ChatItemVotedMessage
+                event = {
+                    result,
+                    cwsprChatMessageId: message.messageId,
+                    cwsprChatConversationId: conversationId ?? '',
+                    credentialStartUrl: AuthUtil.instance.startUrl,
+                    cwsprChatInteractionType: message.vote,
+                }
+                break
+            case 'source-link-click':
+                message = message as SourceLinkClickMessage
+                event = {
+                    result,
+                    cwsprChatMessageId: message.messageId,
+                    cwsprChatConversationId: conversationId ?? '',
+                    credentialStartUrl: AuthUtil.instance.startUrl,
+                    cwsprChatInteractionType: 'clickLink',
+                    cwsprChatInteractionTarget: message.link,
+                }
+                break
+            case 'response-body-link-click':
+                message = message as ResponseBodyLinkClickMessage
+                event = {
+                    result,
+                    cwsprChatMessageId: message.messageId,
+                    cwsprChatConversationId: conversationId ?? '',
+                    credentialStartUrl: AuthUtil.instance.startUrl,
+                    cwsprChatInteractionType: 'clickBodyLink',
+                    cwsprChatInteractionTarget: message.link,
+                }
+                break
+            case 'footer-info-link-click':
+                message = message as FooterInfoLinkClick
+                event = {
+                    result,
+                    cwsprChatMessageId: 'footer',
+                    cwsprChatConversationId: conversationId ?? '',
+                    credentialStartUrl: AuthUtil.instance.startUrl,
+                    cwsprChatInteractionType: 'clickBodyLink',
+                    cwsprChatInteractionTarget: message.link,
+                }
+                break
+        }
+
+        if (!event) {
+            return
+        }
+        telemetry.amazonq_interactWithMessage.emit({ ...event, ...additionalContextInfo })
+
+        const interactWithMessageEvent: CodeWhispererUserClient.ChatInteractWithMessageEvent = {
+            conversationId: event.cwsprChatConversationId,
+            messageId: event.cwsprChatMessageId,
+            interactionType: this.getCWClientTelemetryInteractionType(event.cwsprChatInteractionType),
+            interactionTarget: event.cwsprChatInteractionTarget,
+            acceptedCharacterCount: event.cwsprChatAcceptedCharactersLength,
+            acceptedLineCount: event.cwsprChatAcceptedNumberOfLines,
+            acceptedSnippetHasReference: false,
+            hasProjectLevelContext: this.responseWithContextInfo.get(event.cwsprChatMessageId)
+                ?.cwsprChatHasProjectContext,
+            customizationArn: undefinedIfEmpty(getSelectedCustomization().arn),
+        }
+        if (interactWithMessageEvent.interactionType === 'INSERT_AT_CURSOR' && Auth.instance.isInternalAmazonUser()) {
+            // wait 1 seconds for the user installed 3rd party LSP
+            // to update its diagnostics.
+            void sleep(1000).then(() => {
+                const diagnosticDiff = getDiagnosticsDifferences(
+                    this.documentDiagnostics,
+                    getDiagnosticsOfCurrentFile()
+                )
+                interactWithMessageEvent.addedIdeDiagnostics = diagnosticDiff.added.map((it) => toIdeDiagnostics(it))
+                interactWithMessageEvent.removedIdeDiagnostics = diagnosticDiff.removed.map((it) =>
+                    toIdeDiagnostics(it)
+                )
+                codeWhispererClient
+                    .sendTelemetryEvent({
+                        telemetryEvent: {
+                            chatInteractWithMessageEvent: interactWithMessageEvent,
+                        },
+                    })
+                    .then()
+                    .catch(logSendTelemetryEventFailure)
+            })
+        } else {
+            codeWhispererClient
+                .sendTelemetryEvent({
+                    telemetryEvent: {
+                        chatInteractWithMessageEvent: interactWithMessageEvent,
+                    },
+                    profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
+                })
+                .then()
+                .catch(logSendTelemetryEventFailure)
+        }
+    }
+
+    private getCWClientTelemetryInteractionType(type: CwsprChatInteractionType): ChatMessageInteractionType {
+        switch (type) {
+            case 'copySnippet':
+                return 'COPY_SNIPPET'
+            case 'insertAtCursor':
+                return 'INSERT_AT_CURSOR'
+            case 'clickFollowUp':
+                return 'CLICK_FOLLOW_UP'
+            case 'clickLink':
+                return 'CLICK_LINK'
+            case 'clickBodyLink':
+                return 'CLICK_BODY_LINK'
+            case 'upvote':
+                return 'UPVOTE'
+            case 'downvote':
+                return 'DOWNVOTE'
+            case 'acceptDiff':
+                return 'ACCEPT_DIFF'
+            case 'viewDiff':
+                return 'VIEW_DIFF'
+            default:
+                return 'UNKNOWN'
+        }
+    }
+
+    private getTriggerInteractionFromTriggerEvent(triggerEvent: TriggerEvent | undefined): CwsprChatTriggerInteraction {
+        switch (triggerEvent?.type) {
+            case 'editor_context_command':
+                return triggerEvent.command?.triggerType === 'keybinding' ? 'hotkeys' : 'contextMenu'
+            case 'follow_up':
+            case 'chat_message':
+            default:
+                return 'click'
+        }
+    }
+
+    public recordStartConversation(
+        triggerEvent: TriggerEvent,
+        triggerPayload: TriggerPayload & { projectContextQueryLatencyMs?: number }
+    ) {
+        if (triggerEvent.tabID === undefined) {
+            return
+        }
+
+        if (this.triggerEventsStorage.getTriggerEventsByTabID(triggerEvent.tabID).length > 1) {
+            return
+        }
+
+        const telemetryUserIntent = this.getUserIntentForTelemetry(triggerPayload.userIntent)
+
+        telemetry.amazonq_startConversation.emit({
+            result: 'Succeeded',
+            cwsprChatConversationId: this.getConversationId(triggerEvent.tabID) ?? '',
+            cwsprChatTriggerInteraction: this.getTriggerInteractionFromTriggerEvent(triggerEvent),
+            cwsprChatConversationType: 'Chat',
+            cwsprChatUserIntent: telemetryUserIntent,
+            cwsprChatHasCodeSnippet: triggerPayload.codeSelection && !triggerPayload.codeSelection.isEmpty,
+            cwsprChatProgrammingLanguage: triggerPayload.fileLanguage,
+            credentialStartUrl: AuthUtil.instance.startUrl,
+            cwsprChatHasProjectContext: triggerPayload.relevantTextDocuments
+                ? triggerPayload.relevantTextDocuments.length > 0 && triggerPayload.useRelevantDocuments === true
+                : false,
+            cwsprChatProjectContextQueryMs: triggerPayload.projectContextQueryLatencyMs,
+        })
+    }
+
+    /**
+     * Store the trigger payload and message until the full message round trip finishes
+     *
+     * @calls emitAddMessage when the full message round trip finishes
+     */
+    public recordAddMessage(triggerPayload: TriggerPayload, message: PromptAnswer) {
+        this.messageStorage.set(message.tabID, {
+            triggerPayload,
+            message,
+        })
+    }
+
+    public getAdditionalContextCounts(triggerPayload: TriggerPayload) {
+        const counts = {
+            fileContextCount: 0,
+            folderContextCount: 0,
+            promptContextCount: 0,
+        }
+
+        if (triggerPayload.context) {
+            for (const context of triggerPayload.context) {
+                if (typeof context !== 'string') {
+                    if (context.id === 'file') {
+                        counts.fileContextCount++
+                    } else if (context.id === 'folder') {
+                        counts.folderContextCount++
+                    } else if (context.id === 'prompt') {
+                        counts.promptContextCount++
+                    }
+                }
+            }
+        }
+
+        return counts
+    }
+
+    public emitAddMessage(tabID: string, fullDisplayLatency: number, traceId: string, startTime?: number) {
+        const payload = this.messageStorage.get(tabID)
+        if (!payload) {
+            return
+        }
+
+        const { triggerPayload, message } = payload
+
+        const triggerEvent = this.triggerEventsStorage.getLastTriggerEventByTabID(message.tabID)
+        const hasProjectLevelContext =
+            triggerPayload.relevantTextDocuments &&
+            triggerPayload.relevantTextDocuments.length > 0 &&
+            triggerPayload.useRelevantDocuments === true
+
+        const contextCounts = this.getAdditionalContextCounts(triggerPayload)
+
+        const event: AmazonqAddMessage = {
+            result: 'Succeeded',
+            cwsprChatConversationId: this.getConversationId(message.tabID) ?? '',
+            cwsprChatMessageId: message.messageID,
+            cwsprChatTriggerInteraction: this.getTriggerInteractionFromTriggerEvent(triggerEvent),
+            cwsprChatUserIntent: this.getUserIntentForTelemetry(triggerPayload.userIntent),
+            cwsprChatHasCodeSnippet: triggerPayload.codeSelection && !triggerPayload.codeSelection.isEmpty,
+            cwsprChatProgrammingLanguage: triggerPayload.fileLanguage,
+            cwsprChatActiveEditorTotalCharacters: triggerPayload.fileText.length,
+            cwsprChatActiveEditorImportCount: triggerPayload.codeQuery?.fullyQualifiedNames?.used?.length,
+            cwsprChatResponseCodeSnippetCount: message.totalNumberOfCodeBlocksInResponse,
+            cwsprChatResponseCode: message.responseCode,
+            cwsprChatSourceLinkCount: message.suggestionCount,
+            cwsprChatReferencesCount: message.codeReferenceCount,
+            cwsprChatFollowUpCount: message.followUpCount,
+            cwsprChatTimeToFirstChunk: this.getResponseStreamTimeToFirstChunk(message.tabID),
+            cwsprChatTimeBetweenChunks: JSON.stringify(
+                this.getTimeBetweenChunks(message.tabID, this.responseStreamTimeForChunks)
+            ),
+            cwsprChatFullResponseLatency: this.responseStreamTotalTime.get(message.tabID) ?? 0,
+            cwsprChatTimeToFirstDisplay: this.getFirstDisplayTime(tabID, startTime),
+            cwsprChatTimeToFirstUsableChunk: this.getFirstUsableChunkTime(message.tabID) ?? 0,
+            cwsprChatFullServerResponseLatency: this.conversationStreamTotalTime.get(message.tabID) ?? 0,
+            cwsprChatTimeBetweenDisplays: JSON.stringify(this.getTimeBetweenChunks(tabID, this.displayTimeForChunks)),
+            cwsprChatFullDisplayLatency: fullDisplayLatency,
+            cwsprChatRequestLength: triggerPayload.message.length,
+            cwsprChatResponseLength: message.messageLength,
+            cwsprChatConversationType: 'Chat',
+            credentialStartUrl: AuthUtil.instance.startUrl,
+            codewhispererCustomizationArn: triggerPayload.customization.arn,
+            cwsprChatHasProjectContext: hasProjectLevelContext,
+            cwsprChatHasContextList: triggerPayload.documentReferences.length > 0,
+            cwsprChatFolderContextCount: contextCounts.folderContextCount,
+            cwsprChatFileContextCount: contextCounts.fileContextCount,
+            cwsprChatFileContextLength: triggerPayload.contextLengths.additionalContextLengths.fileContextLength,
+            cwsprChatFileContextTruncatedLength:
+                triggerPayload.contextLengths.truncatedAdditionalContextLengths.fileContextLength,
+            cwsprChatRuleContextCount: triggerPayload.workspaceRulesCount,
+            cwsprChatRuleContextLength: triggerPayload.contextLengths.additionalContextLengths.ruleContextLength,
+            cwsprChatRuleContextTruncatedLength:
+                triggerPayload.contextLengths.truncatedAdditionalContextLengths.ruleContextLength,
+            cwsprChatPromptContextCount: contextCounts.promptContextCount,
+            cwsprChatPromptContextLength: triggerPayload.contextLengths.additionalContextLengths.promptContextLength,
+            cwsprChatPromptContextTruncatedLength:
+                triggerPayload.contextLengths.truncatedAdditionalContextLengths.promptContextLength,
+            cwsprChatFocusFileContextLength: triggerPayload.contextLengths.focusFileContextLength,
+            cwsprChatFocusFileContextTruncatedLength: triggerPayload.contextLengths.truncatedFocusFileContextLength,
+            cwsprChatUserInputContextLength: triggerPayload.contextLengths.userInputContextLength,
+            cwsprChatUserInputContextTruncatedLength: triggerPayload.contextLengths.truncatedUserInputContextLength,
+            cwsprChatWorkspaceContextLength: triggerPayload.contextLengths.workspaceContextLength,
+            cwsprChatWorkspaceContextTruncatedLength: triggerPayload.contextLengths.truncatedWorkspaceContextLength,
+            traceId,
+        }
+
+        telemetry.amazonq_addMessage.emit(event)
+        const language = this.isProgrammingLanguageSupported(triggerPayload.fileLanguage)
+            ? { languageName: triggerPayload.fileLanguage as string }
+            : undefined
+
+        codeWhispererClient
+            .sendTelemetryEvent({
+                telemetryEvent: {
+                    chatAddMessageEvent: {
+                        conversationId: event.cwsprChatConversationId,
+                        messageId: event.cwsprChatMessageId,
+                        userIntent: triggerPayload.userIntent,
+                        hasCodeSnippet: event.cwsprChatHasCodeSnippet,
+                        ...(language !== undefined ? { programmingLanguage: language } : {}),
+                        activeEditorTotalCharacters: event.cwsprChatActiveEditorTotalCharacters,
+                        timeToFirstChunkMilliseconds: event.cwsprChatTimeToFirstChunk,
+                        timeBetweenChunks: this.getTimeBetweenChunks(message.tabID, this.responseStreamTimeForChunks),
+                        fullResponselatency: event.cwsprChatFullResponseLatency,
+                        requestLength: event.cwsprChatRequestLength,
+                        responseLength: event.cwsprChatResponseLength,
+                        numberOfCodeBlocks: event.cwsprChatResponseCodeSnippetCount,
+                        hasProjectLevelContext: hasProjectLevelContext,
+                        customizationArn: undefinedIfEmpty(getSelectedCustomization().arn),
+                    },
+                },
+                profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
+            })
+            .then()
+            .catch(logSendTelemetryEventFailure)
+
+        this.messageStorage.delete(tabID)
+    }
+
+    public recordMessageResponseError(triggerPayload: TriggerPayload, tabID: string, responseCode: number) {
+        const triggerEvent = this.triggerEventsStorage.getLastTriggerEventByTabID(tabID)
+
+        telemetry.amazonq_messageResponseError.emit({
+            result: 'Succeeded',
+            cwsprChatConversationId: this.getConversationId(tabID) ?? '',
+            cwsprChatTriggerInteraction: this.getTriggerInteractionFromTriggerEvent(triggerEvent),
+            cwsprChatUserIntent: this.getUserIntentForTelemetry(triggerPayload.userIntent),
+            cwsprChatHasCodeSnippet: !triggerPayload.codeSelection?.isEmpty,
+            cwsprChatProgrammingLanguage: triggerPayload.fileLanguage,
+            cwsprChatActiveEditorTotalCharacters: triggerPayload.fileText?.length,
+            cwsprChatActiveEditorImportCount: triggerPayload.codeQuery?.fullyQualifiedNames?.used?.length,
+            cwsprChatResponseCode: responseCode,
+            cwsprChatRequestLength: triggerPayload.message?.length ?? 0,
+            cwsprChatConversationType: 'Chat',
+            credentialStartUrl: AuthUtil.instance.startUrl,
+        })
+    }
+
+    public recordEnterFocusConversation(tabID: string) {
+        const conversationId = this.getConversationId(tabID)
+        if (conversationId) {
+            telemetry.amazonq_enterFocusConversation.emit({
+                result: 'Succeeded',
+                cwsprChatConversationId: conversationId,
+            })
+        }
+    }
+
+    public recordExitFocusConversation(tabID: string) {
+        const conversationId = this.getConversationId(tabID)
+        if (conversationId) {
+            telemetry.amazonq_exitFocusConversation.emit({
+                result: 'Succeeded',
+                cwsprChatConversationId: conversationId,
+            })
+        }
+    }
+
+    public setResponseStreamStartTime(tabID: string) {
+        this.responseStreamStartTime.set(tabID, performance.now())
+        this.responseStreamTimeForChunks.set(tabID, [performance.now()])
+        this.displayTimeForChunks.set(tabID, [])
+    }
+
+    public setResponseStreamTimeForChunks(tabID: string) {
+        const chunkTimes = this.responseStreamTimeForChunks.get(tabID) ?? []
+        this.responseStreamTimeForChunks.set(tabID, [...chunkTimes, performance.now()])
+    }
+
+    public setDisplayTimeForChunks(tabID: string, time: number) {
+        const chunkTimes = this.displayTimeForChunks.get(tabID) ?? []
+        this.displayTimeForChunks.set(tabID, [...chunkTimes, time])
+    }
+
+    public setResponseFromAdditionalContext(messageId: string, additionalContextInfo: AdditionalContextInfo) {
+        this.responseWithContextInfo.set(messageId, additionalContextInfo)
+    }
+
+    public setConversationStreamStartTime(tabID: string) {
+        this.conversationStreamStartTime.set(tabID, performance.now())
+    }
+
+    private getResponseStreamTimeToFirstChunk(tabID: string): number {
+        const chunkTimes = this.responseStreamTimeForChunks.get(tabID) ?? [0, 0]
+        if (chunkTimes.length === 1) {
+            return Math.round(performance.now() - chunkTimes[0])
+        }
+        return Math.round(chunkTimes[1] - chunkTimes[0])
+    }
+
+    /**
+     * Finds the time between when a user pressed enter and the first chunk appears in the UI
+     */
+    private getFirstDisplayTime(tabID: string, startTime?: number) {
+        if (!startTime) {
+            return 0
+        }
+        const chunkTimes = this.displayTimeForChunks.get(tabID) ?? [0]
+        return Math.round(chunkTimes[0] - startTime)
+    }
+
+    private getFirstUsableChunkTime(tabID: string) {
+        const startTime = this.conversationStreamStartTime.get(tabID) ?? 0
+        const chunkTimes = this.responseStreamTimeForChunks.get(tabID) ?? [0, 0]
+        // first chunk is the start time, we use the second because thats the first actual usable chunk time
+        return Math.round(chunkTimes[1] - startTime)
+    }
+
+    private getTimeBetweenChunks(tabID: string, chunks: Map): number[] {
+        try {
+            const chunkDeltaTimes: number[] = []
+            const chunkTimes = chunks.get(tabID) ?? [0]
+            for (let idx = 0; idx < chunkTimes.length - 1; idx++) {
+                chunkDeltaTimes.push(Math.round(chunkTimes[idx + 1] - chunkTimes[idx]))
+            }
+
+            return chunkDeltaTimes.slice(0, 100)
+        } catch (e) {
+            getLogger().debug(`Failed to get response time between chunks, message: ${e}`)
+            return []
+        }
+    }
+
+    public setResponseStreamTotalTime(tabID: string) {
+        // time from when the requests started streaming until the end of the stream
+        const totalStreamingTime = performance.now() - (this.responseStreamStartTime.get(tabID) ?? 0)
+        this.responseStreamTotalTime.set(tabID, Math.round(totalStreamingTime))
+
+        // time from the initial server request, including creating the conversation id, until the end of the stream
+        const totalConversationTime = performance.now() - (this.conversationStreamStartTime.get(tabID) ?? 0)
+        this.conversationStreamTotalTime.set(tabID, Math.round(totalConversationTime))
+    }
+
+    public getConversationId(tabID: string): string | undefined {
+        return this.sessionStorage.getSession(tabID).sessionIdentifier
+    }
+
+    private isProgrammingLanguageSupported(programmingLanguage: string | undefined) {
+        return (
+            programmingLanguage !== undefined &&
+            programmingLanguage !== '' &&
+            supportedLanguagesList.includes(programmingLanguage)
+        )
+    }
+}
diff --git a/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts b/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts
new file mode 100644
index 00000000000..4e3eabc0bc8
--- /dev/null
+++ b/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts
@@ -0,0 +1,47 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { UserIntent } from '@amzn/codewhisperer-streaming'
+import { EditorContextCommand } from '../../../commands/registerCommands'
+import { PromptMessage } from '../model'
+import { Auth } from '../../../../auth/auth'
+
+export class UserIntentRecognizer {
+    public getFromContextMenuCommand(command: EditorContextCommand): UserIntent | undefined {
+        switch (command.type) {
+            case 'aws.amazonq.explainCode':
+                return UserIntent.EXPLAIN_CODE_SELECTION
+            case 'aws.amazonq.refactorCode':
+                return UserIntent.SUGGEST_ALTERNATE_IMPLEMENTATION
+            case 'aws.amazonq.fixCode':
+                return UserIntent.APPLY_COMMON_BEST_PRACTICES
+            case 'aws.amazonq.optimizeCode':
+                return UserIntent.IMPROVE_CODE
+            case 'aws.amazonq.generateUnitTests':
+                return UserIntent.GENERATE_UNIT_TESTS
+            default:
+                return undefined
+        }
+    }
+
+    public getFromPromptChatMessage(prompt: PromptMessage): UserIntent | undefined {
+        if (prompt.message === undefined) {
+            return undefined
+        }
+
+        if (prompt.message.startsWith('Explain')) {
+            return UserIntent.EXPLAIN_CODE_SELECTION
+        } else if (prompt.message.startsWith('Refactor')) {
+            return UserIntent.SUGGEST_ALTERNATE_IMPLEMENTATION
+        } else if (prompt.message.startsWith('Fix')) {
+            return UserIntent.APPLY_COMMON_BEST_PRACTICES
+        } else if (prompt.message.startsWith('Optimize')) {
+            return UserIntent.IMPROVE_CODE
+        } else if (prompt.message.startsWith('Generate unit tests') && Auth.instance.isInternalAmazonUser()) {
+            return UserIntent.GENERATE_UNIT_TESTS
+        }
+        return undefined
+    }
+}
diff --git a/packages/core/src/codewhispererChat/editor/codelens.ts b/packages/core/src/codewhispererChat/editor/codelens.ts
new file mode 100644
index 00000000000..4df72d776d6
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/codelens.ts
@@ -0,0 +1,172 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import vscode from 'vscode'
+import globals from '../../shared/extensionGlobals'
+import { ToolkitError } from '../../shared/errors'
+import { Commands, placeholder } from '../../shared/vscode/commands2'
+import { platform } from 'os'
+import { focusAmazonQPanel } from '../commands/registerCommands'
+import { AuthStates, AuthUtil } from '../../codewhisperer/util/authUtil'
+import { inlinehintKey } from '../../codewhisperer/models/constants'
+import { EndState } from '../../codewhisperer/views/lineAnnotationController'
+
+/** When the user clicks the CodeLens that prompts user to try Amazon Q chat */
+export const tryChatCodeLensCommand = Commands.declare(`_aws.amazonq.tryChatCodeLens`, () => async () => {
+    await focusAmazonQPanel.execute(placeholder, 'codeLens')
+})
+
+/**
+ * As part of hinting at users to use Amazon Q Chat, we will show codelenses
+ * prompting them a certain amount of time/uses. Then after
+ * a certain amount of times we will never show it again.
+ *
+ * This codelens appears above every clicked line.
+ */
+export class TryChatCodeLensProvider implements vscode.CodeLensProvider {
+    private _onDidChangeCodeLenses = new vscode.EventEmitter()
+    onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event
+
+    /** How many times we've shown the CodeLens */
+    private count: number = 0
+    /** How many times we want to show the CodeLens */
+    static readonly maxCount = 10
+    static readonly debounceMillis: number = 700
+
+    private static providerDisposable: vscode.Disposable | undefined = undefined
+    private disposables: vscode.Disposable[] = []
+
+    // Assumption: Amazon Q is visible by default and codelens should be hidden
+    private isAmazonQVisible: boolean = true
+
+    constructor(
+        isAmazonQVisibleEvent: vscode.Event,
+        private readonly cursorPositionIfValid = () => TryChatCodeLensProvider._resolveCursorPosition()
+    ) {
+        // when we want to recalculate the codelens
+        this.disposables.push(
+            vscode.window.onDidChangeActiveTextEditor(() => this._onDidChangeCodeLenses.fire()),
+            vscode.window.onDidChangeTextEditorSelection(() => this._onDidChangeCodeLenses.fire())
+        )
+
+        isAmazonQVisibleEvent((visible) => {
+            this.isAmazonQVisible = visible
+            this._onDidChangeCodeLenses.fire()
+        })
+    }
+
+    static async register(isAmazonQVisible: vscode.Event): Promise {
+        const shouldShow = globals.globalState.tryGet('aws.amazonq.showTryChatCodeLens', Boolean, true)
+        if (!shouldShow) {
+            return false
+        }
+
+        if (this.providerDisposable) {
+            throw new ToolkitError(`${this.name} can only be registered once.`)
+        }
+
+        const provider = new TryChatCodeLensProvider(isAmazonQVisible)
+        this.providerDisposable = vscode.languages.registerCodeLensProvider({ scheme: 'file' }, provider)
+        globals.context.subscriptions.push(provider)
+        return true
+    }
+
+    provideCodeLenses(
+        document: vscode.TextDocument,
+        token: vscode.CancellationToken
+    ): vscode.ProviderResult {
+        return new Promise((resolve) => {
+            token.onCancellationRequested(() => resolve([]))
+
+            /**
+             * Disable Codelens if
+             * - lineAnnotationController is visible (i.e not in end state)
+             * - Amazon Q chat is visible
+             */
+            const isLineAnnotationVisible = globals.globalState.tryGet(inlinehintKey, String)
+            if ((isLineAnnotationVisible && isLineAnnotationVisible !== EndState.id) || this.isAmazonQVisible) {
+                return resolve([])
+            }
+
+            if (AuthUtil.instance.getChatAuthStateSync().amazonQ !== AuthStates.connected) {
+                return resolve([])
+            }
+
+            if (this.count >= TryChatCodeLensProvider.maxCount) {
+                // We only want to show this code lens a certain amount of times
+                // to not annoy customers. The following ensures it is never shown again.
+                this.dispose()
+                return resolve([])
+            }
+
+            // We use a timeout as a leading debounce so that the user must
+            // wait on a specific line for a certain amount of time until we show the codelens.
+            // This prevents spamming code lenses if the user changes multiple lines quickly.
+            globals.clock.setTimeout(() => {
+                const position = this.cursorPositionIfValid()
+                if (token.isCancellationRequested || position === undefined) {
+                    return resolve([])
+                }
+
+                resolve([
+                    {
+                        range: new vscode.Range(position, position),
+                        isResolved: true,
+                        command: {
+                            command: tryChatCodeLensCommand.id,
+                            title: `Amazon Q: open chat with (${resolveModifierKey()} + i) - showing ${
+                                TryChatCodeLensProvider.maxCount - this.count
+                            } more times`,
+                        },
+                    },
+                ])
+
+                this.count++
+            }, TryChatCodeLensProvider.debounceMillis)
+        })
+    }
+
+    /**
+     * Resolves the current cursor position in the active document
+     * if the criteria are met.
+     */
+    private static _resolveCursorPosition(): vscode.Position | undefined {
+        const activeEditor = vscode.window.activeTextEditor
+        const activeDocument = activeEditor?.document
+        const textSelection = activeEditor?.selection
+        if (
+            !activeEditor ||
+            !activeDocument ||
+            activeEditor.selections.length > 1 || // is multi-cursor select
+            !textSelection?.isSingleLine ||
+            activeDocument.lineAt(textSelection.start.line).text.length === 0 // is empty line
+        ) {
+            return undefined
+        }
+
+        return textSelection.start
+    }
+
+    dispose() {
+        globals.globalState.tryUpdate('aws.amazonq.showTryChatCodeLens', false)
+        TryChatCodeLensProvider.providerDisposable?.dispose()
+        for (const d of this.disposables) {
+            d.dispose()
+        }
+    }
+}
+
+export function resolveModifierKey() {
+    const platformName = platform()
+    switch (platformName) {
+        case 'win32':
+            return 'ctrl'
+        case 'linux':
+            return 'meta'
+        case 'darwin':
+            return 'cmd'
+        default:
+            return 'ctrl'
+    }
+}
diff --git a/packages/core/src/codewhispererChat/editor/context/extractor.ts b/packages/core/src/codewhispererChat/editor/context/extractor.ts
new file mode 100644
index 00000000000..9d3865038ef
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/context/extractor.ts
@@ -0,0 +1,74 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { FocusAreaContextExtractor } from './focusArea/focusAreaExtractor'
+import { FocusAreaContext } from './focusArea/model'
+import { FileContextExtractor } from './file/fileExtractor'
+import { FileContext } from './file/model'
+import { EditorContext } from './model'
+import { window } from 'vscode'
+
+export type TriggerType = 'ChatMessage' | 'ContextMenu' | 'QuickAction'
+
+export class EditorContextExtractor {
+    private readonly activeFileContextExtractor: FileContextExtractor
+    private readonly focusAreaContextExtractor: FocusAreaContextExtractor
+
+    public constructor() {
+        this.activeFileContextExtractor = new FileContextExtractor()
+        this.focusAreaContextExtractor = new FocusAreaContextExtractor()
+    }
+
+    public async extractContextForTrigger(triggerType: TriggerType): Promise {
+        switch (triggerType) {
+            case 'ChatMessage':
+                return {
+                    activeFileContext: await this.extractActiveFileContext(),
+                    focusAreaContext: await this.extractActiveEditorCodeSelectionContext(),
+                }
+            case 'ContextMenu':
+                return {
+                    activeFileContext: await this.extractActiveFileContext(),
+                    focusAreaContext: await this.extractActiveEditorCodeSelectionContext(),
+                }
+            case 'QuickAction':
+                return {
+                    activeFileContext: undefined,
+                    focusAreaContext: undefined,
+                }
+        }
+        return undefined
+    }
+
+    private async extractActiveEditorCodeSelectionContext(): Promise {
+        const editor = window.activeTextEditor
+        if (editor === undefined) {
+            return undefined
+        }
+
+        return this.focusAreaContextExtractor.extract(editor)
+    }
+
+    public isCodeBlockSelected(): boolean {
+        const editor = window.activeTextEditor
+        if (editor === undefined) {
+            return false
+        }
+
+        return this.focusAreaContextExtractor.isCodeBlockSelected(editor)
+    }
+
+    private async extractActiveFileContext(): Promise {
+        const editor = window.activeTextEditor
+        if (editor === undefined) {
+            return undefined
+        }
+        const currentFile = editor.document
+        if (currentFile === undefined) {
+            return undefined
+        }
+        return this.activeFileContextExtractor.extract(currentFile)
+    }
+}
diff --git a/packages/core/src/codewhispererChat/editor/context/file/fileExtractor.ts b/packages/core/src/codewhispererChat/editor/context/file/fileExtractor.ts
new file mode 100644
index 00000000000..7606fe0708c
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/context/file/fileExtractor.ts
@@ -0,0 +1,57 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { TextDocument } from 'vscode'
+import { extractLanguageNameFromFile, extractAdditionalLanguageMatchPoliciesFromFile } from './languages'
+import { MatchPolicy } from '../../../clients/chat/v0/model'
+import { readImports } from './importReader'
+import { FileContext } from './model'
+
+export class FileContextExtractor {
+    public async extract(file: TextDocument): Promise {
+        const fileText = file.getText()
+        const fileLanguage = extractLanguageNameFromFile(file)
+        const filePath = file.fileName
+        const matchPolicy = await this.extractMatchPolicyFromFile(file, false)
+
+        return {
+            fileText,
+            fileLanguage,
+            filePath,
+            matchPolicy,
+        }
+    }
+
+    private async extractMatchPolicyFromFile(file: TextDocument, isCodeSelected: boolean): Promise {
+        const languageId = file.languageId
+
+        const language = extractLanguageNameFromFile(file)
+        const additionalLanguageContext = extractAdditionalLanguageMatchPoliciesFromFile(file)
+        const should = additionalLanguageContext
+        const must = new Set()
+        if (language !== undefined) {
+            if (isCodeSelected) {
+                must.add(language)
+            } else {
+                should.add(language)
+            }
+        }
+
+        if (languageId !== undefined) {
+            const imports = await readImports(file.getText(), languageId)
+            for (const importKey of imports.filter(function (elem, index, self) {
+                return index === self.indexOf(elem) && elem !== languageId
+            })) {
+                should.add(importKey)
+            }
+        }
+
+        return {
+            must: Array.from(must),
+            should: Array.from(should),
+            mustNot: [],
+        }
+    }
+}
diff --git a/packages/core/src/codewhispererChat/editor/context/file/importReader.ts b/packages/core/src/codewhispererChat/editor/context/file/importReader.ts
new file mode 100644
index 00000000000..ff99a183bd9
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/context/file/importReader.ts
@@ -0,0 +1,25 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import { extractContextFromJavaImports } from './javaImportReader'
+
+export async function readImports(text: string, languageId: string): Promise {
+    const names: any = {}
+    //  TODO: call findNames from @aws/fully-qualified-names for imports
+    //  after promise not resolving issue is fixed
+
+    if (names.fullyQualified === undefined) {
+        return []
+    }
+    if (languageId === 'java') {
+        return extractContextFromJavaImports(names)
+    } else {
+        const imports = names.fullyQualified?.declaredSymbols
+            .map((symbol: { source: string[] }): string => {
+                return symbol.source[0].replace('@', '')
+            })
+            .filter((source: string) => source.length !== 0)
+        return imports
+    }
+}
diff --git a/packages/core/src/codewhispererChat/editor/context/file/javaImportReader.ts b/packages/core/src/codewhispererChat/editor/context/file/javaImportReader.ts
new file mode 100644
index 00000000000..b66a6c44936
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/context/file/javaImportReader.ts
@@ -0,0 +1,58 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export interface JavaImport {
+    readonly tld: string
+    readonly organisation?: string
+    readonly packages?: string[]
+}
+
+export function extractContextFromJavaImports(names: any): string[] {
+    return names.fullyQualified?.declaredSymbols
+        .map((symbol: any): JavaImport => {
+            const sourcesCount = symbol.source.length
+            return {
+                tld: symbol.source[0],
+                organisation: sourcesCount > 1 ? symbol.source[1] : undefined,
+                packages: sourcesCount > 2 ? symbol.source.slice(2) : undefined,
+            }
+        })
+        .map((javaImport: JavaImport): string => {
+            const importStatement = toString(javaImport)
+            if (commonJavaImportsPrefixesRegex.test(importStatement)) {
+                return ''
+            } else if (importStatement.startsWith(awsJavaSdkV1Prefix)) {
+                // @ts-ignore
+                return javaImport.packages?.at(1) ?? ''
+            } else if (importStatement.startsWith(awsJavaSdkV2Prefix)) {
+                // @ts-ignore
+                return javaImport.packages?.at(2) ?? ''
+            } else {
+                // @ts-ignore
+                return javaImport.packages?.at(0) ?? javaImport.organisation ?? javaImport.tld
+            }
+        })
+        .filter((context: string) => context !== '')
+}
+
+function toString(javaImport: JavaImport): string {
+    let importSegments: string[] = []
+    importSegments.push(javaImport.tld)
+    if (javaImport.organisation !== undefined) {
+        importSegments.push(javaImport.organisation)
+    }
+    if (javaImport.packages !== undefined) {
+        importSegments = importSegments.concat(javaImport.packages)
+    }
+    return importSegments.join('.') + '.'
+}
+
+const commonJavaImportsPrefixesRegex = new RegExp(
+    '^(java.|javax.|org.slf4j.|org.apache.log4j.|org.apache.logging.log4j.|org.junit.|org.testng.)'
+)
+
+const awsJavaSdkV1Prefix = 'com.amazonaws.services'
+
+const awsJavaSdkV2Prefix = 'software.amazon.awssdk.services'
diff --git a/packages/core/src/codewhispererChat/editor/context/file/languages.ts b/packages/core/src/codewhispererChat/editor/context/file/languages.ts
new file mode 100644
index 00000000000..51887eaf31b
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/context/file/languages.ts
@@ -0,0 +1,176 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { TextDocument } from 'vscode'
+
+const defaultLanguages = [
+    'yaml',
+    'xsl',
+    'xml',
+    'vue',
+    'tex',
+    'typescript',
+    'swift',
+    'stylus',
+    'sql',
+    'slim',
+    'shaderlab',
+    'sass',
+    'rust',
+    'ruby',
+    'r',
+    'python',
+    'pug',
+    'powershell',
+    'php',
+    'perl',
+    'markdown',
+    'makefile',
+    'lua',
+    'less',
+    'latex',
+    'json',
+    'javascript',
+    'java',
+    'ini',
+    'html',
+    'haml',
+    'handlebars',
+    'groovy',
+    'go',
+    'diff',
+    'css',
+    'c',
+    'coffeescript',
+    'clojure',
+    'bibtex',
+    'abap',
+]
+
+export function extractLanguageNameFromFile(file: TextDocument): string | undefined {
+    const languageId = file.languageId
+
+    if (languageId === undefined) {
+        return undefined
+    }
+    if (defaultLanguages.includes(languageId)) {
+        return languageId
+    }
+    switch (languageId) {
+        case 'bat':
+            return 'bat'
+        case 'cpp':
+            return 'c++'
+        case 'csharp':
+            return 'c#'
+        case 'cuda-cpp':
+            return 'c++'
+        case 'dockerfile':
+            return 'dockerfile'
+        case 'fsharp':
+            return 'f#'
+        case 'git-commit':
+            return 'git'
+        case 'git-rebase':
+            return 'git'
+        case 'javascriptreact':
+            return 'javascript'
+        case 'jsonc':
+            return 'json'
+        case 'objective-c':
+            return 'objective-c'
+        case 'objective-cpp':
+            return 'objective-c++'
+        case 'perl6':
+            return 'raku'
+        case 'plaintext':
+            return undefined
+        case 'jade':
+            return 'pug'
+        case 'razor':
+            return 'razor'
+        case 'scss':
+            return 'sass'
+        case 'shellscript':
+            return 'sh'
+        case 'typescriptreact':
+            return 'typescript'
+        case 'vb':
+            return 'visual-basic'
+        case 'vue-html':
+            return 'vue'
+        default:
+            if (['javascript', 'node'].some((identifier) => languageId.includes(identifier))) {
+                return 'javascript'
+            } else if (languageId.includes('typescript')) {
+                return 'typescript'
+            } else if (languageId.includes('python')) {
+                return 'python'
+            }
+            return undefined
+    }
+}
+
+// eslint-disable-next-line id-length
+export function extractAdditionalLanguageMatchPoliciesFromFile(file: TextDocument): Set {
+    const languageId = file.languageId
+
+    if (languageId === undefined || defaultLanguages.includes(languageId)) {
+        return new Set()
+    }
+    switch (languageId) {
+        case 'bat':
+            return new Set(['windows'])
+        case 'cpp':
+            return new Set()
+        case 'csharp':
+            return new Set()
+        case 'cuda-cpp':
+            return new Set(['cuda'])
+        case 'dockerfile':
+            return new Set(['docker'])
+        case 'fsharp':
+            return new Set()
+        case 'git-commit':
+            return new Set(['commit'])
+        case 'git-rebase':
+            return new Set(['rebase'])
+        case 'javascriptreact':
+            return new Set(['react'])
+        case 'jsonc':
+            return new Set(['comments'])
+        case 'objective-c':
+            return new Set()
+        case 'objective-cpp':
+            return new Set()
+        case 'perl6':
+            return new Set(['perl'])
+        case 'plaintext':
+            return new Set()
+        case 'jade':
+            return new Set()
+        case 'razor':
+            return new Set(['html'])
+        case 'scss':
+            return new Set(['scss', 'css'])
+        case 'shellscript':
+            return new Set()
+        case 'typescriptreact':
+            return new Set(['react'])
+        case 'vb':
+            return new Set()
+        case 'vue-html':
+            return new Set(['html'])
+        default:
+            if (['javascript', 'node'].some((identifier) => languageId.includes(identifier))) {
+                return new Set()
+            } else if (languageId.includes('typescript')) {
+                return new Set()
+            } else if (languageId.includes('python')) {
+                return new Set()
+            }
+            return new Set()
+    }
+}
diff --git a/packages/core/src/codewhispererChat/editor/context/file/model.ts b/packages/core/src/codewhispererChat/editor/context/file/model.ts
new file mode 100644
index 00000000000..42b0846b0cc
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/context/file/model.ts
@@ -0,0 +1,13 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { MatchPolicy } from '../../../clients/chat/v0/model'
+
+export interface FileContext {
+    readonly fileText: string | undefined
+    readonly fileLanguage: string | undefined
+    readonly filePath: string | undefined
+    readonly matchPolicy: MatchPolicy | undefined
+}
diff --git a/packages/core/src/codewhispererChat/editor/context/focusArea/focusAreaExtractor.ts b/packages/core/src/codewhispererChat/editor/context/focusArea/focusAreaExtractor.ts
new file mode 100644
index 00000000000..d782f7147ff
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/context/focusArea/focusAreaExtractor.ts
@@ -0,0 +1,280 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { TextEditor, Selection, TextDocument, Range } from 'vscode'
+
+import { FocusAreaContext, FullyQualifiedName } from './model'
+
+const focusAreaCharLimit = 40_000
+
+export class FocusAreaContextExtractor {
+    public isCodeBlockSelected(editor: TextEditor): boolean {
+        if (editor.document === undefined) {
+            return false
+        }
+
+        // It means we don't really have a selection, but cursor position only
+        if (
+            editor.selection.start.line === editor.selection.end.line &&
+            editor.selection.start.character === editor.selection.end.character
+        ) {
+            return false
+        }
+
+        return true
+    }
+
+    public async extract(editor: TextEditor): Promise {
+        if (editor.document === undefined) {
+            return undefined
+        }
+
+        let importantRange: Range = editor.selection
+
+        // It means we don't really have a selection, but cursor position only
+        if (!this.isCodeBlockSelected(editor)) {
+            // Select the whole line
+            importantRange = editor.document.lineAt(importantRange.start.line).range
+        }
+
+        //  TODO: call findNamesWithInExtent from @aws/fully-qualified-names
+        //  after promise not resolving issue is fixed
+        const names = {}
+        const [simpleNames] = this.prepareSimpleNames(names)
+        const [usedFullyQualifiedNames] = this.prepareFqns(names)
+
+        importantRange = this.trimRangeAccordingToLimits(editor.document, importantRange, focusAreaCharLimit)
+        const codeBlock = this.getRangeText(editor.document, importantRange)
+        const extendedCodeBlockRange = this.getExtendedCodeBlockRange(
+            editor.document,
+            importantRange,
+            focusAreaCharLimit
+        )
+
+        return {
+            extendedCodeBlock: this.getRangeText(editor.document, extendedCodeBlockRange),
+            codeBlock: codeBlock,
+            selectionInsideExtendedCodeBlock: this.getSelectionInsideExtendedCodeBlock(
+                editor.selection,
+                extendedCodeBlockRange
+            ),
+            names:
+                simpleNames.length === 0 && usedFullyQualifiedNames.length === 0
+                    ? undefined
+                    : {
+                          simpleNames,
+                          fullyQualifiedNames: {
+                              used: usedFullyQualifiedNames,
+                          },
+                      },
+        }
+    }
+
+    private getSelectionInsideExtendedCodeBlock(
+        originSelection: Selection,
+        extendedCodeBlockRange: Range
+    ): Selection | undefined {
+        if (
+            originSelection.start.line === originSelection.end.line &&
+            originSelection.start.character === originSelection.end.character
+        ) {
+            return undefined
+        }
+
+        return new Selection(
+            originSelection.start.line - extendedCodeBlockRange.start.line,
+            originSelection.start.line === extendedCodeBlockRange.start.line
+                ? originSelection.start.character - extendedCodeBlockRange.start.character
+                : originSelection.start.character,
+            originSelection.end.line - extendedCodeBlockRange.start.line,
+            originSelection.end.line === extendedCodeBlockRange.end.line
+                ? originSelection.end.character - extendedCodeBlockRange.start.character
+                : originSelection.end.character
+        )
+    }
+
+    // Function to extend the code block range
+    private getExtendedCodeBlockRange(document: TextDocument, importantRange: Range, charLimit: number): Range {
+        // Flag to add line before or after
+        let addLineBefore = true
+        // Loop while range can still be extended
+        while (
+            !(importantRange.start.line === 0 && importantRange.start.character === 0) ||
+            !(
+                importantRange.end.line === document.lineCount - 1 &&
+                importantRange.end.character === document.lineAt(document.lineCount - 1).range.end.character
+            )
+        ) {
+            let tmpRange: Range | undefined = undefined
+            if (addLineBefore) {
+                // Check if we have characters left in the beginning of the current line
+                if (importantRange.start.character !== 0) {
+                    tmpRange = new Range(
+                        importantRange.start.line,
+                        0,
+                        importantRange.end.line,
+                        importantRange.end.character
+                    )
+                } else if (importantRange.start.line !== 0) {
+                    tmpRange = new Range(
+                        importantRange.start.line - 1,
+                        document.lineAt(importantRange.start.line - 1).range.end.character,
+                        importantRange.end.line,
+                        importantRange.end.character
+                    )
+                }
+
+                // Flip flag for next iteration
+                addLineBefore = false
+            } else {
+                // Check if we have characters left in the end of the current line
+                if (importantRange.end.character !== document.lineAt(importantRange.end.line).range.end.character) {
+                    tmpRange = new Range(
+                        importantRange.start.line,
+                        importantRange.start.character,
+                        importantRange.end.line,
+                        document.lineAt(importantRange.end.line).range.end.character
+                    )
+                } else if (importantRange.end.line !== document.lineCount - 1) {
+                    tmpRange = new Range(
+                        importantRange.start.line,
+                        importantRange.start.character,
+                        importantRange.end.line + 1,
+                        0
+                    )
+                }
+
+                // Flip flag for next iteration
+                addLineBefore = true
+            }
+
+            // Check if tmp range was set
+            if (tmpRange === undefined) {
+                continue
+            }
+            // Check character length of tmp range
+            if (this.getRangeText(document, tmpRange).length >= charLimit) {
+                // Break loop if too long
+                break
+            }
+
+            // Update important range
+            importantRange = tmpRange
+        }
+
+        return importantRange
+    }
+
+    private trimRangeAccordingToLimits(document: TextDocument, range: Range, charLimit: number): Range {
+        while (
+            this.getRangeText(document, range).length > charLimit &&
+            (range.start.line !== range.end.line ||
+                (range.start.line === range.end.line && range.start.character !== range.end.character))
+        ) {
+            if (range.end.line === range.start.line) {
+                range = new Range(
+                    range.start.line,
+                    range.start.character,
+                    range.start.line,
+                    range.start.character +
+                        charLimit -
+                        this.getRangeText(
+                            document,
+                            new Range(range.start.line, range.start.character, range.start.line, range.start.character)
+                        ).length -
+                        1
+                )
+            } else {
+                const selectionSizeWithoutLastLine = this.getRangeText(
+                    document,
+                    new Range(
+                        range.start.line,
+                        range.start.character,
+                        range.end.line - 1,
+                        document.lineAt(range.end.line - 1).range.end.character
+                    )
+                ).length
+                if (selectionSizeWithoutLastLine > charLimit) {
+                    range = new Range(
+                        range.start.line,
+                        range.start.character,
+                        range.end.line - 1,
+                        document.lineAt(range.end.line - 1).range.end.character
+                    )
+                } else {
+                    range = new Range(
+                        range.start.line,
+                        range.start.character,
+                        range.end.line,
+                        charLimit - selectionSizeWithoutLastLine - 1
+                    )
+                    return range
+                }
+            }
+        }
+
+        return range
+    }
+
+    private getRangeText(document: TextDocument, range: Range): string {
+        return document.getText(range)
+    }
+
+    private prepareFqns(names: any): [FullyQualifiedName[], boolean] {
+        if (names === undefined || !names.fullyQualified) {
+            return [[], false]
+        }
+        const dedupedUsedFullyQualifiedNames: Map = new Map(
+            names.fullyQualified.usedSymbols.map((name: any) => [
+                JSON.stringify([name.source, name.symbol]),
+                { source: name.source, symbol: name.symbol },
+            ])
+        )
+        const usedFullyQualifiedNames = Array.from(dedupedUsedFullyQualifiedNames.values())
+
+        const maxUsedFullyQualifiedNamesLength = 25
+
+        if (usedFullyQualifiedNames.length > maxUsedFullyQualifiedNamesLength) {
+            const usedFullyQualifiedNamesSorted = usedFullyQualifiedNames.sort(
+                (name, other) => name.source.length + name.symbol.length - (other.source.length + other.symbol.length)
+            )
+            return [usedFullyQualifiedNamesSorted.slice(0, maxUsedFullyQualifiedNamesLength), true]
+        }
+
+        return [usedFullyQualifiedNames, false]
+    }
+
+    private prepareSimpleNames(names: any): [string[], boolean] {
+        if (names === undefined || !names.simple) {
+            return [[], false]
+        }
+        let simpleNames: string[] = names.simple.usedSymbols
+            .concat(names.simple.declaredSymbols)
+            .filter(function (elem: any) {
+                const trimmedElem = elem.symbol.trim()
+                return trimmedElem.length < 129 && trimmedElem.length > 1
+            })
+            .map(function (elem: any) {
+                return elem.symbol.trim()
+            })
+
+        const maxSimpleNamesLength = 100
+
+        let listWasLongerThanMaxLenght = false
+
+        if (simpleNames.length > maxSimpleNamesLength) {
+            listWasLongerThanMaxLenght = true
+
+            simpleNames = [...new Set(simpleNames)]
+
+            if (simpleNames.length > maxSimpleNamesLength) {
+                simpleNames = simpleNames.sort((a, b) => a.length - b.length)
+                simpleNames.splice(0, simpleNames.length - maxSimpleNamesLength)
+            }
+        }
+
+        return [simpleNames, listWasLongerThanMaxLenght]
+    }
+}
diff --git a/packages/core/src/codewhispererChat/editor/context/focusArea/model.ts b/packages/core/src/codewhispererChat/editor/context/focusArea/model.ts
new file mode 100644
index 00000000000..482c35d00b8
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/context/focusArea/model.ts
@@ -0,0 +1,25 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Selection } from 'vscode'
+
+export interface CodeNames {
+    simpleNames: string[]
+    fullyQualifiedNames: {
+        used: FullyQualifiedName[]
+    }
+}
+
+export interface FullyQualifiedName {
+    readonly source: string[]
+    readonly symbol: string[]
+}
+
+export interface FocusAreaContext {
+    readonly codeBlock: string
+    readonly extendedCodeBlock: string
+    readonly selectionInsideExtendedCodeBlock: Selection | undefined
+    readonly names: CodeNames | undefined
+}
diff --git a/packages/core/src/codewhispererChat/editor/context/model.ts b/packages/core/src/codewhispererChat/editor/context/model.ts
new file mode 100644
index 00000000000..30e0511c387
--- /dev/null
+++ b/packages/core/src/codewhispererChat/editor/context/model.ts
@@ -0,0 +1,12 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { FocusAreaContext } from './focusArea/model'
+import { FileContext } from './file/model'
+
+export interface EditorContext {
+    readonly activeFileContext: FileContext | undefined
+    readonly focusAreaContext: FocusAreaContext | undefined
+}
diff --git a/packages/core/src/codewhispererChat/index.ts b/packages/core/src/codewhispererChat/index.ts
new file mode 100644
index 00000000000..b47115fbc4a
--- /dev/null
+++ b/packages/core/src/codewhispererChat/index.ts
@@ -0,0 +1,17 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { FocusAreaContextExtractor } from './editor/context/focusArea/focusAreaExtractor'
+export { TryChatCodeLensProvider, resolveModifierKey, tryChatCodeLensCommand } from './editor/codelens'
+export { focusAmazonQPanel } from './commands/registerCommands'
+export { ChatSession } from './clients/chat/v0/chat'
+export { triggerPayloadToChatRequest, filePathSizeLimit } from './controllers/chat/chatRequest/converter'
+export { ChatTriggerType, PromptMessage, TriggerPayload } from './controllers/chat/model'
+export { UserIntentRecognizer } from './controllers/chat/userIntent/userIntentRecognizer'
+export { EditorContextExtractor } from './editor/context/extractor'
+export { ChatSessionStorage } from './storages/chatSession'
+export { TriggerEventsStorage } from './storages/triggerEvents'
+export { ReferenceLogController } from './view/messages/referenceLogController'
+export { extractLanguageNameFromFile } from './editor/context/file/languages'
diff --git a/packages/core/src/codewhispererChat/storages/chatSession.ts b/packages/core/src/codewhispererChat/storages/chatSession.ts
new file mode 100644
index 00000000000..378d84e4237
--- /dev/null
+++ b/packages/core/src/codewhispererChat/storages/chatSession.ts
@@ -0,0 +1,30 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ChatSession } from '../clients/chat/v0/chat'
+
+export class ChatSessionStorage {
+    private sessions: Map = new Map()
+
+    public getSession(tabID: string): ChatSession {
+        const sessionFromStorage = this.sessions.get(tabID)
+        if (sessionFromStorage !== undefined) {
+            return sessionFromStorage
+        }
+
+        const newSession = new ChatSession()
+        this.sessions.set(tabID, newSession)
+
+        return newSession
+    }
+
+    public deleteSession(tabID: string) {
+        this.sessions.delete(tabID)
+    }
+
+    public deleteAllSessions() {
+        this.sessions.clear()
+    }
+}
diff --git a/packages/core/src/codewhispererChat/storages/triggerEvents.ts b/packages/core/src/codewhispererChat/storages/triggerEvents.ts
new file mode 100644
index 00000000000..1bbf08b6de9
--- /dev/null
+++ b/packages/core/src/codewhispererChat/storages/triggerEvents.ts
@@ -0,0 +1,89 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { EditorContextCommand } from '../commands/registerCommands'
+import { EditorContext } from '../editor/context/model'
+
+export type TriggerEventType =
+    | 'chat_message'
+    | 'editor_context_command'
+    | 'follow_up'
+    | 'onboarding_page_interaction'
+    | 'quick_action'
+    | 'inline_chat'
+
+export interface TriggerEvent {
+    readonly id: string
+    tabID: string | undefined
+    readonly context: EditorContext | undefined
+    readonly message: string | undefined
+    readonly type: TriggerEventType
+    readonly command?: EditorContextCommand
+    readonly quickAction?: string
+}
+
+export class TriggerEventsStorage {
+    private triggerEvents: Map = new Map()
+    private triggerEventsByTabID: Map = new Map()
+
+    public removeTabEvents(tabID: string) {
+        const events = this.triggerEventsByTabID.get(tabID) ?? []
+        // eslint-disable-next-line unicorn/no-array-for-each
+        events.forEach((event: TriggerEvent) => {
+            this.triggerEvents.delete(event.id)
+        })
+
+        this.triggerEventsByTabID.delete(tabID)
+    }
+
+    public getLastTriggerEventByTabID(tabID: string): TriggerEvent | undefined {
+        const events = this.triggerEventsByTabID.get(tabID) ?? []
+
+        if (events.length === 0) {
+            return undefined
+        }
+
+        return events[events.length - 1]
+    }
+
+    private pushEventToTriggerEventsByTabID(event: TriggerEvent) {
+        if (event.tabID === undefined) {
+            return
+        }
+        const currentEventsList = this.triggerEventsByTabID.get(event.tabID) ?? []
+        currentEventsList.push(event)
+        this.triggerEventsByTabID.set(event.tabID, currentEventsList)
+    }
+
+    public addTriggerEvent(event: TriggerEvent) {
+        this.triggerEvents.set(event.id, event)
+        this.pushEventToTriggerEventsByTabID(event)
+    }
+
+    public removeTriggerEvent(id: string) {
+        this.triggerEvents.delete(id)
+    }
+
+    public updateTriggerEventTabIDFromUnknown(id: string, tabID: string) {
+        const currentTriggerEvent = this.triggerEvents.get(id)
+
+        if (currentTriggerEvent === undefined || currentTriggerEvent.tabID !== undefined) {
+            return
+        }
+
+        currentTriggerEvent.tabID = tabID
+
+        this.triggerEvents.set(id, currentTriggerEvent)
+        this.pushEventToTriggerEventsByTabID(currentTriggerEvent)
+    }
+
+    public getTriggerEvent(id: string): TriggerEvent | undefined {
+        return this.triggerEvents.get(id)
+    }
+
+    public getTriggerEventsByTabID(tabID: string): TriggerEvent[] {
+        return this.triggerEventsByTabID.get(tabID) ?? []
+    }
+}
diff --git a/packages/core/src/codewhispererChat/view/connector/connector.ts b/packages/core/src/codewhispererChat/view/connector/connector.ts
new file mode 100644
index 00000000000..6632819688a
--- /dev/null
+++ b/packages/core/src/codewhispererChat/view/connector/connector.ts
@@ -0,0 +1,463 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { MessagePublisher } from '../../../amazonq/messages/messagePublisher'
+import { EditorContextCommandType } from '../../commands/registerCommands'
+import { AuthFollowUpType } from '../../../amazonq/auth/model'
+import {
+    ChatItem,
+    ChatItemButton,
+    ChatItemFormItem,
+    DetailedList,
+    DetailedListItem,
+    MynahUIDataModel,
+    QuickActionCommand,
+} from '@aws/mynah-ui'
+import { DocumentReference } from '../../controllers/chat/model'
+import { TabType } from '../../../amazonq/webview/ui/storages/tabsStorage'
+
+class UiMessage {
+    readonly time: number = Date.now()
+    readonly sender: string = 'CWChat'
+    readonly type: string = ''
+
+    public constructor(public tabID: string | undefined) {}
+}
+
+export class ErrorMessage extends UiMessage {
+    readonly title!: string
+    readonly message!: string
+    override type = 'errorMessage'
+
+    constructor(title: string, message: string, tabID: string) {
+        super(tabID)
+        this.title = title
+        this.message = message
+    }
+}
+
+interface SearchSuggestionCommonProps {
+    readonly title: string
+    readonly url: string
+    readonly body: string
+    readonly id: number
+}
+
+class SearchSuggestionCommon {
+    readonly title!: string
+    readonly url!: string
+    readonly body!: string
+    readonly id!: number
+
+    constructor(props: SearchSuggestionCommonProps) {
+        this.title = props.title
+        this.url = props.url
+        this.body = props.body
+        this.id = props.id
+    }
+}
+
+export class APIDocsSuggestion extends SearchSuggestionCommon {
+    readonly metadata!: APIDocsSuggestionMetadata
+}
+
+interface APIDocsSuggestionMetadata {
+    readonly canonicalExample: CodeExample
+}
+
+interface CodeExample {
+    readonly url: string
+    readonly body: string
+}
+
+export interface SuggestionProps extends SearchSuggestionCommonProps {
+    readonly metadata?: SuggestionMetadata
+    readonly context: string[]
+}
+
+export class Suggestion extends SearchSuggestionCommon {
+    readonly metadata?: SuggestionMetadata
+    readonly context: string[]
+
+    constructor(props: SuggestionProps) {
+        super(props)
+        this.metadata = props.metadata
+        this.context = props.context
+    }
+}
+
+interface SuggestionMetadata {
+    readonly stackOverflow: StackOverflowMetadata
+}
+
+interface StackOverflowMetadata {
+    readonly answerCount: number
+    readonly isAccepted: boolean
+    readonly score: number
+    readonly lastActivityDate: Date
+}
+
+export class SearchView extends UiMessage {
+    readonly suggestions: Suggestion[] | undefined
+    readonly apiDocsSuggestions: APIDocsSuggestion[] | undefined
+    readonly enableAPIDocsTab: boolean = false
+    override type = 'drawNewSearchViewState'
+}
+
+export type ChatMessageType = 'answer-stream' | 'answer-part' | 'answer'
+
+export interface CodeReference {
+    licenseName?: string
+    repository?: string
+    url?: string
+    recommendationContentSpan?: {
+        start?: number
+        end?: number
+    }
+}
+
+export interface AuthNeededExceptionProps {
+    readonly message: string
+    readonly authType: AuthFollowUpType
+    readonly triggerID: string
+}
+
+export class AuthNeededException extends UiMessage {
+    readonly message: string
+    readonly authType: AuthFollowUpType
+    readonly triggerID: string
+    override type = 'authNeededException'
+
+    constructor(props: AuthNeededExceptionProps, tabID: string) {
+        super(tabID)
+        this.message = props.message
+        this.triggerID = props.triggerID
+        this.authType = props.authType
+    }
+}
+
+export class OpenSettingsMessage extends UiMessage {
+    override type = 'openSettingsMessage'
+}
+
+export class RestoreTabMessage extends UiMessage {
+    override type = 'restoreTabMessage'
+    readonly tabType: TabType
+    readonly chats: ChatItem[]
+    readonly historyId: string
+    readonly exportTab?: boolean
+
+    constructor(historyId: string, tabType: TabType, chats: ChatItem[], exportTab?: boolean) {
+        super(undefined)
+        this.chats = chats
+        this.tabType = tabType
+        this.historyId = historyId
+        this.exportTab = exportTab
+    }
+}
+
+export class OpenDetailedListMessage extends UiMessage {
+    override type = 'openDetailedListMessage'
+    readonly detailedList: DetailedList
+    listType: string
+
+    constructor(tabID: string, listType: string, data: DetailedList) {
+        super(tabID)
+        this.listType = listType
+        this.detailedList = data
+    }
+}
+
+export class UpdateDetailedListMessage extends UiMessage {
+    override type = 'updateDetailedListMessage'
+    readonly detailedList: DetailedList
+    listType: string
+
+    constructor(listType: string, data: DetailedList) {
+        super(undefined)
+        this.listType = listType
+        this.detailedList = data
+    }
+}
+
+export class CloseDetailedListMessage extends UiMessage {
+    override type = 'closeDetailedListMessage'
+    listType: string
+
+    constructor(listType: string) {
+        super(undefined)
+        this.listType = listType
+    }
+}
+
+export class ExportChatMessage extends UiMessage {
+    override type = 'exportChatMessage'
+    readonly format: 'markdown' | 'html'
+    readonly uri: string
+
+    constructor(tabID: string, format: 'markdown' | 'html', uri: string) {
+        super(tabID)
+        this.format = format
+        this.uri = uri
+    }
+}
+
+export class SelectTabMessage extends UiMessage {
+    override type = 'selectTabMessage'
+    readonly eventID?: string
+
+    constructor(tabID: string, eventID?: string) {
+        super(tabID)
+        this.eventID = eventID
+    }
+}
+
+export class DetailedListFilterChangeMessage extends UiMessage {
+    override type = 'detailedListFilterChangeMessage'
+    readonly listType: string
+    readonly filterValues: Record
+    readonly isValid: boolean
+
+    constructor(listType: string, filterValues: Record, isValid: boolean) {
+        super(undefined)
+        this.listType = listType
+        this.filterValues = filterValues
+        this.isValid = isValid
+    }
+}
+
+export class DetailedListItemSelectMessage extends UiMessage {
+    override type = 'detailedListItemSelectMessage'
+    readonly listType: string
+    readonly item: DetailedListItem
+
+    constructor(listType: string, item: DetailedListItem) {
+        super(undefined)
+        this.listType = listType
+        this.item = item
+    }
+}
+
+export class DetailedListActionClickMessage extends UiMessage {
+    override type = 'detailedListActionClickMessage'
+    readonly listType: string
+    action: ChatItemButton
+
+    constructor(listType: string, action: ChatItemButton) {
+        super(undefined)
+        this.listType = listType
+        this.action = action
+    }
+}
+
+export class ContextCommandData extends UiMessage {
+    readonly data: MynahUIDataModel['contextCommands']
+    override type = 'contextCommandData'
+    constructor(data: MynahUIDataModel['contextCommands']) {
+        super('tab-1')
+        this.data = data
+    }
+}
+
+export class CustomFormActionMessage extends UiMessage {
+    override type = 'customFormActionMessage'
+    readonly action: {
+        id: string
+        text?: string | undefined
+        formItemValues?: Record | undefined
+    }
+
+    constructor(
+        tabID: string,
+        action: {
+            id: string
+            text?: string | undefined
+            formItemValues?: Record | undefined
+        }
+    ) {
+        super(tabID)
+        this.action = action
+    }
+}
+
+export class ShowCustomFormMessage extends UiMessage {
+    override type = 'showCustomFormMessage'
+    readonly formItems?: ChatItemFormItem[]
+    readonly buttons?: ChatItemButton[]
+    readonly title?: string
+    readonly description?: string
+
+    constructor(
+        tabID: string,
+        formItems?: ChatItemFormItem[],
+        buttons?: ChatItemButton[],
+        title?: string,
+        description?: string
+    ) {
+        super(tabID)
+        this.formItems = formItems
+        this.buttons = buttons
+        this.title = title
+        this.description = description
+    }
+}
+
+export class ContextSelectedMessage extends UiMessage {
+    override type = 'contextSelectedMessage'
+    readonly contextItem: QuickActionCommand
+
+    constructor(tabID: string, contextItem: QuickActionCommand) {
+        super(tabID)
+        this.contextItem = contextItem
+    }
+}
+
+export interface ChatMessageProps {
+    readonly message: string | undefined
+    readonly messageType: ChatMessageType
+    readonly followUps: FollowUp[] | undefined
+    readonly followUpsHeader: string | undefined
+    readonly relatedSuggestions: Suggestion[] | undefined
+    readonly codeReference?: CodeReference[]
+    readonly triggerID: string
+    readonly messageID: string
+    readonly userIntent: string | undefined
+    readonly codeBlockLanguage: string | undefined
+    readonly contextList: DocumentReference[] | undefined
+}
+
+export class ChatMessage extends UiMessage {
+    readonly message: string | undefined
+    readonly messageType: ChatMessageType
+    readonly followUps: FollowUp[] | undefined
+    readonly codeReference: CodeReference[] | undefined
+    readonly relatedSuggestions: Suggestion[] | undefined
+    readonly searchResults: Suggestion[] | undefined
+    readonly followUpsHeader: string | undefined
+    readonly triggerID: string
+    readonly messageID: string | undefined
+    readonly userIntent: string | undefined
+    readonly codeBlockLanguage: string | undefined
+    readonly contextList: DocumentReference[] | undefined
+    override type = 'chatMessage'
+
+    constructor(props: ChatMessageProps, tabID: string) {
+        super(tabID)
+        this.message = props.message
+        this.messageType = props.messageType
+        this.followUps = props.followUps
+        this.followUpsHeader = props.followUpsHeader
+        this.relatedSuggestions = props.relatedSuggestions
+        this.codeReference = props.codeReference
+        this.triggerID = props.triggerID
+        this.messageID = props.messageID
+        this.userIntent = props.userIntent
+        this.codeBlockLanguage = props.codeBlockLanguage
+        this.contextList = props.contextList
+    }
+}
+
+export interface FollowUp {
+    readonly type: string
+    readonly pillText: string
+    readonly prompt: string
+}
+
+export interface EditorContextCommandMessageProps {
+    readonly message: string
+    readonly triggerID: string
+    readonly command?: EditorContextCommandType
+}
+
+export class EditorContextCommandMessage extends UiMessage {
+    readonly message: string
+    readonly triggerID: string
+    readonly command?: EditorContextCommandType
+    override type = 'editorContextCommandMessage'
+
+    constructor(props: EditorContextCommandMessageProps) {
+        super(undefined)
+        this.message = props.message
+        this.triggerID = props.triggerID
+        this.command = props.command
+    }
+}
+
+export interface QuickActionMessageProps {
+    readonly message: string
+    readonly triggerID: string
+}
+
+export class QuickActionMessage extends UiMessage {
+    readonly message: string
+    readonly triggerID: string
+    override type = 'editorContextCommandMessage'
+
+    constructor(props: QuickActionMessageProps) {
+        super(undefined)
+        this.message = props.message
+        this.triggerID = props.triggerID
+    }
+}
+
+export class AppToWebViewMessageDispatcher {
+    constructor(private readonly appsToWebViewMessagePublisher: MessagePublisher) {}
+
+    public sendErrorMessage(message: ErrorMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendChatMessage(message: ChatMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendEditorContextCommandMessage(message: EditorContextCommandMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendQuickActionMessage(message: QuickActionMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendAuthNeededExceptionMessage(message: AuthNeededException) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendOpenSettingsMessage(message: OpenSettingsMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendRestoreTabMessage(message: RestoreTabMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendOpenDetailedListMessage(message: OpenDetailedListMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendUpdateDetailedListMessage(message: UpdateDetailedListMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendCloseDetailedListMessage(message: CloseDetailedListMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendSerializeTabMessage(message: ExportChatMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendSelectTabMessage(message: SelectTabMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendContextCommandData(message: ContextCommandData) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+
+    public sendShowCustomFormMessage(message: ShowCustomFormMessage) {
+        this.appsToWebViewMessagePublisher.publish(message)
+    }
+}
diff --git a/packages/core/src/codewhispererChat/view/messages/messageListener.ts b/packages/core/src/codewhispererChat/view/messages/messageListener.ts
new file mode 100644
index 00000000000..8ad59acb0a7
--- /dev/null
+++ b/packages/core/src/codewhispererChat/view/messages/messageListener.ts
@@ -0,0 +1,359 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import { MessageListener } from '../../../amazonq/messages/messageListener'
+import { ExtensionMessage } from '../../../amazonq/webview/ui/commands'
+import { AuthController } from '../../../amazonq/auth/controller'
+import { ChatControllerMessagePublishers } from '../../controllers/chat/controller'
+import { ReferenceLogController } from './referenceLogController'
+import { getLogger } from '../../../shared/logger/logger'
+import { openSettingsId } from '../../../shared/settings'
+import { Database } from '../../../shared/db/chatDb/chatDb'
+
+export interface UIMessageListenerProps {
+    readonly chatControllerMessagePublishers: ChatControllerMessagePublishers
+    readonly webViewMessageListener: MessageListener
+}
+
+export class UIMessageListener {
+    private chatControllerMessagePublishers: ChatControllerMessagePublishers
+    private webViewMessageListener: MessageListener
+    private referenceLogController: ReferenceLogController
+    private authController: AuthController
+
+    constructor(props: UIMessageListenerProps) {
+        this.chatControllerMessagePublishers = props.chatControllerMessagePublishers
+        this.webViewMessageListener = props.webViewMessageListener
+        this.referenceLogController = new ReferenceLogController()
+        this.authController = new AuthController()
+
+        this.webViewMessageListener.onMessage((msg) => {
+            this.handleMessage(msg)
+        })
+    }
+
+    private handleMessage(msg: ExtensionMessage) {
+        switch (msg.command) {
+            case 'help':
+            case 'clear':
+            case 'transform':
+            case 'chat-prompt':
+                this.processChatMessage(msg)
+                break
+            case 'new-tab-was-created':
+                this.processNewTabWasCreated(msg)
+                break
+            case 'tab-was-removed':
+                this.processTabWasRemoved(msg)
+                break
+            case 'tab-was-changed':
+                this.processTabWasChanged(msg)
+                break
+            case 'auth-follow-up-was-clicked':
+                this.processAuthFollowUpWasClicked(msg)
+                break
+            case 'follow-up-was-clicked':
+                if (msg.followUp?.prompt !== undefined) {
+                    this.processChatMessage({
+                        chatMessage: msg.followUp.prompt,
+                        tabID: msg.tabID,
+                        command: msg.command,
+                        messageId: msg.messageId,
+                        userIntent: msg.followUp.type,
+                    })
+                }
+                break
+            case 'accept_diff':
+                this.processAcceptDiff(msg)
+                break
+            case 'view_diff':
+                this.processViewDiff(msg)
+                break
+            case 'code_was_copied_to_clipboard':
+                this.processCodeWasCopiedToClipboard(msg)
+                break
+            case 'insert_code_at_cursor_position':
+                this.processInsertCodeAtCursorPosition(msg)
+                break
+            case 'trigger-tabID-received':
+                this.processTriggerTabIDReceived(msg)
+                break
+            case 'stop-response':
+                this.stopResponse(msg)
+                break
+            case 'chat-item-voted':
+                this.chatItemVoted(msg)
+                break
+            case 'chat-item-feedback':
+                this.chatItemFeedback(msg).catch((e) => {
+                    getLogger().error('chatItemFeedback failed: %s', (e as Error).message)
+                })
+                break
+            case 'ui-focus':
+                this.processUIFocus(msg)
+                break
+            case 'source-link-click':
+                this.processSourceLinkClick(msg)
+                break
+            case 'response-body-link-click':
+                this.processResponseBodyLinkClick(msg)
+                break
+            case 'footer-info-link-click':
+                this.processFooterInfoLinkClick(msg)
+                break
+            case 'open-settings':
+                this.processOpenSettings(msg)
+                break
+            case 'ui-is-ready':
+                this.processUIIsReady()
+                break
+            case 'quick-command-group-action-click':
+                this.quickCommandGroupActionClicked(msg)
+                break
+            case 'form-action-click':
+                this.processCustomFormAction(msg)
+                break
+            case 'context-selected':
+                this.processContextSelected(msg)
+                break
+            case 'file-click':
+                this.fileClick(msg)
+                break
+            case 'tab-restored':
+                this.tabRestored(msg)
+                break
+            case 'tab-bar-button-clicked':
+                this.tabBarButtonClicked(msg)
+                break
+            case 'save-chat':
+                this.saveChat(msg)
+                break
+            case 'detailed-list-filter-change':
+                this.processDetailedListFilterChange(msg)
+                break
+            case 'detailed-list-item-select':
+                this.processDetailedListItemSelect(msg)
+                break
+            case 'detailed-list-action-click':
+                this.processDetailedListActionClick(msg)
+                break
+        }
+    }
+
+    private processDetailedListFilterChange(msg: any) {
+        this.chatControllerMessagePublishers.processDetailedListFilterChangeMessage.publish(msg)
+    }
+    private processDetailedListItemSelect(msg: any) {
+        this.chatControllerMessagePublishers.processDetailedListItemSelectMessage.publish(msg)
+    }
+    private processDetailedListActionClick(msg: any) {
+        this.chatControllerMessagePublishers.processDetailedListActionClickMessage.publish(msg)
+    }
+
+    private tabRestored(msg: any) {
+        const chatHistoryDb = Database.getInstance()
+        chatHistoryDb.setHistoryIdMapping(msg.newTabId, msg.historyId)
+        if (msg.exportTab) {
+            this.chatControllerMessagePublishers.processTabBarButtonClick.publish({
+                tabID: msg.newTabId,
+                buttonId: 'export_chat',
+            })
+        }
+        chatHistoryDb.updateTabOpenState(msg.newTabId, true)
+    }
+
+    private saveChat(msg: any) {
+        this.chatControllerMessagePublishers.processSaveChat.publish({
+            uri: msg.uri,
+            serializedChat: msg.serializedChat,
+        })
+    }
+
+    private tabBarButtonClicked(msg: any) {
+        this.chatControllerMessagePublishers.processTabBarButtonClick.publish({
+            tabID: msg.tabID,
+            buttonId: msg.buttonId,
+        })
+    }
+
+    private processUIIsReady() {
+        this.chatControllerMessagePublishers.processContextCommandUpdateMessage.publish()
+    }
+
+    private processCustomFormAction(msg: any) {
+        this.chatControllerMessagePublishers.processCustomFormAction.publish({ tabID: msg.tabID, ...msg })
+    }
+
+    private processContextSelected(msg: any) {
+        this.chatControllerMessagePublishers.processContextSelected.publish({ tabID: msg.tabID, ...msg })
+    }
+
+    private quickCommandGroupActionClicked(msg: any) {
+        this.chatControllerMessagePublishers.processQuickCommandGroupActionClicked.publish({
+            tabID: msg.tabID,
+            actionId: msg.actionId,
+            command: 'quick-command-group-action-click',
+        })
+    }
+
+    private processOpenSettings(msg: any) {
+        void openSettingsId(`amazonQ.workspaceIndex`)
+    }
+
+    private processAuthFollowUpWasClicked(msg: any) {
+        this.authController.handleAuth(msg.authType)
+    }
+    private processFooterInfoLinkClick(msg: any) {
+        this.chatControllerMessagePublishers.processFooterInfoLinkClick.publish({
+            tabID: msg.tabID,
+            link: msg.link,
+            command: 'footer-info-link-click',
+        })
+    }
+    private processResponseBodyLinkClick(msg: any) {
+        this.chatControllerMessagePublishers.processResponseBodyLinkClick.publish({
+            command: msg.command,
+            messageId: msg.messageId,
+            tabID: msg.tabID,
+            link: msg.link,
+        })
+    }
+
+    private processSourceLinkClick(msg: any) {
+        this.chatControllerMessagePublishers.processSourceLinkClick.publish({
+            command: msg.command,
+            messageId: msg.messageId,
+            tabID: msg.tabID,
+            link: msg.link,
+        })
+    }
+
+    private processUIFocus(msg: any) {
+        this.chatControllerMessagePublishers.processUIFocusMessage.publish({
+            command: msg.command,
+            type: msg.type,
+        })
+    }
+
+    private processTriggerTabIDReceived(msg: any) {
+        this.chatControllerMessagePublishers.processTriggerTabIDReceived.publish({
+            tabID: msg.tabID,
+            triggerID: msg.triggerID,
+        })
+    }
+
+    private processInsertCodeAtCursorPosition(msg: any) {
+        this.referenceLogController.addReferenceLog(msg.codeReference, (msg.code as string) ?? '')
+        this.chatControllerMessagePublishers.processInsertCodeAtCursorPosition.publish({
+            command: msg.command,
+            tabID: msg.tabID,
+            messageId: msg.messageId,
+            userIntent: msg.userIntent,
+            code: msg.code,
+            insertionTargetType: msg.insertionTargetType,
+            codeReference: msg.codeReference,
+            eventId: msg.eventId,
+            codeBlockIndex: msg.codeBlockIndex,
+            totalCodeBlocks: msg.totalCodeBlocks,
+            codeBlockLanguage: msg.codeBlockLanguage,
+        })
+    }
+
+    private processAcceptDiff(msg: any) {
+        this.chatControllerMessagePublishers.processAcceptDiff.publish({
+            command: msg.command,
+            tabID: msg.tabID || msg.tabId,
+            ...msg,
+        })
+    }
+
+    private processViewDiff(msg: any) {
+        this.chatControllerMessagePublishers.processViewDiff.publish({
+            command: msg.command,
+            tabID: msg.tabID || msg.tabId,
+            ...msg,
+        })
+    }
+
+    private processCodeWasCopiedToClipboard(msg: any) {
+        this.chatControllerMessagePublishers.processCopyCodeToClipboard.publish({
+            command: msg.command,
+            tabID: msg.tabID,
+            messageId: msg.messageId,
+            userIntent: msg.userIntent,
+            code: msg.code,
+            insertionTargetType: msg.insertionTargetType,
+            codeReference: msg.codeReference,
+            eventId: msg.eventId,
+            codeBlockIndex: msg.codeBlockIndex,
+            totalCodeBlocks: msg.totalCodeBlocks,
+            codeBlockLanguage: msg.codeBlockLanguage,
+        })
+    }
+
+    private processTabWasRemoved(msg: any) {
+        this.chatControllerMessagePublishers.processTabClosedMessage.publish({
+            tabID: msg.tabID,
+        })
+    }
+
+    private processNewTabWasCreated(msg: any) {
+        this.chatControllerMessagePublishers.processTabCreatedMessage.publish({
+            tabID: msg.tabID,
+            tabOpenInteractionType: msg.tabOpenInteractionType,
+        })
+    }
+
+    private processTabWasChanged(msg: any) {
+        this.chatControllerMessagePublishers.processTabChangedMessage.publish({
+            tabID: msg.tabID,
+            prevTabID: msg.prevTabID,
+        })
+    }
+
+    private processChatMessage(msg: any) {
+        this.chatControllerMessagePublishers.processPromptChatMessage.publish({
+            message: msg.chatMessage,
+            command: msg.command,
+            tabID: msg.tabID,
+            messageId: msg.messageId,
+            userIntent: msg.userIntent !== '' ? msg.userIntent : undefined,
+            context: msg.chatContext,
+        })
+    }
+
+    private stopResponse(msg: any) {
+        this.chatControllerMessagePublishers.processStopResponseMessage.publish({
+            tabID: msg.tabID,
+        })
+    }
+
+    private chatItemVoted(msg: any) {
+        this.chatControllerMessagePublishers.processChatItemVotedMessage.publish({
+            tabID: msg.tabID,
+            command: msg.command,
+            vote: msg.vote,
+            messageId: msg.messageId,
+        })
+    }
+
+    private async chatItemFeedback(msg: any) {
+        this.chatControllerMessagePublishers.processChatItemFeedbackMessage.publish({
+            messageId: msg.messageId,
+            tabID: msg.tabID,
+            command: msg.command,
+            selectedOption: msg.selectedOption,
+            comment: msg.comment,
+        })
+    }
+
+    private fileClick(msg: any) {
+        this.chatControllerMessagePublishers.processFileClick.publish({
+            messageId: msg.messageId,
+            tabID: msg.tabID,
+            command: msg.command,
+            filePath: msg.filePath,
+        })
+    }
+}
diff --git a/packages/core/src/codewhispererChat/view/messages/referenceLogController.ts b/packages/core/src/codewhispererChat/view/messages/referenceLogController.ts
new file mode 100644
index 00000000000..c48ca188053
--- /dev/null
+++ b/packages/core/src/codewhispererChat/view/messages/referenceLogController.ts
@@ -0,0 +1,18 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { window } from 'vscode'
+import { CodeReference } from '../connector/connector'
+import { ReferenceLogViewProvider } from '../../../codewhisperer/service/referenceLogViewProvider'
+
+export class ReferenceLogController {
+    public addReferenceLog(codeReference: CodeReference[] | undefined, code: string) {
+        const editor = window.activeTextEditor
+        if (codeReference !== undefined && editor !== undefined) {
+            const referenceLog = ReferenceLogViewProvider.getReferenceLog(code, codeReference, editor)
+            ReferenceLogViewProvider.instance.addReferenceLog(referenceLog)
+        }
+    }
+}
diff --git a/packages/core/src/commands.ts b/packages/core/src/commands.ts
new file mode 100644
index 00000000000..f69a8dd173c
--- /dev/null
+++ b/packages/core/src/commands.ts
@@ -0,0 +1,192 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * AWS Toolkit extension commands and implementations.
+ * TODO: We should drop this into packages/toolkit, but some of the commands are required for running tests.
+ * Tests in the core lib cannot yet work with extension activation from packages/toolkit, so the core lib is
+ * activated from ./extension.ts instead.
+ * A pre-req for moving this is also moving the Toolkit related tests to packages/toolkit.
+ */
+
+import * as vscode from 'vscode'
+import globals, { isWeb } from './shared/extensionGlobals'
+
+import * as nls from 'vscode-nls'
+const localize = nls.loadMessageBundle()
+
+import { Auth } from './auth/auth'
+import { TreeNode } from './shared/treeview/resourceTreeDataProvider'
+import { getResourceFromTreeNode } from './shared/treeview/utils'
+import { Connection, createSsoProfile } from './auth/connection'
+import {
+    createIamItem,
+    createSsoItem,
+    createBuilderIdItem,
+    createStartUrlPrompter,
+    showRegionPrompter,
+    createBuilderIdConnection,
+    signout,
+    promptAndUseConnection,
+} from './auth/utils'
+import { showCodeWhispererConnectionPrompt } from './codewhisperer/util/showSsoPrompt'
+import { CommonAuthWebview } from './login/webview/vue/backend'
+import { AuthSource, AuthSources } from './login/webview/util'
+import { ServiceItemId, isServiceItemId } from './login/webview/vue/types'
+import { authHelpUrl } from './shared/constants'
+import { getIdeProperties } from './shared/extensionUtilities'
+import { telemetry } from './shared/telemetry/telemetry'
+import { createCommonButtons } from './shared/ui/buttons'
+import { showQuickPick } from './shared/ui/pickerPrompter'
+import { Instance } from './shared/utilities/typeConstructors'
+import { openUrl } from './shared/utilities/vsCodeUtils'
+import { Commands, VsCodeCommandArg, placeholder, vscodeComponent } from './shared/vscode/commands2'
+import { isValidResponse } from './shared/wizards/wizard'
+import { CancellationError } from './shared/utilities/timeoutUtils'
+import { ToolkitError } from './shared/errors'
+import { getContext, setContext } from './shared/vscode/setContext'
+
+function switchConnections(auth: Auth | TreeNode | unknown) {
+    if (!(auth instanceof Auth)) {
+        try {
+            auth = getResourceFromTreeNode(auth, Instance(Auth))
+        } catch {
+            // Fall back in case this command is called from something in package.json.
+            // If so, then the value of auth will be unusable.
+            auth = Auth.instance
+        }
+    }
+
+    return promptAndUseConnection(auth as Auth)
+}
+
+export function registerCommands(context: vscode.ExtensionContext) {
+    const addConnection = Commands.register(
+        { id: 'aws.toolkit.auth.addConnection', telemetryThrottleMs: false },
+        async () => {
+            const items = [createBuilderIdItem(), createSsoItem(), createIamItem()]
+
+            const resp = await showQuickPick(items, {
+                title: localize('aws.auth.addConnection.title', 'Add a Connection to {0}', getIdeProperties().company),
+                placeholder: localize('aws.auth.addConnection.placeholder', 'Select a connection option'),
+                buttons: createCommonButtons() as vscode.QuickInputButton[],
+            })
+            if (!isValidResponse(resp)) {
+                telemetry.ui_click.emit({ elementId: 'connection_optionescapecancel' })
+                throw new CancellationError('user')
+            }
+
+            switch (resp) {
+                case 'iam':
+                    return await globals.awsContextCommands.onCommandCreateCredentialsProfile()
+                case 'sso': {
+                    const startUrlPrompter = await createStartUrlPrompter('IAM Identity Center')
+                    const startUrl = await startUrlPrompter.prompt()
+                    if (!isValidResponse(startUrl)) {
+                        throw new CancellationError('user')
+                    }
+                    telemetry.ui_click.emit({ elementId: 'connection_startUrl' })
+
+                    const region = await showRegionPrompter()
+
+                    const conn = await Auth.instance.createConnection(createSsoProfile(startUrl, region.id))
+                    return Auth.instance.useConnection(conn)
+                }
+                case 'builderId': {
+                    return createBuilderIdConnection(Auth.instance)
+                }
+            }
+        }
+    )
+
+    const manageConnections = Commands.register(
+        { id: 'aws.toolkit.auth.manageConnections', compositeKey: { 1: 'source' } },
+        async (_: VsCodeCommandArg, source: AuthSource, serviceToShow?: ServiceItemId, blocking?: boolean) => {
+            if (_ !== placeholder) {
+                source = AuthSources.vscodeComponent
+            }
+
+            if (isWeb()) {
+                // TODO: CW no longer exists in toolkit. This should be moved to Amazon Q
+                if (source.toLowerCase().includes('codewhisperer')) {
+                    // Show CW specific quick pick for CW connections
+                    return showCodeWhispererConnectionPrompt()
+                }
+                return addConnection.execute()
+            }
+
+            if (!isServiceItemId(serviceToShow)) {
+                serviceToShow = undefined
+            }
+
+            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
+        }
+    )
+
+    context.subscriptions.push(
+        addConnection,
+        manageConnections,
+        Commands.register('aws.toolkit.auth.help', async () => {
+            await openUrl(vscode.Uri.parse(authHelpUrl))
+            telemetry.aws_help.emit()
+        }),
+        Commands.register('aws.toolkit.auth.switchConnections', (auth: Auth | TreeNode | unknown) => {
+            telemetry.ui_click.emit({ elementId: 'devtools_connectToAws' })
+            return switchConnections(auth)
+        }),
+        Commands.register('_aws.toolkit.auth.useIamCredentials', (auth: Auth) => {
+            telemetry.ui_click.emit({ elementId: 'explorer_IAMselect_VSCode' })
+            return promptAndUseConnection(auth, 'iam')
+        }),
+        Commands.register('aws.toolkit.credentials.edit', () => globals.awsContextCommands.onCommandEditCredentials()),
+        Commands.register('aws.toolkit.credentials.profile.create', async () => {
+            try {
+                await globals.awsContextCommands.onCommandCreateCredentialsProfile()
+            } finally {
+                telemetry.aws_createCredentials.emit()
+            }
+        }),
+        Commands.register('aws.toolkit.login', async () => {
+            const connections = await Auth.instance.listConnections()
+            if (connections.length === 0) {
+                const source: AuthSource = vscodeComponent
+                return manageConnections.execute(placeholder, source)
+            } else {
+                return switchConnections(Auth.instance)
+            }
+        }),
+        Commands.register('aws.toolkit.auth.signout', async () => {
+            telemetry.ui_click.emit({ elementId: 'devtools_signout' })
+            await signout(Auth.instance)
+        }),
+        Commands.register('_aws.toolkit.auth.autoConnect', Auth.instance.tryAutoConnect),
+        Commands.register('_aws.toolkit.auth.reauthenticate', async (auth: Auth, conn: Connection) => {
+            try {
+                return await auth.reauthenticate(conn)
+            } catch (err) {
+                throw ToolkitError.chain(err, 'Unable to authenticate connection')
+            }
+        })
+    )
+}
diff --git a/packages/core/src/dev/activation.ts b/packages/core/src/dev/activation.ts
new file mode 100644
index 00000000000..16b5d7e53ad
--- /dev/null
+++ b/packages/core/src/dev/activation.ts
@@ -0,0 +1,585 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { createCommonButtons } from '../shared/ui/buttons'
+import { createQuickPick } from '../shared/ui/pickerPrompter'
+import { SkipPrompter } from '../shared/ui/common/skipPrompter'
+import { DevSettings } from '../shared/settings'
+import { FileProvider, VirtualFileSystem } from '../shared/virtualFilesystem'
+import { Commands } from '../shared/vscode/commands2'
+import { createInputBox } from '../shared/ui/inputPrompter'
+import { Wizard } from '../shared/wizards/wizard'
+import { deleteDevEnvCommand, installVsixCommand, openTerminalCommand } from './codecatalyst'
+import { isAnySsoConnection } from '../auth/connection'
+import { Auth } from '../auth/auth'
+import { getLogger } from '../shared/logger/logger'
+import { entries } from '../shared/utilities/tsUtils'
+import { getEnvironmentSpecificMemento } from '../shared/utilities/mementos'
+import { setContext } from '../shared/vscode/setContext'
+import { telemetry } from '../shared/telemetry/telemetry'
+import { getSessionId } from '../shared/telemetry/util'
+import { NotificationsController } from '../notifications/controller'
+import { DevNotificationsState } from '../notifications/types'
+import { QuickPickItem } from 'vscode'
+import { ChildProcess } from '../shared/utilities/processUtils'
+
+interface MenuOption {
+    readonly label: string
+    readonly description?: string
+    readonly detail?: string
+    readonly executor: (ctx: vscode.ExtensionContext) => Promise | unknown
+}
+
+export type DevFunction =
+    | 'installVsix'
+    | 'openTerminal'
+    | 'deleteDevEnv'
+    | 'editStorage'
+    | 'resetState'
+    | 'showEnvVars'
+    | 'deleteSsoConnections'
+    | 'expireSsoConnections'
+    | 'editAuthConnections'
+    | 'notificationsSend'
+    | 'forceIdeCrash'
+    | 'startChildProcess'
+
+export type DevOptions = {
+    context: vscode.ExtensionContext
+    auth: () => Auth
+    notificationsController: () => NotificationsController
+    menuOptions?: DevFunction[]
+}
+
+let targetContext: vscode.ExtensionContext
+let globalState: vscode.Memento
+let targetAuth: Auth
+let targetNotificationsController: NotificationsController
+
+/**
+ * Defines AWS Toolkit developer tools.
+ *
+ * Options are displayed as quick-pick items. The {@link MenuOption.executor} callback is ran
+ * on selection. There is no support for name-spacing. Just add the relevant
+ * feature/module as a description so it can be moved around easier.
+ */
+const menuOptions: () => Record = () => {
+    return {
+        installVsix: {
+            label: 'Install VSIX on Remote Environment',
+            description: 'CodeCatalyst',
+            detail: 'Automatically upload/install a VSIX to a remote host',
+            executor: installVsixCommand,
+        },
+        openTerminal: {
+            label: 'Open Remote Terminal',
+            description: 'CodeCatalyst',
+            detail: 'Opens a new terminal connected to the remote environment',
+            executor: openTerminalCommand,
+        },
+        deleteDevEnv: {
+            label: 'Delete Workspace',
+            description: 'CodeCatalyst',
+            detail: 'Deletes the selected Dev Environment',
+            executor: deleteDevEnvCommand,
+        },
+        editStorage: {
+            label: 'Show or Edit globalState',
+            description: 'VS Code',
+            detail: 'Shows all globalState values, or edit a globalState/secret item',
+            executor: openStorageFromInput,
+        },
+        resetState: {
+            label: 'Reset feature state',
+            detail: 'Quick reset the state of extension components or features',
+            executor: resetState,
+        },
+        showEnvVars: {
+            label: 'Show Environment Variables',
+            description: 'AWS Toolkit',
+            detail: 'Shows all environment variable values',
+            executor: () => showState('envvars'),
+        },
+        deleteSsoConnections: {
+            label: 'Auth: Delete SSO Connections',
+            detail: 'Deletes all SSO Connections the extension is using.',
+            executor: deleteSsoConnections,
+        },
+        expireSsoConnections: {
+            label: 'Auth: Expire SSO Connections',
+            detail: 'Force expires all SSO Connections, in to a "needs reauthentication" state.',
+            executor: expireSsoConnections,
+        },
+        editAuthConnections: {
+            label: 'Auth: Edit Connections',
+            detail: 'Opens editor to all Auth Connections the extension is using.',
+            executor: editSsoConnections,
+        },
+        notificationsSend: {
+            label: 'Notifications: Send Notifications',
+            detail: 'Send JSON notifications for testing.',
+            executor: editNotifications,
+        },
+        forceIdeCrash: {
+            label: 'Crash: Force IDE ExtHost Crash',
+            detail: `Will SIGKILL ExtHost, { pid: ${process.pid}, sessionId: '${getSessionId().slice(0, 8)}-...' }, but the IDE itself will not crash.`,
+            executor: forceQuitIde,
+        },
+        startChildProcess: {
+            label: 'ChildProcess: Start child process',
+            detail: 'Start ChildProcess from our utility wrapper for testing',
+            executor: startChildProcess,
+        },
+    }
+}
+
+/**
+ * Provides (readonly, as opposed to `ObjectEditor`) content for the aws-dev2:/ URI scheme.
+ *
+ * ```
+ * aws-dev2:/state/envvars
+ * aws-dev2:/state/globalstate
+ * ```
+ *
+ * TODO: This only purpose of this provider is to avoid an annoying unsaved, empty document that
+ * re-appears after vscode restart. Ideally there should be only one scheme (aws-dev:/).
+ */
+export class DevDocumentProvider implements vscode.TextDocumentContentProvider {
+    provideTextDocumentContent(uri: vscode.Uri): string {
+        if (uri.path.startsWith('/envvars')) {
+            let s = 'Environment variables known to AWS Toolkit:\n\n'
+            for (const [k, v] of Object.entries(process.env)) {
+                s += `${k}=${v}\n`
+            }
+            return s
+        } else if (uri.path.startsWith('/globalstate')) {
+            // lol hax
+            // as of November 2023, all of a memento's properties are stored as property `f` when minified
+            return JSON.stringify((globalState as any).f, undefined, 4)
+        } else {
+            return `unknown URI path: ${uri}`
+        }
+    }
+}
+
+/**
+ * Enables internal developer tools.
+ *
+ * Commands prefixed with `AWS (Developer)` will appear so long as a developer setting is active.
+ *
+ * See {@link DevSettings} for more information.
+ */
+export async function activate(ctx: vscode.ExtensionContext): Promise {
+    const devSettings = DevSettings.instance
+
+    ctx.subscriptions.push(
+        devSettings.onDidChangeActiveSettings(updateDevMode),
+        vscode.workspace.registerTextDocumentContentProvider('aws-dev2', new DevDocumentProvider()),
+        // "AWS (Developer): Open Developer Menu"
+        vscode.commands.registerCommand('aws.dev.openMenu', async () => {
+            await vscode.commands.executeCommand('_aws.dev.invokeMenu', {
+                context: ctx,
+                auth: () => Auth.instance,
+                notificationsController: () => NotificationsController.instance,
+            })
+        }),
+        // Internal command to open dev menu for a specific context and options
+        vscode.commands.registerCommand('_aws.dev.invokeMenu', (opts: DevOptions) => {
+            targetContext = opts.context
+            // eslint-disable-next-line aws-toolkits/no-banned-usages
+            globalState = targetContext.globalState
+            targetAuth = opts.auth()
+            targetNotificationsController = opts.notificationsController()
+            const options = menuOptions()
+            void openMenu(
+                entries(options)
+                    .filter((e) => (opts.menuOptions ?? Object.keys(options)).includes(e[0]))
+                    .map((e) => e[1])
+            )
+        })
+    )
+
+    await updateDevMode()
+
+    const editor = new ObjectEditor()
+    ctx.subscriptions.push(openStorageCommand.register(editor))
+}
+
+async function openMenu(options: MenuOption[]): Promise {
+    const items = options.map((v) => ({
+        label: v.label,
+        detail: v.detail,
+        description: v.description,
+        skipEstimate: true,
+        data: v.executor,
+    }))
+
+    const prompter = createQuickPick(items, {
+        title: 'Developer Menu',
+        buttons: createCommonButtons(),
+        matchOnDescription: true,
+        matchOnDetail: true,
+    })
+
+    await prompter.prompt()
+}
+
+function isSecrets(obj: vscode.Memento | vscode.SecretStorage): obj is vscode.SecretStorage {
+    return (obj as vscode.SecretStorage).store !== undefined
+}
+
+class VirtualObjectFile implements FileProvider {
+    private mTime = 0
+    private size = 0
+    private readonly onDidChangeEmitter = new vscode.EventEmitter()
+    public readonly onDidChange = this.onDidChangeEmitter.event
+
+    public constructor(
+        private readonly storage: vscode.Memento | vscode.SecretStorage,
+        private readonly key: string
+    ) {}
+
+    /** Emits an event indicating this file's content has changed */
+    public refresh() {
+        /**
+         * Per {@link vscode.FileSystemProvider.onDidChangeFile}, if the mTime and/or size does not change, new file content may
+         * not be retrieved due to optimizations. Without this, when we emit a change the text editor did not update.
+         */
+        this.mTime++
+        this.onDidChangeEmitter.fire()
+    }
+
+    public stat(): { ctime: number; mtime: number; size: number } {
+        // This would need to be filled out to track conflicts
+        return { ctime: 0, mtime: this.mTime, size: this.size }
+    }
+
+    public async read(): Promise {
+        const encoder = new TextEncoder()
+
+        const data = encoder.encode(await this.readStore(this.key))
+        this.size = data.length
+        return data
+    }
+
+    public async write(content: Uint8Array): Promise {
+        const decoder = new TextDecoder()
+        const value = JSON.parse(decoder.decode(content))
+
+        await this.updateStore(this.key, value)
+        this.refresh()
+    }
+
+    private async readStore(key: string): Promise {
+        // Could potentially show `undefined` in the editor instead of an empty string
+        if (isSecrets(this.storage)) {
+            const value = (await this.storage.get(key)) ?? ''
+            return JSON.stringify(JSON.parse(value), undefined, 4)
+        } else {
+            if (key === '') {
+                return '(empty key)'
+            }
+            return JSON.stringify(this.storage.get(key, {}), undefined, 4)
+        }
+    }
+
+    private async updateStore(key: string, value: unknown): Promise {
+        if (isSecrets(this.storage)) {
+            return this.storage.store(key, JSON.stringify(value))
+        } else {
+            return this.storage.update(key, value)
+        }
+    }
+}
+
+interface Tab {
+    readonly editor: vscode.TextEditor
+    readonly virtualFile: VirtualObjectFile
+    dispose(): void
+}
+
+class ObjectEditor {
+    private static readonly scheme = 'aws-dev'
+
+    private readonly fs = new VirtualFileSystem()
+    private readonly tabs: Map = new Map()
+
+    public constructor() {
+        vscode.workspace.onDidCloseTextDocument((doc) => {
+            const key = this.fs.uriToKey(doc.uri)
+            this.tabs.get(key)?.dispose()
+            this.tabs.delete(key)
+        })
+
+        vscode.workspace.registerFileSystemProvider(ObjectEditor.scheme, this.fs)
+    }
+
+    public async openStorage(type: 'globalsView' | 'globals' | 'secrets' | 'auth', key: string) {
+        switch (type) {
+            case 'globalsView':
+                return showState('globalstate')
+            case 'globals':
+                return this.openState(globalState, key)
+            case 'secrets':
+                return this.openState(targetContext.secrets, key)
+            case 'auth':
+                // Auth memento is determined in a different way
+                return this.openState(getEnvironmentSpecificMemento(globalState), key)
+        }
+    }
+
+    private async openState(storage: vscode.Memento | vscode.SecretStorage, key: string) {
+        const uri = this.uriFromKey(key, storage)
+        const tab = this.tabs.get(this.fs.uriToKey(uri))
+
+        if (tab) {
+            tab.virtualFile.refresh()
+            await vscode.window.showTextDocument(tab.editor.document)
+            return tab.virtualFile
+        } else {
+            const newTab = await this.createTab(storage, key)
+            const newKey = this.fs.uriToKey(newTab.editor.document.uri)
+            this.tabs.set(newKey, newTab)
+            return newTab.virtualFile
+        }
+    }
+
+    private async createTab(storage: vscode.Memento | vscode.SecretStorage, key: string): Promise {
+        const virtualFile = new VirtualObjectFile(storage, key)
+        let disposable: vscode.Disposable
+        let document: vscode.TextDocument
+        if (key !== '') {
+            const uri = this.uriFromKey(key, storage)
+            disposable = this.fs.registerProvider(uri, virtualFile)
+            document = await vscode.workspace.openTextDocument(uri)
+        } else {
+            // don't tie it to a URI so you can't save this view
+            const stream = await virtualFile.read()
+            document = await vscode.workspace.openTextDocument({
+                content: new TextDecoder().decode(stream),
+            })
+        }
+        const withLanguage = await vscode.languages.setTextDocumentLanguage(document, 'json')
+
+        return {
+            editor: await vscode.window.showTextDocument(withLanguage),
+            virtualFile,
+            dispose: () => disposable.dispose(),
+        }
+    }
+
+    private uriFromKey(key: string, storage: vscode.Memento | vscode.SecretStorage): vscode.Uri {
+        const prefix = isSecrets(storage) ? 'secrets' : 'globals'
+
+        return vscode.Uri.parse(`${ObjectEditor.scheme}:`, true).with({
+            path: `/${prefix}/${key}-${targetContext.extension.id}`,
+        })
+    }
+}
+
+async function openStorageFromInput() {
+    const wizard = new (class extends Wizard<{ target: 'globalsView' | 'globals' | 'secrets'; key: string }> {
+        constructor() {
+            super()
+
+            this.form.target.bindPrompter(() =>
+                createQuickPick(
+                    [
+                        { label: 'Show all globalState', data: 'globalsView' },
+                        { label: 'Edit globalState', data: 'globals' },
+                        { label: 'Secrets', data: 'secrets' },
+                    ],
+                    {
+                        title: 'Select a storage type',
+                    }
+                )
+            )
+
+            this.form.key.bindPrompter(({ target }) => {
+                if (target === 'secrets') {
+                    return createInputBox({
+                        title: 'Enter a key',
+                    })
+                } else if (target === 'globalsView') {
+                    return new SkipPrompter()
+                } else if (target === 'globals') {
+                    // List all globalState keys in the quickpick menu.
+                    const items = globalState
+                        .keys()
+                        .map((key) => {
+                            return {
+                                label: key,
+                                data: key,
+                            }
+                        })
+                        .sort((a, b) => {
+                            return a.data.localeCompare(b.data)
+                        })
+
+                    return createQuickPick(items, { title: 'Select a key' })
+                } else {
+                    throw new Error('invalid storage target')
+                }
+            })
+        }
+    })()
+
+    const response = await wizard.run()
+
+    if (response) {
+        return openStorageCommand.execute(response.target, response.key)
+    }
+}
+
+type ResettableFeature = {
+    name: string
+    executor: () => Promise | void
+} & QuickPickItem
+
+/**
+ * Extend this array with features that may need state resets often for
+ * testing purposes. It will appear as an entry in the "Reset feature state" menu.
+ */
+const resettableFeatures: readonly ResettableFeature[] = [
+    {
+        name: 'notifications',
+        label: 'Notifications',
+        detail: 'Resets memory/global state for the notifications panel (includes dismissed, onReceive).',
+        executor: resetNotificationsState,
+    },
+] as const
+
+// TODO this is *somewhat* similar to `openStorageFromInput`. If we need another
+// one of these prompters, can we make it generic?
+async function resetState() {
+    const wizard = new (class extends Wizard<{ target: string; key: string }> {
+        constructor() {
+            super()
+
+            this.form.target.bindPrompter(() =>
+                createQuickPick(
+                    resettableFeatures.map((f) => {
+                        return {
+                            data: f.name,
+                            label: f.label,
+                            detail: f.detail,
+                        }
+                    }),
+                    {
+                        title: 'Select a feature/component to reset',
+                    }
+                )
+            )
+
+            this.form.key.bindPrompter(({ target }) => {
+                if (target && resettableFeatures.some((f) => f.name === target)) {
+                    return new SkipPrompter()
+                }
+                throw new Error('invalid feature target')
+            })
+        }
+    })()
+
+    const response = await wizard.run()
+
+    if (response) {
+        return resettableFeatures.find((f) => f.name === response.target)?.executor()
+    }
+}
+
+async function editSsoConnections() {
+    void openStorageCommand.execute('auth', 'auth.profiles')
+}
+
+async function deleteSsoConnections() {
+    const conns = targetAuth.listConnections()
+    const ssoConns = (await conns).filter(isAnySsoConnection)
+    await Promise.all(ssoConns.map((conn) => targetAuth.deleteConnection(conn)))
+    void vscode.window.showInformationMessage(`Deleted: ${ssoConns.map((c) => c.startUrl).join(', ')}`)
+}
+
+async function expireSsoConnections() {
+    return telemetry.function_call.run(
+        async () => {
+            const conns = targetAuth.listConnections()
+            const ssoConns = (await conns).filter(isAnySsoConnection)
+            await Promise.all(ssoConns.map((conn) => targetAuth.expireConnection(conn)))
+            void vscode.window.showInformationMessage(`Expired: ${ssoConns.map((c) => c.startUrl).join(', ')}`)
+        },
+        { emit: false, functionId: { name: 'expireSsoConnectionsDev' } }
+    )
+}
+
+export function forceQuitIde() {
+    // This current process is the ExtensionHost. Killing it will cause all the extensions to crash
+    // for the current ExtensionHost (unless using "extensions.experimental.affinity").
+    // The IDE instance itself will remaing running, but a new ExtHost will spawn within it.
+    // The PPID (parent process) is vscode itself, killing it crashes all vscode instances.
+    const vsCodePid = process.pid
+    process.kill(vsCodePid, 'SIGKILL') // SIGTERM would be the graceful shutdown
+}
+
+async function showState(path: string) {
+    const uri = vscode.Uri.parse(`aws-dev2://state/${path}-${targetContext.extension.id}`)
+    const doc = await vscode.workspace.openTextDocument(uri)
+    await vscode.window.showTextDocument(doc, { preview: false })
+}
+
+export const openStorageCommand = Commands.from(ObjectEditor).declareOpenStorage('_aws.dev.openStorage')
+
+export async function updateDevMode() {
+    await setContext('aws.isDevMode', DevSettings.instance.isDevMode())
+}
+
+async function resetNotificationsState() {
+    await targetNotificationsController.reset()
+}
+
+async function editNotifications() {
+    const storageKey = 'aws.notifications.dev'
+    const current = globalState.get(storageKey) ?? {}
+    const isValid = (item: any) => {
+        if (typeof item !== 'object' || !Array.isArray(item.startUp) || !Array.isArray(item.emergency)) {
+            return false
+        }
+        return true
+    }
+    if (!isValid(current)) {
+        // Set a default state if the developer does not have it or it's malformed.
+        await globalState.update(storageKey, { startUp: [], emergency: [] } as DevNotificationsState)
+    }
+
+    // Monitor for when the global state is updated.
+    // A notification will be sent based on the contents.
+    const virtualFile = await openStorageCommand.execute('globals', storageKey)
+    virtualFile?.onDidChange(async () => {
+        const val = globalState.get(storageKey) as DevNotificationsState
+        if (!isValid(val)) {
+            void vscode.window.showErrorMessage(
+                'Dev mode: invalid notification object provided. State data must take the form: { "startUp": ToolkitNotification[], "emergency": ToolkitNotification[] }'
+            )
+            return
+        }
+
+        // This relies on the controller being built with DevFetcher, as opposed to
+        // the default RemoteFetcher. DevFetcher will check for notifications in the
+        // global state, which was just modified.
+        await targetNotificationsController.pollForStartUp()
+        await targetNotificationsController.pollForEmergencies()
+    })
+}
+
+async function startChildProcess() {
+    const result = await createInputBox({
+        title: 'Enter a command',
+    }).prompt()
+    if (result) {
+        const [command, ...args] = result?.toString().split(' ') ?? []
+        getLogger().info(`Starting child process: '${command}'`)
+        const processResult = await ChildProcess.run(command, args, { collect: true })
+        getLogger().info(`Child process exited with code ${processResult.exitCode}`)
+    }
+}
diff --git a/packages/core/src/dev/beta.ts b/packages/core/src/dev/beta.ts
new file mode 100644
index 00000000000..329907aa5a3
--- /dev/null
+++ b/packages/core/src/dev/beta.ts
@@ -0,0 +1,192 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as path from 'path'
+import * as nls from 'vscode-nls'
+import * as vscode from 'vscode'
+import AdmZip from 'adm-zip'
+import got from 'got'
+import globals from '../shared/extensionGlobals'
+import { getLogger } from '../shared/logger/logger'
+import fs from '../shared/fs/fs'
+import { VSCODE_EXTENSION_ID } from '../shared/extensions'
+import { makeTemporaryToolkitFolder } from '../shared/filesystemUtilities'
+import { reloadWindowPrompt } from '../shared/utilities/vsCodeUtils'
+import { isUserCancelledError, ToolkitError } from '../shared/errors'
+import { telemetry } from '../shared/telemetry/telemetry'
+import { cast } from '../shared/utilities/typeConstructors'
+import { CancellationError } from '../shared/utilities/timeoutUtils'
+import { isAmazonQ, productName } from '../shared/extensionUtilities'
+import * as devConfig from './config'
+import { isReleaseVersion } from '../shared/vscode/env'
+import { getRelativeDate } from '../shared/datetime'
+
+const localize = nls.loadMessageBundle()
+const logger = getLogger('dev/beta')
+
+const downloadIntervalMs = 1000 * 60 * 60 * 3 // 3 hours (8 times/day).
+
+interface BetaToolkit {
+    readonly needUpdate: boolean
+    readonly lastCheck: number
+}
+
+function getBetaToolkitData(vsixUrl: string): BetaToolkit | undefined {
+    return globals.globalState.tryGet>('dev.beta', Object, {})[vsixUrl]
+}
+
+async function updateBetaToolkitData(vsixUrl: string, data: BetaToolkit) {
+    await globals.globalState.update('dev.beta', {
+        ...globals.globalState.get>('dev.beta', {}),
+        [vsixUrl]: data,
+    })
+}
+
+/**
+ * Set up "beta" update monitoring.
+ */
+export async function activate(ctx: vscode.ExtensionContext) {
+    const betaUrl = isAmazonQ() ? devConfig.betaUrl.amazonq : devConfig.betaUrl.toolkit
+    if (!isReleaseVersion() && betaUrl) {
+        ctx.subscriptions.push(watchBetaVSIX(betaUrl))
+    }
+}
+
+/**
+ * Watch the beta VSIX daily for changes.
+ * If this is the first time we are watching the beta version or if its been 24 hours since it was last checked then try to prompt for update
+ */
+export function watchBetaVSIX(vsixUrl: string): vscode.Disposable {
+    const toolkit = getBetaToolkitData(vsixUrl)
+    const lastCheckRel = toolkit ? getRelativeDate(new Date(toolkit.lastCheck)) : ''
+    logger.info('watching beta artifacts url (lastCheck: %s): %s', lastCheckRel, vsixUrl)
+
+    if (!toolkit || toolkit.needUpdate || Date.now() - toolkit.lastCheck > downloadIntervalMs) {
+        runAutoUpdate(vsixUrl).catch((e) => {
+            logger.error('runAutoUpdate failed: %s', (e as Error).message)
+        })
+    }
+
+    const interval = globals.clock.setInterval(() => runAutoUpdate(vsixUrl), downloadIntervalMs)
+    return { dispose: () => clearInterval(interval) }
+}
+
+async function runAutoUpdate(vsixUrl: string) {
+    logger.debug(`checking url for a new version: %s`, vsixUrl)
+
+    try {
+        await telemetry.aws_autoUpdateBeta.run(() => checkBetaUrl(vsixUrl))
+    } catch (e) {
+        if (!isUserCancelledError(e)) {
+            logger.warn('beta extension auto-update failed: %s', e)
+        }
+    }
+}
+
+/**
+ * Prompt to update the beta extension when required
+ */
+async function checkBetaUrl(vsixUrl: string): Promise {
+    const resp = await got(vsixUrl).buffer()
+    const latestBetaInfo = await getExtensionInfo(resp)
+    const extId = isAmazonQ() ? VSCODE_EXTENSION_ID.amazonq : VSCODE_EXTENSION_ID.awstoolkit
+    if (extId !== `${latestBetaInfo.publisher}.${latestBetaInfo.name}`) {
+        throw new ToolkitError('URL does not point to an AWS Toolkit artifact', { code: 'InvalidExtensionName' })
+    }
+
+    const currentVersion = vscode.extensions.getExtension(extId)?.packageJSON.version
+    if (latestBetaInfo.version !== currentVersion) {
+        const tmpFolder = await makeTemporaryToolkitFolder()
+        const betaPath = vscode.Uri.joinPath(vscode.Uri.file(tmpFolder), path.basename(vsixUrl))
+        await fs.writeFile(betaPath, resp)
+
+        try {
+            await promptInstallToolkit(betaPath, latestBetaInfo.version, vsixUrl)
+        } finally {
+            await fs.delete(tmpFolder, { recursive: true })
+        }
+    } else {
+        await updateBetaToolkitData(vsixUrl, {
+            lastCheck: Date.now(),
+            needUpdate: false,
+        })
+    }
+}
+
+interface ExtensionInfo {
+    readonly name: string
+    readonly version: string
+    readonly publisher: string
+}
+
+/**
+ * Get information about the extension or error if no version could be found
+ *
+ * @param extension The URI of the extension on disk or the raw data
+ * @returns The version + name of the extension
+ * @throws Error if the extension manifest could not be found or parsed
+ */
+async function getExtensionInfo(extension: Buffer): Promise
+async function getExtensionInfo(extensionLocation: vscode.Uri): Promise
+async function getExtensionInfo(extensionOrLocation: vscode.Uri | Buffer): Promise {
+    const fileNameOrData = extensionOrLocation instanceof vscode.Uri ? extensionOrLocation.fsPath : extensionOrLocation
+    const packageFile = new AdmZip(fileNameOrData).getEntry('extension/package.json')
+    const packageJSON = packageFile?.getData().toString()
+    if (!packageJSON) {
+        throw new ToolkitError('Extension does not have a `package.json`', { code: 'NoPackageJson' })
+    }
+
+    try {
+        const data = JSON.parse(packageJSON)
+
+        return {
+            name: cast(data.name, String),
+            version: cast(data.version, String),
+            publisher: cast(data.publisher, String),
+        }
+    } catch (e) {
+        throw ToolkitError.chain(e, 'Unable to parse extension data', { code: 'BadParse' })
+    }
+}
+
+async function promptInstallToolkit(pluginPath: vscode.Uri, newVersion: string, vsixUrl: string): Promise {
+    const vsixName = path.basename(pluginPath.fsPath)
+    const installBtn = localize('AWS.missingExtension.install', 'Install...')
+
+    const response = await vscode.window.showInformationMessage(
+        localize(
+            'AWS.dev.beta.updatePrompt',
+            'New version of {0} is available at the [beta URL]({1}). Install the new version "{2}" to continue using the beta.',
+            productName(),
+            vsixUrl,
+            newVersion
+        ),
+        installBtn
+    )
+
+    switch (response) {
+        case installBtn:
+            try {
+                logger.info(`installing artifact: ${vsixName}`)
+                await vscode.commands.executeCommand('workbench.extensions.installExtension', pluginPath)
+                await updateBetaToolkitData(vsixUrl, {
+                    lastCheck: Date.now(),
+                    needUpdate: false,
+                })
+                reloadWindowPrompt(
+                    localize('AWS.dev.beta.reloadPrompt', 'Reload now to use the new beta {0}.', productName())
+                )
+            } catch (e) {
+                throw ToolkitError.chain(e, `Failed to install ${vsixName}`, { code: 'FailedExtensionInstall' })
+            }
+            break
+        case undefined:
+            await updateBetaToolkitData(vsixUrl, {
+                lastCheck: Date.now(),
+                needUpdate: true,
+            })
+            throw new CancellationError('user')
+    }
+}
diff --git a/src/dev/codecatalyst.ts b/packages/core/src/dev/codecatalyst.ts
similarity index 80%
rename from src/dev/codecatalyst.ts
rename to packages/core/src/dev/codecatalyst.ts
index 28f23ece9e7..d9dd4e9e5a9 100644
--- a/src/dev/codecatalyst.ts
+++ b/packages/core/src/dev/codecatalyst.ts
@@ -1,20 +1,18 @@
 /*!
- * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import * as glob from 'glob'
-import * as fs from 'fs-extra'
+import { glob } from 'glob'
 import * as path from 'path'
 import * as vscode from 'vscode'
 import * as manifest from '../../package.json'
-import { promisify } from 'util'
-import { getLogger } from '../shared/logger'
+import { getLogger } from '../shared/logger/logger'
 import { selectCodeCatalystResource } from '../codecatalyst/wizards/selectResource'
-import { ExtContext, VSCODE_EXTENSION_ID } from '../shared/extensions'
+import { VSCODE_EXTENSION_ID } from '../shared/extensions'
 import { DevEnvironment, CodeCatalystClient } from '../shared/clients/codecatalystClient'
 import { prepareDevEnvConnection } from '../codecatalyst/model'
-import { ChildProcess } from '../shared/utilities/childProcess'
+import { ChildProcess } from '../shared/utilities/processUtils'
 import { Timeout } from '../shared/utilities/timeoutUtils'
 import { CodeCatalystCommands } from '../codecatalyst/commands'
 import { showViewLogsMessage } from '../shared/utilities/messages'
@@ -22,14 +20,18 @@ import { startVscodeRemote } from '../shared/extensions/ssh'
 import { isValidResponse } from '../shared/wizards/wizard'
 import { createQuickPick } from '../shared/ui/pickerPrompter'
 import { createCommonButtons } from '../shared/ui/buttons'
+import { fs } from '../shared/fs/fs'
 
 type LazyProgress = vscode.Progress & vscode.Disposable & { getToken(): Timeout }
 
+/**
+ * Progress dialog that does not show until `report()` is called.
+ */
 function lazyProgress>(timeout: Timeout): LazyProgress {
     let dispose!: () => void
     let progress: vscode.Progress
     const location = vscode.ProgressLocation.Notification
-    const thenable = new Promise(resolve => {
+    const thenable = new Promise((resolve) => {
         dispose = resolve
         timeout.token.onCancellationRequested(() => resolve)
     })
@@ -37,11 +39,11 @@ function lazyProgress>(timeout: Timeout): LazyProg
     return {
         dispose,
         getToken: () => timeout,
-        report: value => {
+        report: (value) => {
             if (!progress) {
-                vscode.window.withProgress({ location, cancellable: true }, (p, t) => {
+                void vscode.window.withProgress({ location, cancellable: true }, (p, t) => {
                     progress = p
-                    t.onCancellationRequested(e => timeout.cancel())
+                    t.onCancellationRequested((e) => timeout.cancel())
                     return thenable
                 })
             }
@@ -50,8 +52,8 @@ function lazyProgress>(timeout: Timeout): LazyProg
     }
 }
 
-export async function openTerminalCommand(ctx: ExtContext) {
-    const commands = CodeCatalystCommands.fromContext(ctx.extensionContext)
+export async function openTerminalCommand(ctx: vscode.ExtensionContext) {
+    const commands = CodeCatalystCommands.fromContext(ctx)
     const progress = lazyProgress<{ message: string }>(new Timeout(900000))
 
     await commands.withClient(openTerminal, progress).finally(() => progress.dispose())
@@ -80,10 +82,10 @@ async function openTerminal(client: CodeCatalystClient, progress: LazyProgress<{
     vscode.window.createTerminal(options).show()
 }
 
-export async function installVsixCommand(ctx: ExtContext) {
-    const commands = CodeCatalystCommands.fromContext(ctx.extensionContext)
+export async function installVsixCommand(ctx: vscode.ExtensionContext) {
+    const commands = CodeCatalystCommands.fromContext(ctx)
 
-    await commands.withClient(async client => {
+    await commands.withClient(async (client) => {
         const env = await selectCodeCatalystResource(client, 'devEnvironment')
         if (!env) {
             return
@@ -94,27 +96,21 @@ export async function installVsixCommand(ctx: ExtContext) {
             await installVsix(ctx, client, progress, env).finally(() => progress.dispose())
         } catch (err) {
             getLogger().error(`installVsixCommand: installation failed: %O`, err)
-            showViewLogsMessage('VSIX installation failed')
+            void showViewLogsMessage('VSIX installation failed')
         }
     })
 }
 
 async function promptVsix(
-    ctx: ExtContext,
+    ctx: vscode.ExtensionContext,
     progress?: LazyProgress<{ message: string }>
 ): Promise {
     const folders = (vscode.workspace.workspaceFolders ?? [])
-        .map(f => f.uri)
-        .concat(vscode.Uri.file(ctx.extensionContext.extensionPath))
-
-    enum ExtensionMode {
-        Production = 1,
-        Development = 2,
-        Test = 3,
-    }
+        .map((f) => f.uri)
+        .concat(vscode.Uri.file(ctx.extensionPath))
 
-    const isDevelopmentWindow = ctx.extensionContext.extensionMode === ExtensionMode.Development
-    const extPath = isDevelopmentWindow ? ctx.extensionContext.extensionPath : folders[0].fsPath
+    const isDevelopmentWindow = ctx.extensionMode === vscode.ExtensionMode.Development
+    const extPath = isDevelopmentWindow ? ctx.extensionPath : folders[0].fsPath
 
     const packageNew = {
         label: 'Create new VSIX',
@@ -169,14 +165,14 @@ async function promptVsix(
         yield [seps.shift()!, packageNew, localInstall]
 
         for (const f of folders) {
-            const paths = await promisify(glob)('*.vsix', { cwd: f.fsPath })
-            const uris = paths.map(v => vscode.Uri.file(path.join(f.fsPath, v)))
+            const paths = await glob('*.vsix', { cwd: f.fsPath })
+            const uris = paths.map((v) => vscode.Uri.file(path.join(f.fsPath, v)))
 
             if (uris.length > 0 && seps.length > 0) {
                 yield [seps.shift()!]
             }
 
-            yield uris.map(v => ({
+            yield uris.map((v) => ({
                 label: path.basename(v.fsPath),
                 detail: v.fsPath,
                 data: v,
@@ -197,12 +193,12 @@ async function promptVsix(
  * Bootstrap an environment for remote development/debugging
  */
 async function installVsix(
-    ctx: ExtContext,
+    ctx: vscode.ExtensionContext,
     client: CodeCatalystClient,
     progress: LazyProgress<{ message: string }>,
     env: DevEnvironment
 ): Promise {
-    const resp = await promptVsix(ctx, progress).then(r => r?.fsPath)
+    const resp = await promptVsix(ctx, progress).then((r) => r?.fsPath)
 
     if (!resp) {
         return
@@ -221,14 +217,14 @@ async function installVsix(
     if (path.extname(resp) !== '.vsix') {
         progress.report({ message: 'Copying extension...' })
 
-        const packageData = await fs.readFile(path.join(resp, 'package.json'), 'utf-8')
+        const packageData = await fs.readFileText(path.join(resp, 'package.json'))
         const targetManfiest: typeof manifest = JSON.parse(packageData)
         const destName = `${extPath}/${extId}-${targetManfiest.version}`
         const source = `${resp}${path.sep}`
 
         // Using `.vscodeignore` would be nice here but `rsync` doesn't understand glob patterns
         const excludes = ['.git/', 'node_modules/', '/src/', '/scripts/', '/dist/src/test/']
-            .map(p => ['--exclude', p])
+            .map((p) => ['--exclude', p])
             .reduce((a, b) => a.concat(b))
 
         const installCommand = [`cd ${destName}`, 'npm i --ignore-scripts'].join(' && ')
@@ -247,7 +243,7 @@ async function installVsix(
             .split('-')
             .reverse()
             .slice(0, 2)
-            .map(s => s.replace('.vsix', ''))
+            .map((s) => s.replace('.vsix', ''))
         const destName = [extId, ...suffixParts.reverse()].join('-')
 
         const installCmd = [
@@ -266,10 +262,10 @@ async function installVsix(
     await startVscodeRemote(SessionProcess, hostname, '/projects', vscPath)
 }
 
-export async function deleteDevEnvCommand(ctx: ExtContext) {
-    const commands = CodeCatalystCommands.fromContext(ctx.extensionContext)
+export async function deleteDevEnvCommand(ctx: vscode.ExtensionContext) {
+    const commands = CodeCatalystCommands.fromContext(ctx)
 
-    await commands.withClient(async client => {
+    await commands.withClient(async (client) => {
         const devenv = await selectCodeCatalystResource(client, 'devEnvironment')
         if (!devenv) {
             return
diff --git a/packages/core/src/dev/config.ts b/packages/core/src/dev/config.ts
new file mode 100644
index 00000000000..d5fa49b2426
--- /dev/null
+++ b/packages/core/src/dev/config.ts
@@ -0,0 +1,12 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// This file is for internal testing.
+// Nothing in this file should have a truthy value on mainline
+
+export const betaUrl = {
+    amazonq: '',
+    toolkit: '',
+}
diff --git a/packages/core/src/dev/index.ts b/packages/core/src/dev/index.ts
new file mode 100644
index 00000000000..111920c99ee
--- /dev/null
+++ b/packages/core/src/dev/index.ts
@@ -0,0 +1,7 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { DevOptions, updateDevMode } from './activation'
+export * as beta from './beta'
diff --git a/packages/core/src/docdb/activation.ts b/packages/core/src/docdb/activation.ts
new file mode 100644
index 00000000000..62b61d5e490
--- /dev/null
+++ b/packages/core/src/docdb/activation.ts
@@ -0,0 +1,125 @@
+/*!
+ * 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 { DBResourceNode } from './explorer/dbResourceNode'
+import { DocumentDBNode } from './explorer/docdbNode'
+import { DBClusterNode } from './explorer/dbClusterNode'
+import { DBInstanceNode } from './explorer/dbInstanceNode'
+import { addRegion } from './commands/addRegion'
+import { createCluster } from './commands/createCluster'
+import { deleteCluster } from './commands/deleteCluster'
+import { renameCluster } from './commands/renameCluster'
+import { startCluster } from './commands/startCluster'
+import { stopCluster } from './commands/stopCluster'
+import { createInstance } from './commands/createInstance'
+import { deleteInstance } from './commands/deleteInstance'
+import { modifyInstance } from './commands/modifyInstance'
+import { rebootInstance } from './commands/rebootInstance'
+import { renameInstance } from './commands/renameInstance'
+import { addTag, listTags, removeTag } from './commands/tagCommands'
+import { Uri } from 'vscode'
+import { openUrl } from '../shared/utilities/vsCodeUtils'
+import { getLogger } from '../shared/logger/logger'
+
+/**
+ * A utility function to automatically invoke trackChanges after a command.
+ */
+
+function withTrackChanges(
+    command: (node: T) => Promise,
+    commandName: string = 'UnnamedCommand'
+): (node: T) => Promise {
+    return async (node: T) => {
+        const arn = node.arn || 'UnknownARN'
+        const startTime = new Date().toISOString()
+
+        getLogger().info(
+            `[${startTime}] Executing command "${commandName}" for resource with ARN: ${arn}. Tracking changes will be invoked post-execution.`
+        )
+
+        await command(node)
+
+        const endTime = new Date().toISOString()
+        getLogger().info(
+            `[${endTime}] Successfully executed command "${commandName}" for resource with ARN: ${arn}. Invoking trackChanges now.`
+        )
+
+        await node.trackChangesWithWaitProcessingStatus()
+    }
+}
+
+/**
+ * Activates DocumentDB components.
+ */
+export async function activate(ctx: ExtContext): Promise {
+    ctx.extensionContext.subscriptions.push(
+        Commands.register('aws.docdb.createCluster', async (node?: DocumentDBNode) => {
+            await createCluster(node)
+        }),
+
+        Commands.register('aws.docdb.deleteCluster', withTrackChanges(deleteCluster, 'deleteCluster')),
+
+        Commands.register('aws.docdb.renameCluster', withTrackChanges(renameCluster, 'renameCluster')),
+
+        Commands.register('aws.docdb.startCluster', withTrackChanges(startCluster, 'startCluster')),
+
+        Commands.register('aws.docdb.stopCluster', withTrackChanges(stopCluster, 'stopCluster')),
+
+        Commands.register('aws.docdb.addRegion', withTrackChanges(addRegion, 'addRegion')),
+
+        Commands.register(
+            'aws.docdb.createInstance',
+            withTrackChanges(createInstance, 'createInstance')
+        ),
+
+        Commands.register(
+            'aws.docdb.deleteInstance',
+            withTrackChanges(deleteInstance, 'deleteInstance')
+        ),
+
+        Commands.register(
+            'aws.docdb.modifyInstance',
+            withTrackChanges(modifyInstance, 'modifyInstance')
+        ),
+
+        Commands.register(
+            'aws.docdb.rebootInstance',
+            withTrackChanges(rebootInstance, 'rebootInstance')
+        ),
+
+        Commands.register(
+            'aws.docdb.renameInstance',
+            withTrackChanges(renameInstance, 'renameInstance')
+        ),
+
+        Commands.register('aws.docdb.listTags', async (node: DBResourceNode) => {
+            await listTags(node)
+        }),
+
+        Commands.register('aws.docdb.addTag', async (node: DBResourceNode) => {
+            await addTag(node)
+        }),
+
+        Commands.register('aws.docdb.removeTag', async (node: DBResourceNode) => {
+            await removeTag(node)
+        }),
+
+        Commands.register('aws.docdb.viewConsole', async (node?: DBResourceNode) => {
+            await node?.openInBrowser()
+        }),
+
+        Commands.register('aws.docdb.viewDocs', async () => {
+            await openUrl(
+                Uri.parse('https://docs.aws.amazon.com/documentdb/latest/developerguide/get-started-guide.html')
+            )
+        }),
+
+        Commands.register('aws.docdb.copyEndpoint', async (node?: DBResourceNode) => {
+            await node?.copyEndpoint()
+        })
+    )
+}
diff --git a/packages/core/src/docdb/commands/addRegion.ts b/packages/core/src/docdb/commands/addRegion.ts
new file mode 100644
index 00000000000..ab3e6d1b721
--- /dev/null
+++ b/packages/core/src/docdb/commands/addRegion.ts
@@ -0,0 +1,165 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { getLogger } from '../../shared/logger/logger'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { DBClusterNode } from '../explorer/dbClusterNode'
+import { DBGlobalClusterNode } from '../explorer/dbGlobalClusterNode'
+import { DefaultDocumentDBClient } from '../../shared/clients/docdbClient'
+import { ToolkitError } from '../../shared/errors'
+import { showViewLogsMessage } from '../../shared/utilities/messages'
+import { isValidResponse } from '../../shared/wizards/wizard'
+import { CancellationError } from '../../shared/utilities/timeoutUtils'
+import { CreateGlobalClusterWizard } from '../wizards/createGlobalClusterWizard'
+import { CreateDBClusterMessage } from '@aws-sdk/client-docdb'
+import { createInstancesForCluster } from './createCluster'
+import { isSupportedGlobalInstanceClass } from '../utils'
+
+export async function addRegion(node: DBClusterNode | DBGlobalClusterNode): Promise {
+    if (!node) {
+        throw new ToolkitError('No node specified for AddRegion')
+    }
+
+    return telemetry.docdb_addRegion.run(async () => {
+        let globalClusterName = undefined
+
+        if (node.cluster.StorageEncrypted) {
+            void vscode.window.showErrorMessage('Encrypted clusters are not supported')
+            return
+        }
+
+        if (node instanceof DBClusterNode) {
+            if (node.clusterRole !== 'regional') {
+                void vscode.window.showErrorMessage('Only regional clusters are supported')
+                return
+            }
+
+            if (node.cluster.DBClusterMembers?.length === 0) {
+                void vscode.window.showErrorMessage(
+                    localize(
+                        'AWS.docdb.addRegion.noInstances',
+                        'Cluster must have at least one instance to add a region'
+                    )
+                )
+                throw new ToolkitError('Cluster must have at least one instance to add a region', { cancelled: true })
+            }
+
+            const unsupportedInstanceFound = node.instances.find(
+                (instance) => !isSupportedGlobalInstanceClass(instance.DBInstanceClass!)
+            )
+
+            if (unsupportedInstanceFound) {
+                void vscode.window.showErrorMessage(
+                    localize(
+                        'AWS.docdb.addRegion.unsupportedInstanceClass',
+                        'Instance class {0} not supported for global cluster.  Upgrade the instances then try again.',
+                        unsupportedInstanceFound.DBInstanceClass
+                    )
+                )
+                throw new ToolkitError('Instance class not supported for global cluster', {
+                    cancelled: true,
+                    code: 'docdbInstanceClassNotSupported',
+                })
+            }
+        } else {
+            globalClusterName = node.cluster.GlobalClusterIdentifier
+
+            if (node.cluster.GlobalClusterMembers!.length > 4) {
+                void vscode.window.showErrorMessage(
+                    localize('AWS.docdb.addRegion.maxRegions', 'Global clusters can have a maximum of 5 regions')
+                )
+                throw new ToolkitError('Global clusters can have a maximum of 5 regions', {
+                    cancelled: true,
+                    code: 'docdbMaxRegionsInUse',
+                })
+            }
+        }
+
+        if (!node.isAvailable) {
+            void vscode.window.showErrorMessage(localize('AWS.docdb.clusterStopped', 'Cluster must be running'))
+            throw new ToolkitError('Cluster not available', { cancelled: true, code: 'docdbClusterStopped' })
+        }
+
+        const wizard = new CreateGlobalClusterWizard(node.regionCode, node.cluster.EngineVersion, node.client, {
+            initState: { GlobalClusterName: globalClusterName },
+        })
+        const response = await wizard.run()
+
+        if (!isValidResponse(response)) {
+            throw new CancellationError('user')
+        }
+
+        const regionCode = response.RegionCode
+        let input: CreateDBClusterMessage
+        let clusterName = response.GlobalClusterName
+
+        try {
+            if (node instanceof DBClusterNode) {
+                // Create new global cluster from regional cluster
+                const primaryCluster = node.cluster
+
+                getLogger().info(`docdb: Creating global cluster: ${clusterName}`)
+                const globalCluster = await node.client.createGlobalCluster({
+                    GlobalClusterIdentifier: response.GlobalClusterName,
+                    SourceDBClusterIdentifier: primaryCluster.DBClusterArn,
+                })
+
+                input = {
+                    GlobalClusterIdentifier: globalCluster?.GlobalClusterIdentifier,
+                    DBClusterIdentifier: response.Cluster.DBClusterIdentifier,
+                    DeletionProtection: primaryCluster.DeletionProtection,
+                    Engine: primaryCluster.Engine,
+                    EngineVersion: primaryCluster.EngineVersion,
+                    StorageType: primaryCluster.StorageType,
+                    StorageEncrypted: globalCluster?.StorageEncrypted,
+                }
+            } else {
+                // Add secondary cluster to global cluster
+                const globalCluster = node.cluster
+
+                input = {
+                    GlobalClusterIdentifier: globalClusterName,
+                    DBClusterIdentifier: response.Cluster.DBClusterIdentifier,
+                    DeletionProtection: globalCluster.DeletionProtection,
+                    Engine: globalCluster.Engine,
+                    EngineVersion: globalCluster.EngineVersion,
+                    StorageEncrypted: globalCluster.StorageEncrypted,
+                }
+            }
+
+            clusterName = response.Cluster.DBClusterIdentifier
+            getLogger().info(`docdb: Creating secondary cluster: ${clusterName} in region ${regionCode}`)
+
+            const client = DefaultDocumentDBClient.create(regionCode)
+            const newCluster = await client.createCluster(input)
+
+            if (response.Cluster.DBInstanceCount) {
+                await createInstancesForCluster(
+                    client,
+                    clusterName,
+                    response.Cluster.DBInstanceClass,
+                    response.Cluster.DBInstanceCount
+                )
+            }
+
+            getLogger().info('docdb: Created cluster: %O', newCluster)
+            void vscode.window.showInformationMessage(localize('AWS.docdb.addRegion.success', 'Region added'))
+
+            if (node instanceof DBClusterNode) {
+                node?.parent.refresh()
+            } else {
+                node?.refresh()
+            }
+        } catch (e) {
+            getLogger().error(`docdb: Failed to create cluster ${clusterName}: %s`, e)
+            void showViewLogsMessage(
+                localize('AWS.docdb.createCluster.error', 'Failed to create cluster: {0}', clusterName)
+            )
+            throw ToolkitError.chain(e, `Failed to create cluster ${clusterName}`)
+        }
+    })
+}
diff --git a/packages/core/src/docdb/commands/createCluster.ts b/packages/core/src/docdb/commands/createCluster.ts
new file mode 100644
index 00000000000..62689319158
--- /dev/null
+++ b/packages/core/src/docdb/commands/createCluster.ts
@@ -0,0 +1,105 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { ToolkitError } from '../../shared/errors'
+import { getLogger } from '../../shared/logger/logger'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { showViewLogsMessage } from '../../shared/utilities/messages'
+import { DocumentDBNode } from '../explorer/docdbNode'
+import { CreateClusterWizard } from '../wizards/createClusterWizard'
+import { CreateClusterInput } from '@aws-sdk/client-docdb-elastic'
+import { DocDBEngine, DocumentDBClient } from '../../shared/clients/docdbClient'
+
+/**
+ * Creates a DocumentDB cluster.
+ *
+ * Prompts the user for the cluster name.
+ * Creates the cluster.
+ * Refreshes the node.
+ */
+export async function createCluster(node?: DocumentDBNode) {
+    getLogger().debug('docdb: CreateCluster called for: %O', node)
+
+    await telemetry.docdb_createCluster.run(async (span) => {
+        if (!node) {
+            throw new ToolkitError('No node specified for CreateCluster')
+        }
+
+        span.record({ awsRegion: node?.client.regionCode })
+        const wizard = new CreateClusterWizard(node?.client, {})
+        const result = await wizard.run()
+
+        if (!result) {
+            getLogger().debug('docdb: createCluster cancelled')
+            throw new ToolkitError('User cancelled createCluster wizard', { cancelled: true })
+        }
+
+        const clusterName = result.RegionalCluster?.DBClusterIdentifier ?? result.ElasticCluster?.clusterName
+        getLogger().info(`docdb: Creating cluster: ${clusterName}`)
+        let cluster
+
+        try {
+            if (result.ClusterType === 'elastic') {
+                cluster = await node.client.createElasticCluster(result.ElasticCluster as CreateClusterInput)
+            } else {
+                cluster = await node.client.createCluster(result.RegionalCluster)
+
+                // create instances for cluster
+                if (cluster && result.RegionalCluster.DBInstanceCount) {
+                    await createInstancesForCluster(
+                        node.client,
+                        clusterName,
+                        result.RegionalCluster.DBInstanceClass,
+                        result.RegionalCluster.DBInstanceCount
+                    )
+                }
+            }
+
+            getLogger().info('docdb: Created cluster: %O', cluster)
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.createCluster.success', 'Created cluster: {0}', clusterName)
+            )
+
+            node?.refresh()
+            return cluster
+        } catch (e) {
+            getLogger().error(`docdb: Failed to create cluster ${clusterName}: %s`, e)
+            void showViewLogsMessage(
+                localize('AWS.docdb.createCluster.error', 'Failed to create cluster: {0}', clusterName)
+            )
+            throw ToolkitError.chain(e, `Failed to create cluster ${clusterName}`)
+        }
+    })
+}
+
+export async function createInstancesForCluster(
+    client: DocumentDBClient,
+    clusterName: string,
+    instanceClass: string = 'db.t3.medium',
+    instanceCount: number
+) {
+    const tasks = []
+
+    for (let index = 0; index < instanceCount; index++) {
+        tasks.push(
+            client.createInstance({
+                Engine: DocDBEngine,
+                DBClusterIdentifier: clusterName,
+                DBInstanceIdentifier: index === 0 ? clusterName : `${clusterName}${index + 1}`,
+                DBInstanceClass: instanceClass,
+            })
+        )
+    }
+
+    try {
+        await Promise.all(tasks)
+    } catch (e) {
+        throw ToolkitError.chain(e, `Failed to create instance for cluster ${clusterName}`, {
+            code: 'docdbCreateInstanceForCluster',
+        })
+    }
+}
diff --git a/packages/core/src/docdb/commands/createInstance.ts b/packages/core/src/docdb/commands/createInstance.ts
new file mode 100644
index 00000000000..34e7808ce62
--- /dev/null
+++ b/packages/core/src/docdb/commands/createInstance.ts
@@ -0,0 +1,86 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { ToolkitError } from '../../shared/errors'
+import { getLogger } from '../../shared/logger/logger'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { showViewLogsMessage } from '../../shared/utilities/messages'
+import { DBClusterNode } from '../explorer/dbClusterNode'
+import { CreateInstanceWizard } from '../wizards/createInstanceWizard'
+import { CreateDBInstanceMessage } from '@aws-sdk/client-docdb'
+import { DocDBEngine, MaxInstanceCount } from '../../shared/clients/docdbClient'
+
+/**
+ * Creates a DocumentDB instance.
+ *
+ * Prompts the user for the instance name and class.
+ * Creates the instance.
+ * Refreshes the cluster node.
+ */
+export async function createInstance(node: DBClusterNode) {
+    getLogger().debug('docdb: CreateInstance called for: %O', node)
+
+    await telemetry.docdb_createInstance.run(async () => {
+        if (!node) {
+            throw new ToolkitError('No node specified for CreateInstance')
+        }
+
+        const instances = await node.client.listInstances([node.arn])
+        if (instances.length >= MaxInstanceCount) {
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.createInstance.limitReached', 'Max instances in use')
+            )
+            throw new ToolkitError('Max instances in use', { code: 'documentDBMaxInstancesInUse' })
+        }
+
+        const generateInstanceName = (clusterName: string) =>
+            instances.length === 0 ? clusterName : `${clusterName}${++instances.length}`
+
+        const options = {
+            implicitState: {
+                DBInstanceIdentifier: generateInstanceName(node.cluster.DBClusterIdentifier ?? ''),
+                DBInstanceClass: instances[0]?.DBInstanceClass,
+            },
+        }
+        const wizard = new CreateInstanceWizard(node.regionCode, node.cluster, options, node.client)
+
+        const result = await wizard.run()
+
+        if (!result) {
+            getLogger().debug('docdb: CreateInstance cancelled')
+            throw new ToolkitError('User cancelled createInstance wizard', { cancelled: true })
+        }
+
+        const instanceName = result.DBInstanceIdentifier
+        getLogger().info(`docdb: Creating instance: ${instanceName}`)
+
+        try {
+            const request: CreateDBInstanceMessage = {
+                Engine: DocDBEngine,
+                DBClusterIdentifier: node.cluster.DBClusterIdentifier,
+                DBInstanceIdentifier: result.DBInstanceIdentifier,
+                DBInstanceClass: result.DBInstanceClass !== '' ? result.DBInstanceClass : undefined,
+            }
+
+            const instance = await node.createInstance(request)
+
+            getLogger().info('docdb: Created instance: %O', instance)
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.createInstance.success', 'Creating instance: {0}', instanceName)
+            )
+
+            node.refresh()
+            return instance
+        } catch (e) {
+            getLogger().error(`docdb: Failed to create instance ${instanceName}: %s`, e)
+            void showViewLogsMessage(
+                localize('AWS.docdb.createInstance.error', 'Failed to create instance: {0}', instanceName)
+            )
+            throw ToolkitError.chain(e, `Failed to create instance ${instanceName}`)
+        }
+    })
+}
diff --git a/packages/core/src/docdb/commands/deleteCluster.ts b/packages/core/src/docdb/commands/deleteCluster.ts
new file mode 100644
index 00000000000..bb725ae2ab1
--- /dev/null
+++ b/packages/core/src/docdb/commands/deleteCluster.ts
@@ -0,0 +1,108 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { ToolkitError } from '../../shared/errors'
+import { getLogger } from '../../shared/logger/logger'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { showViewLogsMessage } from '../../shared/utilities/messages'
+import { DBClusterNode } from '../explorer/dbClusterNode'
+import { showQuickPick } from '../../shared/ui/pickerPrompter'
+import { formatDate, formatTime } from '../../shared/date'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { DBElasticClusterNode } from '../explorer/dbElasticClusterNode'
+import { assertNodeAvailable } from '../utils'
+
+/**
+ * Deletes a DocumentDB cluster.
+ *
+ * Prompts the user for confirmation, and whether to keep a snapshot
+ * Deletes the cluster and all instances.
+ * Refreshes the cluster node.
+ */
+export async function deleteCluster(node: DBClusterNode | DBElasticClusterNode) {
+    getLogger().debug('docdb: DeleteCluster called for: %O', node)
+
+    await telemetry.docdb_deleteCluster.run(async (span) => {
+        assertNodeAvailable(node, 'DeleteCluster')
+        const clusterName = node.name
+        const isRegionalCluster = node instanceof DBClusterNode
+
+        if (isRegionalCluster && node.cluster.DeletionProtection) {
+            void vscode.window.showErrorMessage(
+                localize(
+                    'AWS.docdb.deleteCluster.protected',
+                    'Clusters cannot be deleted while deletion protection is enabled'
+                )
+            )
+            throw new ToolkitError('Deletion protection is active', {
+                cancelled: true,
+                code: 'docdbDeletionProtectionInUse',
+            })
+        }
+
+        const takeSnapshot = await showQuickPick(
+            [
+                { label: localize('AWS.generic.response.yes', 'Yes'), data: true },
+                { label: localize('AWS.generic.response.no', 'No'), data: false },
+            ],
+            {
+                title: localize(
+                    'AWS.docdb.deleteCluster.promptSnapshot',
+                    'Delete Cluster - Keep a snapshot of the data?'
+                ),
+            }
+        )
+
+        if (takeSnapshot === undefined) {
+            getLogger().debug('docdb: DeleteCluster cancelled')
+            throw new ToolkitError('User cancelled deleteCluster wizard', { cancelled: true })
+        }
+
+        const isConfirmed = await showConfirmationDialog()
+        if (!isConfirmed) {
+            getLogger().debug('docdb: DeleteCluster cancelled')
+            throw new ToolkitError('User cancelled deleteCluster wizard', { cancelled: true })
+        }
+
+        try {
+            getLogger().debug(`docdb: Deleting cluster: ${clusterName}`)
+
+            let finalSnapshotId: string | undefined = undefined
+            if (takeSnapshot) {
+                finalSnapshotId = `${clusterName}-${formatDate()}-${formatTime()}`
+            }
+
+            const cluster = await node.deleteCluster(finalSnapshotId)
+
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.deleteCluster.success', 'Deleting cluster: {0}', clusterName)
+            )
+
+            await node.waitUntilStatusChanged()
+            node.parent.refresh()
+            getLogger().info('docdb: Deleted cluster: %O', cluster)
+            return cluster
+        } catch (e) {
+            getLogger().error(`docdb: Failed to delete cluster ${clusterName}: %s`, e)
+            void showViewLogsMessage(
+                localize('AWS.docdb.deleteCluster.error', 'Failed to delete cluster: {0}', clusterName)
+            )
+            throw ToolkitError.chain(e, `Failed to delete cluster ${clusterName}`)
+        }
+    })
+}
+
+async function showConfirmationDialog(): Promise {
+    const prompt = localize('AWS.docdb.deleteCluster.prompt', "Enter 'delete entire cluster' to confirm deletion")
+    const confirmValue = localize('AWS.docdb.deleteCluster.confirmValue', 'delete entire cluster').toLowerCase()
+    const confirmationInput = await vscode.window.showInputBox({
+        prompt,
+        placeHolder: confirmValue,
+        validateInput: (input) => (input?.toLowerCase() !== confirmValue ? prompt : undefined),
+    })
+
+    return confirmationInput === confirmValue
+}
diff --git a/packages/core/src/docdb/commands/deleteInstance.ts b/packages/core/src/docdb/commands/deleteInstance.ts
new file mode 100644
index 00000000000..6202d317a07
--- /dev/null
+++ b/packages/core/src/docdb/commands/deleteInstance.ts
@@ -0,0 +1,78 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { ToolkitError } from '../../shared/errors'
+import { getLogger } from '../../shared/logger/logger'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { showViewLogsMessage } from '../../shared/utilities/messages'
+import { DBInstanceNode } from '../explorer/dbInstanceNode'
+import { DBClusterNode } from '../explorer/dbClusterNode'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { assertNodeAvailable } from '../utils'
+
+/**
+ * Deletes a DocumentDB instance.
+ *
+ * Prompts the user for confirmation.
+ * Deletes the instance.
+ * Refreshes the parent cluster node.
+ */
+export async function deleteInstance(node: DBInstanceNode) {
+    getLogger().debug('docdb: DeleteInstance called for: %O', node)
+
+    await telemetry.docdb_deleteInstance.run(async () => {
+        assertNodeAvailable(node, 'DeleteInstance')
+        const parent = node.parent as DBClusterNode
+        const client = parent.client
+        const instanceName = node.instance.DBInstanceIdentifier ?? ''
+
+        if (!parent?.isAvailable) {
+            void vscode.window.showErrorMessage(
+                localize('AWS.docdb.deleteInstance.clusterStopped', 'Cluster must be started to delete instances')
+            )
+            throw new ToolkitError('Cluster not running', { cancelled: true })
+        }
+
+        const isConfirmed = await showConfirmationDialog(instanceName)
+        if (!isConfirmed) {
+            getLogger().debug('docdb: DeleteInstance cancelled')
+            throw new ToolkitError('User cancelled deleteInstance', { cancelled: true })
+        }
+
+        try {
+            getLogger().info(`docdb: Deleting instance: ${instanceName}`)
+
+            const instance = await client.deleteInstance({
+                DBInstanceIdentifier: instanceName,
+            })
+
+            getLogger().info('docdb: Deleted instance: %O', instance)
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.deleteInstance.success', 'Deleting instance: {0}', instanceName)
+            )
+
+            parent.refresh()
+            return instance
+        } catch (e) {
+            getLogger().error(`docdb: Failed to delete instance ${instanceName}: %s`, e)
+            void showViewLogsMessage(
+                localize('AWS.docdb.deleteInstance.error', 'Failed to delete instance: {0}', instanceName)
+            )
+            throw ToolkitError.chain(e, `Failed to delete instance ${instanceName}`)
+        }
+    })
+}
+
+async function showConfirmationDialog(instanceName: string): Promise {
+    const prompt = localize('AWS.docdb.deleteInstance.prompt', 'Enter {0} to confirm deletion', instanceName)
+    const confirmationInput = await vscode.window.showInputBox({
+        prompt,
+        placeHolder: instanceName,
+        validateInput: (input) => (input !== instanceName ? prompt : undefined),
+    })
+
+    return confirmationInput === instanceName
+}
diff --git a/packages/core/src/docdb/commands/modifyInstance.ts b/packages/core/src/docdb/commands/modifyInstance.ts
new file mode 100644
index 00000000000..3df5febaf45
--- /dev/null
+++ b/packages/core/src/docdb/commands/modifyInstance.ts
@@ -0,0 +1,98 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { ToolkitError } from '../../shared/errors'
+import { getLogger } from '../../shared/logger/logger'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { showViewLogsMessage } from '../../shared/utilities/messages'
+import { DBInstanceNode } from '../explorer/dbInstanceNode'
+import { DBCluster, ModifyDBInstanceMessage } from '@aws-sdk/client-docdb'
+import { DBStorageType, DocumentDBClient } from '../../shared/clients/docdbClient'
+import { createQuickPick, DataQuickPickItem } from '../../shared/ui/pickerPrompter'
+import { isValidResponse } from '../../shared/wizards/wizard'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { assertNodeAvailable } from '../utils'
+
+/**
+ * Modifies a DocumentDB instance.
+ *
+ * Prompts the user for the instance class.
+ * Updates the instance.
+ * Refreshes the node.
+ */
+export async function modifyInstance(node: DBInstanceNode) {
+    getLogger().debug('docdb: ModifyInstance called for: %O', node)
+
+    await telemetry.docdb_resizeInstance.run(async () => {
+        assertNodeAvailable(node, 'ModifyInstance')
+        const instanceName = node.instance.DBInstanceIdentifier
+        const parent = node.parent
+
+        const quickPickItems = await getInstanceClassOptions(parent.client, parent.cluster)
+        const newInstanceClass = await promptForInstanceClass(quickPickItems, node.instance.DBInstanceClass ?? '')
+
+        if (!newInstanceClass) {
+            getLogger().debug('docdb: ModifyInstance cancelled')
+            throw new ToolkitError('User cancelled modifyInstance wizard', { cancelled: true })
+        }
+
+        try {
+            const request: ModifyDBInstanceMessage = {
+                DBInstanceIdentifier: instanceName,
+                DBInstanceClass: newInstanceClass,
+                ApplyImmediately: true,
+            }
+
+            const instance = await parent.client.modifyInstance(request)
+
+            getLogger().info('docdb: Modified instance: %O', instanceName)
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.modifyInstance.success', 'Modified instance: {0}', instanceName)
+            )
+
+            await node.waitUntilStatusChanged()
+            parent.refresh()
+            return instance
+        } catch (e) {
+            getLogger().error(`docdb: Failed to modify instance ${instanceName}: %s`, e)
+            void showViewLogsMessage(
+                localize('AWS.docdb.modifyInstance.error', 'Failed to modify instance: {0}', instanceName)
+            )
+            throw ToolkitError.chain(e, `Failed to modify instance ${instanceName}`)
+        }
+    })
+}
+
+async function getInstanceClassOptions(
+    client: DocumentDBClient,
+    cluster: DBCluster
+): Promise[]> {
+    const options = await client.listInstanceClassOptions(
+        cluster.EngineVersion,
+        cluster.StorageType ?? DBStorageType.Standard
+    )
+
+    const items: DataQuickPickItem[] = options.map((option) => {
+        return {
+            data: option.DBInstanceClass,
+            label: option.DBInstanceClass ?? '(unknown)',
+        }
+    })
+
+    return items
+}
+
+async function promptForInstanceClass(items: any[], currentValue: string) {
+    const prompter = createQuickPick(items, {
+        title: localize('AWS.docdb.createInstance.instanceClass.prompt', 'Select instance class'),
+        value: currentValue,
+    })
+
+    prompter.recentItem = items.find((item) => item.data === currentValue)
+
+    const response = await prompter.prompt()
+    return isValidResponse(response) ? response : undefined
+}
diff --git a/packages/core/src/docdb/commands/rebootInstance.ts b/packages/core/src/docdb/commands/rebootInstance.ts
new file mode 100644
index 00000000000..ab706803d87
--- /dev/null
+++ b/packages/core/src/docdb/commands/rebootInstance.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 * as localizedText from '../../shared/localizedText'
+import { ToolkitError } from '../../shared/errors'
+import { getLogger } from '../../shared/logger/logger'
+import { CancellationError } from '../../shared/utilities/timeoutUtils'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { showConfirmationMessage, showViewLogsMessage } from '../../shared/utilities/messages'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { DBInstanceNode } from '../explorer/dbInstanceNode'
+import { assertNodeAvailable } from '../utils'
+
+/**
+ * Reboots a DocumentDB instance.
+ * Refreshes the parent node.
+ */
+export async function rebootInstance(node: DBInstanceNode) {
+    getLogger().debug('docdb: RebootInstance called for: %O', node)
+
+    await telemetry.docdb_rebootInstance.run(async () => {
+        assertNodeAvailable(node, 'RebootInstance')
+
+        const isConfirmed = await showConfirmationMessage({
+            prompt: localize(
+                'AWS.docdb.rebootInstance.prompt',
+                'Are you sure you want to reboot instance {0}?',
+                node.name
+            ),
+            confirm: localizedText.yes,
+            cancel: localizedText.cancel,
+        })
+        if (!isConfirmed) {
+            getLogger().debug('docdb: RebootInstance canceled')
+            throw new CancellationError('user')
+        }
+
+        const instanceName = node.instance.DBInstanceIdentifier
+        try {
+            const instance = await node.rebootInstance()
+
+            getLogger().info('docdb: Rebooting instance: %s', instanceName)
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.rebootInstance.success', 'Rebooting instance: {0}', instanceName)
+            )
+
+            node.parent.refresh()
+            return instance
+        } catch (e) {
+            getLogger().error(`docdb: Failed to reboot instance ${instanceName}: %s`, e)
+            void showViewLogsMessage(
+                localize('AWS.docdb.rebootInstance.error', 'Failed to reboot instance: {0}', instanceName)
+            )
+            throw ToolkitError.chain(e, `Failed to reboot instance ${instanceName}`)
+        }
+    })
+}
diff --git a/packages/core/src/docdb/commands/renameCluster.ts b/packages/core/src/docdb/commands/renameCluster.ts
new file mode 100644
index 00000000000..f64ae7b1d98
--- /dev/null
+++ b/packages/core/src/docdb/commands/renameCluster.ts
@@ -0,0 +1,61 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { ToolkitError } from '../../shared/errors'
+import { getLogger } from '../../shared/logger/logger'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { showViewLogsMessage } from '../../shared/utilities/messages'
+import { assertNodeAvailable, validateClusterName } from '../utils'
+import { DBClusterNode } from '../explorer/dbClusterNode'
+import { DBGlobalClusterNode } from '../explorer/dbGlobalClusterNode'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { sleep } from '../../shared/utilities/timeoutUtils'
+
+/**
+ * Renames a DocumentDB cluster.
+ *
+ * Prompts the user for the new cluster name
+ * Updates the cluster.
+ * Refreshes the node.
+ */
+export async function renameCluster(node: DBClusterNode | DBGlobalClusterNode) {
+    getLogger().debug('docdb: RenameCluster called for: %O', node)
+
+    await telemetry.docdb_renameCluster.run(async () => {
+        assertNodeAvailable(node, 'RenameCluster')
+        const clusterName = node.name
+
+        const newClusterName = await vscode.window.showInputBox({
+            prompt: localize('AWS.docdb.renameCluster.prompt', 'New cluster name'),
+            value: clusterName,
+            validateInput: validateClusterName,
+        })
+
+        if (!newClusterName) {
+            getLogger().debug('docdb: RenameCluster cancelled')
+            throw new ToolkitError('User cancelled renameCluster', { cancelled: true })
+        }
+
+        try {
+            const cluster = await node.renameCluster(newClusterName)
+
+            getLogger().info('docdb: Renamed cluster: %O', cluster)
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.renameCluster.success', 'Updated cluster: {0}', clusterName)
+            )
+
+            await sleep(1000) // wait for server to update status
+            node.parent.refresh()
+            return cluster
+        } catch (e) {
+            getLogger().error(`docdb: Failed to rename cluster ${clusterName}: %s`, e)
+            void showViewLogsMessage(
+                localize('AWS.docdb.renameCluster.error', 'Failed to rename cluster: {0}', clusterName)
+            )
+            throw ToolkitError.chain(e, `Failed to rename cluster ${clusterName}`)
+        }
+    })
+}
diff --git a/packages/core/src/docdb/commands/renameInstance.ts b/packages/core/src/docdb/commands/renameInstance.ts
new file mode 100644
index 00000000000..414f780e521
--- /dev/null
+++ b/packages/core/src/docdb/commands/renameInstance.ts
@@ -0,0 +1,58 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { getLogger } from '../../shared/logger/logger'
+import { ToolkitError } from '../../shared/errors'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { showViewLogsMessage } from '../../shared/utilities/messages'
+import { assertNodeAvailable, validateInstanceName } from '../utils'
+import { DBInstanceNode } from '../explorer/dbInstanceNode'
+import { telemetry } from '../../shared/telemetry/telemetry'
+
+/**
+ * Renames a DocumentDB instance.
+ *
+ * Prompts the user for the new instance name
+ * Updates the instance.
+ * Refreshes the node.
+ */
+export async function renameInstance(node: DBInstanceNode) {
+    getLogger().debug('docdb: RenameInstance called for: %O', node)
+
+    await telemetry.docdb_renameInstance.run(async () => {
+        assertNodeAvailable(node, 'RenameInstance')
+        const instanceName = node.instance.DBInstanceIdentifier
+
+        const newInstanceName = await vscode.window.showInputBox({
+            prompt: localize('AWS.docdb.renameInstance.prompt', 'New instance name'),
+            value: instanceName,
+            validateInput: validateInstanceName,
+        })
+
+        if (!newInstanceName) {
+            getLogger().debug('docdb: RenameInstance cancelled')
+            throw new ToolkitError('User cancelled renameInstance', { cancelled: true })
+        }
+
+        try {
+            const instance = await node.renameInstance(newInstanceName)
+
+            getLogger().info('docdb: Renamed instance: %O', instance)
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.renameInstance.success', 'Updated instance: {0}', instanceName)
+            )
+
+            node.refresh()
+            return instance
+        } catch (e) {
+            getLogger().error(`docdb: Failed to rename instance ${instanceName}: %s`, e)
+            void showViewLogsMessage(
+                localize('AWS.docdb.renameInstance.error', 'Failed to rename instance: {0}', instanceName)
+            )
+            throw ToolkitError.chain(e, `Failed to rename instance ${instanceName}`)
+        }
+    })
+}
diff --git a/packages/core/src/docdb/commands/startCluster.ts b/packages/core/src/docdb/commands/startCluster.ts
new file mode 100644
index 00000000000..60dbda80e70
--- /dev/null
+++ b/packages/core/src/docdb/commands/startCluster.ts
@@ -0,0 +1,22 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { getLogger } from '../../shared/logger/logger'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { DBClusterNode } from '../explorer/dbClusterNode'
+
+export function startCluster(node?: DBClusterNode): Promise {
+    return telemetry.docdb_startCluster.run(async () => {
+        if (node?.arn && node?.regionCode) {
+            await node.client.startCluster(node.arn)
+            getLogger().info('docdb: Start cluster: %O', node.name)
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.startCluster.success', 'Starting cluster: {0}', node.name)
+            )
+        }
+    })
+}
diff --git a/packages/core/src/docdb/commands/stopCluster.ts b/packages/core/src/docdb/commands/stopCluster.ts
new file mode 100644
index 00000000000..46f23f3eab9
--- /dev/null
+++ b/packages/core/src/docdb/commands/stopCluster.ts
@@ -0,0 +1,39 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import * as localizedText from '../../shared/localizedText'
+import { getLogger } from '../../shared/logger/logger'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { CancellationError } from '../../shared/utilities/timeoutUtils'
+import { showConfirmationMessage } from '../../shared/utilities/messages'
+import { DBClusterNode } from '../explorer/dbClusterNode'
+
+export function stopCluster(node?: DBClusterNode): Promise {
+    return telemetry.docdb_stopCluster.run(async () => {
+        if (node?.arn && node?.regionCode) {
+            const isConfirmed = await showConfirmationMessage({
+                prompt: localize(
+                    'AWS.docdb.stopCluster.prompt',
+                    'Are you sure you want to stop cluster {0}?',
+                    node.name
+                ),
+                confirm: localizedText.yes,
+                cancel: localizedText.cancel,
+            })
+            if (!isConfirmed) {
+                getLogger().debug('docdb: StopCluster cancelled')
+                throw new CancellationError('user')
+            }
+
+            await node.client.stopCluster(node.arn)
+            getLogger().info('docdb: Stop cluster: %O', node.name)
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.stopCluster.success', 'Stopping cluster: {0}', node.name)
+            )
+        }
+    })
+}
diff --git a/packages/core/src/docdb/commands/tagCommands.ts b/packages/core/src/docdb/commands/tagCommands.ts
new file mode 100644
index 00000000000..d3c46176adb
--- /dev/null
+++ b/packages/core/src/docdb/commands/tagCommands.ts
@@ -0,0 +1,112 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { getLogger } from '../../shared/logger/logger'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { DBResourceNode } from '../explorer/dbResourceNode'
+import { DataQuickPickItem, showQuickPick } from '../../shared/ui/pickerPrompter'
+import { ToolkitError } from '../../shared/errors'
+
+export async function listTags(node: DBResourceNode): Promise {
+    return telemetry.docdb_listTags.run(async () => {
+        const tagMap = await node.listTags()
+        const tags = Object.entries(tagMap ?? {}).map(([key, value]) => `• ${key} = ${value}`)
+        const detail = tags.length ? tags.join('\r\n') : '[No tags assigned]'
+
+        const addCommandText = localize('AWS.docdb.tags.add', 'Add tag...')
+        const removeCommandText = tags.length ? localize('AWS.docdb.tags.remove', 'Remove...') : ''
+        const commands = tags.length ? [addCommandText, removeCommandText] : [addCommandText]
+
+        const response = await vscode.window.showInformationMessage(
+            `Tags for ${node.name}:`,
+            { modal: true, detail },
+            ...commands
+        )
+
+        switch (response) {
+            case addCommandText:
+                await addTag(node)
+                break
+            case removeCommandText:
+                await removeTag(node)
+                break
+        }
+    })
+}
+
+export async function addTag(node: DBResourceNode): Promise {
+    return telemetry.docdb_addTag.run(async () => {
+        const key = await vscode.window.showInputBox({
+            title: 'Add Tag',
+            prompt: localize('AWS.docdb.tags.add.keyPrompt', 'Enter a key for the new tag'),
+            validateInput: (input) => validateTag(input, 1, 'key'),
+        })
+        if (key === undefined) {
+            getLogger().info('docdb: AddTag cancelled')
+            throw new ToolkitError('User cancelled', { cancelled: true })
+        }
+
+        const value = await vscode.window.showInputBox({
+            title: 'Add Tag',
+            prompt: localize('AWS.docdb.tags.add.valuePrompt', 'Enter the value for the new tag (optional)'),
+            validateInput: (input) => validateTag(input, 0, 'value'),
+        })
+        if (value === undefined) {
+            getLogger().info('docdb: AddTag cancelled')
+            throw new ToolkitError('User cancelled', { cancelled: true })
+        }
+
+        const tag = { [key.trim()]: value.trim() }
+        await node.client.addResourceTags({ resourceArn: node.arn, tags: tag })
+        getLogger().info('docdb: Added resource tag for: %O', node.name)
+        void vscode.window.showInformationMessage(localize('AWS.docdb.tags.add.success', 'Tag added'))
+    })
+}
+
+export async function removeTag(node: DBResourceNode): Promise {
+    return telemetry.docdb_removeTag.run(async () => {
+        const tagMap = await node.listTags()
+        const items = Object.entries(tagMap ?? {}).map>(([key, value]) => {
+            return {
+                data: key,
+                label: key,
+                description: value,
+            }
+        })
+        if (items.length === 0) {
+            return
+        }
+
+        const resp = await showQuickPick(items, {
+            title: localize('AWS.docdb.tags.remove.title', 'Remove a tag from {0}', node.name),
+        })
+
+        if (resp === undefined) {
+            getLogger().info('docdb: RemoveTag cancelled')
+            throw new ToolkitError('User cancelled', { cancelled: true })
+        }
+
+        await node.client.removeResourceTags({ resourceArn: node.arn, tagKeys: [resp] })
+        getLogger().info('docdb: Removed resource tag for: %O', node.name)
+        void vscode.window.showInformationMessage(localize('AWS.docdb.tags.remove.success', 'Tag removed'))
+    })
+}
+
+export function validateTag(input: string, minLength: number, name: string): string | undefined {
+    if (input.trim().length < minLength) {
+        return localize('AWS.docdb.validateTag.error.invalidLength', `Tag ${name} cannot be blank`)
+    }
+
+    if (!/^([\p{L}\p{Z}\p{N}\._:/=+\-@]*)$/u.test(input)) {
+        return localize(
+            'AWS.docdb.validateTag.error.invalidCharacters',
+            `Tag ${name} may only contain unicode letters, digits, whitespace, or one of these symbols: _ . : / = + - @`
+        )
+    }
+
+    return undefined
+}
diff --git a/packages/core/src/docdb/explorer/dbClusterNode.ts b/packages/core/src/docdb/explorer/dbClusterNode.ts
new file mode 100644
index 00000000000..5544523d6f9
--- /dev/null
+++ b/packages/core/src/docdb/explorer/dbClusterNode.ts
@@ -0,0 +1,192 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as os from 'os'
+import * as vscode from 'vscode'
+import { inspect } from 'util'
+import { copyToClipboard } from '../../shared/utilities/messages'
+import { makeChildrenNodes } from '../../shared/treeview/utils'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { CreateDBInstanceMessage, DBCluster, ModifyDBClusterMessage } from '@aws-sdk/client-docdb'
+import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
+import { DBResourceNode } from './dbResourceNode'
+import { DBInstanceNode } from './dbInstanceNode'
+import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode'
+import { DBInstance, DocumentDBClient } from '../../shared/clients/docdbClient'
+import { DocDBContext } from './docdbContext'
+import { toTitleCase } from '../../shared/utilities/textUtilities'
+import { getAwsConsoleUrl } from '../../shared/awsConsole'
+import { getLogger } from '../../shared/logger/logger'
+
+export type DBClusterRole = 'global' | 'regional' | 'primary' | 'secondary'
+
+/**
+ * An AWS Explorer node representing DocumentDB clusters.
+ *
+ * Contains instances for a specific cluster as child nodes.
+ */
+export class DBClusterNode extends DBResourceNode {
+    override name = this.cluster.DBClusterIdentifier!
+    override arn = this.cluster.DBClusterArn!
+    public instances: DBInstance[] = []
+    private childNodes: DBInstanceNode[] = []
+
+    constructor(
+        public readonly parent: AWSTreeNodeBase,
+        readonly cluster: DBCluster,
+        client: DocumentDBClient,
+        readonly clusterRole: DBClusterRole = 'regional'
+    ) {
+        super(client, cluster.DBClusterIdentifier ?? '[Cluster]', vscode.TreeItemCollapsibleState.Collapsed)
+        getLogger().debug(`NEW DBClusterNode: ${cluster.DBClusterArn}`)
+        this.arn = cluster.DBClusterArn ?? ''
+        this.name = cluster.DBClusterIdentifier ?? ''
+        this.contextValue = this.getContext()
+        this.iconPath = new vscode.ThemeIcon(
+            this.isAvailable ? 'layers-active' : this.isStopped ? 'layers-dot' : 'loading~spin'
+        )
+        this.description = this.getDescription()
+        this.tooltip = `${this.name}${os.EOL}Engine: ${this.cluster.EngineVersion}${os.EOL}Status: ${this.cluster.Status}`
+        if (this.isStatusRequiringPolling()) {
+            getLogger().debug(`${this.arn} requires polling.`)
+            this.trackChanges()
+        } else {
+            getLogger().debug(`${this.arn} does NOT require polling.`)
+        }
+    }
+
+    public override async getChildren(): Promise {
+        getLogger().debug(`DBClusterNode.getChildren() called`)
+        return telemetry.docdb_listInstances.run(async () => {
+            return await makeChildrenNodes({
+                getChildNodes: async () => {
+                    this.instances = (await this.client.listInstances([this.arn])).map((i) => {
+                        const member = this.cluster.DBClusterMembers?.find(
+                            (m) => m.DBInstanceIdentifier === i.DBInstanceIdentifier
+                        )
+                        return { ...i, ...member }
+                    })
+                    const nodes = this.instances.map((instance) => new DBInstanceNode(this, instance))
+                    this.childNodes = nodes
+                    return nodes
+                },
+                getNoChildrenPlaceholderNode: async () => {
+                    const title = localize('AWS.explorerNode.docdb.addInstance', 'Add instance...')
+                    const placeholder = new PlaceholderNode(this, title)
+                    placeholder.contextValue = 'awsDocDB.placeholder'
+                    placeholder.command = { title, command: 'aws.docdb.createInstance', arguments: [this] }
+                    return placeholder
+                },
+                sort: (item1, item2) => item1.name.localeCompare(item2.name),
+            })
+        })
+    }
+
+    private getContext() {
+        const context = `${DocDBContext.Cluster}-${this.clusterRole}`
+        if (this.isAvailable) {
+            return `${context}-running`
+        } else if (this.isStopped) {
+            return `${context}-stopped`
+        }
+        return context
+    }
+
+    public getDescription(): string | boolean {
+        const role = toTitleCase(this.clusterRole)
+        if (!this.isAvailable) {
+            return `${role} cluster • ${toTitleCase(this.status ?? ' ')}`
+        }
+        return `${role} cluster`
+    }
+
+    public async createInstance(request: CreateDBInstanceMessage): Promise {
+        return await this.client.createInstance(request)
+    }
+
+    public async renameCluster(clusterName: string): Promise {
+        const request: ModifyDBClusterMessage = {
+            DBClusterIdentifier: this.cluster.DBClusterIdentifier,
+            NewDBClusterIdentifier: clusterName,
+            ApplyImmediately: true,
+        }
+        const response = await this.client.modifyCluster(request)
+        this.name = response?.DBClusterIdentifier ?? this.name
+        return response
+    }
+
+    public async deleteCluster(finalSnapshotId: string | undefined): Promise {
+        const instances = await this.client.listInstances([this.arn])
+
+        const tasks = []
+        for (const instance of instances) {
+            tasks.push(
+                this.client.deleteInstance({
+                    DBInstanceIdentifier: instance.DBInstanceIdentifier,
+                })
+            )
+        }
+        await Promise.all(tasks)
+
+        return await this.client.deleteCluster({
+            DBClusterIdentifier: this.cluster.DBClusterIdentifier,
+            FinalDBSnapshotIdentifier: finalSnapshotId,
+            SkipFinalSnapshot: finalSnapshotId === undefined,
+        })
+    }
+
+    override get status() {
+        return this.cluster.Status
+    }
+
+    override async getStatus() {
+        const clusters = await this.client.listClusters(this.arn)
+        const cluster = clusters[0]
+
+        if (!cluster) {
+            getLogger().warn(`No cluster found for ARN: ${this.arn}`)
+            return undefined
+        }
+
+        getLogger().debug(`Get Status: status ${cluster.Status} for cluster ${this.arn}`)
+
+        this.cluster.Status = cluster.Status
+        return cluster.Status
+    }
+
+    override getConsoleUrl() {
+        return getAwsConsoleUrl('docdb', this.regionCode).with({
+            fragment: `cluster-details/${this.name}`,
+        })
+    }
+
+    override copyEndpoint() {
+        if (this.cluster.Endpoint) {
+            return copyToClipboard(this.cluster.Endpoint, this.name)
+        }
+        return Promise.reject()
+    }
+
+    override refreshTree(): void {
+        getLogger().debug(`(DBClusterNode) Refreshing tree for instance: ${this.arn}`)
+        this.refresh()
+        this.parent.refresh()
+    }
+
+    override clearTimer(): void {
+        this.pollingSet.delete(this.arn)
+        this.pollingSet.clearTimer()
+        for (const node of this.childNodes) {
+            getLogger().debug(`(clearTimer) Removing Polling from node: ${node.arn}`)
+            node.pollingSet.delete(node.arn)
+            node.pollingSet.clearTimer()
+        }
+    }
+
+    public override [inspect.custom](): string {
+        return 'DBClusterNode'
+    }
+}
diff --git a/packages/core/src/docdb/explorer/dbElasticClusterNode.ts b/packages/core/src/docdb/explorer/dbElasticClusterNode.ts
new file mode 100644
index 00000000000..d228c3bbefd
--- /dev/null
+++ b/packages/core/src/docdb/explorer/dbElasticClusterNode.ts
@@ -0,0 +1,117 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { inspect } from 'util'
+import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
+import { DBResourceNode } from './dbResourceNode'
+import { DBElasticCluster, DocumentDBClient } from '../../shared/clients/docdbClient'
+import { DocDBContext } from './docdbContext'
+import { copyToClipboard } from '../../shared/utilities/messages'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { getAwsConsoleUrl } from '../../shared/awsConsole'
+import { getLogger } from '../../shared/logger/logger'
+
+/**
+ * An AWS Explorer node representing DocumentDB elastic clusters.
+ */
+export class DBElasticClusterNode extends DBResourceNode {
+    override name = this.cluster.clusterName!
+    override arn = this.cluster.clusterArn!
+
+    constructor(
+        public readonly parent: AWSTreeNodeBase,
+        public cluster: DBElasticCluster,
+        client: DocumentDBClient
+    ) {
+        super(client, cluster.clusterName ?? '[Cluster]', vscode.TreeItemCollapsibleState.None)
+        this.contextValue = this.getContext()
+        this.iconPath = new vscode.ThemeIcon(
+            this.isAvailable ? 'layers-active' : this.isStopped ? 'layers-dot' : 'loading~spin'
+        )
+        this.description = this.getDescription()
+        this.tooltip = `${this.name}\nStatus: ${this.status}`
+        if (this.isStatusRequiringPolling()) {
+            getLogger().debug(`${this.arn} requires polling.`)
+            this.trackChanges()
+        } else {
+            getLogger().debug(`${this.arn} does NOT require polling.`)
+        }
+    }
+
+    private getContext() {
+        if (this.isAvailable) {
+            return `${DocDBContext.ElasticCluster}-running`
+        } else if (this.isStopped) {
+            return `${DocDBContext.ElasticCluster}-stopped`
+        }
+        return DocDBContext.ElasticCluster
+    }
+
+    public getDescription(): string | boolean {
+        if (!this.isAvailable) {
+            return `Elastic cluster • ${this.status}`
+        }
+        return 'Elastic cluster'
+    }
+
+    public async deleteCluster(finalSnapshotId: string | undefined): Promise {
+        if (finalSnapshotId !== undefined) {
+            void vscode.window.showInformationMessage(
+                localize('AWS.docdb.deleteCluster.snapshot', 'Taking snapshot of cluster: {0}', this.name)
+            )
+
+            await this.client.createClusterSnapshot({
+                clusterArn: this.cluster.clusterArn,
+                snapshotName: finalSnapshotId,
+            })
+        }
+        return await this.client.deleteElasticCluster(this.arn)
+    }
+
+    override get status() {
+        return this.cluster.status?.toLowerCase()
+    }
+
+    override async getStatus() {
+        const cluster = await this.client.getElasticCluster(this.arn)
+        getLogger().debug(`Get Status: status ${cluster?.status} for elastic-cluster ${this.arn}`)
+        this.cluster.status = cluster?.status
+        return cluster?.status?.toLowerCase()
+    }
+
+    override get isAvailable() {
+        return this.status === 'active'
+    }
+
+    override getConsoleUrl() {
+        return getAwsConsoleUrl('docdb', this.regionCode).with({
+            fragment: `elastic-cluster-details/${this.arn}`,
+        })
+    }
+
+    override async copyEndpoint() {
+        // get the full cluster record if we don't have it already
+        if (this.cluster.clusterEndpoint === undefined) {
+            this.cluster = (await this.client.getElasticCluster(this.arn)) ?? this.cluster
+        }
+        await copyToClipboard(this.cluster.clusterEndpoint!, this.name)
+    }
+
+    override clearTimer(): void {
+        this.pollingSet.delete(this.arn)
+        this.pollingSet.clearTimer()
+    }
+
+    override refreshTree(): void {
+        getLogger().debug(`(DBElasticClusterNode) Refreshing tree for instance: ${this.arn}`)
+        this.refresh()
+        this.parent.refresh()
+    }
+
+    public override [inspect.custom](): string {
+        return 'DBElasticClusterNode'
+    }
+}
diff --git a/packages/core/src/docdb/explorer/dbGlobalClusterNode.ts b/packages/core/src/docdb/explorer/dbGlobalClusterNode.ts
new file mode 100644
index 00000000000..7ab15e01a15
--- /dev/null
+++ b/packages/core/src/docdb/explorer/dbGlobalClusterNode.ts
@@ -0,0 +1,167 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { inspect } from 'util'
+import { makeChildrenNodes } from '../../shared/treeview/utils'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
+import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode'
+import { DBClusterNode, DBClusterRole } from './dbClusterNode'
+import { DefaultDocumentDBClient, DocumentDBClient } from '../../shared/clients/docdbClient'
+import { DBCluster, GlobalCluster, GlobalClusterMember, ModifyGlobalClusterMessage } from '@aws-sdk/client-docdb'
+import { DBResourceNode } from './dbResourceNode'
+import { DocDBContext } from './docdbContext'
+import { copyToClipboard } from '../../shared/utilities/messages'
+import { getAwsConsoleUrl } from '../../shared/awsConsole'
+import { getLogger } from '../../shared/logger/logger'
+
+function getRegionFromArn(arn: string) {
+    const match = arn.match(/:rds:([^:]+):.*:cluster:/)
+    return match?.at(1)
+}
+
+/**
+ * An AWS Explorer node representing DocumentDB global clusters.
+ *
+ * Contains regional clusters of a global cluster as child nodes.
+ */
+export class DBGlobalClusterNode extends DBResourceNode {
+    override name = this.cluster.GlobalClusterIdentifier!
+    override arn = this.cluster.GlobalClusterArn!
+    readonly clusterRole: DBClusterRole = 'global'
+
+    constructor(
+        public readonly parent: AWSTreeNodeBase,
+        readonly cluster: GlobalCluster,
+        private readonly clusterMap: Map,
+        client: DocumentDBClient
+    ) {
+        super(client, cluster.GlobalClusterIdentifier ?? '[Cluster]', vscode.TreeItemCollapsibleState.Collapsed)
+        this.arn = cluster.GlobalClusterArn ?? ''
+        this.name = cluster.GlobalClusterIdentifier ?? ''
+        this.contextValue = this.getContext()
+        this.iconPath = new vscode.ThemeIcon('globe') // TODO: determine icon for global cluster
+        this.description = 'Global cluster'
+        this.tooltip = `${this.name}\nEngine: ${this.cluster.EngineVersion}\nStatus: ${this.cluster.Status} (read-only)`
+        if (this.isStatusRequiringPolling()) {
+            getLogger().debug(`${this.arn} requires polling.`)
+            this.trackChanges()
+        } else {
+            getLogger().debug(`${this.arn} does NOT require polling.`)
+        }
+    }
+
+    public override async getChildren(): Promise {
+        return telemetry.docdb_listInstances.run(async () => {
+            return await makeChildrenNodes({
+                getChildNodes: async () => {
+                    const members = this.cluster.GlobalClusterMembers ?? []
+                    await this.getMemberClusters(members)
+
+                    const nodes = members.map((member) => {
+                        const memberRole: DBClusterRole = member.IsWriter ? 'primary' : 'secondary'
+                        const [cluster, client] = this.clusterMap.get(member.DBClusterArn!) ?? []
+
+                        return new DBClusterNode(this, cluster!, client!, memberRole)
+                    })
+
+                    return nodes
+                },
+                getNoChildrenPlaceholderNode: async () =>
+                    new PlaceholderNode(this, localize('AWS.explorerNode.docdb.noClusters', '[No Clusters found]')),
+                sort: (item1, item2) => {
+                    if (item1.clusterRole === 'primary') {
+                        return -1
+                    }
+                    if (item2.clusterRole === 'primary') {
+                        return 1
+                    }
+                    return item1.name.localeCompare(item2.name)
+                },
+            })
+        })
+    }
+
+    private getContext() {
+        if (this.isAvailable) {
+            return `${DocDBContext.GlobalCluster}-running`
+        } else if (this.status === 'stopped') {
+            return `${DocDBContext.GlobalCluster}-stopped`
+        }
+        return DocDBContext.GlobalCluster
+    }
+
+    // retrieve member cluster details from other regions
+    private async getMemberClusters(members: GlobalClusterMember[]): Promise {
+        await Promise.all(
+            members.map(async (member) => {
+                if (!this.clusterMap.has(member.DBClusterArn!)) {
+                    const regionCode = getRegionFromArn(member.DBClusterArn!)
+                    if (regionCode) {
+                        const client = DefaultDocumentDBClient.create(regionCode)
+                        const [cluster] = await client.listClusters(member.DBClusterArn!)
+                        this.clusterMap.set(member.DBClusterArn!, [cluster, client])
+                    }
+                }
+            })
+        )
+    }
+
+    public async renameCluster(clusterName: string): Promise {
+        const request: ModifyGlobalClusterMessage = {
+            GlobalClusterIdentifier: this.cluster.GlobalClusterIdentifier,
+            NewGlobalClusterIdentifier: clusterName,
+        }
+        const response = await this.client.modifyGlobalCluster(request)
+        this.name = response?.GlobalClusterIdentifier ?? this.name
+        return response
+    }
+
+    override get status() {
+        return this.cluster.Status
+    }
+
+    override async getStatus() {
+        const client = DefaultDocumentDBClient.create(this.regionCode)
+        const clusters = await client.listClusters(this.arn)
+        const cluster = clusters[0]
+
+        if (!cluster) {
+            getLogger().warn(`No global cluster found for ARN: ${this.arn}`)
+            return undefined
+        }
+
+        getLogger().debug(`Get Status: status ${cluster.Status} for global cluster ${this.arn}`)
+        this.cluster.Status = cluster.Status
+        return cluster.Status
+    }
+
+    override copyEndpoint(): Promise {
+        return copyToClipboard(this.cluster.GlobalClusterResourceId!, this.name)
+    }
+
+    override getConsoleUrl(): vscode.Uri {
+        return getAwsConsoleUrl('docdb', this.regionCode).with({
+            fragment: `global-cluster-details/${this.name}`,
+        })
+    }
+
+    override refreshTree(): void {
+        getLogger().debug(`(DBGlobalClusterNode) Refreshing tree for instance: ${this.arn}`)
+        this.refresh()
+        this.parent.refresh()
+    }
+
+    override clearTimer(): void {
+        this.pollingSet.delete(this.arn)
+        this.pollingSet.clearTimer()
+    }
+
+    public override [inspect.custom](): string {
+        return 'DBGlobalClusterNode'
+    }
+}
diff --git a/packages/core/src/docdb/explorer/dbInstanceNode.ts b/packages/core/src/docdb/explorer/dbInstanceNode.ts
new file mode 100644
index 00000000000..3a839971e08
--- /dev/null
+++ b/packages/core/src/docdb/explorer/dbInstanceNode.ts
@@ -0,0 +1,119 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { inspect } from 'util'
+import { DBInstance } from '../../shared/clients/docdbClient'
+import { DocDBContext, DocDBNodeContext } from './docdbContext'
+import { DBResourceNode } from './dbResourceNode'
+import { DBClusterNode } from './dbClusterNode'
+import { ModifyDBInstanceMessage } from '@aws-sdk/client-docdb'
+import { copyToClipboard } from '../../shared/utilities/messages'
+import { getAwsConsoleUrl } from '../../shared/awsConsole'
+import { getLogger } from '../../shared/logger/logger'
+import { toTitleCase } from '../../shared/utilities/textUtilities'
+
+/**
+ * An AWS Explorer node representing a DocumentDB instance.
+ */
+export class DBInstanceNode extends DBResourceNode {
+    override name = this.instance.DBInstanceIdentifier!
+    override arn = this.instance.DBInstanceArn!
+
+    constructor(
+        public readonly parent: DBClusterNode,
+        readonly instance: DBInstance
+    ) {
+        super(parent.client, instance.DBInstanceIdentifier ?? '[Instance]', vscode.TreeItemCollapsibleState.None)
+        getLogger().debug(`NEW DBInstanceNode: ${instance.DBInstanceArn}`)
+        this.description = this.makeDescription()
+        this.contextValue = this.getContext()
+        this.iconPath = this.isAvailable || this.isStopped ? undefined : new vscode.ThemeIcon('loading~spin')
+        this.tooltip = `${this.name}\nClass: ${this.instance.DBInstanceClass}\nStatus: ${this.status}`
+        getLogger().debug(`Parent of ${instance.DBInstanceArn} is ${parent.arn}`)
+        if (this.isStatusRequiringPolling()) {
+            getLogger().debug(`${instance.DBInstanceArn} requires polling.`)
+            this.trackChanges()
+        } else {
+            getLogger().debug(`${instance.DBInstanceArn} does NOT require polling.`)
+        }
+    }
+
+    public override isStatusRequiringPolling(): boolean {
+        const instanceRequiresPolling = super.isStatusRequiringPolling()
+        const parentRequiresPolling = this.parent.isStatusRequiringPolling()
+        const requiresPolling = instanceRequiresPolling || parentRequiresPolling
+
+        getLogger().debug(
+            `isStatusRequiringPolling (DBInstanceNode): Instance ${this.arn} requires polling: ${instanceRequiresPolling}, Parent ${this.parent.arn} requires polling: ${parentRequiresPolling}, Combined result: ${requiresPolling}`
+        )
+
+        return requiresPolling
+    }
+
+    private makeDescription(): string {
+        const type = this.instance.IsClusterWriter ? 'Primary' : 'Replica'
+        if (this.getContext() !== DocDBContext.InstanceAvailable) {
+            return `${toTitleCase(this.status ?? ' ')} ${type} instance`
+        }
+        return `${type} instance • ${this.instance.DBInstanceClass}`
+    }
+
+    private getContext(): DocDBNodeContext {
+        if (this.isAvailable) {
+            return DocDBContext.InstanceAvailable
+        }
+        return DocDBContext.Instance
+    }
+
+    public async rebootInstance(): Promise {
+        const client = this.parent.client
+        return await client.rebootInstance(this.instance.DBInstanceIdentifier!)
+    }
+
+    public async renameInstance(instanceName: string): Promise {
+        const request: ModifyDBInstanceMessage = {
+            DBInstanceIdentifier: this.instance.DBInstanceIdentifier,
+            NewDBInstanceIdentifier: instanceName,
+            ApplyImmediately: true,
+        }
+        return await this.parent.client.modifyInstance(request)
+    }
+
+    override get status() {
+        return this.instance.DBInstanceStatus
+    }
+
+    override async getStatus() {
+        const instance = await this.parent.client.getInstance(this.instance.DBInstanceIdentifier!)
+        this.instance.DBInstanceStatus = instance?.DBInstanceStatus
+        return instance?.DBInstanceStatus
+    }
+
+    override getConsoleUrl() {
+        return getAwsConsoleUrl('docdb', this.regionCode).with({
+            fragment: `instance-details/${this.name}`,
+        })
+    }
+
+    override copyEndpoint() {
+        return copyToClipboard(this.instance.Endpoint?.Address ?? '', this.name)
+    }
+
+    override refreshTree(): void {
+        getLogger().debug(`(DBInstanceNode) Refreshing tree for instance: ${this.arn}`)
+        this.refresh()
+        this.parent.refreshTree()
+    }
+
+    override clearTimer(): void {
+        this.pollingSet.delete(this.arn)
+        this.pollingSet.clearTimer()
+    }
+
+    public override [inspect.custom](): string {
+        return 'DBInstanceNode'
+    }
+}
diff --git a/packages/core/src/docdb/explorer/dbResourceNode.ts b/packages/core/src/docdb/explorer/dbResourceNode.ts
new file mode 100644
index 00000000000..259a867024c
--- /dev/null
+++ b/packages/core/src/docdb/explorer/dbResourceNode.ts
@@ -0,0 +1,175 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { inspect } from 'util'
+import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode'
+import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
+import { DocumentDBClient } from '../../shared/clients/docdbClient'
+import { waitUntil } from '../../shared/utilities/timeoutUtils'
+import { getLogger } from '../../shared/logger/logger'
+import { PollingSet } from '../../shared/utilities/pollingSet'
+
+/** An AWS Explorer node representing a DocumentDB resource. */
+export abstract class DBResourceNode extends AWSTreeNodeBase implements AWSResourceNode {
+    public override readonly regionCode: string
+    public abstract readonly arn: string
+    public abstract readonly name: string
+    public readonly pollingSet: PollingSet = new PollingSet(30000, this.updateNodeStatus.bind(this))
+    private static readonly globalPollingArns: Set = new Set()
+    public processingStatuses = new Set([
+        'creating',
+        'modifying',
+        'rebooting',
+        'starting',
+        'stopping',
+        'renaming',
+    ])
+
+    protected constructor(
+        public readonly client: DocumentDBClient,
+        label: string,
+        collapsibleState?: vscode.TreeItemCollapsibleState
+    ) {
+        super(label, collapsibleState)
+        this.regionCode = client.regionCode
+        getLogger().debug(`NEW DBResourceNode`)
+    }
+
+    public isStatusRequiringPolling(): boolean {
+        const currentStatus = this.status?.toLowerCase()
+        const isProcessingStatus = currentStatus !== undefined && this.processingStatuses.has(currentStatus)
+        getLogger().debug(
+            `isStatusRequiringPolling (DBResourceNode):: Checking if status "${currentStatus}" for ARN: ${this.arn} requires polling: ${isProcessingStatus}`
+        )
+        return isProcessingStatus
+    }
+
+    public [inspect.custom](): string {
+        return 'DBResourceNode'
+    }
+
+    public abstract get status(): string | undefined
+
+    public abstract getStatus(): Promise
+
+    public abstract refreshTree(): void
+
+    public abstract clearTimer(): void
+
+    public get isAvailable(): boolean {
+        return this.status === 'available'
+    }
+
+    public get isStopped(): boolean {
+        return this.status === 'stopped'
+    }
+
+    public get isPolling(): boolean {
+        const isPolling = DBResourceNode.globalPollingArns.has(this.arn)
+        getLogger().debug(`isPolling: ARN ${this.arn} is ${isPolling ? '' : 'not '}being polled.`)
+        return isPolling
+    }
+
+    public set isPolling(value: boolean) {
+        if (value) {
+            if (!this.isPolling) {
+                DBResourceNode.globalPollingArns.add(this.arn)
+                getLogger().info(`Polling started for ARN: ${this.arn}`)
+            } else {
+                getLogger().info(`Polling already active for ARN: ${this.arn}`)
+            }
+        } else {
+            if (this.isPolling) {
+                DBResourceNode.globalPollingArns.delete(this.arn)
+                getLogger().info(`Polling stopped for ARN: ${this.arn}`)
+            } else {
+                getLogger().info(`Polling was not active for ARN: ${this.arn}`)
+            }
+        }
+    }
+
+    public async waitUntilStatusChanged(
+        checkProcessingStatuses: boolean = false,
+        timeout: number = 1200000,
+        interval: number = 5000
+    ): Promise {
+        await waitUntil(
+            async () => {
+                const status = await this.getStatus()
+                if (checkProcessingStatuses) {
+                    const isProcessingStatus = status !== undefined && this.processingStatuses.has(status.toLowerCase())
+                    getLogger().debug('docdb: waitUntilStatusChangedToProcessingStatus: %O', isProcessingStatus)
+                    return isProcessingStatus
+                } else {
+                    const hasStatusChanged = status !== this.status
+                    getLogger().debug('docdb: waitUntilStatusChanged (status): %O', hasStatusChanged)
+                    return hasStatusChanged
+                }
+            },
+            { timeout, interval, truthy: true }
+        )
+        this.refreshTree()
+        return false
+    }
+
+    public async trackChangesWithWaitProcessingStatus() {
+        getLogger().debug(
+            `Preparing to track changes with waiting a processing status for ARN: ${this.arn}; condition: ${this.isPolling};`
+        )
+        if (!this.isPolling) {
+            this.isPolling = true
+            await this.waitUntilStatusChanged(true, 60000, 1000)
+            getLogger().debug(`Tracking changes for a processing status wait is over`)
+            this.pollingSet.add(this.arn)
+            getLogger().debug(`Tracking changes for ARN: ${this.arn}; condition: ${this.isPolling};`)
+        } else {
+            getLogger().debug(`ARN: ${this.arn} already being tracked`)
+        }
+    }
+
+    public trackChanges() {
+        getLogger().debug(`Preparing to track immediately for ARN: ${this.arn}; condition: ${this.isPolling};`)
+        if (!this.isPolling) {
+            this.isPolling = true
+            this.pollingSet.add(this.arn)
+            getLogger().debug(`Tracking changes for ARN: ${this.arn}; condition: ${this.isPolling};`)
+        } else {
+            getLogger().debug(`ARN: ${this.arn} already being tracked`)
+        }
+    }
+
+    public async listTags() {
+        return await this.client.listResourceTags(this.arn)
+    }
+
+    public abstract copyEndpoint(): Promise
+
+    public abstract getConsoleUrl(): vscode.Uri
+
+    public openInBrowser() {
+        return vscode.env.openExternal(this.getConsoleUrl())
+    }
+
+    private async updateNodeStatus() {
+        const currentStatus = this.status
+        const newStatus = await this.getStatus()
+        getLogger().debug(
+            `docdb: ${this.arn} updateNodeStatus (new status): ${newStatus} (old status): ${currentStatus}`
+        )
+        if (currentStatus !== newStatus) {
+            getLogger().info(`docdb: ${this.arn} status: ${newStatus}, refreshing UI`)
+            this.refreshTree()
+        }
+        if (!this.isStatusRequiringPolling()) {
+            getLogger().info(`docdb: ${this.arn} status: ${newStatus}, refreshing UI`)
+            getLogger().debug(`pollingSet delete ${this.arn} updateNodeStatus`)
+            this.pollingSet.delete(this.arn)
+            this.pollingSet.clearTimer()
+            this.isPolling = false
+            this.refreshTree()
+        }
+    }
+}
diff --git a/packages/core/src/docdb/explorer/docdbContext.ts b/packages/core/src/docdb/explorer/docdbContext.ts
new file mode 100644
index 00000000000..23e2b2fbbad
--- /dev/null
+++ b/packages/core/src/docdb/explorer/docdbContext.ts
@@ -0,0 +1,14 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export const DocDBContext = {
+    Cluster: 'awsDocDB-cluster',
+    ElasticCluster: 'awsDocDB-cluster-elastic',
+    GlobalCluster: 'awsDocDB-cluster-global',
+    Instance: 'awsDocDB-instance',
+    InstanceAvailable: 'awsDocDB-instance-available',
+} as const
+
+export type DocDBNodeContext = (typeof DocDBContext)[keyof typeof DocDBContext]
diff --git a/packages/core/src/docdb/explorer/docdbNode.ts b/packages/core/src/docdb/explorer/docdbNode.ts
new file mode 100644
index 00000000000..e94e7c41152
--- /dev/null
+++ b/packages/core/src/docdb/explorer/docdbNode.ts
@@ -0,0 +1,88 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { inspect } from 'util'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
+import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode'
+import { makeChildrenNodes } from '../../shared/treeview/utils'
+import { DBElasticCluster, DocumentDBClient } from '../../shared/clients/docdbClient'
+import { DBClusterNode } from './dbClusterNode'
+import { DBElasticClusterNode } from './dbElasticClusterNode'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { DBGlobalClusterNode } from './dbGlobalClusterNode'
+import { DBCluster } from '@aws-sdk/client-docdb'
+import { getLogger } from '../../shared/logger/logger'
+import { DBResourceNode } from './dbResourceNode'
+
+/**
+ * An AWS Explorer node representing DocumentDB.
+ *
+ * Contains clusters for a specific region as child nodes.
+ */
+export class DocumentDBNode extends AWSTreeNodeBase {
+    public override readonly regionCode: string
+
+    public constructor(public readonly client: DocumentDBClient) {
+        super('DocumentDB', vscode.TreeItemCollapsibleState.Collapsed)
+        this.contextValue = 'awsDocDBNode'
+        this.regionCode = client.regionCode
+    }
+
+    public override async getChildren(): Promise {
+        return telemetry.docdb_listClusters.run(async () => {
+            return await makeChildrenNodes({
+                getChildNodes: () => {
+                    return this.getClusterNodes()
+                },
+                getNoChildrenPlaceholderNode: async () =>
+                    new PlaceholderNode(this, localize('AWS.explorerNode.docdb.noClusters', '[No Clusters found]')),
+                sort: (item1, item2) => item1.name.localeCompare(item2.name),
+            })
+        })
+    }
+
+    private async getClusterNodes() {
+        const [globalClusters, clusters, elasticClusters] = await Promise.all([
+            this.client.listGlobalClusters(),
+            this.client.listClusters(),
+            this.client.listElasticClusters(),
+        ])
+
+        // contains clusters that are part of a global cluster
+        const globalClusterMap = new Map()
+
+        for (const globalCluster of globalClusters) {
+            for (const member of globalCluster.GlobalClusterMembers ?? []) {
+                const match = clusters.find((c) => c.DBClusterArn === member.DBClusterArn)
+                if (match?.DBClusterArn) {
+                    globalClusterMap.set(match.DBClusterArn, [match, this.client])
+                }
+            }
+        }
+
+        // contains clusters that are not part of a global cluster
+        const regionalClusters = clusters.filter((c) => !globalClusterMap.has(c.DBClusterArn!))
+
+        const nodes: DBResourceNode[] = []
+        nodes.push(
+            ...globalClusters.map((cluster) => new DBGlobalClusterNode(this, cluster, globalClusterMap, this.client))
+        )
+        getLogger().info(`Repopulating child regional clusters...`)
+        nodes.push(...regionalClusters.map((cluster) => new DBClusterNode(this, cluster, this.client)))
+        nodes.push(
+            ...elasticClusters.map(
+                (cluster) => new DBElasticClusterNode(this, cluster as DBElasticCluster, this.client)
+            )
+        )
+
+        return nodes
+    }
+
+    public [inspect.custom](): string {
+        return 'DocumentDBNode'
+    }
+}
diff --git a/packages/core/src/docdb/utils.ts b/packages/core/src/docdb/utils.ts
new file mode 100644
index 00000000000..09b71d9fdf9
--- /dev/null
+++ b/packages/core/src/docdb/utils.ts
@@ -0,0 +1,154 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { ToolkitError } from '../shared/errors'
+import { localize } from '../shared/utilities/vsCodeUtils'
+import { DBInstanceNode } from './explorer/dbInstanceNode'
+import { DBResourceNode } from './explorer/dbResourceNode'
+
+/**
+ * Validates a cluster name for the CreateCluster API.
+ * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/docdb/command/CreateDBClusterCommand/
+ * @returns undefined if the name passes validation. Otherwise, an error message is returned.
+ */
+export function validateClusterName(name: string): string | undefined {
+    if (name.length < 1 || name.length > 63) {
+        return localize(
+            'AWS.docdb.validateClusterName.error.invalidLength',
+            'Cluster name must be between 1 and 63 characters long'
+        )
+    }
+
+    if (!/^[a-z]/.test(name)) {
+        return localize(
+            'AWS.docdb.validateClusterName.error.invalidStart',
+            'Cluster name must start with a lowercase letter'
+        )
+    }
+
+    if (/-$/.test(name) || /--/.test(name)) {
+        return localize(
+            'AWS.docdb.validateClusterName.error.invalidEnd',
+            'Cluster name cannot end with a hyphen or contain 2 consecutive hyphens'
+        )
+    }
+
+    if (!/^[a-z0-9\-]+$/.test(name)) {
+        return localize(
+            'AWS.docdb.validateClusterName.error.invalidCharacters',
+            'Cluster name must only contain lowercase letters, numbers, and hyphens'
+        )
+    }
+
+    return undefined
+}
+
+/**
+ * Validates a username
+ * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/docdb/command/CreateDBClusterCommand/
+ * @returns undefined if the name passes validation. Otherwise, an error message is returned.
+ */
+export function validateUsername(name: string): string | undefined {
+    if (name.length < 1 || name.length > 63) {
+        return localize(
+            'AWS.docdb.validateUsername.error.invalidLength',
+            'Username name must be between 1 and 63 characters long'
+        )
+    }
+
+    if (!/^[a-zA-Z]/.test(name)) {
+        return localize('AWS.docdb.validateUsername.error.invalidStart', 'Username must start with a letter')
+    }
+
+    if (!/^[a-zA-Z0-9]+$/.test(name)) {
+        return localize(
+            'AWS.docdb.validateUsername.error.invalidCharacters',
+            'Username must only contain letters and numbers'
+        )
+    }
+
+    return undefined
+}
+
+/**
+ * Validates a password
+ * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/docdb/command/CreateDBClusterCommand/
+ * @returns undefined if validation passes. Otherwise, an error message is returned.
+ */
+export function validatePassword(password: string): string | undefined {
+    if (password.length < 8 || password.length > 100) {
+        return localize(
+            'AWS.docdb.validatePassword.error.invalidLength',
+            'Password must be between 8 and 100 characters long'
+        )
+    }
+
+    if (/["\/\@]/.test(password) || !/^[ -~]*$/.test(password)) {
+        return localize(
+            'AWS.docdb.validatePassword.error.invalidCharacters',
+            'Password must only contain printable ASCII characters (except for slash, double quotes and @ symbol)'
+        )
+    }
+
+    return undefined
+}
+
+/**
+ * Validates an instance name for the CreateInstance API.
+ * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/docdb/command/CreateDBInstanceCommand/
+ * @returns undefined if the name passes validation. Otherwise, an error message is returned.
+ */
+export function validateInstanceName(name: string): string | undefined {
+    if (name.length < 1 || name.length > 63) {
+        return localize(
+            'AWS.docdb.validateInstanceName.error.invalidLength',
+            'Instance name must be between 1 and 63 characters long'
+        )
+    }
+
+    if (!/^[a-z]/.test(name)) {
+        return localize(
+            'AWS.docdb.validateInstanceName.error.invalidStart',
+            'Instance name must start with a lowercase letter'
+        )
+    }
+
+    if (/-$/.test(name) || /--/.test(name)) {
+        return localize(
+            'AWS.docdb.validateInstanceName.error.invalidEnd',
+            'Instance name cannot end with a hyphen or contain 2 consecutive hyphens'
+        )
+    }
+
+    if (!/^[a-z0-9\-]+$/.test(name)) {
+        return localize(
+            'AWS.docdb.validateInstanceName.error.invalidCharacters',
+            'Instance name must only contain lowercase letters, numbers, and hyphens'
+        )
+    }
+
+    return undefined
+}
+
+export function isSupportedGlobalInstanceClass(instanceClass: string) {
+    return /(t3|t4g|r4)/.test(instanceClass) === false
+}
+
+export function assertNodeAvailable(node: DBResourceNode | undefined, action: string) {
+    if (!node) {
+        throw new ToolkitError(`No node specified for ${action}`)
+    }
+
+    if (!node.isAvailable) {
+        if (node instanceof DBInstanceNode) {
+            void vscode.window.showErrorMessage(localize('AWS.docdb.instanceStopped', 'Instance must be running'))
+            throw new ToolkitError('Instance not running', { cancelled: true, code: 'docdbInstanceNotAvailable' })
+        }
+
+        void vscode.window.showErrorMessage(localize('AWS.docdb.clusterStopped', 'Cluster must be running'))
+        throw new ToolkitError('Cluster not running', { cancelled: true, code: 'docdbClusterStopped' })
+    }
+}
diff --git a/packages/core/src/docdb/wizards/createClusterWizard.ts b/packages/core/src/docdb/wizards/createClusterWizard.ts
new file mode 100644
index 00000000000..5e81dd89c63
--- /dev/null
+++ b/packages/core/src/docdb/wizards/createClusterWizard.ts
@@ -0,0 +1,82 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { createCommonButtons } from '../../shared/ui/buttons'
+import { createExitPrompter } from '../../shared/ui/common/exitPrompter'
+import { DataQuickPickItem, createQuickPick } from '../../shared/ui/pickerPrompter'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { Wizard, WizardOptions } from '../../shared/wizards/wizard'
+import { RegionalClusterConfiguration, RegionalClusterWizard } from './regionalClusterWizard'
+import { ElasticClusterConfiguration, ElasticClusterWizard } from './elasticClusterWizard'
+import { DocumentDBClient } from '../../shared/clients/docdbClient'
+
+const DocDBClusterHelpUrl =
+    'https://docs.aws.amazon.com/documentdb/latest/developerguide/docdb-using-elastic-clusters.html'
+
+type ClusterType = 'regional' | 'elastic' | undefined
+
+export interface CreateClusterState {
+    ClusterType: ClusterType
+    readonly RegionalCluster: RegionalClusterConfiguration
+    readonly ElasticCluster: ElasticClusterConfiguration
+}
+
+/**
+ * A wizard to prompt configuration of a new cluster
+ */
+export class CreateClusterWizard extends Wizard {
+    title: string
+    constructor(
+        readonly client: DocumentDBClient,
+        options: WizardOptions = {}
+    ) {
+        super({
+            initState: options.initState,
+            implicitState: options.implicitState,
+            exitPrompterProvider: createExitPrompter,
+        })
+        this.title = localize('AWS.docdb.createCluster.title', 'Create DocumentDB Cluster')
+    }
+
+    public override async init(): Promise {
+        this.form.ClusterType.bindPrompter(() => createClusterTypePrompter())
+
+        const regionalClusterWizard = await new RegionalClusterWizard(this.client, this.title).init()
+        this.form.RegionalCluster.applyBoundForm(regionalClusterWizard.boundForm, {
+            showWhen: (state) => state.ClusterType === 'regional',
+        })
+
+        const elasticClusterWizard = new ElasticClusterWizard(this.client, this.title)
+        this.form.ElasticCluster.applyBoundForm(elasticClusterWizard.boundForm, {
+            showWhen: (state) => state.ClusterType === 'elastic',
+        })
+
+        return this
+    }
+}
+
+function createClusterTypePrompter() {
+    const regionalType: DataQuickPickItem = {
+        data: 'regional',
+        label: localize('AWS.docdb.createCluster.clusterType.regional.label', 'Instance Based Cluster'),
+        detail: localize(
+            'AWS.docdb.createCluster.clusterType.regional.detail',
+            'Instance based cluster can scale your database to millions of reads per second and up to 128 TiB of storage capacity. With instance based clusters you can choose your instance type based on your requirements.'
+        ),
+    }
+    const elasticType: DataQuickPickItem = {
+        data: 'elastic',
+        label: localize('AWS.docdb.createCluster.clusterType.elastic.label', 'Elastic Cluster'),
+        detail: localize(
+            'AWS.docdb.createCluster.clusterType.elastic.detail',
+            'Elastic clusters can scale your database to millions of reads and writes per second, with petabytes of storage capacity. Elastic clusters support MongoDB compatible sharding APIs. With Elastic Clusters, you do not need to choose, manage or upgrade instances.'
+        ),
+    }
+
+    return createQuickPick([regionalType, elasticType], {
+        title: localize('AWS.docdb.createCluster.clusterType.prompt', 'Cluster type'),
+        buttons: createCommonButtons(DocDBClusterHelpUrl),
+    })
+}
diff --git a/packages/core/src/docdb/wizards/createGlobalClusterWizard.ts b/packages/core/src/docdb/wizards/createGlobalClusterWizard.ts
new file mode 100644
index 00000000000..de88522dff4
--- /dev/null
+++ b/packages/core/src/docdb/wizards/createGlobalClusterWizard.ts
@@ -0,0 +1,74 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import globals from '../../shared/extensionGlobals'
+import { DocDBEngine, DocumentDBClient } from '../../shared/clients/docdbClient'
+import { createExitPrompter } from '../../shared/ui/common/exitPrompter'
+import { createRegionPrompter } from '../../shared/ui/common/region'
+import { createInputBox } from '../../shared/ui/inputPrompter'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { Wizard, WizardOptions } from '../../shared/wizards/wizard'
+import { validateClusterName } from '../utils'
+import { RegionalClusterConfiguration, RegionalClusterWizard } from './regionalClusterWizard'
+
+const DocDBGlobalHelpUrl = 'https://docs.aws.amazon.com/documentdb/latest/developerguide/global-clusters.html'
+
+export interface CreateGlobalClusterState {
+    RegionCode: string
+    GlobalClusterName: string
+    readonly Cluster: RegionalClusterConfiguration
+}
+
+/**
+ * A wizard to prompt configuration of a new global cluster
+ */
+export class CreateGlobalClusterWizard extends Wizard {
+    constructor(
+        readonly region: string,
+        readonly engineVersion: string | undefined,
+        readonly client: DocumentDBClient,
+        options: WizardOptions = {}
+    ) {
+        super({
+            initState: options.initState,
+            implicitState: options.implicitState,
+            exitPrompterProvider: createExitPrompter,
+        })
+    }
+
+    public override async init(): Promise {
+        this.form.RegionCode.bindPrompter(async () => {
+            const regions = globals.regionProvider.getRegions().filter((r) => r.id !== this.region)
+            return createRegionPrompter(regions, {
+                serviceFilter: DocDBEngine,
+                title: localize('AWS.docdb.addRegion.region.prompt', 'Secondary region'),
+                helpUrl: DocDBGlobalHelpUrl,
+            }).transform((region) => region.id)
+        })
+
+        this.form.GlobalClusterName.bindPrompter(
+            () =>
+                createInputBox({
+                    title: localize('AWS.docdb.addRegion.name.title', 'Global Cluster Id'),
+                    prompt: localize(
+                        'AWS.docdb.addRegion.name.prompt',
+                        'Specify a unique identifier for the global cluster'
+                    ),
+                    validateInput: validateClusterName,
+                }),
+            {
+                showWhen: (state) => state.GlobalClusterName === undefined,
+            }
+        )
+
+        const title = localize('AWS.docdb.addRegion.cluster.title', 'Secondary cluster')
+        const regionalClusterWizard = await new RegionalClusterWizard(this.client, title, false, {
+            initState: { EngineVersion: this.engineVersion },
+        }).init()
+        this.form.Cluster.applyBoundForm(regionalClusterWizard.boundForm)
+
+        return this
+    }
+}
diff --git a/packages/core/src/docdb/wizards/createInstanceWizard.ts b/packages/core/src/docdb/wizards/createInstanceWizard.ts
new file mode 100644
index 00000000000..8bf5b952e02
--- /dev/null
+++ b/packages/core/src/docdb/wizards/createInstanceWizard.ts
@@ -0,0 +1,96 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { DBCluster, OrderableDBInstanceOption } from '@aws-sdk/client-docdb'
+import { DefaultDocumentDBClient, DocumentDBClient, DBStorageType } from '../../shared/clients/docdbClient'
+import { validateInstanceName } from '../utils'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { Wizard, WizardOptions } from '../../shared/wizards/wizard'
+import { createInputBox } from '../../shared/ui/inputPrompter'
+import { createExitPrompter } from '../../shared/ui/common/exitPrompter'
+import { DataQuickPickItem, createQuickPick } from '../../shared/ui/pickerPrompter'
+import { createCommonButtons } from '../../shared/ui/buttons'
+import { SkipPrompter } from '../../shared/ui/common/skipPrompter'
+
+const DocDBHelpUrl = 'https://docs.aws.amazon.com/documentdb/latest/developerguide/db-instances.html'
+
+export interface CreateInstanceState {
+    DBInstanceIdentifier: string
+    DBInstanceClass: string
+}
+
+/**
+ * A wizard to prompt configuration of a new instance
+ */
+export class CreateInstanceWizard extends Wizard {
+    title: string
+    cluster: DBCluster
+    constructor(
+        region: string,
+        cluster: DBCluster,
+        options: WizardOptions = {},
+        readonly client: DocumentDBClient = DefaultDocumentDBClient.create(region)
+    ) {
+        super({
+            initState: {
+                ...options.initState,
+            },
+            implicitState: options.implicitState,
+            exitPrompterProvider: createExitPrompter,
+        })
+        this.cluster = cluster
+        this.client = client
+        this.title = localize('AWS.docdb.createInstance.title', 'Add Instance')
+    }
+
+    public override async init(): Promise {
+        const form = this.form
+
+        form.DBInstanceIdentifier.bindPrompter(() =>
+            createInputBox({
+                step: 1,
+                title: this.title,
+                prompt: localize('AWS.docdb.createInstance.name.prompt', 'Instance Name'),
+                placeholder: localize('AWS.docdb.createInstance.name.placeholder', 'Specify a unique identifier'),
+                validateInput: validateInstanceName,
+            })
+        )
+
+        form.DBInstanceClass.bindPrompter(async (state) => await this.createInstanceClassPrompter(state.stepCache))
+
+        return this
+    }
+
+    private async createInstanceClassPrompter(cache: { [key: string]: any }) {
+        const cachedOptions: OrderableDBInstanceOption[] = cache[this.client.regionCode]
+        const options =
+            cachedOptions ??
+            (await this.client.listInstanceClassOptions(
+                this.cluster.EngineVersion,
+                this.cluster.StorageType ?? DBStorageType.Standard
+            ))
+        cache[this.client.regionCode] = options
+
+        const items: DataQuickPickItem[] = options.map((option) => {
+            return {
+                data: option.DBInstanceClass,
+                label: option.DBInstanceClass ?? '(unknown)',
+                description: undefined,
+                detail: undefined,
+                recentlyUsed: false,
+            }
+        })
+
+        if (items.length === 0) {
+            return new SkipPrompter()
+        }
+
+        return createQuickPick(items, {
+            step: 2,
+            title: localize('AWS.docdb.createInstance.instanceClass.prompt', 'Select instance class'),
+            buttons: createCommonButtons(DocDBHelpUrl),
+        })
+    }
+}
diff --git a/packages/core/src/docdb/wizards/elasticClusterWizard.ts b/packages/core/src/docdb/wizards/elasticClusterWizard.ts
new file mode 100644
index 00000000000..b28d2f20751
--- /dev/null
+++ b/packages/core/src/docdb/wizards/elasticClusterWizard.ts
@@ -0,0 +1,132 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { DocumentDBClient } from '../../shared/clients/docdbClient'
+import { validateClusterName, validatePassword, validateUsername } from '../utils'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { Wizard } from '../../shared/wizards/wizard'
+import { createInputBox } from '../../shared/ui/inputPrompter'
+import { DataQuickPickItem, createQuickPick } from '../../shared/ui/pickerPrompter'
+import { createCommonButtons } from '../../shared/ui/buttons'
+import { Auth, CreateClusterInput } from '@aws-sdk/client-docdb-elastic'
+import { Prompter } from '../../shared/ui/prompter'
+
+const DocDBElasticHelpUrl = 'https://docs.aws.amazon.com/documentdb/latest/developerguide/elastic-how-it-works.html'
+const DefaultShardCount = 2
+const DefaultCapacity = 2
+
+export interface ElasticClusterConfiguration extends Partial {}
+
+/**
+ * A wizard to prompt configuration of a new cluster
+ */
+export class ElasticClusterWizard extends Wizard {
+    constructor(
+        readonly client: DocumentDBClient,
+        readonly title: string
+    ) {
+        super()
+        this.client = client
+        const form = this.form
+
+        form.clusterName.bindPrompter(() =>
+            createInputBox({
+                title: this.title,
+                prompt: localize('AWS.docdb.createCluster.name.prompt', 'Specify a unique cluster name'),
+                validateInput: validateClusterName,
+                buttons: createCommonButtons(DocDBElasticHelpUrl),
+            })
+        )
+
+        form.adminUserName.bindPrompter(() => createUsernamePrompter(this.title))
+        form.adminUserPassword.bindPrompter(() => createPasswordPrompter(this.title))
+        form.authType.setDefault(() => Auth.PLAIN_TEXT)
+        form.shardCount.bindPrompter(() => createShardCountPrompter(this.title), {
+            setDefault: () => DefaultShardCount,
+        })
+        form.shardInstanceCount.bindPrompter(
+            () =>
+                createQuickPick(instanceCountItems(2), {
+                    title: localize(
+                        'AWS.docdb.createCluster.dbInstanceCount.prompt',
+                        'The number of replica instances applying to all shards in the elastic cluster'
+                    ),
+                    buttons: createCommonButtons(DocDBElasticHelpUrl),
+                }),
+            { setDefault: () => 2 }
+        )
+        form.shardCapacity.bindPrompter(() => createShardCapacityPrompter(this.title), {
+            setDefault: () => DefaultCapacity,
+        })
+
+        return this
+    }
+}
+
+function createUsernamePrompter(title: string) {
+    return createInputBox({
+        title,
+        prompt: localize('AWS.docdb.createCluster.username.prompt', 'Specify a login username'),
+        validateInput: validateUsername,
+        buttons: createCommonButtons(DocDBElasticHelpUrl),
+    })
+}
+
+function createPasswordPrompter(title: string) {
+    return createInputBox({
+        title,
+        prompt: localize('AWS.docdb.createCluster.password.prompt', 'Specify a login password (8 characters minimum)'),
+        password: true,
+        validateInput: validatePassword,
+        buttons: createCommonButtons(DocDBElasticHelpUrl),
+    })
+}
+
+function createShardCountPrompter(title: string): Prompter {
+    const maxShardCount = 32
+    const prompter = createInputBox({
+        title,
+        prompt: localize('AWS.docdb.createCluster.shardCount.prompt', 'Number of shards the Elastic Cluster will use'),
+        validateInput: (value) => {
+            const num = parseInt(value)
+            if (num < 1 || num > maxShardCount || isNaN(num)) {
+                return localize(
+                    'AWS.docdb.createCluster.shardCount.invalidValue',
+                    `Enter a numeric value between 1 and ${maxShardCount}`
+                )
+            }
+            return undefined
+        },
+        buttons: createCommonButtons(DocDBElasticHelpUrl),
+    })
+    return prompter.transform((value) => parseInt(value))
+}
+
+function createShardCapacityPrompter(title: string): Prompter {
+    const items = [2, 4, 8, 16, 32, 64].map>((data) => ({
+        data,
+        label: data.toString(),
+    }))
+    return createQuickPick(items, {
+        title,
+        placeholder: localize('AWS.docdb.createCluster.shardCapacity.placeholder', 'vCPU capacity of shard instances'),
+        buttons: createCommonButtons(DocDBElasticHelpUrl),
+    })
+}
+
+function instanceCountItems(defaultCount: number, max: number = 16): DataQuickPickItem[] {
+    const items = []
+    for (let i = 1; i <= max; i++) {
+        const item: DataQuickPickItem = {
+            label: i.toString(),
+            data: i,
+            description: i === defaultCount ? '(recommended)' : undefined,
+        }
+
+        items.push(item)
+    }
+
+    return items
+}
diff --git a/packages/core/src/docdb/wizards/regionalClusterWizard.ts b/packages/core/src/docdb/wizards/regionalClusterWizard.ts
new file mode 100644
index 00000000000..069c4f6d36a
--- /dev/null
+++ b/packages/core/src/docdb/wizards/regionalClusterWizard.ts
@@ -0,0 +1,206 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CreateDBClusterCommandInput } from '@aws-sdk/client-docdb'
+import { DBStorageType, DocDBEngine, DocumentDBClient, MaxInstanceCount } from '../../shared/clients/docdbClient'
+import { isSupportedGlobalInstanceClass, validateClusterName, validatePassword, validateUsername } from '../utils'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { Wizard, WizardOptions } from '../../shared/wizards/wizard'
+import { createInputBox } from '../../shared/ui/inputPrompter'
+import { DataQuickPickItem, createQuickPick } from '../../shared/ui/pickerPrompter'
+import { createCommonButtons } from '../../shared/ui/buttons'
+import { SkipPrompter } from '../../shared/ui/common/skipPrompter'
+
+const DocDBClusterHelpUrl = 'https://docs.aws.amazon.com/documentdb/latest/developerguide/db-cluster-parameters.html'
+
+export interface RegionalClusterConfiguration extends CreateDBClusterCommandInput {
+    DBClusterIdentifier: string
+    // These options cannot be changed later
+    EngineVersion: string
+    MasterUsername: string
+    MasterUserPassword: string
+    StorageEncrypted: boolean
+    KmsKeyId?: string | undefined
+    DBSubnetGroupName?: string | undefined
+    VpcSecurityGroupIds?: string[] | undefined
+    // Instance fields
+    DBInstanceCount?: number | undefined
+    DBInstanceClass?: string | undefined
+}
+
+/**
+ * A wizard to prompt configuration of a new cluster
+ */
+export class RegionalClusterWizard extends Wizard {
+    constructor(
+        readonly client: DocumentDBClient,
+        readonly title: string,
+        readonly isPrimaryCluster: boolean = true,
+        options: WizardOptions = {}
+    ) {
+        super(options)
+        this.client = client
+    }
+
+    public override async init(): Promise {
+        const form = this.form
+
+        form.DBClusterIdentifier.bindPrompter(
+            () =>
+                createInputBox({
+                    step: 1,
+                    title: this.title,
+                    prompt: localize('AWS.docdb.createCluster.name.prompt', 'Specify a unique cluster name'),
+                    validateInput: validateClusterName,
+                    buttons: createCommonButtons(DocDBClusterHelpUrl),
+                }),
+            { relativeOrder: 1 }
+        )
+
+        form.Engine.setDefault(() => DocDBEngine)
+        form.EngineVersion.bindPrompter(async () => await createEngineVersionPrompter(this.client), {
+            showWhen: () => this.isPrimaryCluster,
+            setDefault: () => this.options.initState?.EngineVersion,
+        })
+        form.MasterUsername.bindPrompter(() => createUsernamePrompter(this.title), {
+            showWhen: () => this.isPrimaryCluster,
+        })
+        form.MasterUserPassword.bindPrompter(() => createPasswordPrompter(this.title), {
+            showWhen: () => this.isPrimaryCluster,
+        })
+        form.StorageEncrypted.bindPrompter(() => createEncryptedStoragePrompter(), {
+            showWhen: () => this.isPrimaryCluster,
+        })
+
+        form.DBInstanceCount.bindPrompter(
+            () =>
+                createQuickPick(instanceCountItems(3), {
+                    title: localize('AWS.docdb.createCluster.dbInstanceCount.prompt', 'Number of instances'),
+                    buttons: createCommonButtons(DocDBClusterHelpUrl),
+                }),
+            {
+                setDefault: () => 3,
+            }
+        )
+
+        form.DBInstanceClass.bindPrompter(
+            async (state) =>
+                await createInstanceClassPrompter(this.client, state.EngineVersion!, this.isPrimaryCluster),
+            {
+                setDefault: () => 'db.t3.medium',
+                showWhen: (state) => state.DBInstanceCount! > 0,
+            }
+        )
+
+        return this
+    }
+}
+
+function createUsernamePrompter(title: string) {
+    return createInputBox({
+        step: 3,
+        title,
+        prompt: localize('AWS.docdb.createCluster.username.prompt', 'Specify a login username'),
+        validateInput: validateUsername,
+        buttons: createCommonButtons(DocDBClusterHelpUrl),
+    })
+}
+
+function createPasswordPrompter(title: string) {
+    return createInputBox({
+        step: 4,
+        title,
+        prompt: localize('AWS.docdb.createCluster.password.prompt', 'Specify a login password (8 characters minimum)'),
+        password: true,
+        validateInput: validatePassword,
+        buttons: createCommonButtons(DocDBClusterHelpUrl),
+    })
+}
+
+function createEncryptedStoragePrompter() {
+    return createQuickPick(
+        [
+            {
+                label: localize('AWS.docdb.createCluster.storage.encrypted', 'Encrypt'),
+                description: '(recommended)',
+                data: true,
+            },
+            {
+                label: localize('AWS.docdb.createCluster.storage.notEncrypted', "Don't encrypt"),
+                data: false,
+            },
+        ],
+        {
+            title: localize('AWS.docdb.createCluster.storageEncrypted.prompt', 'Specify storage encryption'),
+            buttons: createCommonButtons(DocDBClusterHelpUrl),
+        }
+    )
+}
+
+async function createEngineVersionPrompter(docdbClient: DocumentDBClient) {
+    const versions = await docdbClient.listEngineVersions()
+    // sort in descending order
+    versions.sort((a, b) => b.EngineVersion!.localeCompare(a.EngineVersion!))
+
+    const items: DataQuickPickItem[] = versions.map((v) => {
+        return {
+            label: v.EngineVersion ?? '',
+            data: v.EngineVersion,
+        }
+    })
+
+    if (items.length === 0) {
+        items.push({ label: '5.0.0 (default)', data: '5.0.0' })
+    }
+
+    items[0].picked = true
+
+    return createQuickPick(items, {
+        title: localize('AWS.docdb.createCluster.engineVersion.prompt', 'Select engine version'),
+        buttons: createCommonButtons(DocDBClusterHelpUrl),
+    })
+}
+
+async function createInstanceClassPrompter(
+    docdbClient: DocumentDBClient,
+    engineVersion: string,
+    isPrimaryCluster: boolean
+) {
+    const options = await docdbClient.listInstanceClassOptions(engineVersion, DBStorageType.Standard)
+
+    const items: DataQuickPickItem[] = options
+        .filter((option) => isPrimaryCluster || isSupportedGlobalInstanceClass(option.DBInstanceClass!))
+        .map((option) => ({
+            data: option.DBInstanceClass,
+            label: option.DBInstanceClass ?? '(unknown)',
+            description: undefined,
+            detail: undefined,
+        }))
+
+    if (items.length === 0) {
+        return new SkipPrompter()
+    }
+
+    return createQuickPick(items, {
+        title: localize('AWS.docdb.createInstance.instanceClass.prompt', 'Select instance class'),
+        buttons: createCommonButtons(DocDBClusterHelpUrl),
+    })
+}
+
+// TODO: Make this it's own picker class
+function instanceCountItems(defaultCount: number, max: number = MaxInstanceCount): DataQuickPickItem[] {
+    const items = []
+
+    for (let index = 1; index <= max; index++) {
+        const item: DataQuickPickItem = {
+            label: index.toString(),
+            data: index,
+            description: index === defaultCount ? '(recommended)' : undefined,
+        }
+        items.push(item)
+    }
+
+    return items
+}
diff --git a/packages/core/src/dynamicResources/activation.ts b/packages/core/src/dynamicResources/activation.ts
new file mode 100644
index 00000000000..a40002ae128
--- /dev/null
+++ b/packages/core/src/dynamicResources/activation.ts
@@ -0,0 +1,131 @@
+/*!
+ * 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 { ResourceCodeLensProvider } from '../shared/codelens/resourceCodeLensProvider'
+import { configureResources } from './commands/configure'
+import { copyIdentifier } from './commands/copyIdentifier'
+import { deleteResource } from './commands/deleteResource'
+import { getDiagnostics, openResource } from './commands/openResource'
+import { saveResource } from './commands/saveResource'
+import { ResourcesNode } from './explorer/nodes/resourcesNode'
+import { ResourceNode } from './explorer/nodes/resourceNode'
+import { ResourceTypeNode } from './explorer/nodes/resourceTypeNode'
+import { resourceFileGlobPattern } from './awsResourceManager'
+import { Commands } from '../shared/vscode/commands2'
+import globals from '../shared/extensionGlobals'
+import { openUrl } from '../shared/utilities/vsCodeUtils'
+
+const localize = nls.loadMessageBundle()
+
+/**
+ * Activates Resources components.
+ */
+export async function activate(context: vscode.ExtensionContext): Promise {
+    const resourceDiagnostics = vscode.languages.createDiagnosticCollection(
+        localize('AWS.explorerNode.resources.label', 'Resources')
+    )
+    const resourceManager = globals.resourceManager
+
+    context.subscriptions.push(
+        vscode.workspace.registerTextDocumentContentProvider('awsResource', new VirtualDocumentProvider()),
+        Commands.register('aws.resources.openResourcePreview', async (node: ResourceNode) => {
+            await openResource({
+                source: node,
+                preview: true,
+                resourceManager,
+                diagnostics: resourceDiagnostics,
+            })
+        }),
+        Commands.register('aws.resources.copyIdentifier', async (node: ResourceNode) => {
+            await copyIdentifier(node.parent.typeName, node.identifier)
+        }),
+        Commands.register('aws.resources.configure', async (node: ResourcesNode) => {
+            if (await configureResources()) {
+                node.refresh()
+            }
+        }),
+        Commands.register('aws.resources.createResource', async (node: ResourceTypeNode) => {
+            await resourceManager.new(node)
+        }),
+        Commands.register('aws.resources.deleteResource', async (node: ResourceNode) => {
+            if (await deleteResource(node.parent.cloudControl, node.parent.typeName, node.identifier)) {
+                await resourceManager.close(resourceManager.toUri(node)!)
+                node.parent.clearChildren()
+                node.parent.refresh()
+            }
+        }),
+        Commands.register('aws.resources.updateResource', async (node: ResourceNode) => {
+            await openResource({
+                source: node,
+                preview: false,
+                resourceManager,
+                diagnostics: resourceDiagnostics,
+            })
+        }),
+        Commands.register('aws.resources.updateResourceInline', async (uri: vscode.Uri) => {
+            await openResource({
+                source: uri,
+                preview: false,
+                resourceManager,
+                diagnostics: resourceDiagnostics,
+            })
+        }),
+        vscode.workspace.onDidSaveTextDocument(async (doc: vscode.TextDocument) => {
+            return await saveResource(doc, resourceManager, resourceDiagnostics)
+        }),
+        Commands.register('aws.resources.saveResource', async (uri: vscode.Uri) => {
+            await vscode.window.showTextDocument(uri)
+            await vscode.commands.executeCommand('workbench.action.files.save')
+        }),
+        Commands.register('aws.resources.closeResource', async (uri: vscode.Uri) => {
+            if (resourceManager.fromUri(uri) instanceof ResourceNode) {
+                await openResource({
+                    source: uri,
+                    preview: true,
+                    resourceManager,
+                    diagnostics: resourceDiagnostics,
+                })
+                resourceDiagnostics.delete(uri)
+            } else {
+                await resourceManager.close(uri)
+            }
+        }),
+        Commands.register('aws.resources.viewDocs', async (node: ResourceTypeNode) => {
+            await openUrl(vscode.Uri.parse(node.metadata.documentation))
+        }),
+        vscode.workspace.onDidChangeTextDocument((textDocumentEvent) => {
+            if (resourceDiagnostics.has(textDocumentEvent.document.uri)) {
+                let diagnostics: vscode.Diagnostic[] = []
+                const resource = resourceManager.fromUri(textDocumentEvent.document.uri)
+                if (resource instanceof ResourceNode) {
+                    const schema = resourceManager.getSchema(resource.parent.typeName)
+                    if (schema) {
+                        diagnostics = getDiagnostics(schema, textDocumentEvent.document)
+                    }
+                }
+                resourceDiagnostics.set(textDocumentEvent.document.uri, diagnostics)
+            }
+        }),
+        vscode.workspace.onDidCloseTextDocument((closeDocumentEvent) => {
+            return resourceManager.close(closeDocumentEvent.uri, true)
+        }),
+        vscode.languages.registerCodeLensProvider(
+            {
+                language: 'json',
+                scheme: 'file',
+                pattern: resourceFileGlobPattern,
+            },
+            new ResourceCodeLensProvider(resourceManager)
+        )
+    )
+}
+
+export class VirtualDocumentProvider implements vscode.TextDocumentContentProvider {
+    provideTextDocumentContent(uri: vscode.Uri): string {
+        return uri.query
+    }
+}
diff --git a/src/dynamicResources/awsResourceManager.ts b/packages/core/src/dynamicResources/awsResourceManager.ts
similarity index 96%
rename from src/dynamicResources/awsResourceManager.ts
rename to packages/core/src/dynamicResources/awsResourceManager.ts
index bf30526e2b5..6dbcc64e1ec 100644
--- a/src/dynamicResources/awsResourceManager.ts
+++ b/packages/core/src/dynamicResources/awsResourceManager.ts
@@ -1,13 +1,12 @@
 /*!
- * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { writeFileSync } from 'fs'
-import { remove } from 'fs-extra'
+import { writeFileSync } from 'fs' // eslint-disable-line no-restricted-imports
 import * as path from 'path'
 import * as vscode from 'vscode'
-import { CloudFormationClient } from '../shared/clients/cloudFormationClient'
+import { CloudFormationClient } from '../shared/clients/cloudFormation'
 import {
     getNonexistentFilename,
     makeTemporaryToolkitFolder,
@@ -18,8 +17,8 @@ import { getLogger } from '../shared/logger/logger'
 import { getTabSizeSetting } from '../shared/utilities/editorUtilities'
 import { ResourceNode } from './explorer/nodes/resourceNode'
 import { ResourceTypeNode } from './explorer/nodes/resourceTypeNode'
-import { isCloud9 } from '../shared/extensionUtilities'
 import globals from '../shared/extensionGlobals'
+import { fs } from '../shared/fs/fs'
 
 export const resourceFileGlobPattern = '**/*.awsResource.json'
 
@@ -72,7 +71,7 @@ export class AwsResourceManager {
             }
 
             const doc = await vscode.workspace.openTextDocument(uri)
-            if (existing && !isCloud9()) {
+            if (existing) {
                 await this.close(existing)
             }
 
@@ -97,7 +96,7 @@ export class AwsResourceManager {
             }
 
             if (uri.scheme === 'file') {
-                remove(uri.fsPath)
+                await fs.delete(uri.fsPath)
 
                 globals.schemaService.registerMapping({
                     uri,
@@ -157,7 +156,7 @@ export class AwsResourceManager {
         await this.initialize()
 
         const normalizedTypeName = getNormalizedTypeName(typeName)
-        const filename = getNonexistentFilename(
+        const filename = await getNonexistentFilename(
             this.folder!,
             encodeURIComponent(identifier),
             `.${normalizedTypeName}.awsResource.json`
diff --git a/src/dynamicResources/commands/configure.ts b/packages/core/src/dynamicResources/commands/configure.ts
similarity index 93%
rename from src/dynamicResources/commands/configure.ts
rename to packages/core/src/dynamicResources/commands/configure.ts
index fa2c30bc03b..e2ce2a54517 100644
--- a/src/dynamicResources/commands/configure.ts
+++ b/packages/core/src/dynamicResources/commands/configure.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -40,7 +40,7 @@ export async function configureResources(settings = new ResourcesSettings()): Pr
     if (result) {
         await settings.update(
             'enabledResources',
-            result.map(res => res.label)
+            result.map((res) => res.label)
         )
         telemetry.dynamicresource_selectResources.emit()
         return true
diff --git a/packages/core/src/dynamicResources/commands/copyIdentifier.ts b/packages/core/src/dynamicResources/commands/copyIdentifier.ts
new file mode 100644
index 00000000000..11f1f684997
--- /dev/null
+++ b/packages/core/src/dynamicResources/commands/copyIdentifier.ts
@@ -0,0 +1,12 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { copyToClipboard } from '../../shared/utilities/messages'
+
+export async function copyIdentifier(typeName: string, identifier: string) {
+    await copyToClipboard(identifier, 'identifier')
+    telemetry.dynamicresource_copyIdentifier.emit({ resourceType: typeName })
+}
diff --git a/src/dynamicResources/commands/deleteResource.ts b/packages/core/src/dynamicResources/commands/deleteResource.ts
similarity index 80%
rename from src/dynamicResources/commands/deleteResource.ts
rename to packages/core/src/dynamicResources/commands/deleteResource.ts
index 454340ae214..8ed475aea61 100644
--- a/src/dynamicResources/commands/deleteResource.ts
+++ b/packages/core/src/dynamicResources/commands/deleteResource.ts
@@ -1,14 +1,13 @@
 /*!
- * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * 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 { Window } from '../../shared/vscode/window'
 import { getLogger } from '../../shared/logger/logger'
 import { showConfirmationMessage, showViewLogsMessage } from '../../shared/utilities/messages'
-import { CloudControlClient } from '../../shared/clients/cloudControlClient'
+import { CloudControlClient } from '../../shared/clients/cloudControl'
 import globals from '../../shared/extensionGlobals'
 import { telemetry } from '../../shared/telemetry/telemetry'
 import { millisecondsSince, Result } from '../../shared/telemetry/telemetry'
@@ -17,29 +16,25 @@ const localize = nls.loadMessageBundle()
 export async function deleteResource(
     cloudControl: CloudControlClient,
     typeName: string,
-    identifier: string,
-    window = Window.vscode()
+    identifier: string
 ): Promise {
     getLogger().info(`deleteResource called for type ${typeName} identifier ${identifier}`)
-    const ok = await showConfirmationMessage(
-        {
-            prompt: localize('aws.resources.deleteResource.prompt', 'Delete resource {0} ({1})?', identifier, typeName),
-            confirm: localize('AWS.generic.delete', 'Delete'),
-            cancel: localize('AWS.generic.cancel', 'Cancel'),
-        },
-        window
-    )
+    const ok = await showConfirmationMessage({
+        prompt: localize('aws.resources.deleteResource.prompt', 'Delete resource {0} ({1})?', identifier, typeName),
+        confirm: localize('AWS.generic.delete', 'Delete'),
+        cancel: localize('AWS.generic.cancel', 'Cancel'),
+    })
     if (!ok) {
         getLogger().info(`Cancelled delete resource type ${typeName} identifier ${identifier}`)
         return false
     }
 
-    return await window.withProgress(
+    return await vscode.window.withProgress(
         {
             location: vscode.ProgressLocation.Notification,
             cancellable: false,
         },
-        async progress => {
+        async (progress) => {
             let result: Result = 'Succeeded'
             const startTime = new globals.clock.Date()
 
@@ -55,7 +50,7 @@ export async function deleteResource(
 
                 getLogger().info(`Deleted resource type ${typeName} identifier ${identifier}`)
 
-                window.showInformationMessage(
+                void vscode.window.showInformationMessage(
                     localize('aws.resources.deleteResource.success', 'Deleted resource {0} ({1})', identifier, typeName)
                 )
                 return true
@@ -66,7 +61,7 @@ export async function deleteResource(
                     getLogger().warn(
                         `Resource type ${typeName} does not support DELETE action in ${cloudControl.regionCode}`
                     )
-                    window.showWarningMessage(
+                    void vscode.window.showWarningMessage(
                         localize(
                             'aws.resources.deleteResource.unsupported',
                             'Resource type {0} does not currently support delete in {1}',
@@ -78,14 +73,13 @@ export async function deleteResource(
                 }
                 result = 'Failed'
                 getLogger().error(`Failed to delete resource type ${typeName} identifier ${identifier}: %s`, e)
-                showViewLogsMessage(
+                void showViewLogsMessage(
                     localize(
                         'aws.resources.deleteResource.failure',
                         'Failed to delete resource {0} ({1})',
                         identifier,
                         typeName
-                    ),
-                    window
+                    )
                 )
                 return false
             } finally {
diff --git a/src/dynamicResources/commands/openResource.ts b/packages/core/src/dynamicResources/commands/openResource.ts
similarity index 89%
rename from src/dynamicResources/commands/openResource.ts
rename to packages/core/src/dynamicResources/commands/openResource.ts
index 9d958fc3335..8b4cb24ca76 100644
--- a/src/dynamicResources/commands/openResource.ts
+++ b/packages/core/src/dynamicResources/commands/openResource.ts
@@ -1,11 +1,10 @@
 /*!
- * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * 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 { Window } from '../../shared/vscode/window'
 import { getLogger } from '../../shared/logger/logger'
 import { Result } from '../../shared/telemetry/telemetry'
 import { ResourceNode } from '../explorer/nodes/resourceNode'
@@ -13,27 +12,24 @@ import { AwsResourceManager, TypeSchema } from '../awsResourceManager'
 import { telemetry } from '../../shared/telemetry/telemetry'
 const localize = nls.loadMessageBundle()
 
-export async function openResource(
-    opts: {
-        source: ResourceNode | vscode.Uri
-        preview: boolean
-        resourceManager: AwsResourceManager
-        diagnostics: vscode.DiagnosticCollection
-    },
-    window = Window.vscode()
-): Promise {
+export async function openResource(opts: {
+    source: ResourceNode | vscode.Uri
+    preview: boolean
+    resourceManager: AwsResourceManager
+    diagnostics: vscode.DiagnosticCollection
+}): Promise {
     const resource = opts.source instanceof vscode.Uri ? opts.resourceManager.fromUri(opts.source) : opts.source
     if (!resource || !(resource instanceof ResourceNode)) {
         throw new Error('could not resolve resource')
     }
     getLogger().info(`openResource called for type ${resource.parent.typeName} identifier ${resource.identifier}`)
 
-    return await window.withProgress(
+    return await vscode.window.withProgress(
         {
             location: vscode.ProgressLocation.Notification,
             cancellable: false,
         },
-        async progress => {
+        async (progress) => {
             let result: Result = 'Succeeded'
 
             progress.report({
@@ -43,7 +39,7 @@ export async function openResource(
             try {
                 const editor = await opts.resourceManager.open(resource, opts.preview)
                 if (!opts.preview) {
-                    vscode.window.showWarningMessage(
+                    void vscode.window.showWarningMessage(
                         localize(
                             'aws.resources.editResource.notice',
                             'You are editing an AWS resource. Any saved changes will be reflected on the remote resource.'
@@ -64,7 +60,7 @@ export async function openResource(
                     resource.parent.typeName
                 )
 
-                window.showErrorMessage(errorMessage)
+                void vscode.window.showErrorMessage(errorMessage)
                 getLogger().error('Error opening resource: %s', error)
                 result = 'Failed'
             } finally {
diff --git a/src/dynamicResources/commands/saveResource.ts b/packages/core/src/dynamicResources/commands/saveResource.ts
similarity index 88%
rename from src/dynamicResources/commands/saveResource.ts
rename to packages/core/src/dynamicResources/commands/saveResource.ts
index 154df09261f..be395bc7f48 100644
--- a/src/dynamicResources/commands/saveResource.ts
+++ b/packages/core/src/dynamicResources/commands/saveResource.ts
@@ -1,11 +1,10 @@
 /*!
- * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * 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 { Window } from '../../shared/vscode/window'
 import { getLogger } from '../../shared/logger/logger'
 import { showViewLogsMessage } from '../../shared/utilities/messages'
 import { millisecondsSince, Result } from '../../shared/telemetry/telemetry'
@@ -13,8 +12,8 @@ import { compare, Operation } from 'fast-json-patch'
 import { ResourceNode } from '../explorer/nodes/resourceNode'
 import { ResourceTypeNode } from '../explorer/nodes/resourceTypeNode'
 import { AwsResourceManager } from '../awsResourceManager'
-import { CloudControlClient } from '../../shared/clients/cloudControlClient'
-import { CloudControl } from 'aws-sdk'
+import { CloudControlClient } from '../../shared/clients/cloudControl'
+import { ResourceDescription } from '@aws-sdk/client-cloudcontrol'
 import globals from '../../shared/extensionGlobals'
 import { telemetry } from '../../shared/telemetry/telemetry'
 
@@ -35,7 +34,7 @@ export async function saveResource(
                     resource.clearChildren()
                     await vscode.commands.executeCommand('aws.refreshAwsExplorerNode', resource)
                     const resourceNodes = (await resource.getChildren()) as ResourceNode[]
-                    const newNode = resourceNodes.find(node => node.identifier === identifier)
+                    const newNode = resourceNodes.find((node) => node.identifier === identifier)
                     if (newNode) {
                         await resourceManager.open(newNode, true)
                     }
@@ -71,15 +70,14 @@ export async function saveResource(
 export async function createResource(
     typeName: string,
     definition: string,
-    cloudControl: CloudControlClient,
-    window = Window.vscode()
+    cloudControl: CloudControlClient
 ): Promise {
-    return await window.withProgress(
+    return await vscode.window.withProgress(
         {
             location: vscode.ProgressLocation.Notification,
             cancellable: false,
         },
-        async progress => {
+        async (progress) => {
             const startTime = new globals.clock.Date()
             let result: Result = 'Succeeded'
 
@@ -93,7 +91,7 @@ export async function createResource(
                 })
                 const identifier = result.ProgressEvent!.Identifier!
                 getLogger().info(`Created resource type ${typeName} identifier ${identifier}`)
-                window.showInformationMessage(
+                void vscode.window.showInformationMessage(
                     localize('aws.resources.createResource.success', 'Created resource {0} ({1})', identifier, typeName)
                 )
                 return identifier
@@ -104,7 +102,7 @@ export async function createResource(
                     getLogger().warn(
                         `Resource type ${typeName} does not support CREATE action in ${cloudControl.regionCode}`
                     )
-                    window.showWarningMessage(
+                    void vscode.window.showWarningMessage(
                         localize(
                             'aws.resources.createResource.unsupported',
                             '{0} does not currently support resource creation in {1}',
@@ -115,9 +113,8 @@ export async function createResource(
                 } else {
                     result = 'Failed'
                     getLogger().error(`Failed to create resource type ${typeName}: %O`, error.message)
-                    showViewLogsMessage(
-                        localize('aws.resources.createResource.failure', 'Failed to create resource ({0})', typeName),
-                        window
+                    void showViewLogsMessage(
+                        localize('aws.resources.createResource.failure', 'Failed to create resource ({0})', typeName)
                     )
                     throw e
                 }
@@ -138,15 +135,14 @@ export async function updateResource(
     identifier: string,
     definition: string,
     cloudControl: CloudControlClient,
-    window = Window.vscode(),
     diff?: Operation[]
 ): Promise {
-    return await window.withProgress(
+    return await vscode.window.withProgress(
         {
             location: vscode.ProgressLocation.Notification,
             cancellable: false,
         },
-        async progress => {
+        async (progress) => {
             const startTime = new globals.clock.Date()
             let result: Result = 'Succeeded'
             try {
@@ -162,7 +158,7 @@ export async function updateResource(
 
                 if (patch.length === 0) {
                     result = 'Cancelled'
-                    window.showWarningMessage(
+                    void vscode.window.showWarningMessage(
                         localize(
                             'aws.resources.updateResource.noDiff',
                             'Update cancelled - no diff between local and remote definitions',
@@ -180,7 +176,7 @@ export async function updateResource(
                 })
                 getLogger().info(`Updated resource type ${typeName} identifier ${identifier}`)
 
-                window.showInformationMessage(
+                void vscode.window.showInformationMessage(
                     localize('aws.resources.updateResource.success', 'Updated resource {0} ({1})', identifier, typeName)
                 )
                 return true
@@ -191,7 +187,7 @@ export async function updateResource(
                     getLogger().warn(
                         `Resource type ${typeName} does not support UPDATE action in ${cloudControl.regionCode}`
                     )
-                    window.showWarningMessage(
+                    void vscode.window.showWarningMessage(
                         localize(
                             'aws.resources.createResource.unsupported',
                             '{0} does not currently support resource updating in {1}',
@@ -206,14 +202,13 @@ export async function updateResource(
                         `Failed to update resource type ${typeName} identifier ${identifier}: %O`,
                         error.message
                     )
-                    showViewLogsMessage(
+                    void showViewLogsMessage(
                         localize(
                             'aws.resources.updateResource.failure',
                             'Failed to update resource {0} ({1})',
                             identifier,
                             typeName
-                        ),
-                        window
+                        )
                     )
                     throw e
                 }
@@ -229,7 +224,7 @@ export async function updateResource(
     )
 }
 
-function computeDiff(currentDefinition: CloudControl.ResourceDescription, updatedDefinition: string): Operation[] {
+function computeDiff(currentDefinition: ResourceDescription, updatedDefinition: string): Operation[] {
     const current = JSON.parse(currentDefinition.Properties!)
     const updated = JSON.parse(updatedDefinition)
     return compare(current, updated)
@@ -239,5 +234,5 @@ export function updateDiagnostics(err: Error, doc: vscode.TextDocument, diagnost
     const range = new vscode.Range(doc.positionAt(0), doc.positionAt(doc.getText().length))
     const diag = new vscode.Diagnostic(range, err.message)
     diagnostics.set(doc.uri, [diag])
-    vscode.commands.executeCommand('workbench.actions.view.problems')
+    void vscode.commands.executeCommand('workbench.actions.view.problems')
 }
diff --git a/packages/core/src/dynamicResources/explorer/nodes/resourceNode.ts b/packages/core/src/dynamicResources/explorer/nodes/resourceNode.ts
new file mode 100644
index 00000000000..28e655abc9b
--- /dev/null
+++ b/packages/core/src/dynamicResources/explorer/nodes/resourceNode.ts
@@ -0,0 +1,35 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as nls from 'vscode-nls'
+
+import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase'
+import { ResourceTypeNode } from './resourceTypeNode'
+import { parse, validate } from '@aws-sdk/util-arn-parser'
+
+const localize = nls.loadMessageBundle()
+
+export class ResourceNode extends AWSTreeNodeBase {
+    public constructor(
+        public readonly parent: ResourceTypeNode,
+        public readonly identifier: string,
+        public override contextValue?: string
+    ) {
+        super('')
+        this.contextValue = contextValue ?? 'ResourceNode'
+        const friendlyName = this.getFriendlyName(identifier)
+        this.label = friendlyName
+        this.tooltip = identifier
+        this.command = {
+            title: localize('AWS.generic.preview', 'Preview'),
+            command: 'aws.resources.openResourcePreview',
+            arguments: [this],
+        }
+    }
+
+    private getFriendlyName(identifier: string): string {
+        return validate(identifier) ? parse(identifier).resource : identifier
+    }
+}
diff --git a/src/dynamicResources/explorer/nodes/resourceTypeNode.ts b/packages/core/src/dynamicResources/explorer/nodes/resourceTypeNode.ts
similarity index 84%
rename from src/dynamicResources/explorer/nodes/resourceTypeNode.ts
rename to packages/core/src/dynamicResources/explorer/nodes/resourceTypeNode.ts
index 1b450060b4f..afc21ff5d16 100644
--- a/src/dynamicResources/explorer/nodes/resourceTypeNode.ts
+++ b/packages/core/src/dynamicResources/explorer/nodes/resourceTypeNode.ts
@@ -1,12 +1,12 @@
 /*!
- * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import * as vscode from 'vscode'
 import { ChildNodeLoader, ChildNodePage } from '../../../awsexplorer/childNodeLoader'
-import { CloudControlClient } from '../../../shared/clients/cloudControlClient'
-import { getLogger } from '../../../shared/logger'
+import { CloudControlClient } from '../../../shared/clients/cloudControl'
+import { getLogger } from '../../../shared/logger/logger'
 import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase'
 import { LoadMoreNode } from '../../../shared/treeview/nodes/loadMoreNode'
 import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode'
@@ -15,9 +15,9 @@ import { localize } from '../../../shared/utilities/vsCodeUtils'
 import { ResourcesNode } from './resourcesNode'
 import { ResourceNode } from './resourceNode'
 import { Result } from '../../../shared/telemetry/telemetry'
-import { CloudControl } from 'aws-sdk'
+import { ResourceDescription } from '@aws-sdk/client-cloudcontrol'
 import { ResourceTypeMetadata } from '../../model/resources'
-import { DefaultS3Client } from '../../../shared/clients/s3Client'
+import { S3Client } from '../../../shared/clients/s3'
 import { telemetry } from '../../../shared/telemetry/telemetry'
 
 export const contextValueResourceOperations: any = {
@@ -30,7 +30,7 @@ export const contextValueResource = 'ResourceNode'
 const unavailableResource = localize('AWS.explorerNode.resources.unavailable', 'Unavailable in region')
 
 export class ResourceTypeNode extends AWSTreeNodeBase implements LoadMoreNode {
-    private readonly childLoader: ChildNodeLoader = new ChildNodeLoader(this, token => this.loadPage(token))
+    private readonly childLoader: ChildNodeLoader = new ChildNodeLoader(this, (token) => this.loadPage(token))
     private readonly childContextValue: string
 
     public constructor(
@@ -45,7 +45,7 @@ export class ResourceTypeNode extends AWSTreeNodeBase implements LoadMoreNode {
         )
         this.tooltip = typeName
         const supportedOperations = metadata.operations
-            ? metadata.operations.map(op => contextValueResourceOperations[op])
+            ? metadata.operations.map((op) => contextValueResourceOperations[op])
             : Object.values(contextValueResourceOperations)
 
         if (!metadata.available) {
@@ -113,9 +113,9 @@ export class ResourceTypeNode extends AWSTreeNodeBase implements LoadMoreNode {
 
         // S3::Bucket's resource handler LIST is not regionalized at this time
         if (this.typeName === 'AWS::S3::Bucket') {
-            const s3 = new DefaultS3Client(this.parent.region)
+            const s3 = new S3Client(this.parent.region)
             const buckets = await s3.listBuckets()
-            newResources = buckets.buckets.map(bucket => new ResourceNode(this, bucket.name, this.childContextValue))
+            newResources = buckets.buckets.map((bucket) => new ResourceNode(this, bucket.Name, this.childContextValue))
         } else {
             const response = await this.cloudControl.listResources({
                 TypeName: this.typeName,
@@ -123,15 +123,12 @@ export class ResourceTypeNode extends AWSTreeNodeBase implements LoadMoreNode {
             })
 
             newResources = response.ResourceDescriptions
-                ? response.ResourceDescriptions.reduce(
-                      (accumulator: ResourceNode[], current: CloudControl.ResourceDescription) => {
-                          if (current.Identifier) {
-                              accumulator.push(new ResourceNode(this, current.Identifier, this.childContextValue))
-                          }
-                          return accumulator
-                      },
-                      []
-                  )
+                ? response.ResourceDescriptions.reduce((accumulator: ResourceNode[], current: ResourceDescription) => {
+                      if (current.Identifier) {
+                          accumulator.push(new ResourceNode(this, current.Identifier, this.childContextValue))
+                      }
+                      return accumulator
+                  }, [])
                 : []
             nextToken = response.NextToken
         }
diff --git a/src/dynamicResources/explorer/nodes/resourcesNode.ts b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts
similarity index 80%
rename from src/dynamicResources/explorer/nodes/resourcesNode.ts
rename to packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts
index 95829f4b5c1..65cc8c09d3b 100644
--- a/src/dynamicResources/explorer/nodes/resourcesNode.ts
+++ b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts
@@ -1,21 +1,20 @@
 /*!
- * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * 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 { CloudFormationClient, DefaultCloudFormationClient } from '../../../shared/clients/cloudFormationClient'
+import { CloudFormationClient } from '../../../shared/clients/cloudFormation'
 import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase'
 import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode'
 import { makeChildrenNodes } from '../../../shared/treeview/utils'
 import { toArrayAsync, updateInPlace } from '../../../shared/utilities/collectionUtils'
 import { ResourceTypeNode } from './resourceTypeNode'
-import { CloudFormation } from 'aws-sdk'
-import { CloudControlClient, DefaultCloudControlClient } from '../../../shared/clients/cloudControlClient'
+import { CloudControlClient } from '../../../shared/clients/cloudControl'
 import { memoizedGetResourceTypes, ResourceTypeMetadata } from '../../model/resources'
-import { isCloud9 } from '../../../shared/extensionUtilities'
 import { ResourcesSettings } from '../../commands/configure'
+import { TypeSummary } from '@aws-sdk/client-cloudformation'
 
 const localize = nls.loadMessageBundle()
 
@@ -24,8 +23,8 @@ export class ResourcesNode extends AWSTreeNodeBase {
 
     public constructor(
         public readonly region: string,
-        public readonly cloudFormation: CloudFormationClient = new DefaultCloudFormationClient(region),
-        private readonly cloudControl: CloudControlClient = new DefaultCloudControlClient(region),
+        public readonly cloudFormation: CloudFormationClient = new CloudFormationClient(region),
+        private readonly cloudControl: CloudControlClient = new CloudControlClient(region),
         private readonly settings = new ResourcesSettings()
     ) {
         super(localize('AWS.explorerNode.resources.label', 'Resources'), vscode.TreeItemCollapsibleState.Collapsed)
@@ -57,14 +56,13 @@ export class ResourcesNode extends AWSTreeNodeBase {
 
     public async updateChildren(): Promise {
         const resourceTypes = memoizedGetResourceTypes()
-        const defaultResources = isCloud9() ? Array.from(resourceTypes.keys()) : []
-        const enabledResources = this.settings.get('enabledResources', defaultResources)
+        const enabledResources = this.settings.get('enabledResources', [])
 
         // Use the most recently update type definition per-type
         const types = await toArrayAsync(this.cloudFormation.listTypes())
         types.sort((a, b) => (a.LastUpdated?.getTime() ?? 0) - (b.LastUpdated?.getTime() ?? 0))
 
-        const availableTypes: Map = new Map()
+        const availableTypes: Map = new Map()
         for (const type of types) {
             if (type.TypeName) {
                 availableTypes.set(type.TypeName!, type)
@@ -74,8 +72,8 @@ export class ResourcesNode extends AWSTreeNodeBase {
         updateInPlace(
             this.resourceTypeNodes,
             enabledResources,
-            key => this.resourceTypeNodes.get(key)!.clearChildren(),
-            key => {
+            (key) => this.resourceTypeNodes.get(key)!.clearChildren(),
+            (key) => {
                 const metadata = resourceTypes.get(key) ?? ({} as ResourceTypeMetadata)
                 metadata.available = availableTypes.has(key)
                 return new ResourceTypeNode(this, key, this.cloudControl, metadata)
diff --git a/src/dynamicResources/model/resources.ts b/packages/core/src/dynamicResources/model/resources.ts
similarity index 93%
rename from src/dynamicResources/model/resources.ts
rename to packages/core/src/dynamicResources/model/resources.ts
index c4ea844509a..fcf92a3255e 100644
--- a/src/dynamicResources/model/resources.ts
+++ b/packages/core/src/dynamicResources/model/resources.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
diff --git a/src/dynamicResources/model/supported_resources.json b/packages/core/src/dynamicResources/model/supported_resources.json
similarity index 100%
rename from src/dynamicResources/model/supported_resources.json
rename to packages/core/src/dynamicResources/model/supported_resources.json
diff --git a/packages/core/src/eventSchemas/activation.ts b/packages/core/src/eventSchemas/activation.ts
new file mode 100644
index 00000000000..5e68b7df069
--- /dev/null
+++ b/packages/core/src/eventSchemas/activation.ts
@@ -0,0 +1,38 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { downloadSchemaItemCode } from '../eventSchemas/commands/downloadSchemaItemCode'
+import { createSearchSchemasWebView } from './vue/searchSchemas'
+import { viewSchemaItem } from '../eventSchemas/commands/viewSchemaItem'
+import { RegistryItemNode } from '../eventSchemas/explorer/registryItemNode'
+import { SchemaItemNode } from '../eventSchemas/explorer/schemaItemNode'
+import { SchemasNode } from '../eventSchemas/explorer/schemasNode'
+import { ExtContext } from '../shared/extensions'
+import { Commands } from '../shared/vscode/commands2'
+
+/**
+ * Activate Schemas functionality for the extension.
+ */
+export async function activate(context: ExtContext): Promise {
+    await registerSchemasCommands(context)
+}
+
+async function registerSchemasCommands(context: ExtContext): Promise {
+    context.extensionContext.subscriptions.push(
+        Commands.register('aws.viewSchemaItem', async (node: SchemaItemNode) => await viewSchemaItem(node)),
+        Commands.register(
+            'aws.downloadSchemaItemCode',
+            async (node: SchemaItemNode) => await downloadSchemaItemCode(node, context.outputChannel)
+        ),
+        Commands.register(
+            'aws.searchSchema',
+            async (node: SchemasNode) => await createSearchSchemasWebView(context, node)
+        ),
+        Commands.register(
+            'aws.searchSchemaPerRegistry',
+            async (node: RegistryItemNode) => await createSearchSchemasWebView(context, node)
+        )
+    )
+}
diff --git a/src/eventSchemas/commands/downloadSchemaItemCode.ts b/packages/core/src/eventSchemas/commands/downloadSchemaItemCode.ts
similarity index 91%
rename from src/eventSchemas/commands/downloadSchemaItemCode.ts
rename to packages/core/src/eventSchemas/commands/downloadSchemaItemCode.ts
index 95d780104c8..0abc1e340e2 100644
--- a/src/eventSchemas/commands/downloadSchemaItemCode.ts
+++ b/packages/core/src/eventSchemas/commands/downloadSchemaItemCode.ts
@@ -1,18 +1,18 @@
 /*!
- * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import * as nls from 'vscode-nls'
 const localize = nls.loadMessageBundle()
-import { Schemas } from 'aws-sdk'
+import { PutCodeBindingResponse } from '@aws-sdk/client-schemas'
 import fs = require('fs')
 import path = require('path')
 import * as vscode from 'vscode'
 import { SchemaClient } from '../../shared/clients/schemaClient'
 import { makeTemporaryToolkitFolder, tryRemoveFolder } from '../../shared/filesystemUtilities'
 import * as localizedText from '../../shared/localizedText'
-import { getLogger, Logger } from '../../shared/logger'
+import { getLogger, Logger } from '../../shared/logger/logger'
 import { Result } from '../../shared/telemetry/telemetry'
 import { SchemaItemNode } from '../explorer/schemaItemNode'
 import { getLanguageDetails } from '../models/schemaCodeLangs'
@@ -23,7 +23,7 @@ import {
     SchemaCodeDownloadWizardResponse,
 } from '../wizards/schemaCodeDownloadWizard'
 
-import * as admZip from 'adm-zip'
+import admZip from 'adm-zip'
 import globals from '../../shared/extensionGlobals'
 import { telemetry } from '../../shared/telemetry/telemetry'
 
@@ -48,7 +48,7 @@ export async function downloadSchemaItemCode(node: SchemaItemNode, outputChannel
             return
         }
 
-        vscode.window.showInformationMessage(
+        void vscode.window.showInformationMessage(
             localize(
                 'AWS.message.info.schemas.downloadCodeBindings.start',
                 'Downloading code for schema {0}...',
@@ -67,7 +67,7 @@ export async function downloadSchemaItemCode(node: SchemaItemNode, outputChannel
         }
         const schemaCodeDownloader = createSchemaCodeDownloaderObject(node.client, outputChannel)
         const coreCodeFilePath = await schemaCodeDownloader.downloadCode(request)
-        vscode.window.showInformationMessage(
+        void vscode.window.showInformationMessage(
             localize(
                 'AWS.message.info.schemas.downloadCodeBindings.finished',
                 'Downloaded code for schema {0}!',
@@ -89,7 +89,7 @@ export async function downloadSchemaItemCode(node: SchemaItemNode, outputChannel
         if (error instanceof UserNotifiedError && error.message) {
             errorMessage = error.message
         }
-        vscode.window.showErrorMessage(errorMessage)
+        void vscode.window.showErrorMessage(errorMessage)
         logger.error('Error downloading schema: %s', error)
     } finally {
         telemetry.schemas_download.emit({ result: downloadResult })
@@ -138,9 +138,9 @@ export class SchemaCodeDownloader {
             zipContents = await this.downloader.download(request)
         } catch (err) {
             const error = err as Error
-            if (error.name == 'ResourceNotFound') {
-                //If the code generation wasn't previously kicked off, do so
-                vscode.window.showInformationMessage(
+            if (error.name === 'ResourceNotFound') {
+                // If the code generation wasn't previously kicked off, do so
+                void vscode.window.showInformationMessage(
                     localize(
                         'AWS.message.info.schemas.downloadCodeBindings.generate',
                         '{0}: Generating code (this may take a few seconds the first time)...',
@@ -149,11 +149,11 @@ export class SchemaCodeDownloader {
                 )
                 await this.generator.generate(request)
 
-                //Then, poll for completion
+                // Then, poll for completion
                 await this.poller.pollForCompletion(request)
 
-                //Download generated code bindings
-                vscode.window.showInformationMessage(
+                // Download generated code bindings
+                void vscode.window.showInformationMessage(
                     localize(
                         'AWS.message.info.schemas.downloadCodeBindings.downloading',
                         '{0}: Downloading code...',
@@ -165,7 +165,7 @@ export class SchemaCodeDownloader {
                 throw err // Unexpected exception, throw
             }
         }
-        vscode.window.showInformationMessage(
+        void vscode.window.showInformationMessage(
             localize(
                 'AWS.message.info.schemas.downloadCodeBindings.extracting',
                 '{0}: Extracting/copying code...',
@@ -180,10 +180,8 @@ export class SchemaCodeDownloader {
 export class CodeGenerator {
     public constructor(public client: SchemaClient) {}
 
-    public async generate(
-        codeDownloadRequest: SchemaCodeDownloadRequestDetails
-    ): Promise {
-        let response: Schemas.PutCodeBindingResponse
+    public async generate(codeDownloadRequest: SchemaCodeDownloadRequestDetails): Promise {
+        let response: PutCodeBindingResponse
         try {
             response = await this.client.putCodeBinding(
                 codeDownloadRequest.language,
@@ -236,7 +234,7 @@ export class CodeGenerationStatusPoller {
                 )
             }
 
-            await new Promise(resolve => globals.clock.setTimeout(resolve, retryInterval))
+            await new Promise((resolve) => globals.clock.setTimeout(resolve, retryInterval))
         }
         throw new UserNotifiedError(
             localize(
@@ -274,7 +272,7 @@ export class CodeDownloader {
 
             return zipContents
         } else {
-            throw new Error('Response body should be Buffer type')
+            throw new TypeError('Response body should be Buffer type')
         }
     }
 }
@@ -294,7 +292,7 @@ export class CodeExtractor {
             const codeZipFile = path.join(codeZipDir, fileName)
             const destinationDirectory = request.destinationDirectory.fsPath
 
-            //write binary data into a temp zip file in a temp directory
+            // write binary data into a temp zip file in a temp directory
             const zipContentsBinary = new Uint8Array(zipContents)
             const fd = fs.openSync(codeZipFile, 'w')
             fs.writeSync(fd, zipContentsBinary, 0, zipContentsBinary.byteLength, 0)
@@ -318,7 +316,7 @@ export class CodeExtractor {
 
             return undefined
         } finally {
-            tryRemoveFolder(codeZipDir)
+            await tryRemoveFolder(codeZipDir)
         }
     }
 
@@ -328,7 +326,7 @@ export class CodeExtractor {
         const zipEntries = zip.getEntries()
         const detectedCollisions: string[] = []
 
-        zipEntries.forEach(function (zipEntry) {
+        for (const zipEntry of zipEntries) {
             if (zipEntry.isDirectory) {
                 // Ignore directories because those can/will merged
             } else {
@@ -337,7 +335,7 @@ export class CodeExtractor {
                     detectedCollisions.push(intendedDestinationPath)
                 }
             }
-        })
+        }
 
         if (detectedCollisions.length > 0) {
             this.writeToOutputChannel(detectedCollisions)
diff --git a/src/eventSchemas/commands/viewSchemaItem.ts b/packages/core/src/eventSchemas/commands/viewSchemaItem.ts
similarity index 85%
rename from src/eventSchemas/commands/viewSchemaItem.ts
rename to packages/core/src/eventSchemas/commands/viewSchemaItem.ts
index c9c66c137a9..cc40911ea98 100644
--- a/src/eventSchemas/commands/viewSchemaItem.ts
+++ b/packages/core/src/eventSchemas/commands/viewSchemaItem.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -7,7 +7,7 @@ import * as nls from 'vscode-nls'
 const localize = nls.loadMessageBundle()
 
 import * as vscode from 'vscode'
-import { getLogger, Logger } from '../../shared/logger'
+import { getLogger, Logger } from '../../shared/logger/logger'
 import { Result } from '../../shared/telemetry/telemetry'
 import { getTabSizeSetting } from '../../shared/utilities/editorUtilities'
 import { SchemaItemNode } from '../explorer/schemaItemNode'
@@ -23,7 +23,7 @@ export async function viewSchemaItem(node: SchemaItemNode) {
     } catch (err) {
         viewResult = 'Failed'
         const error = err as Error
-        vscode.window.showErrorMessage(
+        void vscode.window.showErrorMessage(
             localize(
                 'AWS.message.error.schemas.viewSchema.could_not_open',
                 'Could not fetch and display schema {0} contents',
@@ -51,5 +51,5 @@ export async function showSchemaContent(
         language: 'json',
     })
     const editor = await vscode.window.showTextDocument(newDoc, vscode.ViewColumn.One, false)
-    await editor.edit(edit => edit.insert(new vscode.Position(/*line*/ 0, /*character*/ 0), prettySchemaContent))
+    await editor.edit((edit) => edit.insert(new vscode.Position(/* line*/ 0, /* character*/ 0), prettySchemaContent))
 }
diff --git a/packages/core/src/eventSchemas/explorer/registryItemNode.ts b/packages/core/src/eventSchemas/explorer/registryItemNode.ts
new file mode 100644
index 00000000000..8141259b955
--- /dev/null
+++ b/packages/core/src/eventSchemas/explorer/registryItemNode.ts
@@ -0,0 +1,79 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as nls from 'vscode-nls'
+const localize = nls.loadMessageBundle()
+
+import { RegistrySummary } from '@aws-sdk/client-schemas'
+import * as os from 'os'
+import * as vscode from 'vscode'
+
+import { listSchemaItems } from '../utils'
+
+import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
+import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode'
+import { makeChildrenNodes } from '../../shared/treeview/utils'
+import { toMapAsync, updateInPlace } from '../../shared/utilities/collectionUtils'
+import { SchemaItemNode } from './schemaItemNode'
+import { getIcon } from '../../shared/icons'
+import { SchemaClient } from '../../shared/clients/schemaClient'
+
+export class RegistryItemNode extends AWSTreeNodeBase {
+    private readonly schemaNodes: Map
+    public override readonly regionCode: string = this.client.regionCode
+
+    public constructor(
+        private registryItemOutput: RegistrySummary,
+        private readonly client: SchemaClient
+    ) {
+        super('', vscode.TreeItemCollapsibleState.Collapsed)
+
+        this.update(registryItemOutput)
+        this.contextValue = 'awsRegistryItemNode'
+        this.schemaNodes = new Map()
+        this.iconPath = getIcon('aws-schemas-registry')
+    }
+
+    public get registryName(): string {
+        return (
+            this.registryItemOutput.RegistryName ||
+            localize('AWS.explorerNode.registry.registryName.Not.Found', 'Registry name not found')
+        )
+    }
+
+    public override async getChildren(): Promise {
+        return await makeChildrenNodes({
+            getChildNodes: async () => {
+                await this.updateChildren()
+
+                return [...this.schemaNodes.values()]
+            },
+            getNoChildrenPlaceholderNode: async () =>
+                new PlaceholderNode(this, localize('AWS.explorerNode.registry.noSchemas', '[No Registry Schemas]')),
+            sort: (nodeA, nodeB) => nodeA.schemaName.localeCompare(nodeB.schemaName),
+        })
+    }
+
+    public update(registryItemOutput: RegistrySummary): void {
+        this.registryItemOutput = registryItemOutput
+        this.label = `${this.registryName}`
+        let registryArn = ''
+        if (this.registryItemOutput.RegistryArn) {
+            registryArn = `${os.EOL}${this.registryItemOutput.RegistryArn}`
+        }
+        this.tooltip = `${this.registryName}${registryArn}`
+    }
+
+    public async updateChildren(): Promise {
+        const schemas = await toMapAsync(listSchemaItems(this.client, this.registryName), (schema) => schema.SchemaName)
+
+        updateInPlace(
+            this.schemaNodes,
+            schemas.keys(),
+            (key) => this.schemaNodes.get(key)!.update(schemas.get(key)!),
+            (key) => new SchemaItemNode(schemas.get(key)!, this.client, this.registryName)
+        )
+    }
+}
diff --git a/src/eventSchemas/explorer/schemaItemNode.ts b/packages/core/src/eventSchemas/explorer/schemaItemNode.ts
similarity index 83%
rename from src/eventSchemas/explorer/schemaItemNode.ts
rename to packages/core/src/eventSchemas/explorer/schemaItemNode.ts
index b59a2c36e80..790834e165c 100644
--- a/src/eventSchemas/explorer/schemaItemNode.ts
+++ b/packages/core/src/eventSchemas/explorer/schemaItemNode.ts
@@ -1,10 +1,9 @@
 /*!
- * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { Schemas } from 'aws-sdk'
-
+import { SchemaSummary, SchemaVersionSummary } from '@aws-sdk/client-schemas'
 import * as os from 'os'
 import { SchemaClient } from '../../shared/clients/schemaClient'
 
@@ -15,7 +14,7 @@ import { localize } from '../../shared/utilities/vsCodeUtils'
 
 export class SchemaItemNode extends AWSTreeNodeBase {
     public constructor(
-        private schemaItem: Schemas.SchemaSummary,
+        private schemaItem: SchemaSummary,
         public readonly client: SchemaClient,
         public readonly registryName: string
     ) {
@@ -30,7 +29,7 @@ export class SchemaItemNode extends AWSTreeNodeBase {
         }
     }
 
-    public update(schemaItem: Schemas.SchemaSummary): void {
+    public update(schemaItem: SchemaSummary): void {
         this.schemaItem = schemaItem
         this.label = this.schemaItem.SchemaName || ''
         let schemaArn = ''
@@ -50,7 +49,7 @@ export class SchemaItemNode extends AWSTreeNodeBase {
         return response.Content!
     }
 
-    public async listSchemaVersions(): Promise {
+    public async listSchemaVersions(): Promise {
         const versions = await toArrayAsync(this.client.listSchemaVersions(this.registryName, this.schemaName))
 
         return versions
diff --git a/src/eventSchemas/explorer/schemasNode.ts b/packages/core/src/eventSchemas/explorer/schemasNode.ts
similarity index 86%
rename from src/eventSchemas/explorer/schemasNode.ts
rename to packages/core/src/eventSchemas/explorer/schemasNode.ts
index 512043bccbd..4f9b584b82e 100644
--- a/src/eventSchemas/explorer/schemasNode.ts
+++ b/packages/core/src/eventSchemas/explorer/schemasNode.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -40,13 +40,13 @@ export class SchemasNode extends AWSTreeNodeBase {
     }
 
     public async updateChildren(): Promise {
-        const registries = await toMapAsync(listRegistryItems(this.client), registry => registry.RegistryName)
+        const registries = await toMapAsync(listRegistryItems(this.client), (registry) => registry.RegistryName)
 
         updateInPlace(
             this.registryNodes,
             registries.keys(),
-            key => this.registryNodes.get(key)!.update(registries.get(key)!),
-            key => new RegistryItemNode(registries.get(key)!, this.client)
+            (key) => this.registryNodes.get(key)!.update(registries.get(key)!),
+            (key) => new RegistryItemNode(registries.get(key)!, this.client)
         )
     }
 }
diff --git a/src/eventSchemas/models/schemaCodeGenUtils.ts b/packages/core/src/eventSchemas/models/schemaCodeGenUtils.ts
similarity index 75%
rename from src/eventSchemas/models/schemaCodeGenUtils.ts
rename to packages/core/src/eventSchemas/models/schemaCodeGenUtils.ts
index 4577105777d..155b600b82d 100644
--- a/src/eventSchemas/models/schemaCodeGenUtils.ts
+++ b/packages/core/src/eventSchemas/models/schemaCodeGenUtils.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -65,24 +65,20 @@ class CodeGenPackageBuilder {
 
     public append(segment: string): CodeGenPackageBuilder {
         if (this.builder.length > 0) {
-            this.builder = this.builder.concat(IdentifierFormatter.packageSeparator)
+            this.builder = this.builder.concat(packageSeparator)
         }
-        this.builder = this.builder.concat(IdentifierFormatter.toValidIdentifier(segment.toLowerCase()))
+        this.builder = this.builder.concat(toValidIdentifier(segment.toLowerCase()))
 
         return this
     }
 }
 
-export namespace IdentifierFormatter {
-    export const packageSeparator = '.'
-    const potentialPackageSeparator = '@'
-    const notValidIdentifierRegex = new RegExp(`[^a-zA-Z0-9_${potentialPackageSeparator}]`, 'g')
-    const potentialPackageSeparatorRegex = new RegExp(potentialPackageSeparator, 'g')
-    const underscore = '_'
+export const packageSeparator = '.'
+const potentialPackageSeparator = '@'
+const notValidIdentifierRegex = new RegExp(`[^a-zA-Z0-9_${potentialPackageSeparator}]`, 'g')
+const potentialPackageSeparatorRegex = new RegExp(potentialPackageSeparator, 'g')
+const underscore = '_'
 
-    export function toValidIdentifier(name: string): string {
-        return name
-            .replace(notValidIdentifierRegex, underscore)
-            .replace(potentialPackageSeparatorRegex, packageSeparator)
-    }
+export function toValidIdentifier(name: string): string {
+    return name.replace(notValidIdentifierRegex, underscore).replace(potentialPackageSeparatorRegex, packageSeparator)
 }
diff --git a/src/eventSchemas/models/schemaCodeLangs.ts b/packages/core/src/eventSchemas/models/schemaCodeLangs.ts
similarity index 85%
rename from src/eventSchemas/models/schemaCodeLangs.ts
rename to packages/core/src/eventSchemas/models/schemaCodeLangs.ts
index 93b6e22e0da..fe83459251a 100644
--- a/src/eventSchemas/models/schemaCodeLangs.ts
+++ b/packages/core/src/eventSchemas/models/schemaCodeLangs.ts
@@ -1,9 +1,9 @@
 /*!
- * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { Runtime } from 'aws-sdk/clients/lambda'
+import { Runtime } from '@aws-sdk/client-lambda'
 import { Set as ImmutableSet } from 'immutable'
 import { goRuntimes } from '../../lambda/models/samLambdaRuntime'
 
@@ -54,7 +54,16 @@ export function getLanguageDetails(language: SchemaCodeLangs): {
 }
 
 export function supportsEventBridgeTemplates(runtime: Runtime): boolean {
-    return ['python3.7', 'python3.8', 'python3.9', 'go1.x'].includes(runtime)
+    return [
+        'python3.7',
+        'python3.8',
+        'python3.9',
+        'python3.10',
+        'python3.11',
+        'python3.12',
+        'python3.13',
+        'go1.x',
+    ].includes(runtime)
 }
 
 export function getApiValueForSchemasDownload(runtime: Runtime): string {
diff --git a/src/eventSchemas/providers/schemasDataProvider.ts b/packages/core/src/eventSchemas/providers/schemasDataProvider.ts
similarity index 79%
rename from src/eventSchemas/providers/schemasDataProvider.ts
rename to packages/core/src/eventSchemas/providers/schemasDataProvider.ts
index 5324ea0516a..e0717b6b6d3 100644
--- a/src/eventSchemas/providers/schemasDataProvider.ts
+++ b/packages/core/src/eventSchemas/providers/schemasDataProvider.ts
@@ -1,13 +1,13 @@
 /*!
- * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import * as AWS from '@aws-sdk/types'
-import { Schemas } from 'aws-sdk'
 import { SchemaClient } from '../../shared/clients/schemaClient'
-import { getLogger, Logger } from '../../shared/logger'
+import { getLogger, Logger } from '../../shared/logger/logger'
 import { toArrayAsync } from '../../shared/utilities/collectionUtils'
+import { SchemaSummary } from '@aws-sdk/client-schemas'
 
 export class Cache {
     public constructor(public readonly credentialsRegionDataList: credentialsRegionDataListMap[]) {}
@@ -26,7 +26,7 @@ export interface regionRegistryMap {
 
 export interface registrySchemasMap {
     registryName: string
-    schemaList: Schemas.SchemaSummary[]
+    schemaList: SchemaSummary[]
 }
 
 /**
@@ -40,23 +40,23 @@ export class SchemasDataProvider {
 
     public async getRegistries(region: string, client: SchemaClient, credentials: AWS.Credentials) {
         const cachedRegion = this.cache.credentialsRegionDataList
-            .filter(x => x.credentials === credentials)
+            .filter((x) => x.credentials === credentials)
             .shift()
-            ?.regionDataList.filter(x => x.region === region)
+            ?.regionDataList.filter((x) => x.region === region)
             .shift()
 
         try {
             // if region is not cached, make api query and retain results
             if (!cachedRegion || cachedRegion.registryNames.length === 0) {
                 const registrySummary = await toArrayAsync(client.listRegistries())
-                const registryNames = registrySummary.map(x => x.RegistryName!)
+                const registryNames = registrySummary.map((x) => x.RegistryName!)
                 this.pushRegionDataIntoCache(region, registryNames, [], credentials)
 
                 return registryNames
             }
         } catch (err) {
             const error = err as Error
-            this.logger.error('Error retrieving registries: %s', error)
+            this.logger.error('Failed to get registries: %s', error)
 
             return undefined
         }
@@ -66,17 +66,17 @@ export class SchemasDataProvider {
 
     public async getSchemas(region: string, registryName: string, client: SchemaClient, credentials: AWS.Credentials) {
         const registrySchemasMapList = this.cache.credentialsRegionDataList
-            .filter(x => x.credentials === credentials)
+            .filter((x) => x.credentials === credentials)
             .shift()
-            ?.regionDataList.filter(x => x.region === region)
+            ?.regionDataList.filter((x) => x.region === region)
             .shift()?.registrySchemasMapList
-        let schemas = registrySchemasMapList?.filter(x => x.registryName === registryName).shift()?.schemaList
+        let schemas = registrySchemasMapList?.filter((x) => x.registryName === registryName).shift()?.schemaList
         try {
             // if no schemas found, make api query and retain results given that registryName && region already cached
             if (!schemas || schemas.length === 0) {
                 schemas = await toArrayAsync(client.listSchemas(registryName))
                 const singleItem: registrySchemasMap = { registryName: registryName, schemaList: schemas }
-                //wizard setup always calls getRegistries method prior to getSchemas, so this shouldn't be undefined
+                // wizard setup always calls getRegistries method prior to getSchemas, so this shouldn't be undefined
                 if (!registrySchemasMapList) {
                     this.pushRegionDataIntoCache(region, [], [singleItem], credentials)
                 }
@@ -87,7 +87,7 @@ export class SchemasDataProvider {
             }
         } catch (err) {
             const error = err as Error
-            this.logger.error('Error retrieving schemas: %s', error)
+            this.logger.error('Failed to get schemas: %s', error)
 
             return undefined
         }
@@ -107,7 +107,9 @@ export class SchemasDataProvider {
             registrySchemasMapList: registrySchemasMapList,
         }
 
-        const cachedCredential = this.cache.credentialsRegionDataList.filter(x => x.credentials === credentials).shift()
+        const cachedCredential = this.cache.credentialsRegionDataList
+            .filter((x) => x.credentials === credentials)
+            .shift()
         cachedCredential?.regionDataList.push(regionData)
 
         if (!cachedCredential) {
diff --git a/src/eventSchemas/templates/schemasAppTemplateUtils.ts b/packages/core/src/eventSchemas/templates/schemasAppTemplateUtils.ts
similarity index 93%
rename from src/eventSchemas/templates/schemasAppTemplateUtils.ts
rename to packages/core/src/eventSchemas/templates/schemasAppTemplateUtils.ts
index 5082198c84c..51bbaca2695 100644
--- a/src/eventSchemas/templates/schemasAppTemplateUtils.ts
+++ b/packages/core/src/eventSchemas/templates/schemasAppTemplateUtils.ts
@@ -1,10 +1,10 @@
 /*!
- * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import _ = require('lodash')
-import { IdentifierFormatter, SchemaCodeGenUtils } from '../../eventSchemas/models/schemaCodeGenUtils'
+import { SchemaCodeGenUtils, toValidIdentifier } from '../../eventSchemas/models/schemaCodeGenUtils'
 import { SchemaClient } from '../../shared/clients/schemaClient'
 
 const xAmazonEventSource = 'x-amazon-events-source'
@@ -92,13 +92,13 @@ function buildRootSchemaEventName(schemaNode: any, awsEventNode: any) {
     if (_.isString(refValue) && refValue.includes(componentsSchemasPath)) {
         const awsEventDetailRef = refValue.split(componentsSchemasPath).pop()
         if (!_.isEmpty(awsEventDetailRef)) {
-            return IdentifierFormatter.toValidIdentifier(awsEventDetailRef!)
+            return toValidIdentifier(awsEventDetailRef!)
         }
     }
 
     const schemaRoots = _.keysIn(_.get(schemaNode, components.concat('.', schemas)))
     if (!_.isEmpty(schemaRoots)) {
-        return IdentifierFormatter.toValidIdentifier(schemaRoots[0])
+        return toValidIdentifier(schemaRoots[0])
     }
 
     return undefined
diff --git a/packages/core/src/eventSchemas/utils.ts b/packages/core/src/eventSchemas/utils.ts
new file mode 100644
index 00000000000..a42d4a26829
--- /dev/null
+++ b/packages/core/src/eventSchemas/utils.ts
@@ -0,0 +1,54 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as nls from 'vscode-nls'
+const localize = nls.loadMessageBundle()
+
+import { RegistrySummary, SchemaSummary, SearchSchemaSummary } from '@aws-sdk/client-schemas'
+import * as vscode from 'vscode'
+import { SchemaClient } from '../shared/clients/schemaClient'
+
+export async function* listRegistryItems(client: SchemaClient): AsyncIterableIterator {
+    const status = vscode.window.setStatusBarMessage(
+        localize('AWS.message.statusBar.loading.registries', 'Loading Registry Items...')
+    )
+
+    try {
+        yield* client.listRegistries()
+    } finally {
+        status.dispose()
+    }
+}
+
+export async function* listSchemaItems(
+    client: SchemaClient,
+    registryName: string
+): AsyncIterableIterator {
+    const status = vscode.window.setStatusBarMessage(
+        localize('AWS.message.statusBar.loading.schemaItems', 'Loading Schema Items...')
+    )
+
+    try {
+        yield* client.listSchemas(registryName)
+    } finally {
+        status.dispose()
+    }
+}
+
+export async function* searchSchemas(
+    client: SchemaClient,
+    keyword: string,
+    registryName: string
+): AsyncIterableIterator {
+    const status = vscode.window.setStatusBarMessage(
+        localize('AWS.message.statusBar.searching.schemas', 'Searching Schemas...')
+    )
+
+    try {
+        yield* client.searchSchemas(keyword, registryName)
+    } finally {
+        status.dispose()
+    }
+}
diff --git a/packages/core/src/eventSchemas/vue/index.ts b/packages/core/src/eventSchemas/vue/index.ts
new file mode 100644
index 00000000000..eac937636d8
--- /dev/null
+++ b/packages/core/src/eventSchemas/vue/index.ts
@@ -0,0 +1,16 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { createApp } from 'vue'
+import component from './searchSchemas.vue'
+
+const create = () => createApp(component)
+const app = create()
+app.mount('#vue-app')
+
+window.addEventListener('remount', () => {
+    app.unmount()
+    create().mount('#vue-app')
+})
diff --git a/src/eventSchemas/vue/searchSchemas.ts b/packages/core/src/eventSchemas/vue/searchSchemas.ts
similarity index 88%
rename from src/eventSchemas/vue/searchSchemas.ts
rename to packages/core/src/eventSchemas/vue/searchSchemas.ts
index fec3955895f..7d5c71fa3b0 100644
--- a/src/eventSchemas/vue/searchSchemas.ts
+++ b/packages/core/src/eventSchemas/vue/searchSchemas.ts
@@ -1,11 +1,11 @@
 /*!
- * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import * as nls from 'vscode-nls'
 const localize = nls.loadMessageBundle()
-import { Schemas } from 'aws-sdk'
+import { SchemaSummary, SearchSchemaSummary } from '@aws-sdk/client-schemas'
 import * as vscode from 'vscode'
 import { downloadSchemaItemCode } from '../commands/downloadSchemaItemCode'
 import { RegistryItemNode } from '../explorer/registryItemNode'
@@ -14,7 +14,7 @@ import { SchemasNode } from '../explorer/schemasNode'
 import { listRegistryItems, searchSchemas } from '../utils'
 import { DefaultSchemaClient, SchemaClient } from '../../shared/clients/schemaClient'
 
-import { getLogger, Logger } from '../../shared/logger'
+import { getLogger, Logger } from '../../shared/logger/logger'
 import { Result } from '../../shared/telemetry/telemetry'
 import { toArrayAsync } from '../../shared/utilities/collectionUtils'
 import { getTabSizeSetting } from '../../shared/utilities/editorUtilities'
@@ -37,15 +37,15 @@ interface InitialData {
 }
 
 export class SearchSchemasWebview extends VueWebview {
+    public static readonly sourcePath: string = 'src/eventSchemas/vue/index.js'
     public readonly id = 'remoteInvoke'
-    public readonly source = 'src/eventSchemas/vue/index.js'
 
     public constructor(
         private readonly channel: vscode.OutputChannel,
         private readonly client: SchemaClient,
         private readonly data: InitialData
     ) {
-        super()
+        super(SearchSchemasWebview.sourcePath)
     }
 
     public init() {
@@ -93,7 +93,7 @@ export class SearchSchemasWebview extends VueWebview {
     }
 
     public async downloadCodeBindings(summary: SchemaVersionedSummary) {
-        const schemaItem: Schemas.SchemaSummary = {
+        const schemaItem: SchemaSummary = {
             SchemaName: getSchemaNameFromTitle(summary.Title),
         }
         const schemaItemNode = new SchemaItemNode(schemaItem, this.client, summary.RegistryName)
@@ -113,7 +113,9 @@ export async function createSearchSchemasWebView(context: ExtContext, node: Regi
         const client = new DefaultSchemaClient(node.regionCode)
         const registryNames = await getRegistryNames(node, client)
         if (registryNames.length === 0) {
-            vscode.window.showInformationMessage(localize('AWS.schemas.search.no_registries', 'No Schema Registries'))
+            void vscode.window.showInformationMessage(
+                localize('AWS.schemas.search.no_registries', 'No Schema Registries')
+            )
 
             return
         }
@@ -149,11 +151,13 @@ export async function getRegistryNames(node: RegistryItemNode | SchemasNode, cli
     if (node instanceof SchemasNode) {
         try {
             const registries = await toArrayAsync(listRegistryItems(client))
-            registries.forEach(element => registryNames.push(element.RegistryName!))
+            for (const element of registries) {
+                registryNames.push(element.RegistryName!)
+            }
         } catch (err) {
             const error = err as Error
             getLogger().error(error)
-            vscode.window.showErrorMessage(
+            void vscode.window.showErrorMessage(
                 localize('AWS.message.error.schemas.search.failed_to_load_resources', 'Error loading Schemas resources')
             )
         }
@@ -193,7 +197,7 @@ export async function getSearchListForSingleRegistry(
         const err = error as Error
         getLogger().error(err)
 
-        vscode.window.showErrorMessage(
+        void vscode.window.showErrorMessage(
             localize(
                 'AWS.message.error.schemas.search.failed_to_search_registry',
                 'Unable to search registry {0}',
@@ -213,7 +217,7 @@ export async function getSearchResults(
     let results: SchemaVersionedSummary[] = []
 
     await Promise.all(
-        registries.map(async registryName => {
+        registries.map(async (registryName) => {
             let prefix = ''
             if (registries.length !== 1) {
                 prefix = registryName.concat('/')
@@ -226,12 +230,12 @@ export async function getSearchResults(
     return results
 }
 
-export function getSchemaVersionedSummary(searchSummary: Schemas.SearchSchemaSummary[], prefix: string) {
-    const results = searchSummary.map(searchSchemaSummary => ({
+export function getSchemaVersionedSummary(searchSummary: SearchSchemaSummary[], prefix: string) {
+    const results = searchSummary.map((searchSchemaSummary) => ({
         RegistryName: searchSchemaSummary.RegistryName!,
         Title: prefix.concat(searchSchemaSummary.SchemaName!),
         VersionList: searchSchemaSummary
-            .SchemaVersions!.map(summary => summary.SchemaVersion!)
+            .SchemaVersions!.map((summary) => summary.SchemaVersion!)
             .sort(sortNumericStringsInDescendingOrder),
     }))
 
diff --git a/src/eventSchemas/vue/searchSchemas.vue b/packages/core/src/eventSchemas/vue/searchSchemas.vue
similarity index 100%
rename from src/eventSchemas/vue/searchSchemas.vue
rename to packages/core/src/eventSchemas/vue/searchSchemas.vue
diff --git a/src/eventSchemas/wizards/schemaCodeDownloadWizard.ts b/packages/core/src/eventSchemas/wizards/schemaCodeDownloadWizard.ts
similarity index 94%
rename from src/eventSchemas/wizards/schemaCodeDownloadWizard.ts
rename to packages/core/src/eventSchemas/wizards/schemaCodeDownloadWizard.ts
index 7df5caea744..4db9d29ba27 100644
--- a/src/eventSchemas/wizards/schemaCodeDownloadWizard.ts
+++ b/packages/core/src/eventSchemas/wizards/schemaCodeDownloadWizard.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -26,6 +26,7 @@ import * as codeLang from '../models/schemaCodeLangs'
 
 import { SchemaItemNode } from '../explorer/schemaItemNode'
 import { recentlyUsed } from '../../shared/localizedText'
+import { openUrl } from '../../shared/utilities/vsCodeUtils'
 
 export interface SchemaCodeDownloadWizardContext {
     readonly schemaLangs: ImmutableSet
@@ -64,7 +65,7 @@ export class DefaultSchemaCodeDownloadWizardContext extends WizardContext implem
                 totalSteps: this.totalSteps,
             },
             buttons: [this.helpButton, vscode.QuickInputButtons.Back],
-            items: this.schemaLangs.toArray().map(language => ({
+            items: this.schemaLangs.toArray().map((language) => ({
                 label: language,
                 alwaysShow: language === currLanguage,
                 description: language === currLanguage ? recentlyUsed : '',
@@ -77,7 +78,7 @@ export class DefaultSchemaCodeDownloadWizardContext extends WizardContext implem
                 if (button === vscode.QuickInputButtons.Back) {
                     resolve(undefined)
                 } else if (button === this.helpButton) {
-                    vscode.env.openExternal(vscode.Uri.parse(eventBridgeSchemasDocUrl))
+                    void openUrl(vscode.Uri.parse(eventBridgeSchemasDocUrl))
                 }
             },
         })
@@ -102,7 +103,7 @@ export class DefaultSchemaCodeDownloadWizardContext extends WizardContext implem
                 totalSteps: this.totalSteps,
             },
             buttons: [this.helpButton, vscode.QuickInputButtons.Back],
-            items: versions!.map(schemaVersion => ({
+            items: versions!.map((schemaVersion) => ({
                 label: schemaVersion.SchemaVersion!,
                 alwaysShow: schemaVersion.SchemaVersion === currSchemaVersion,
                 description: schemaVersion === currSchemaVersion ? recentlyUsed : '',
@@ -115,7 +116,7 @@ export class DefaultSchemaCodeDownloadWizardContext extends WizardContext implem
                 if (button === vscode.QuickInputButtons.Back) {
                     resolve(undefined)
                 } else if (button === this.helpButton) {
-                    vscode.env.openExternal(vscode.Uri.parse(eventBridgeSchemasDocUrl))
+                    void openUrl(vscode.Uri.parse(eventBridgeSchemasDocUrl))
                 }
             },
         })
diff --git a/packages/core/src/extension.ts b/packages/core/src/extension.ts
new file mode 100644
index 00000000000..e400c3e0ddb
--- /dev/null
+++ b/packages/core/src/extension.ts
@@ -0,0 +1,220 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * This module contains shared code between the main extension and browser/web
+ * extension entrypoints.
+ *
+ * See `arch_develop.md` in `docs/` for more info.
+ */
+
+import vscode from 'vscode'
+import * as nls from 'vscode-nls'
+
+import globals, { initialize, isWeb } from './shared/extensionGlobals'
+import { join } from 'path'
+import { Commands } from './shared/vscode/commands2'
+import { endpointsFileUrl, githubCreateIssueUrl, githubUrl } from './shared/constants'
+import { getIdeProperties, aboutExtension, getDocUrl, isSageMaker } from './shared/extensionUtilities'
+import { logAndShowError, logAndShowWebviewError } from './shared/utilities/logAndShowUtils'
+import { telemetry } from './shared/telemetry/telemetry'
+import { openUrl } from './shared/utilities/vsCodeUtils'
+import { activateViewsShared } from './awsexplorer/activationShared'
+import fs from './shared/fs/fs'
+import * as errors from './shared/errors'
+import { activate as activateLogger } from './shared/logger/activation'
+import { initializeComputeRegion } from './shared/extensionUtilities'
+import { activate as activateTelemetry } from './shared/telemetry/activation'
+import { DefaultAwsContext } from './shared/awsContext'
+import { Settings } from './shared/settings'
+import { DefaultAWSClientBuilder } from './shared/awsClientBuilder'
+import { initialize as initializeAuth } from './auth/activation'
+import { LoginManager } from './auth/deprecated/loginManager'
+import { CredentialsStore } from './auth/credentials/store'
+import { initializeAwsCredentialsStatusBarItem } from './auth/ui/statusBarItem'
+import { RegionProvider, getEndpointsFromFetcher } from './shared/regions/regionProvider'
+import { getMachineId, isAutomation } from './shared/vscode/env'
+import { registerCommandErrorHandler } from './shared/vscode/commands2'
+import { registerWebviewErrorHandler } from './webviews/server'
+import { ExtContext, VSCODE_EXTENSION_ID } from './shared/extensions'
+import { getSamCliContext } from './shared/sam/cli/samCliContext'
+import { UriHandler } from './shared/vscode/uriHandler'
+import { disableAwsSdkWarning } from './shared/awsClientBuilder'
+import { FileResourceFetcher } from './shared/resourcefetcher/fileResourceFetcher'
+import { ResourceFetcher } from './shared/resourcefetcher/resourcefetcher'
+import { registerCommands } from './commands'
+
+// In web mode everything must be in a single file, so things like the endpoints file will not be available.
+// The following imports the endpoints file, which causes webpack to bundle it in the final output file
+import endpoints from '../resources/endpoints.json'
+import { showViewLogsMessage } from './shared/utilities/messages'
+import { AWSClientBuilderV3 } from './shared/awsClientBuilderV3'
+import { setupUninstallHandler } from './shared/handleUninstall'
+import { maybeShowMinVscodeWarning } from './shared/extensionStartup'
+import { getLogger } from './shared/logger/logger'
+import { setContext } from './shared/vscode/setContext'
+
+disableAwsSdkWarning()
+
+let localize: nls.LocalizeFunc
+
+/**
+ * Activation/setup code that is shared by the regular (nodejs) extension AND web mode extension.
+ * Most setup code should live here, unless there is a reason not to.
+ */
+export async function activateCommon(
+    context: vscode.ExtensionContext,
+    contextPrefix: string,
+    isWeb: boolean
+): Promise {
+    localize = nls.loadMessageBundle()
+
+    initialize(context, isWeb)
+    const homeDirLogs = await fs.init(context, (homeDir) => {
+        void showViewLogsMessage(`Invalid home directory (check $HOME): "${homeDir}"`)
+    })
+    errors.init(fs.getUsername(), isAutomation())
+    await initializeComputeRegion()
+
+    globals.contextPrefix = '' // todo: disconnect supplied argument
+
+    registerCommandErrorHandler((info, error) => {
+        const defaultMessage = localize('AWS.generic.message.error', 'Failed to run command: {0}', info.id)
+        void logAndShowError(localize, error, info.id, defaultMessage)
+    })
+
+    registerWebviewErrorHandler((error: unknown, webviewId: string, command: string) => {
+        return logAndShowWebviewError(localize, error, webviewId, command)
+    })
+
+    // Setup the logger
+    const toolkitOutputChannel = vscode.window.createOutputChannel('AWS Toolkit', { log: true })
+    const toolkitLogChannel = vscode.window.createOutputChannel('AWS Toolkit Logs', { log: true })
+    await activateLogger(context, contextPrefix, toolkitLogChannel, toolkitOutputChannel)
+    globals.outputChannel = toolkitOutputChannel
+    globals.logOutputChannel = toolkitLogChannel
+
+    if (homeDirLogs.length > 0) {
+        getLogger().error('fs.init: invalid home directory given by env vars: %O', homeDirLogs)
+    }
+
+    void maybeShowMinVscodeWarning('1.83.0')
+
+    // setup globals
+    globals.machineId = await getMachineId()
+    globals.awsContext = new DefaultAwsContext()
+    globals.sdkClientBuilder = new DefaultAWSClientBuilder(globals.awsContext)
+    globals.sdkClientBuilderV3 = new AWSClientBuilderV3(globals.awsContext)
+    globals.loginManager = new LoginManager(globals.awsContext, new CredentialsStore())
+
+    // order matters here
+    globals.manifestPaths.endpoints = context.asAbsolutePath(join('resources', 'endpoints.json'))
+    globals.manifestPaths.lambdaSampleRequests = context.asAbsolutePath(
+        join('resources', 'vs-lambda-sample-request-manifest.xml')
+    )
+    globals.regionProvider = RegionProvider.fromEndpointsProvider(makeEndpointsProvider())
+
+    // telemetry
+    await activateTelemetry(context, globals.awsContext, Settings.instance, 'AWS Toolkit For VS Code')
+
+    // set context var to identify if its SageMaker Unified Studio or not
+    await setContext('aws.isSageMakerUnifiedStudio', isSageMaker('SMUS'))
+
+    // Create this now, but don't call vscode.window.registerUriHandler() until after all
+    // Toolkit services have a chance to register their path handlers. #4105
+    globals.uriHandler = new UriHandler()
+
+    // Generic extension commands
+    registerGenericCommands(context, contextPrefix)
+
+    // Toolkit specific commands
+    registerCommands(context)
+    context.subscriptions.push(
+        // No-op command used for decoration-only codelenses.
+        vscode.commands.registerCommand('aws.doNothingCommand', () => {}),
+        // "Show AWS Commands..."
+        Commands.register('aws.listCommands', () =>
+            vscode.commands.executeCommand('workbench.action.quickOpen', `> ${getIdeProperties().company}:`)
+        ),
+        // register URLs in extension menu
+        Commands.register(`aws.toolkit.help`, async () => {
+            void openUrl(getDocUrl())
+            telemetry.aws_help.emit()
+        })
+    )
+
+    // Handle AWS Toolkit un-installation.
+    setupUninstallHandler(VSCODE_EXTENSION_ID.awstoolkit, context.extension.id, context)
+
+    // auth
+    await initializeAuth(globals.loginManager)
+    await initializeAwsCredentialsStatusBarItem(globals.awsContext, context)
+
+    const extContext: ExtContext = {
+        extensionContext: context,
+        awsContext: globals.awsContext,
+        samCliContext: getSamCliContext,
+        regionProvider: globals.regionProvider,
+        outputChannel: globals.outputChannel,
+        telemetryService: globals.telemetry,
+        uriHandler: globals.uriHandler,
+        credentialsStore: globals.loginManager.store,
+    }
+
+    await activateViewsShared(extContext.extensionContext)
+
+    return extContext
+}
+
+/** Deactivation code that is shared between nodejs and web implementations */
+export async function deactivateCommon() {
+    await globals.telemetry.shutdown()
+}
+/**
+ * Registers generic commands used by both web and node versions of the toolkit.
+ */
+export function registerGenericCommands(extensionContext: vscode.ExtensionContext, contextPrefix: string) {
+    extensionContext.subscriptions.push(
+        // register URLs in extension menu
+        Commands.register(`aws.${contextPrefix}.github`, async () => {
+            void openUrl(vscode.Uri.parse(githubUrl))
+            telemetry.aws_showExtensionSource.emit()
+        }),
+        Commands.register(`aws.${contextPrefix}.createIssueOnGitHub`, async () => {
+            void openUrl(vscode.Uri.parse(githubCreateIssueUrl))
+            telemetry.aws_reportPluginIssue.emit()
+        }),
+        Commands.register(`aws.${contextPrefix}.aboutExtension`, async () => {
+            await aboutExtension()
+        })
+    )
+}
+
+/**
+ * Returns an object that provides AWS service endpoints that the toolkit supports.
+ *
+ * https://docs.aws.amazon.com/general/latest/gr/rande.html
+ */
+export function makeEndpointsProvider() {
+    let localManifestFetcher: ResourceFetcher
+    let remoteManifestFetcher: ResourceFetcher
+    if (isWeb()) {
+        localManifestFetcher = { get: async () => JSON.stringify(endpoints) }
+        // Cannot use HttpResourceFetcher due to web mode breaking on import
+        remoteManifestFetcher = { get: async () => await fetch(endpointsFileUrl) }
+    } else {
+        localManifestFetcher = new FileResourceFetcher(globals.manifestPaths.endpoints)
+        // HACK: HttpResourceFetcher breaks web mode when imported, so we use webpack.IgnorePlugin()
+        // to exclude it from the bundle.
+        // TODO: Make HttpResourceFetcher web mode compatible
+        const { HttpResourceFetcher } = require('./shared/resourcefetcher/httpResourceFetcher')
+        remoteManifestFetcher = new HttpResourceFetcher(endpointsFileUrl, { showUrl: true })
+    }
+
+    return {
+        local: () => getEndpointsFromFetcher(localManifestFetcher),
+        remote: () => getEndpointsFromFetcher(remoteManifestFetcher),
+    }
+}
diff --git a/packages/core/src/extensionNode.ts b/packages/core/src/extensionNode.ts
new file mode 100644
index 00000000000..a8a7855913e
--- /dev/null
+++ b/packages/core/src/extensionNode.ts
@@ -0,0 +1,376 @@
+/*!
+ * 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 * as codecatalyst from './codecatalyst/activation'
+import { activate as activateAppBuilder } from './awsService/appBuilder/activation'
+import { activate as activateAwsExplorer } from './awsexplorer/activation'
+import { activate as activateCloudWatchLogs } from './awsService/cloudWatchLogs/activation'
+import { activate as activateSchemas } from './eventSchemas/activation'
+import { activate as activateLambda } from './lambda/activation'
+import { activate as activateCloudFormationTemplateRegistry } from './shared/cloudformation/activation'
+import { AwsContextCommands } from './shared/awsContextCommands'
+import {
+    getIdeProperties,
+    getExtEnvironmentDetails,
+    isSageMaker,
+    showWelcomeMessage,
+} from './shared/extensionUtilities'
+import { getLogger, Logger } from './shared/logger/logger'
+import { activate as activateEcr } from './awsService/ecr/activation'
+import { activate as activateEc2, deactivate as deactivateEc2 } from './awsService/ec2/activation'
+import { activate as activateSam } from './shared/sam/activation'
+import { activate as activateS3 } from './awsService/s3/activation'
+import * as filetypes from './shared/filetypes'
+import { activate as activateApiGateway } from './awsService/apigateway/activation'
+import { activate as activateStepFunctions } from './stepFunctions/activation'
+import { activate as activateStepFunctionsWorkflowStudio } from './stepFunctions/workflowStudio/activation'
+import { activate as activateSsmDocument } from './ssmDocument/activation'
+import { activate as activateDynamicResources } from './dynamicResources/activation'
+import { activate as activateEcs } from './awsService/ecs/activation'
+import { activate as activateAppRunner } from './awsService/apprunner/activation'
+import { activate as activateIot } from './awsService/iot/activation'
+import { activate as activateDev } from './dev/activation'
+import * as beta from './dev/beta'
+import { activate as activateApplicationComposer } from './applicationcomposer/activation'
+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 { activate as activateSageMakerUnifiedStudio } from './sagemakerunifiedstudio/activation'
+import { SchemaService } from './shared/schemas'
+import { AwsResourceManager } from './dynamicResources/awsResourceManager'
+import globals from './shared/extensionGlobals'
+import { Experiments, Settings, showSettingsFailedMsg } from './shared/settings'
+import { isReleaseVersion } from './shared/vscode/env'
+import { AuthStatus, AuthUserState, telemetry } from './shared/telemetry/telemetry'
+import { ExtStartUpSources } from './shared/telemetry/util'
+import { Auth } from './auth/auth'
+import { getTelemetryMetadataForConn } from './auth/connection'
+import { registerSubmitFeedback } from './feedback/vue/submitFeedback'
+import { activateCommon, deactivateCommon } from './extension'
+import { learnMoreAmazonQCommand, qExtensionPageCommand, dismissQTree } from './amazonq/explorer/amazonQChildrenNodes'
+import { codeWhispererCoreScopes } from './codewhisperer/util/authUtil'
+import { installAmazonQExtension } from './codewhisperer/commands/basicCommands'
+import { VSCODE_EXTENSION_ID } from './shared/extensions'
+import { isExtensionInstalled } from './shared/utilities/vsCodeUtils'
+import { ExtensionUse, getAuthFormIdsFromConnection, initializeCredentialsProviderManager } from './auth/utils'
+import { activate as activateThreatComposerEditor } from './threatComposer/activation'
+import { isSsoConnection, hasScopes } from './auth/connection'
+import { CrashMonitoring } from './shared/crashMonitoring'
+import { setContext } from './shared/vscode/setContext'
+import { AuthFormId } from './login/webview/vue/types'
+
+let localize: nls.LocalizeFunc
+
+/**
+ * The entrypoint for the nodejs version of the toolkit
+ *
+ * **CONTRIBUTORS** If you are adding code to this function prioritize adding it to
+ * {@link activateCommon} if appropriate
+ */
+export async function activate(context: vscode.ExtensionContext) {
+    const activationStartedOn = Date.now()
+    localize = nls.loadMessageBundle()
+    const contextPrefix = 'toolkit'
+
+    try {
+        // IMPORTANT: If you are doing setup that should also work in web mode (browser), it should be done in the function below
+        const extContext = await activateCommon(context, contextPrefix, false)
+
+        // Intentionally do not await since this can be slow and non-critical
+        void (await CrashMonitoring.instance())?.start()
+
+        initializeCredentialsProviderManager()
+
+        const toolkitEnvDetails = getExtEnvironmentDetails()
+        // Splits environment details by new line, filter removes the empty string
+        for (const line of toolkitEnvDetails.split(/\r?\n/).filter(Boolean)) {
+            getLogger().info(line)
+        }
+
+        globals.awsContextCommands = new AwsContextCommands(globals.regionProvider, Auth.instance)
+        globals.schemaService = new SchemaService()
+        globals.resourceManager = new AwsResourceManager(context)
+
+        const settings = Settings.instance
+        const experiments = Experiments.instance
+
+        experiments.onDidChange(({ key }) => {
+            telemetry.aws_experimentActivation.run((span) => {
+                // Record the key prior to reading the setting as `get` may throw
+                span.record({ experimentId: key })
+                span.record({ experimentState: experiments.get(key) ? 'activated' : 'deactivated' })
+            })
+        })
+
+        await globals.schemaService.start()
+        filetypes.activate()
+
+        try {
+            await activateDev(context)
+            await beta.activate(context)
+        } catch (error) {
+            getLogger().debug(`Developer Tools (internal): failed to activate: ${(error as Error).message}`)
+        }
+
+        context.subscriptions.push(registerSubmitFeedback(context, 'AWS Toolkit', contextPrefix))
+
+        // do not enable codecatalyst for sagemaker
+        // TODO: remove setContext if SageMaker adds the context to their IDE
+        if (!isSageMaker()) {
+            await setContext('aws.isSageMaker', false)
+            await codecatalyst.activate(extContext)
+        } else {
+            await setContext('aws.isSageMaker', true)
+        }
+
+        // wrap auth related setup in a context for telemetry
+        await telemetry.function_call.run(
+            async () => {
+                // Clean up remaining logins after codecatalyst activated and ran its cleanup.
+                // Because we are splitting auth sessions by extension, we can't use Amazon Q
+                // connections anymore.
+                // TODO: Remove after some time?
+                for (const conn of await Auth.instance.listConnections()) {
+                    if (isSsoConnection(conn) && hasScopes(conn, codeWhispererCoreScopes)) {
+                        getLogger().debug(
+                            `forgetting connection: ${conn.id} with starturl/scopes: ${conn.startUrl} / %O`,
+                            conn.scopes
+                        )
+                        await Auth.instance.forgetConnection(conn)
+                    }
+                }
+            },
+            { emit: false, functionId: { name: 'activate', class: 'ExtensionNodeCore' } }
+        )
+
+        await activateCloudFormationTemplateRegistry(context)
+
+        await activateAwsExplorer({
+            context: extContext,
+            regionProvider: globals.regionProvider,
+            toolkitOutputChannel: globals.outputChannel,
+        })
+
+        await activateAppRunner(extContext)
+
+        await activateApiGateway({
+            extContext: extContext,
+            outputChannel: globals.outputChannel,
+        })
+
+        await activateLambda(extContext)
+
+        await activateSsmDocument(context, globals.awsContext, globals.regionProvider, globals.outputChannel)
+
+        await activateSam(extContext)
+
+        await activateS3(extContext)
+
+        await activateEc2(extContext)
+
+        await activateEcr(context)
+
+        await activateCloudWatchLogs(context, settings)
+
+        await activateDynamicResources(context)
+
+        await activateIot(extContext)
+
+        await activateEcs(extContext)
+
+        await activateSchemas(extContext)
+
+        await activateSagemaker(extContext)
+
+        if (!isSageMaker()) {
+            // Amazon Q Tree setup.
+            learnMoreAmazonQCommand.register()
+            qExtensionPageCommand.register()
+            dismissQTree.register()
+            installAmazonQExtension.register()
+
+            await handleAmazonQInstall()
+        }
+
+        await activateSageMakerUnifiedStudio(context)
+
+        await activateApplicationComposer(context)
+        await activateThreatComposerEditor(context)
+
+        await activateStepFunctions(context, globals.awsContext, globals.outputChannel)
+
+        await activateStepFunctionsWorkflowStudio()
+
+        await activateRedshift(extContext)
+
+        await activateAppBuilder(extContext)
+
+        await activateDocumentDb(extContext)
+
+        await activateIamPolicyChecks(extContext)
+
+        context.subscriptions.push(
+            vscode.window.registerUriHandler({
+                handleUri: (uri) =>
+                    telemetry.runRoot(() => {
+                        telemetry.record({ source: 'UriHandler' })
+
+                        return globals.uriHandler.handleUri(uri)
+                    }),
+            })
+        )
+
+        showWelcomeMessage(context)
+
+        const settingsValid = await settings.isReadable()
+        if (!settingsValid) {
+            void showSettingsFailedMsg('read')
+        }
+        recordToolkitInitialization(activationStartedOn, settingsValid, getLogger())
+
+        if (!isReleaseVersion()) {
+            globals.telemetry.assertPassiveTelemetry(globals.didReload)
+        }
+
+        // TODO: Should probably emit for web as well.
+        // Will the web metric look the same?
+        telemetry.auth_userState.emit({
+            passive: true,
+            result: 'Succeeded',
+            source: ExtensionUse.instance.sourceForTelemetry(),
+            ...(await getAuthState()),
+        })
+
+        void activateNotifications(context, getAuthState)
+    } catch (error) {
+        const stacktrace = (error as Error).stack?.split('\n')
+        // truncate if the stacktrace is unusually long
+        if (stacktrace !== undefined && stacktrace.length > 40) {
+            stacktrace.length = 40
+        }
+        getLogger().error(
+            localize(
+                'AWS.channel.aws.toolkit.activation.error',
+                'Error Activating {0} Toolkit: {1} \n{2}',
+                getIdeProperties().company,
+                (error as Error).message,
+                stacktrace?.join('\n')
+            )
+        )
+        throw error
+    }
+}
+
+export async function deactivate() {
+    // Run concurrently to speed up execution. stop() does not throw so it is safe
+    await Promise.all([await (await CrashMonitoring.instance())?.shutdown(), deactivateCommon(), deactivateEc2()])
+    globals.sdkClientBuilderV3.clearServiceCache()
+    await globals.resourceManager.dispose()
+}
+
+async function handleAmazonQInstall() {
+    const dismissedInstall = globals.globalState.get('aws.toolkit.amazonqInstall.dismissed')
+    if (dismissedInstall) {
+        return
+    }
+
+    if (isExtensionInstalled(VSCODE_EXTENSION_ID.amazonq)) {
+        await globals.globalState.update('aws.toolkit.amazonqInstall.dismissed', true)
+        return
+    }
+
+    await telemetry.toolkit_showNotification.run(async () => {
+        telemetry.record({ id: 'amazonQStandaloneChange' })
+        void vscode.window
+            .showInformationMessage(
+                'Try Amazon Q, a generative AI assistant, with chat and code suggestions.',
+                'Install',
+                'Learn More'
+            )
+            .then(async (resp) => {
+                await telemetry.toolkit_invokeAction.run(async () => {
+                    telemetry.record({
+                        source: ExtensionUse.instance.isFirstUse()
+                            ? ExtStartUpSources.firstStartUp
+                            : ExtStartUpSources.none,
+                    })
+
+                    if (resp === 'Learn More') {
+                        // Clicking learn more will open the q extension page
+                        telemetry.record({ action: 'learnMore' })
+                        await qExtensionPageCommand.execute()
+                        return
+                    }
+
+                    if (resp === 'Install') {
+                        telemetry.record({ action: 'installAmazonQ' })
+                        await installAmazonQExtension.execute()
+                    } else {
+                        telemetry.record({ action: 'dismissQNotification' })
+                    }
+                    await globals.globalState.update('aws.toolkit.amazonqInstall.dismissed', true)
+                })
+            })
+    })
+}
+
+function recordToolkitInitialization(activationStartedOn: number, settingsValid: boolean, logger?: Logger) {
+    try {
+        const activationFinishedOn = Date.now()
+        const duration = activationFinishedOn - activationStartedOn
+
+        if (settingsValid) {
+            telemetry.toolkit_init.emit({ duration, result: 'Succeeded' })
+        } else {
+            telemetry.toolkit_init.emit({ duration, result: 'Failed', reason: 'UserSettingsRead' })
+        }
+    } catch (err) {
+        logger?.error(err as Error)
+    }
+}
+
+async function getAuthState(): Promise> {
+    let authStatus: AuthStatus = 'notConnected'
+    const enabledConnections: Set = new Set()
+    const enabledScopes: Set = new Set()
+    if (Auth.instance.hasConnections) {
+        authStatus = 'expired'
+        for (const conn of await Auth.instance.listConnections()) {
+            const state = Auth.instance.getConnectionState(conn)
+            if (state === 'valid') {
+                authStatus = 'connected'
+            }
+
+            for (const id of getAuthFormIdsFromConnection(conn)) {
+                enabledConnections.add(id)
+            }
+            if (isSsoConnection(conn)) {
+                if (conn.scopes) {
+                    for (const s of conn.scopes) {
+                        enabledScopes.add(s)
+                    }
+                }
+            }
+        }
+    }
+
+    // There may be other SSO connections in toolkit, but there is no use case for
+    // displaying registration info for non-active connections at this time.
+    const activeConn = Auth.instance.activeConnection
+    if (activeConn?.type === 'sso') {
+        telemetry.record(await getTelemetryMetadataForConn(activeConn))
+    }
+
+    return {
+        authStatus,
+        authEnabledConnections: [...enabledConnections].sort().join(','),
+        authScopes: [...enabledScopes].sort().join(','),
+    }
+}
diff --git a/packages/core/src/extensionWeb.ts b/packages/core/src/extensionWeb.ts
new file mode 100644
index 00000000000..01fa0f667db
--- /dev/null
+++ b/packages/core/src/extensionWeb.ts
@@ -0,0 +1,34 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { getLogger } from './shared/logger/logger'
+import { activateCommon, deactivateCommon } from './extension'
+import { activateWebShared } from './extensionWebShared'
+
+export async function activate(context: vscode.ExtensionContext) {
+    const contextPrefix = 'toolkit'
+
+    await activateWebShared(context)
+
+    try {
+        // IMPORTANT: Any new activation code should be done in the function below unless
+        // it is web mode specific activation code.
+        // This should happen as early as possible, as initialize() must be called before
+        // isWeb() calls will work.
+        await activateCommon(context, contextPrefix, true)
+    } catch (error) {
+        const stacktrace = (error as Error).stack?.split('\n')
+        // truncate if the stacktrace is unusually long
+        if (stacktrace !== undefined && stacktrace.length > 40) {
+            stacktrace.length = 40
+        }
+        getLogger().error(`Failed to activate extension`, error)
+    }
+}
+
+export async function deactivate() {
+    await deactivateCommon()
+}
diff --git a/packages/core/src/extensionWebShared.ts b/packages/core/src/extensionWebShared.ts
new file mode 100644
index 00000000000..1a62ab723a4
--- /dev/null
+++ b/packages/core/src/extensionWebShared.ts
@@ -0,0 +1,28 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { getLogger } from './shared/logger/logger'
+import os from 'os'
+
+/**
+ * This executes the web activation code that all extensions share. So any extension supporting web
+ * should run this as part of their web activation.
+ */
+export async function activateWebShared(context: vscode.ExtensionContext) {
+    try {
+        patchOsVersion()
+    } catch (error) {
+        getLogger().error(`Failed activation in extensionWebShared:`, error)
+    }
+}
+
+/**
+ * The browserfied version of os does not have a `version()` method,
+ * so we patch it.
+ */
+function patchOsVersion() {
+    ;(os.version as any) = () => '1.0.0'
+}
diff --git a/packages/core/src/feedback/index.ts b/packages/core/src/feedback/index.ts
new file mode 100644
index 00000000000..8392d4e6949
--- /dev/null
+++ b/packages/core/src/feedback/index.ts
@@ -0,0 +1,6 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { registerSubmitFeedback } from './vue/submitFeedback'
diff --git a/packages/core/src/feedback/vue/index.ts b/packages/core/src/feedback/vue/index.ts
new file mode 100644
index 00000000000..becfc1774d3
--- /dev/null
+++ b/packages/core/src/feedback/vue/index.ts
@@ -0,0 +1,16 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { createApp } from 'vue'
+import component from './submitFeedback.vue'
+
+const create = () => createApp(component)
+const app = create()
+app.mount('#vue-app')
+
+window.addEventListener('remount', () => {
+    app.unmount()
+    create().mount('#vue-app')
+})
diff --git a/packages/core/src/feedback/vue/submitFeedback.ts b/packages/core/src/feedback/vue/submitFeedback.ts
new file mode 100644
index 00000000000..928c0eb3249
--- /dev/null
+++ b/packages/core/src/feedback/vue/submitFeedback.ts
@@ -0,0 +1,128 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import globals from '../../shared/extensionGlobals'
+import { getLogger } from '../../shared/logger/logger'
+import * as vscode from 'vscode'
+import { TelemetryService } from '../../shared/telemetry/telemetryService'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { VueWebview, VueWebviewPanel } from '../../webviews/main'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { Commands, RegisteredCommand, VsCodeCommandArg, placeholder } from '../../shared/vscode/commands2'
+import { i18n } from '../../shared/i18n-helper'
+
+export interface FeedbackMessage {
+    comment: string
+    sentiment: string
+}
+
+export class FeedbackWebview extends VueWebview {
+    public static readonly sourcePath: string = 'src/feedback/vue/index.js'
+    public readonly id = 'submitFeedback'
+
+    public constructor(
+        private readonly telemetry: TelemetryService,
+        private readonly feedbackName: string,
+        /** Arbitrary caller-defined data appended to comment when sending the feedback request. */
+        private commentData?: string
+    ) {
+        super(FeedbackWebview.sourcePath)
+    }
+    public async getFeedbackName(): Promise {
+        return this.feedbackName
+    }
+
+    public async submit(message: FeedbackMessage): Promise {
+        const logger = getLogger()
+
+        if (!message.sentiment) {
+            return 'Choose a reaction (smile/frown)'
+        }
+
+        if (message.comment.length < 188) {
+            return 'Please add atleast 100 characters in the template describing your issue.'
+        }
+
+        if (this.commentData) {
+            message.comment = `${message.comment}\n\n${this.commentData}`
+        }
+
+        try {
+            await this.telemetry.postFeedback({
+                comment: message.comment,
+                sentiment: message.sentiment,
+            })
+        } catch (err) {
+            const errorMessage = (err as Error).message || 'Failed to submit feedback'
+            logger.error(`feedback failed: "${message.sentiment}": ${errorMessage}`)
+
+            telemetry.feedback_result.emit({ result: 'Failed' })
+
+            return errorMessage
+        }
+
+        logger.info(`feedback sent: "${message.sentiment}"`)
+
+        telemetry.feedback_result.emit({ result: 'Succeeded' })
+
+        this.dispose()
+
+        void vscode.window.showInformationMessage(
+            localize('AWS.message.info.submitFeedback.success', 'Thanks for the feedback!')
+        )
+    }
+}
+
+type FeedbackId = 'AWS Toolkit' | 'Amazon Q' | 'Infrastructure Composer' | 'Threat Composer' | 'Workflow Studio'
+
+let _submitFeedback:
+    | RegisteredCommand<(_: VsCodeCommandArg, id: FeedbackId, commentData?: string) => Promise>
+    | undefined
+
+/**
+ * @param id Feedback name
+ * @param commentData Arbitrary caller-defined data appended to the comment when sending the
+ * feedback request.
+ */
+export function submitFeedback(_: VsCodeCommandArg, id: FeedbackId, commentData?: string) {
+    if (_submitFeedback === undefined) {
+        getLogger().error(
+            'Attempted to access "submitFeedback" command, but it was never initialized.' +
+                '\nThis should be initialized during extension activation.'
+        )
+        throw new Error(i18n('AWS.amazonq.featureDev.error.submitFeedback'))
+    }
+    return _submitFeedback.execute(_, id, commentData)
+}
+
+export function registerSubmitFeedback(context: vscode.ExtensionContext, defaultId: FeedbackId, contextPrefix: string) {
+    _submitFeedback = Commands.register(
+        { id: `aws.${contextPrefix}.submitFeedback`, autoconnect: false },
+        async (_: VsCodeCommandArg, id: FeedbackId, commentData?: string) => {
+            if (_ !== placeholder) {
+                // No args exist, we must supply them
+                id = defaultId
+            }
+            await showFeedbackView(context, id, commentData)
+        }
+    )
+    getLogger().info(`initialized \'submitFeedback\' command with default feedback id: ${defaultId}`)
+
+    return _submitFeedback
+}
+
+let activeWebview: VueWebviewPanel | undefined
+
+export async function showFeedbackView(context: vscode.ExtensionContext, feedbackName: string, commentData?: string) {
+    const Panel = VueWebview.compilePanel(FeedbackWebview)
+    activeWebview ??= new Panel(context, globals.telemetry, feedbackName, commentData)
+
+    const webviewPanel = await activeWebview.show({
+        title: localize('AWS.submitFeedback.title', 'Send Feedback'),
+        cssFiles: ['submitFeedback.css'],
+    })
+
+    webviewPanel.onDidDispose(() => (activeWebview = undefined))
+}
diff --git a/packages/core/src/feedback/vue/submitFeedback.vue b/packages/core/src/feedback/vue/submitFeedback.vue
new file mode 100644
index 00000000000..b97233ba494
--- /dev/null
+++ b/packages/core/src/feedback/vue/submitFeedback.vue
@@ -0,0 +1,132 @@
+/*! * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */
+
+
+
+
diff --git a/packages/core/src/lambda/activation.ts b/packages/core/src/lambda/activation.ts
new file mode 100644
index 00000000000..9b010eceff8
--- /dev/null
+++ b/packages/core/src/lambda/activation.ts
@@ -0,0 +1,279 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as path from 'path'
+import * as vscode from 'vscode'
+import * as nls from 'vscode-nls'
+
+import { FunctionConfiguration } from '@aws-sdk/client-lambda'
+import { deleteLambda } from './commands/deleteLambda'
+import { uploadLambdaCommand } from './commands/uploadLambda'
+import { LambdaFunctionNode } from './explorer/lambdaFunctionNode'
+import { downloadLambdaCommand, openLambdaFile } from './commands/downloadLambda'
+import { tryRemoveFolder } from '../shared/filesystemUtilities'
+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 { copyLambdaUrl } from './commands/copyLambdaUrl'
+import { generateLambdaNodeFromResource, ResourceNode } from '../awsService/appBuilder/explorer/nodes/resourceNode'
+import { isTreeNode, TreeNode } from '../shared/treeview/resourceTreeDataProvider'
+import { getSourceNode } from '../shared/utilities/treeNodeUtils'
+import { tailLogGroup } from '../awsService/cloudWatchLogs/commands/tailLogGroup'
+import { liveTailRegistry, liveTailCodeLensProvider } from '../awsService/cloudWatchLogs/activation'
+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 {
+    confirmOutdatedChanges,
+    deleteFilesInFolder,
+    deployFromTemp,
+    getReadme,
+    openLambdaFolderForEdit,
+    watchForUpdates,
+} from './commands/editLambda'
+import { compareCodeSha, getFunctionInfo, getTempLocation, setFunctionInfo } from './utils'
+import { registerLambdaUriHandler } from './uriHandlers'
+import globals from '../shared/extensionGlobals'
+
+const localize = nls.loadMessageBundle()
+import { activateRemoteDebugging } from './remoteDebugging/ldkController'
+import { ExtContext } from '../shared/extensions'
+
+async function openReadme() {
+    const readmeUri = vscode.Uri.file(await getReadme())
+    // We only want to do it if there's not a readme already
+    const isPreviewOpen = vscode.window.tabGroups.all.some((group) =>
+        group.tabs.some((tab) => tab.label.includes('README'))
+    )
+    if (!isPreviewOpen) {
+        await vscode.commands.executeCommand('markdown.showPreviewToSide', readmeUri)
+    }
+}
+
+async function quickEditActivation() {
+    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.includes(tempPath)) {
+                const name = path.basename(workspaceFolder.uri.fsPath)
+                const region = path.basename(path.dirname(workspaceFolder.uri.fsPath))
+
+                const lambda = { name, region, configuration: undefined }
+
+                watchForUpdates(lambda, vscode.Uri.file(workspacePath))
+
+                await openReadme()
+
+                // Open handler function
+                try {
+                    const handler = await getFunctionInfo(lambda, 'handlerFile')
+                    const lambdaLocation = path.join(workspacePath, handler)
+                    await openLambdaFile(lambdaLocation, vscode.ViewColumn.One)
+                } catch (e) {
+                    void vscode.window.showWarningMessage(
+                        localize('AWS.lambda.openFile.failure', `Failed to determine handler location: ${e}`)
+                    )
+                }
+
+                // Check if there are changes that need overwritten
+                try {
+                    // Checking if there are changes that need to be overwritten
+                    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?'
+                    )
+
+                    // Adding delay to give the authentication time to catch up
+                    await new Promise((resolve) => globals.clock.setTimeout(resolve, 1000))
+
+                    const overwriteChanges = !(await compareCodeSha(lambda))
+                        ? await confirmOutdatedChanges(prompt)
+                        : false
+                    if (overwriteChanges) {
+                        // Close all open tabs from this workspace
+                        const workspaceUri = vscode.Uri.file(workspacePath)
+                        for (const tabGroup of vscode.window.tabGroups.all) {
+                            const tabsToClose = tabGroup.tabs.filter(
+                                (tab) =>
+                                    tab.input instanceof vscode.TabInputText &&
+                                    tab.input.uri.fsPath.startsWith(workspaceUri.fsPath)
+                            )
+                            if (tabsToClose.length > 0) {
+                                await vscode.window.tabGroups.close(tabsToClose)
+                            }
+                        }
+
+                        // Delete all files in the directory
+                        await deleteFilesInFolder(workspacePath)
+
+                        // Show message to user about next steps
+                        void vscode.window.showInformationMessage(
+                            localize(
+                                'AWS.lambda.refresh.complete',
+                                'Local workspace cleared. Navigate to the Toolkit explorer to get fresh code from the cloud.'
+                            )
+                        )
+
+                        await setFunctionInfo(lambda, { undeployed: false })
+
+                        // Remove workspace folder
+                        const workspaceIndex = vscode.workspace.workspaceFolders?.findIndex(
+                            (folder) => folder.uri.fsPath.toLowerCase() === workspacePath
+                        )
+                        if (workspaceIndex !== undefined && workspaceIndex >= 0) {
+                            vscode.workspace.updateWorkspaceFolders(workspaceIndex, 1)
+                        }
+                    }
+                } catch (e) {
+                    void vscode.window.showWarningMessage(
+                        localize(
+                            'AWS.lambda.pull.failure',
+                            `Failed to pull latest changes from the cloud, you can still edit locally: ${e}`
+                        )
+                    )
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Activates Lambda components.
+ */
+export async function activate(context: ExtContext): Promise {
+    void quickEditActivation()
+
+    context.extensionContext.subscriptions.push(
+        Commands.register('aws.deleteLambda', async (node: LambdaFunctionNode | TreeNode) => {
+            const sourceNode = getSourceNode(node)
+            await deleteLambda(sourceNode.configuration, new DefaultLambdaClient(sourceNode.regionCode))
+            await vscode.commands.executeCommand('aws.refreshAwsExplorerNode', sourceNode.parent)
+        }),
+        Commands.register('aws.invokeLambda', async (node: LambdaFunctionNode | TreeNode) => {
+            let source: string = 'AwsExplorerRemoteInvoke'
+            if (isTreeNode(node)) {
+                // if appbuilder, create lambda node on the fly
+                let tmpNode: LambdaFunctionNode | undefined = getSourceNode(node)
+                if (!tmpNode) {
+                    // failed to extract, meaning this is appbuilder function node
+                    tmpNode = await generateLambdaNodeFromResource(node.resource as any)
+                }
+                node = tmpNode
+                source = 'AppBuilderRemoteInvoke'
+            }
+            await invokeRemoteLambda(context, {
+                outputChannel: context.outputChannel,
+                functionNode: node,
+                source: source,
+            })
+        }),
+
+        // Capture debug finished events, and delete the temporary directory if it exists
+        vscode.debug.onDidTerminateDebugSession(async (session) => {
+            if (
+                session.configuration?.sam?.buildDir === undefined &&
+                session.configuration?.baseBuildDir !== undefined
+            ) {
+                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({
+                    name: arg.functionName,
+                    region: arg.regionCode,
+                    configuration: arg.configuration,
+                })
+            } else if (arg instanceof vscode.Uri) {
+                await uploadLambdaCommand(undefined, arg)
+            } else {
+                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))
+        }),
+
+        registerSamInvokeVueCommand(context.extensionContext),
+
+        Commands.register('aws.launchDebugConfigForm', async (node: ResourceNode) =>
+            registerSamDebugInvokeVueCommand(context.extensionContext, { resource: node })
+        ),
+
+        Commands.register('aws.appBuilder.tailLogs', async (node: LambdaFunctionNode | TreeNode) => {
+            let functionConfiguration: FunctionConfiguration
+            try {
+                let tmpNode: LambdaFunctionNode | undefined = getSourceNode(node)
+                if (!tmpNode && isTreeNode(node)) {
+                    // failed to extract, meaning this is appbuilder function node
+                    tmpNode = await generateLambdaNodeFromResource(node.resource as any)
+                }
+                functionConfiguration = tmpNode.configuration
+                const logGroupInfo = {
+                    regionName: tmpNode.regionCode,
+                    groupName: getFunctionLogGroupName(functionConfiguration),
+                }
+
+                const source = isTreeNode(node) ? 'AppBuilder' : 'AwsExplorerLambdaNode'
+                // Show all log streams without having to choose
+                const logStreamFilterData: LogStreamFilterResponse = { type: 'all' }
+                await tailLogGroup(
+                    liveTailRegistry,
+                    source,
+                    liveTailCodeLensProvider,
+                    logGroupInfo,
+                    logStreamFilterData
+                )
+            } catch (err) {
+                if (isError(err as Error, 'ResourceNotFoundException', "LogGroup doesn't exist.")) {
+                    // If we caught this error, then we know `functionConfiguration` actually has a value
+                    throw ToolkitError.chain(
+                        err,
+                        `Unable to fetch logs. Log group for function '${functionConfiguration!.FunctionName}' does not exist. ` +
+                            'Invoking your function at least once will create the log group.'
+                    )
+                } else {
+                    throw err
+                }
+            }
+        }),
+
+        registerLambdaUriHandler()
+    )
+
+    void activateRemoteDebugging()
+}
diff --git a/packages/core/src/lambda/commands/copyLambdaUrl.ts b/packages/core/src/lambda/commands/copyLambdaUrl.ts
new file mode 100644
index 00000000000..835525d0610
--- /dev/null
+++ b/packages/core/src/lambda/commands/copyLambdaUrl.ts
@@ -0,0 +1,59 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { DefaultLambdaClient, LambdaClient } from '../../shared/clients/lambdaClient'
+import { LambdaFunctionNode } from '../explorer/lambdaFunctionNode'
+import { copyToClipboard } from '../../shared/utilities/messages'
+import { addCodiconToString } from '../../shared/utilities/textUtilities'
+import { createQuickPick, QuickPickPrompter } from '../../shared/ui/pickerPrompter'
+import { isValidResponse } from '../../shared/wizards/wizard'
+import { FunctionUrlConfig } from '@aws-sdk/client-lambda'
+import { CancellationError } from '../../shared/utilities/timeoutUtils'
+import { lambdaFunctionUrlConfigUrl } from '../../shared/constants'
+
+export const noLambdaFuncMessage = `No URL for Lambda function. [How to create URL.](${lambdaFunctionUrlConfigUrl})`
+
+export async function copyLambdaUrl(
+    node: Pick,
+    client: LambdaClient = new DefaultLambdaClient(node.regionCode),
+    quickPickUrl = _quickPickUrl
+): Promise {
+    const configs = await client.getFunctionUrlConfigs(node.name)
+
+    if (configs.length === 0) {
+        void vscode.window.showWarningMessage(noLambdaFuncMessage)
+        vscode.window.setStatusBarMessage(addCodiconToString('circle-slash', 'No URL for Lambda function.'), 5000)
+    } else {
+        let url: string | undefined = undefined
+        if (configs.length > 1) {
+            url = await quickPickUrl(configs)
+        } else {
+            url = configs[0].FunctionUrl
+        }
+
+        if (url) {
+            await copyToClipboard(url, 'URL')
+        }
+    }
+}
+
+async function _quickPickUrl(configList: FunctionUrlConfig[]): Promise {
+    const res = await createLambdaFuncUrlPrompter(configList).prompt()
+    if (!isValidResponse(res)) {
+        throw new CancellationError('user')
+    }
+    return res
+}
+
+export function createLambdaFuncUrlPrompter(configList: FunctionUrlConfig[]): QuickPickPrompter {
+    const items = configList
+        .filter((c) => c.FunctionArn && c.FunctionUrl)
+        .map((c) => ({
+            label: c.FunctionArn!,
+            data: c.FunctionUrl!,
+        }))
+    return createQuickPick(items, { title: 'Select function to copy url from.' })
+}
diff --git a/src/lambda/commands/createNewSamApp.ts b/packages/core/src/lambda/commands/createNewSamApp.ts
similarity index 88%
rename from src/lambda/commands/createNewSamApp.ts
rename to packages/core/src/lambda/commands/createNewSamApp.ts
index 49efcc04af1..7801976c435 100644
--- a/src/lambda/commands/createNewSamApp.ts
+++ b/packages/core/src/lambda/commands/createNewSamApp.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -22,7 +22,7 @@ import { ActivationReloadState, SamInitState } from '../../shared/activationRelo
 import { AwsContext } from '../../shared/awsContext'
 
 import { fileExists, isInDirectory, readFileAsString } from '../../shared/filesystemUtilities'
-import { getLogger } from '../../shared/logger'
+import { getLogger } from '../../shared/logger/logger'
 import { RegionProvider } from '../../shared/regions/regionProvider'
 import { getSamCliVersion, getSamCliContext, SamCliContext } from '../../shared/sam/cli/samCliContext'
 import { runSamCliInit, SamCliInitArgs } from '../../shared/sam/cli/samCliInit'
@@ -39,15 +39,16 @@ import { isTemplateTargetProperties } from '../../shared/sam/debugger/awsSamDebu
 import { TemplateTargetProperties } from '../../shared/sam/debugger/awsSamDebugConfiguration'
 import { openLaunchJsonFile } from '../../shared/sam/debugger/commands/addSamDebugConfiguration'
 import { waitUntil } from '../../shared/utilities/timeoutUtils'
-import { debugNewSamAppUrl, launchConfigDocUrl } from '../../shared/constants'
-import { getIdeProperties, isCloud9 } from '../../shared/extensionUtilities'
-import { execSync } from 'child_process'
-import { writeFile } from 'fs-extra'
+import { getIdeProperties, getDebugNewSamAppDocUrl, getLaunchConfigDocUrl } from '../../shared/extensionUtilities'
 import { checklogs } from '../../shared/localizedText'
 import globals from '../../shared/extensionGlobals'
 import { telemetry } from '../../shared/telemetry/telemetry'
-import { LambdaArchitecture, Result, Runtime } from '../../shared/telemetry/telemetry'
+import { LambdaArchitecture, Result, Runtime as TelemetryRuntime } from '../../shared/telemetry/telemetry'
+import { Runtime } from '@aws-sdk/client-lambda'
 import { getTelemetryReason, getTelemetryResult } from '../../shared/errors'
+import { openUrl, replaceVscodeVars } from '../../shared/utilities/vsCodeUtils'
+import { fs } from '../../shared/fs/fs'
+import { ChildProcess } from '../../shared/utilities/processUtils'
 
 export const samInitTemplateFiles: string[] = ['template.yaml', 'template.yml']
 export const samInitReadmeFile: string = 'README.TOOLKIT.md'
@@ -62,6 +63,7 @@ export async function resumeCreateNewSamApp(
     let samVersion: string | undefined
     const samInitState: SamInitState | undefined = activationReloadState.getSamInitState()
     try {
+        getLogger().debug('SAM: resumeCreateNewSamApp')
         const templateUri = vscode.Uri.file(samInitState!.template!)
         const readmeUri = vscode.Uri.file(samInitState!.readme!)
         const folder = vscode.workspace.getWorkspaceFolder(templateUri)
@@ -70,7 +72,7 @@ export async function resumeCreateNewSamApp(
             reason = 'error'
             // Should never happen, because `samInitState.path` is only set if
             // `uri` is in the newly-added workspace folder.
-            vscode.window.showErrorMessage(
+            void vscode.window.showErrorMessage(
                 localize(
                     'AWS.samcli.initWizard.source.error.notInWorkspace',
                     "Could not open file '{0}'. If this file exists on disk, try adding it to your workspace.",
@@ -87,7 +89,7 @@ export async function resumeCreateNewSamApp(
             extContext,
             folder,
             templateUri,
-            samInitState?.isImage ? (samInitState?.runtime as Runtime | undefined) : undefined
+            samInitState?.isImage ? samInitState?.runtime : undefined
         )
         const tryOpenReadme = await writeToolkitReadme(readmeUri.fsPath, configs)
         if (tryOpenReadme) {
@@ -98,7 +100,7 @@ export async function resumeCreateNewSamApp(
         reason = 'error'
 
         globals.outputChannel.show(true)
-        getLogger('channel').error(
+        getLogger().error(
             localize('AWS.samcli.initWizard.resume.error', 'Error resuming SAM Application creation. {0}', checklogs())
         )
 
@@ -111,7 +113,7 @@ export async function resumeCreateNewSamApp(
             lambdaArchitecture: arch,
             result: createResult,
             reason: reason,
-            runtime: samInitState?.runtime as Runtime,
+            runtime: samInitState?.runtime as TelemetryRuntime,
             version: samVersion,
         })
     }
@@ -145,7 +147,9 @@ export async function createNewSamApplication(
 
         const credentials = await awsContext.getCredentials()
         samVersion = await getSamCliVersion(samCliContext)
-        const schemaRegions = regionProvider.getRegions().filter(r => regionProvider.isServiceInRegion('schemas', r.id))
+        const schemaRegions = regionProvider
+            .getRegions()
+            .filter((r) => regionProvider.isServiceInRegion('schemas', r.id))
         const defaultRegion = awsContext.getCredentialDefaultRegion()
 
         const config = await new CreateNewSamAppWizard({
@@ -191,7 +195,7 @@ export async function createNewSamApplication(
             initArguments.baseImage = `amazon/${createRuntime}-base`
         } else {
             lambdaPackageType = 'Zip'
-            initArguments.runtime = createRuntime
+            initArguments.runtime! = createRuntime
             // in theory, templates could be provided with image-based lambdas, but that is currently not supported by SAM
             initArguments.template = config.template
         }
@@ -210,7 +214,9 @@ export async function createNewSamApplication(
         // Needs to be done or else gopls won't start
         if (goRuntimes.includes(createRuntime)) {
             try {
-                execSync('go mod tidy', { cwd: path.join(path.dirname(templateUri.fsPath), 'hello-world') })
+                await ChildProcess.run('go', ['mod', 'tidy'], {
+                    spawnOptions: { cwd: path.join(path.dirname(templateUri.fsPath), 'hello-world') },
+                })
             } catch (err) {
                 getLogger().warn(
                     localize(
@@ -231,7 +237,7 @@ export async function createNewSamApplication(
                 destinationDirectory: vscode.Uri.file(destinationDirectory),
             }
             schemaCodeDownloader = createSchemaCodeDownloaderObject(client!, globals.outputChannel)
-            getLogger('channel').info(
+            getLogger().info(
                 localize(
                     'AWS.message.info.schemas.downloadCodeBindings.start',
                     'Downloading code for schema {0}...',
@@ -241,7 +247,7 @@ export async function createNewSamApplication(
 
             await schemaCodeDownloader!.downloadCode(request!)
 
-            vscode.window.showInformationMessage(
+            void vscode.window.showInformationMessage(
                 localize(
                     'AWS.message.info.schemas.downloadCodeBindings.finished',
                     'Downloaded code for schema {0}!',
@@ -270,7 +276,7 @@ export async function createNewSamApplication(
         // Race condition where SAM app is created but template doesn't register in time.
         // Poll for 5 seconds, otherwise direct user to codelens.
         const isTemplateRegistered = await waitUntil(
-            async () => globals.templateRegistry.getRegisteredItem(templateUri),
+            async () => (await globals.templateRegistry).getItem(templateUri),
             {
                 timeout: 5000,
                 interval: 500,
@@ -289,7 +295,10 @@ export async function createNewSamApplication(
             )
             tryOpenReadme = await writeToolkitReadme(readmeUri.fsPath, newLaunchConfigs)
             if (newLaunchConfigs && newLaunchConfigs.length > 0) {
-                showCompletionNotification(config.name, `"${newLaunchConfigs.map(config => config.name).join('", "')}"`)
+                void showCompletionNotification(
+                    config.name,
+                    `"${newLaunchConfigs.map((config) => config.name).join('", "')}"`
+                )
             }
             reason = 'complete'
         } else {
@@ -297,7 +306,7 @@ export async function createNewSamApplication(
             reason = 'fileNotFound'
 
             const helpText = localize('AWS.generic.message.getHelp', 'Get Help...')
-            vscode.window
+            void vscode.window
                 .showWarningMessage(
                     localize(
                         'AWS.samcli.initWizard.launchConfigFail',
@@ -307,9 +316,9 @@ export async function createNewSamApplication(
                     ),
                     helpText
                 )
-                .then(async buttonText => {
+                .then(async (buttonText) => {
                     if (buttonText === helpText) {
-                        vscode.env.openExternal(vscode.Uri.parse(launchConfigDocUrl))
+                        void openUrl(getLaunchConfigDocUrl())
                     }
                 })
         }
@@ -326,7 +335,7 @@ export async function createNewSamApplication(
         reason = getTelemetryReason(err)
 
         globals.outputChannel.show(true)
-        getLogger('channel').error(
+        getLogger().error(
             localize('AWS.samcli.initWizard.general.error', 'Error creating new SAM Application. {0}', checklogs())
         )
 
@@ -340,7 +349,7 @@ export async function createNewSamApplication(
             lambdaArchitecture: initArguments?.architecture,
             result: createResult,
             reason: reason,
-            runtime: createRuntime,
+            runtime: createRuntime as TelemetryRuntime,
             version: samVersion,
         })
     }
@@ -367,7 +376,7 @@ export async function getProjectUri(
             return vscode.Uri.file(cfnTemplatePath)
         }
     }
-    vscode.window.showWarningMessage(
+    void vscode.window.showWarningMessage(
         localize(
             'AWS.samcli.initWizard.source.error.notFound',
             'Project created successfully, but {0} file not found: {1}',
@@ -392,10 +401,9 @@ export async function addInitialLaunchConfiguration(
     if (configurations) {
         // add configurations that target the new template file
         const targetDir: string = path.dirname(targetUri.fsPath)
-        const filtered = configurations.filter(config => {
+        const filtered = configurations.filter((config) => {
             let templatePath: string = (config.invokeTarget as TemplateTargetProperties).templatePath
-            // TODO: write utility function that does this for other variables too
-            templatePath = templatePath.replace('${workspaceFolder}', folder.uri.fsPath)
+            templatePath = replaceVscodeVars(templatePath, folder.uri.fsPath)
 
             return (
                 isTemplateTargetProperties(config.invokeTarget) &&
@@ -405,12 +413,12 @@ export async function addInitialLaunchConfiguration(
 
         // optional for ZIP-lambdas but required for Image-lambdas
         if (runtime !== undefined) {
-            filtered.forEach(configuration => {
+            for (const configuration of filtered) {
                 if (!configuration.lambda) {
                     configuration.lambda = {}
                 }
                 configuration.lambda.runtime = runtime
-            })
+            }
         }
 
         await launchConfiguration.addDebugConfigurations(filtered)
@@ -435,7 +443,7 @@ async function showCompletionNotification(appName: string, configs: string): Pro
     if (action === openJson) {
         await openLaunchJsonFile()
     } else if (action === learnMore) {
-        vscode.env.openExternal(vscode.Uri.parse(debugNewSamAppUrl))
+        void openUrl(getDebugNewSamAppDocUrl())
     }
 }
 
@@ -461,12 +469,10 @@ export async function writeToolkitReadme(
             .replace(/\$\{LISTOFCONFIGURATIONS\}/g, configString)
             .replace(
                 /\$\{DOCURL\}/g,
-                isCloud9()
-                    ? 'https://docs.aws.amazon.com/cloud9/latest/user-guide/serverless-apps-toolkit.html'
-                    : 'https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/serverless-apps.html'
+                'https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/serverless-apps.html'
             )
 
-        await writeFile(readmeLocation, readme)
+        await fs.writeFile(readmeLocation, readme)
         getLogger().debug(`writeToolkitReadme: wrote file: %O`, readmeLocation)
 
         return true
diff --git a/packages/core/src/lambda/commands/deleteCloudFormation.ts b/packages/core/src/lambda/commands/deleteCloudFormation.ts
new file mode 100644
index 00000000000..035e7110c86
--- /dev/null
+++ b/packages/core/src/lambda/commands/deleteCloudFormation.ts
@@ -0,0 +1,73 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as nls from 'vscode-nls'
+const localize = nls.loadMessageBundle()
+
+import * as vscode from 'vscode'
+import { CloudFormationClient } from '../../shared/clients/cloudFormation'
+
+import * as localizedText from '../../shared/localizedText'
+import { getLogger, Logger } from '../../shared/logger/logger'
+import { Result } from '../../shared/telemetry/telemetry'
+import { CloudFormationStackNode } from '../explorer/cloudFormationNodes'
+import { showConfirmationMessage } from '../../shared/utilities/messages'
+import { getIdeProperties } from '../../shared/extensionUtilities'
+import { telemetry } from '../../shared/telemetry/telemetry'
+
+export async function deleteCloudFormation(refresh: () => void, node?: CloudFormationStackNode) {
+    const logger: Logger = getLogger()
+    let deleteResult: Result = 'Succeeded'
+    const stackName = node?.stackName ?? ''
+    try {
+        if (!node) {
+            deleteResult = 'Failed'
+            void vscode.window.showErrorMessage(
+                localize(
+                    'AWS.message.error.cloudFormation.unsupported',
+                    'Unable to delete a CloudFormation Stack. No stack provided.'
+                )
+            )
+
+            return
+        }
+
+        const userResponse = await showConfirmationMessage({
+            prompt: localize(
+                'AWS.message.prompt.deleteCloudFormation',
+                'Are you sure you want to delete {0}?',
+                stackName
+            ),
+            confirm: localizedText.localizedDelete,
+            cancel: localizedText.cancel,
+        })
+
+        if (userResponse) {
+            const client = new CloudFormationClient(node.regionCode)
+
+            await client.deleteStack(stackName)
+
+            void vscode.window.showInformationMessage(
+                localize('AWS.message.info.cloudFormation.delete', 'Deleted CloudFormation Stack {0}', stackName)
+            )
+
+            refresh()
+        }
+    } catch (err) {
+        deleteResult = 'Failed'
+        logger.error(err as Error)
+
+        void vscode.window.showInformationMessage(
+            localize(
+                'AWS.message.error.cloudFormation.delete',
+                'An error occurred while deleting {0}. Please check the stack events on the {1} Console',
+                stackName,
+                getIdeProperties().company
+            )
+        )
+    } finally {
+        telemetry.cloudformation_delete.emit({ result: deleteResult })
+    }
+}
diff --git a/packages/core/src/lambda/commands/deleteLambda.ts b/packages/core/src/lambda/commands/deleteLambda.ts
new file mode 100644
index 00000000000..c6290eab3c6
--- /dev/null
+++ b/packages/core/src/lambda/commands/deleteLambda.ts
@@ -0,0 +1,62 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as nls from 'vscode-nls'
+const localize = nls.loadMessageBundle()
+
+import * as localizedText from '../../shared/localizedText'
+import { DefaultLambdaClient } from '../../shared/clients/lambdaClient'
+import { Result } from '../../shared/telemetry/telemetry'
+import { showConfirmationMessage, showViewLogsMessage } from '../../shared/utilities/messages'
+import { FunctionConfiguration } from '@aws-sdk/client-lambda'
+import { getLogger } from '../../shared/logger/logger'
+import { telemetry } from '../../shared/telemetry/telemetry'
+
+async function confirmDeletion(functionName: string): Promise {
+    return showConfirmationMessage({
+        prompt: localize(
+            'AWS.command.deleteLambda.confirm',
+            "Are you sure you want to delete lambda function '{0}'?",
+            functionName
+        ),
+        confirm: localizedText.localizedDelete,
+        cancel: localizedText.cancel,
+    })
+}
+
+export async function deleteLambda(
+    lambda: Pick,
+    client: Pick
+): Promise {
+    if (!lambda.FunctionName) {
+        telemetry.lambda_delete.emit({ duration: 0, result: 'Failed' })
+
+        throw new TypeError('Lambda does not have a function name')
+    }
+
+    let deleteResult: Result = 'Succeeded'
+
+    try {
+        if (await confirmDeletion(lambda.FunctionName)) {
+            await client.deleteFunction(lambda.FunctionName)
+        } else {
+            deleteResult = 'Cancelled'
+        }
+    } catch (err) {
+        deleteResult = 'Failed'
+        getLogger().error(`Failed to delete lambda function "${lambda.FunctionName}": %s`, err)
+        const message = localize(
+            'AWS.command.deleteLambda.error',
+            "There was an error deleting lambda function '{0}'",
+            lambda.FunctionName
+        )
+
+        void showViewLogsMessage(message)
+    } finally {
+        telemetry.lambda_delete.emit({
+            result: deleteResult,
+        })
+    }
+}
diff --git a/packages/core/src/lambda/commands/downloadLambda.ts b/packages/core/src/lambda/commands/downloadLambda.ts
new file mode 100644
index 00000000000..baf82b5b30c
--- /dev/null
+++ b/packages/core/src/lambda/commands/downloadLambda.ts
@@ -0,0 +1,300 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import AdmZip from 'adm-zip'
+import * as _ from 'lodash'
+import * as path from 'path'
+import * as vscode from 'vscode'
+import { LambdaFunctionNode } from '../explorer/lambdaFunctionNode'
+import { showConfirmationMessage } from '../../shared/utilities/messages'
+import { LaunchConfiguration, getReferencedHandlerPaths } from '../../shared/debug/launchConfiguration'
+
+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'
+import { createCodeAwsSamDebugConfig } from '../../shared/sam/debugger/awsSamDebugConfiguration'
+import * as pathutils from '../../shared/utilities/pathUtils'
+import { localize } from '../../shared/utilities/vsCodeUtils'
+import { addFolderToWorkspace } from '../../shared/utilities/workspaceUtils'
+import { promptUserForLocation, WizardContext } from '../../shared/wizards/multiStepWizard'
+import { getLambdaDetails } from '../utils'
+import { Progress } from 'got/dist/source'
+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'
+import globals from '../../shared/extensionGlobals'
+
+// Workspace state key for Lambda function ARN to local path cache
+const LAMBDA_ARN_CACHE_KEY = 'aws.lambda.functionArnToLocalPathCache' // eslint-disable-line @typescript-eslint/naming-convention
+
+async function setLambdaArnCache(functionArn: string, localPath: string): Promise {
+    try {
+        const cache: Record = globals.context.workspaceState.get(LAMBDA_ARN_CACHE_KEY, {})
+        cache[functionArn] = localPath
+        await globals.context.workspaceState.update(LAMBDA_ARN_CACHE_KEY, cache)
+        getLogger().debug(`lambda: cached local path for function ARN: ${functionArn} -> ${localPath}`)
+    } catch (error) {
+        getLogger().error(`lambda: failed to cache local path for function ARN: ${functionArn}`, error)
+    }
+}
+
+export function getCachedLocalPath(functionArn: string): string | undefined {
+    const cache: Record = globals.context.workspaceState.get(LAMBDA_ARN_CACHE_KEY, {})
+    return cache[functionArn]
+}
+
+export async function downloadLambdaCommand(functionNode: LambdaFunctionNode) {
+    const result = await runDownloadLambda(functionNode)
+    // check if result is Result
+    if (result instanceof vscode.Uri) {
+        return
+    }
+
+    telemetry.lambda_import.emit({
+        result,
+        runtime: functionNode.configuration.Runtime as Runtime | undefined,
+    })
+}
+
+export async function runDownloadLambda(
+    functionNode: LambdaFunctionNode,
+    returnDir: boolean = false
+): Promise {
+    const workspaceFolders = vscode.workspace.workspaceFolders || []
+    const functionName = functionNode.configuration.FunctionName!
+
+    if (workspaceFolders.length === 0) {
+        void vscode.window.showErrorMessage(
+            localize(
+                'AWS.lambda.download.noWorkspaceFolders',
+                'Open a workspace and add a folder to it before downloading a Lambda function.'
+            )
+        )
+        return 'Cancelled'
+    }
+    const selectedUri = await promptUserForLocation(new WizardContext(), { step: 1, totalSteps: 1 })
+    if (!selectedUri) {
+        return 'Cancelled'
+    }
+
+    const downloadLocation = path.join(selectedUri.fsPath, functionName, path.sep)
+    const downloadLocationName = vscode.workspace.asRelativePath(downloadLocation, true)
+
+    if (await fs.exists(downloadLocation)) {
+        const isConfirmed = await showConfirmationMessage({
+            prompt: localize(
+                'AWS.lambda.download.prompt',
+                'Downloading {0} into: {1}\nExisting directory will be overwritten: {0}\nProceed with download?',
+                functionName,
+                downloadLocationName
+            ),
+            confirm: localize('AWS.lambda.download.download', 'Download'),
+            cancel: localizedText.cancel,
+        })
+
+        if (!isConfirmed) {
+            getLogger().info('DownloadLambda cancelled')
+            return 'Cancelled'
+        }
+
+        // customer accepted, we should make sure the target dir is clean
+        await fs.delete(downloadLocation, { recursive: true })
+    }
+
+    return await downloadLambdaInLocation(
+        { name: functionName, region: functionNode.regionCode, configuration: functionNode.configuration },
+        downloadLocationName,
+        downloadLocation,
+        workspaceFolders,
+        selectedUri,
+        returnDir
+    )
+}
+
+export async function downloadLambdaInLocation(
+    lambda: LambdaFunction,
+    downloadLocationName: string,
+    downloadLocation: string,
+    workspaceFolders?: readonly vscode.WorkspaceFolder[],
+    selectedUri?: vscode.Uri,
+    returnDir: boolean = false
+): Promise {
+    const result = await vscode.window.withProgress(
+        {
+            location: vscode.ProgressLocation.Notification,
+            cancellable: false,
+            title: localize(
+                'AWS.lambda.download.status',
+                'Downloading Lambda function {0} into {1}...',
+                lambda.name,
+                downloadLocationName
+            ),
+        },
+        async (progress) => {
+            let lambdaLocation: string
+
+            try {
+                await downloadAndUnzipLambda(progress, lambda, downloadLocation)
+                // Cache the mapping of function ARN to downloaded location
+                if (lambda.configuration?.FunctionArn) {
+                    await setLambdaArnCache(lambda.configuration.FunctionArn, downloadLocation)
+                }
+                lambdaLocation = path.join(downloadLocation, getLambdaDetails(lambda.configuration!).fileName)
+                if (!(await fs.exists(lambdaLocation))) {
+                    // if file ext is mjs, change to js or vice versa
+                    const currentExt = path.extname(lambdaLocation)
+                    const alternativeExt = currentExt === '.mjs' ? '.js' : '.mjs'
+                    const alternativePath = lambdaLocation.replace(currentExt, alternativeExt)
+
+                    if (await fs.exists(alternativePath)) {
+                        lambdaLocation = alternativePath
+                    }
+                }
+            } catch (e) {
+                // initial download failed or runtime is unsupported.
+                // show error and return a failure
+                const err = e as Error
+                getLogger().error(err)
+                void vscode.window.showErrorMessage(
+                    localize(
+                        'AWS.lambda.download.downloadError',
+                        'Error downloading Lambda function {0}: {1}',
+                        lambda.configuration!.FunctionArn!,
+                        err.message
+                    )
+                )
+
+                return 'Failed'
+            }
+
+            try {
+                await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup')
+                await openLambdaFile(lambdaLocation)
+                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)
+                }
+                return 'Succeeded'
+            } catch (e) {
+                // failed to open handler file or add launch config.
+                // not a failure since the function is downloaded to a workspace directory.
+                return 'Succeeded'
+            }
+        }
+    )
+
+    if (returnDir) {
+        return vscode.Uri.file(downloadLocation)
+    } else {
+        return result
+    }
+}
+
+async function downloadAndUnzipLambda(
+    progress: vscode.Progress<{
+        message?: string | undefined
+        increment?: number | undefined
+    }>,
+    lambda: LambdaFunction,
+    extractLocation: string,
+    lambdaClient = new DefaultLambdaClient(lambda.region)
+): Promise {
+    const functionArn = lambda.configuration!.FunctionArn!
+    let tempDir: string | undefined
+    try {
+        tempDir = await makeTemporaryToolkitFolder()
+        const downloadLocation = path.join(tempDir, 'function.zip')
+
+        const response = await lambdaClient.getFunction(functionArn)
+        const codeLocation = response.Code!.Location!
+
+        // arbitrary increments since there's no "busy" state for progress bars
+        progress.report({ message: 'Starting download' })
+
+        const fetcher = new HttpResourceFetcher(codeLocation, {
+            showUrl: false,
+            friendlyName: 'Lambda Function .zip file',
+        })
+        const streams = fetcher.get(downloadLocation)
+
+        let last: number = 0
+        streams.requestStream.on('downloadProgress', (p: Progress) => {
+            // I think these are bytes...
+            const message = p.total ? `Downloading ${p.transferred}/${p.total} bytes` : 'Downloading...'
+            const increment = p.total ? ((p.transferred - last) / p.total) * 100 : 0
+            last = p.transferred
+            progress.report({ message, increment })
+        })
+
+        await streams
+        progress.report({ message: 'Extracting...' })
+        new AdmZip(downloadLocation).extractAllTo(extractLocation, true)
+    } finally {
+        await tryRemoveFolder(tempDir)
+    }
+}
+
+export async function openLambdaFile(lambdaLocation: string, viewColumn?: vscode.ViewColumn): Promise {
+    if (!(await fs.exists(lambdaLocation))) {
+        const warning = localize(
+            'AWS.lambda.download.fileNotFound',
+            'Handler file {0} not found in downloaded function.',
+            lambdaLocation
+        )
+        getLogger().warn(warning)
+        void vscode.window.showWarningMessage(warning)
+        throw new Error()
+    }
+    await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup')
+    const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(lambdaLocation))
+    await vscode.window.showTextDocument(doc, viewColumn)
+}
+
+async function addLaunchConfigEntry(
+    lambdaLocation: string,
+    lambda: LambdaFunction,
+    workspaceFolder: vscode.WorkspaceFolder
+): Promise {
+    const handler = lambda.configuration!.Handler!
+
+    const samDebugConfig = createCodeAwsSamDebugConfig(
+        workspaceFolder,
+        handler,
+        computeLambdaRoot(lambdaLocation, lambda),
+        lambda.configuration!.Runtime!
+    )
+
+    const launchConfig = new LaunchConfiguration(vscode.Uri.file(lambdaLocation))
+    const refPaths = await getReferencedHandlerPaths(launchConfig)
+    if (!refPaths.has(pathutils.normalize(path.join(path.dirname(lambdaLocation), handler)))) {
+        await launchConfig.addDebugConfiguration(samDebugConfig)
+    }
+}
+
+/**
+ * Computes the Lambda root.
+ * We cannot assume that the Lambda root is the dirname since CodeUri is not a required field (can be merged with handler)
+ * @param lambdaLocation Lambda handler file location
+ * @param functionNode Function node
+ */
+function computeLambdaRoot(lambdaLocation: string, lambda: LambdaFunction): string {
+    const lambdaDetails = getLambdaDetails(lambda.configuration!)
+    const normalizedLocation = pathutils.normalize(lambdaLocation)
+
+    const lambdaIndex = normalizedLocation.indexOf(`/${lambdaDetails.fileName}`)
+
+    return lambdaIndex > -1 ? normalizedLocation.slice(0, lambdaIndex) : path.dirname(normalizedLocation)
+}
diff --git a/packages/core/src/lambda/commands/editLambda.ts b/packages/core/src/lambda/commands/editLambda.ts
new file mode 100644
index 00000000000..5ea8169d280
--- /dev/null
+++ b/packages/core/src/lambda/commands/editLambda.ts
@@ -0,0 +1,285 @@
+/*!
+ * 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,
+    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'
+import { getFunctionWithCredentials } from '../../shared/clients/lambdaClient'
+import { getLogger } from '../../shared/logger/logger'
+
+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 or emptied
+        if (fileUri.fsPath !== projectUri.fsPath) {
+            // Check if directory is empty before prompting for sync
+            try {
+                const entries = await fs.readdir(projectUri.fsPath)
+                if (entries.length > 0) {
+                    await promptForSync(lambda, projectUri, fileUri)
+                }
+            } catch (err) {
+                getLogger().debug(`Failed to check Lambda directory contents: ${err}`)
+            }
+        }
+    })
+}
+
+// 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)
+    }
+}
+
+export 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 deleteFilesInFolder(location: string) {
+    const entries = await fs.readdir(location)
+    await Promise.all(
+        entries.map((entry) => fs.delete(path.join(location, entry[0]), { recursive: true, force: true }))
+    )
+}
+
+export async function editLambdaCommand(functionNode: LambdaFunctionNode) {
+    const region = functionNode.regionCode
+    const functionName = functionNode.configuration.FunctionName!
+    return await editLambda({ name: functionName, region, configuration: functionNode.configuration }, 'explorer')
+}
+
+export async function overwriteChangesForEdit(lambda: LambdaFunction, downloadLocation: string) {
+    try {
+        // Clear directory contents instead of deleting to avoid Windows EBUSY errors
+        if (await fs.existsDir(downloadLocation)) {
+            await deleteFilesInFolder(downloadLocation)
+        } else {
+            await fs.mkdir(downloadLocation)
+        }
+
+        await downloadLambdaInLocation(lambda, 'local', 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,
+            handlerFile: getLambdaDetails(lambda.configuration!).fileName,
+        })
+        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' })
+    }
+}
+
+export async function editLambda(lambda: LambdaFunction, source?: 'workspace' | 'explorer') {
+    return await telemetry.lambda_quickEditFunction.run(async () => {
+        telemetry.record({ source })
+        const downloadLocation = getTempLocation(lambda.name, lambda.region)
+
+        // 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)) {
+            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 isDirectoryEmpty = (await fs.existsDir(downloadLocation))
+            ? (await fs.readdir(downloadLocation)).length === 0
+            : true
+
+        const overwriteChanges =
+            !localExists ||
+            isDirectoryEmpty ||
+            (!(await compareCodeSha(lambda)) ? await confirmOutdatedChanges(prompt) : false)
+
+        if (overwriteChanges) {
+            await overwriteChangesForEdit(lambda, downloadLocation)
+        } else if (source === 'explorer') {
+            // If the source is the explorer, we want to open, otherwise we just wait to open in the workspace
+            const lambdaLocation = path.join(downloadLocation, getLambdaDetails(lambda.configuration!).fileName)
+            await openLambdaFile(lambdaLocation)
+            watchForUpdates(lambda, vscode.Uri.file(downloadLocation))
+        }
+
+        return downloadLocation
+    })
+}
+
+export async function openLambdaFolderForEdit(name: string, region: string) {
+    const downloadLocation = getTempLocation(name, region)
+
+    // Do all authentication work before opening workspace to avoid race condition
+    const getFunctionOutput = await getFunctionWithCredentials(region, name)
+    const configuration = getFunctionOutput.Configuration
+
+    // Download and set up Lambda code before opening workspace
+    await editLambda(
+        {
+            name,
+            region,
+            configuration: configuration as any,
+        },
+        'workspace'
+    )
+
+    try {
+        await vscode.commands.executeCommand('vscode.openFolder', vscode.Uri.file(downloadLocation), {
+            newWindow: true,
+            noRecentEntry: true,
+        })
+    } catch (e) {
+        throw new ToolkitError(`Failed to open your function as a workspace: ${e}`, { code: 'folderOpenFailure' })
+    }
+}
+
+export async function getReadme(): Promise {
+    const readmeSource = path.join('resources', 'markdown', 'lambdaEdit.md')
+    const readmeDestination = path.join(lambdaTempPath, 'README.md')
+    try {
+        const readmeContent = await fs.readFileText(globals.context.asAbsolutePath(readmeSource))
+        await fs.writeFile(readmeDestination, readmeContent)
+    } catch (e) {
+        getLogger().info(`Failed to copy content for Lambda README: ${e}`)
+    }
+
+    try {
+        const createStackIconSource = path.join('resources', 'icons', 'aws', 'lambda', 'create-stack-light.svg')
+        const createStackIconDestination = path.join(lambdaTempPath, 'create-stack.svg')
+        await fs.copy(globals.context.asAbsolutePath(createStackIconSource), createStackIconDestination)
+
+        // Copy VS Code built-in icons
+        const vscodeIconPath = path.join('resources', 'icons', 'vscode', 'light')
+
+        const invokeIconSource = path.join(vscodeIconPath, 'run.svg')
+        const invokeIconDestination = path.join(lambdaTempPath, 'invoke.svg')
+        await fs.copy(globals.context.asAbsolutePath(invokeIconSource), invokeIconDestination)
+
+        const deployIconSource = path.join(vscodeIconPath, 'cloud-upload.svg')
+        const deployIconDestination = path.join(lambdaTempPath, 'deploy.svg')
+        await fs.copy(globals.context.asAbsolutePath(deployIconSource), deployIconDestination)
+    } catch (e) {
+        getLogger().info(`Failed to copy content for Lambda README: ${e}`)
+    }
+
+    return readmeDestination
+}
diff --git a/packages/core/src/lambda/commands/listSamResources.ts b/packages/core/src/lambda/commands/listSamResources.ts
new file mode 100644
index 00000000000..80d13a1c71b
--- /dev/null
+++ b/packages/core/src/lambda/commands/listSamResources.ts
@@ -0,0 +1,43 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { getLogger } from '../../shared/logger/logger'
+import { runSamCliListResource } from '../../shared/sam/cli/samCliListResources'
+
+export interface StackResource {
+    LogicalResourceId: string
+    PhysicalResourceId: string
+}
+
+/*
+This function return exclusively the deployed resources
+Newly added but yet-to-be deployed resources are not included in this result
+*/
+export async function getDeployedResources(params: any) {
+    try {
+        const samCliListResourceOutput = await runSamCliListResource(params.listResourcesParams, params.invoker).then(
+            (output) => parseSamListResourceOutput(output)
+        )
+        // Filter out resources that are not deployed
+        return samCliListResourceOutput.filter((resource) => resource.PhysicalResourceId !== '-')
+    } catch (err) {
+        const error = err as Error
+        getLogger().error(error)
+    }
+}
+
+function parseSamListResourceOutput(output: any): StackResource[] {
+    try {
+        if ((Array.isArray(output) && output.length === 0) || '[]' === output) {
+            // Handle if the output is instance or stringify version of an empty array to avoid parsing error
+            return []
+        }
+        return JSON.parse(output) as StackResource[]
+    } catch (error: any) {
+        void vscode.window.showErrorMessage(`Failed to parse SAM CLI output: ${error.message}`)
+        return []
+    }
+}
diff --git a/src/lambda/commands/uploadLambda.ts b/packages/core/src/lambda/commands/uploadLambda.ts
similarity index 81%
rename from src/lambda/commands/uploadLambda.ts
rename to packages/core/src/lambda/commands/uploadLambda.ts
index a465160c45f..98149674587 100644
--- a/src/lambda/commands/uploadLambda.ts
+++ b/packages/core/src/lambda/commands/uploadLambda.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -8,32 +8,26 @@ import * as nls from 'vscode-nls'
 
 const localize = nls.loadMessageBundle()
 
-import * as AdmZip from 'adm-zip'
-import * as fs from 'fs-extra'
+import AdmZip from 'adm-zip'
 import * as path from 'path'
+import { fs } from '../../shared/fs/fs'
 import { showConfirmationMessage, showViewLogsMessage } from '../../shared/utilities/messages'
-import {
-    fileExists,
-    cloud9Findfile,
-    makeTemporaryToolkitFolder,
-    tryRemoveFolder,
-} from '../../shared/filesystemUtilities'
+import { makeTemporaryToolkitFolder, tryRemoveFolder } from '../../shared/filesystemUtilities'
 import * as localizedText from '../../shared/localizedText'
-import { getLogger } from '../../shared/logger'
+import { getLogger } from '../../shared/logger/logger'
 import { SamCliBuildInvocation } from '../../shared/sam/cli/samCliBuild'
 import { getSamCliContext } from '../../shared/sam/cli/samCliContext'
 import { SamTemplateGenerator } from '../../shared/templates/sam/samTemplateGenerator'
-import { Window } from '../../shared/vscode/window'
 import { addCodiconToString } from '../../shared/utilities/textUtilities'
 import { getLambdaDetails, listLambdaFunctions } from '../utils'
-import { getIdeProperties, isCloud9 } from '../../shared/extensionUtilities'
+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 { ToolkitError } from '../../shared/errors'
-import { FunctionConfiguration } from 'aws-sdk/clients/lambda'
+import { FunctionConfiguration } from '@aws-sdk/client-lambda'
 import globals from '../../shared/extensionGlobals'
 import { toArrayAsync } from '../../shared/utilities/collectionUtils'
 import { fromExtensionManifest } from '../../shared/settings'
@@ -53,7 +47,9 @@ class LambdaSettings extends fromExtensionManifest('aws.lambda', { recentlyUploa
         try {
             return this.get('recentlyUploaded')
         } catch (error) {
-            this.delete('recentlyUploaded')
+            this.delete('recentlyUploaded').catch((e) => {
+                getLogger().error('TypedSettings.delete() failed: %s', (e as Error).message)
+            })
         }
     }
 
@@ -112,10 +108,10 @@ export async function uploadLambdaCommand(lambdaArg?: LambdaFunction, path?: vsc
     } catch (err) {
         result = 'Failed'
         if (err instanceof ToolkitError) {
-            showViewLogsMessage(`Could not upload lambda: ${err.message}`)
+            void showViewLogsMessage(`Could not upload lambda: ${err.message}`)
             getLogger().error(`Lambda upload failed: %O`, err.cause ?? err)
         } else {
-            showViewLogsMessage(`Could not upload lambda (unexpected exception)`)
+            void showViewLogsMessage(`Could not upload lambda (unexpected exception)`)
             getLogger().error(`Lambda upload failed: %s`, err)
         }
     } finally {
@@ -126,7 +122,7 @@ export async function uploadLambdaCommand(lambdaArg?: LambdaFunction, path?: vsc
         if (result === 'Succeeded') {
             const profile = globals.awsContext.getCredentialProfileName()
             if (profile && lambda) {
-                LambdaSettings.instance.setRecentLambda(profile, lambda.region, lambda.name)
+                await LambdaSettings.instance.setRecentLambda(profile, lambda.region, lambda.name)
             }
         }
     }
@@ -185,7 +181,7 @@ function createConfirmDeploymentPrompter(lambda: LambdaFunction) {
     // TODO(sijaden): make this a quick pick? Tried to keep as close to possible as the original impl.
     return new (class extends Prompter {
         protected promptUser(): Promise> {
-            return confirmLambdaDeployment(lambda).then(res => res || WIZARD_BACK)
+            return confirmLambdaDeployment(lambda).then((res) => res || WIZARD_BACK)
         }
 
         // Stubs. Need to thin-out the `Prompter` interface to avoid this.
@@ -208,16 +204,15 @@ export interface UploadLambdaWizardState {
 }
 
 export class UploadLambdaWizard extends Wizard {
-    constructor(lambda?: LambdaFunction, invokePath?: vscode.Uri) {
-        super({ initState: { lambda } })
-        this.form.lambda.region.bindPrompter(() => createRegionPrompter().transform(region => region.id))
+    public override async init(): Promise {
+        this.form.lambda.region.bindPrompter(() => createRegionPrompter().transform((region) => region.id))
 
-        if (invokePath) {
+        if (this.invokePath) {
             this.form.uploadType.setDefault('directory')
-            if (fs.statSync(invokePath.fsPath).isFile()) {
-                this.form.targetUri.setDefault(vscode.Uri.file(path.dirname(invokePath.fsPath)))
+            if (await fs.existsFile(this.invokePath.fsPath)) {
+                this.form.targetUri.setDefault(vscode.Uri.file(path.dirname(this.invokePath.fsPath)))
             } else {
-                this.form.targetUri.setDefault(invokePath)
+                this.form.targetUri.setDefault(this.invokePath)
             }
         } else {
             this.form.uploadType.bindPrompter(() => createUploadTypePrompter())
@@ -239,9 +234,9 @@ export class UploadLambdaWizard extends Wizard {
             })
         }
 
-        this.form.lambda.name.bindPrompter(state => {
+        this.form.lambda.name.bindPrompter((state) => {
             // invoking from the command palette passes no arguments
-            if (!invokePath) {
+            if (!this.invokePath) {
                 if (state.uploadType === 'directory') {
                     return createFunctionNamePrompter(state.lambda!.region!, state.targetUri)
                 } else {
@@ -252,10 +247,10 @@ export class UploadLambdaWizard extends Wizard {
                     )
                 }
             } else {
-                return createFunctionNamePrompter(state.lambda!.region!, invokePath)
+                return createFunctionNamePrompter(state.lambda!.region!, this.invokePath)
             }
         })
-        if (lambda) {
+        if (this.lambda) {
             this.form.directoryBuildType.bindPrompter(() => createBuildPrompter(), {
                 showWhen: ({ uploadType }) => uploadType === 'directory',
             })
@@ -263,7 +258,16 @@ export class UploadLambdaWizard extends Wizard {
             this.form.directoryBuildType.setDefault('zip')
         }
 
-        this.form.confirmedDeploy.bindPrompter(state => createConfirmDeploymentPrompter(state.lambda!))
+        this.form.confirmedDeploy.bindPrompter((state) => createConfirmDeploymentPrompter(state.lambda!))
+
+        return this
+    }
+
+    constructor(
+        readonly lambda?: LambdaFunction,
+        readonly invokePath?: vscode.Uri
+    ) {
+        super({ initState: { lambda } })
     }
 }
 
@@ -273,21 +277,16 @@ 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,
-    window = Window.vscode()
-) {
+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 {
-        return await window.withProgress(
+        return await vscode.window.withProgress(
             {
                 location: vscode.ProgressLocation.Notification,
                 cancellable: false,
             },
-            async progress => {
+            async (progress) => {
                 return await zipAndUploadDirectory(lambda, parentDir.fsPath, progress)
             }
         )
@@ -304,29 +303,22 @@ async function runUploadDirectory(
  * @param parentDir Parent dir to build
  * @param window Wrapper around vscode.window functionality for testing
  */
-async function runUploadLambdaWithSamBuild(
-    lambda: Required,
-    parentDir: vscode.Uri,
-    window = Window.vscode()
-) {
+async function runUploadLambdaWithSamBuild(lambda: Required, parentDir: vscode.Uri) {
     // Detect if handler is present and provide strong guidance against proceeding if not.
     try {
         const handlerFile = path.join(parentDir.fsPath, getLambdaDetails(lambda.configuration).fileName)
-        if (!(await fileExists(handlerFile))) {
-            const isConfirmed = await showConfirmationMessage(
-                {
-                    prompt: localize(
-                        'AWS.lambda.upload.handlerNotFound',
-                        "{0} Toolkit can't find a file corresponding to handler: {1} at filepath {2}.\n\nThis directory likely will not work with this function.\n\nProceed with upload anyway?",
-                        getIdeProperties().company,
-                        lambda.configuration.Handler,
-                        handlerFile
-                    ),
-                    confirm: localizedText.yes,
-                    cancel: localizedText.no,
-                },
-                window
-            )
+        if (!(await fs.exists(handlerFile))) {
+            const isConfirmed = await showConfirmationMessage({
+                prompt: localize(
+                    'AWS.lambda.upload.handlerNotFound',
+                    "{0} Toolkit can't find a file corresponding to handler: {1} at filepath {2}.\n\nThis directory likely will not work with this function.\n\nProceed with upload anyway?",
+                    getIdeProperties().company,
+                    lambda.configuration.Handler,
+                    handlerFile
+                ),
+                confirm: localizedText.yes,
+                cancel: localizedText.no,
+            })
 
             if (!isConfirmed) {
                 getLogger().info('Handler file not found. Aborting runUploadLambdaWithSamBuild')
@@ -340,12 +332,12 @@ async function runUploadLambdaWithSamBuild(
         )
     }
 
-    return await window.withProgress(
+    return await vscode.window.withProgress(
         {
             location: vscode.ProgressLocation.Notification,
             cancellable: false,
         },
-        async progress => {
+        async (progress) => {
             let tempDir: string | undefined
             try {
                 const invoker = getSamCliContext().invoker
@@ -402,19 +394,16 @@ async function runUploadLambdaWithSamBuild(
  * @param lambda LambdaFunction
  * @param window Wrapper around vscode.window functionality for testing
  */
-async function confirmLambdaDeployment(lambda: LambdaFunction, window = Window.vscode()): Promise {
-    const isConfirmed = await showConfirmationMessage(
-        {
-            prompt: localize(
-                'AWS.lambda.upload.confirm',
-                'This will immediately publish the selected code as the $LATEST version of Lambda: {0}.\n\nContinue?',
-                lambda.name
-            ),
-            confirm: localizedText.yes,
-            cancel: localizedText.no,
-        },
-        window
-    )
+async function confirmLambdaDeployment(lambda: LambdaFunction): Promise {
+    const isConfirmed = await showConfirmationMessage({
+        prompt: localize(
+            'AWS.lambda.upload.confirm',
+            'This will immediately publish the selected code as the $LATEST version of Lambda: {0}.\n\nContinue?',
+            lambda.name
+        ),
+        confirm: localizedText.yes,
+        cancel: localizedText.no,
+    })
 
     if (!isConfirmed) {
         getLogger().info('UploadLambda confirmation cancelled.')
@@ -423,14 +412,14 @@ async function confirmLambdaDeployment(lambda: LambdaFunction, window = Window.v
     return isConfirmed
 }
 
-async function runUploadLambdaZipFile(lambda: LambdaFunction, zipFileUri: vscode.Uri, window = Window.vscode()) {
-    return await window.withProgress(
+async function runUploadLambdaZipFile(lambda: LambdaFunction, zipFileUri: vscode.Uri) {
+    return await vscode.window.withProgress(
         {
             location: vscode.ProgressLocation.Notification,
             cancellable: false,
         },
-        async progress => {
-            const zipFile = await fs.readFile(zipFileUri.fsPath).catch(err => {
+        async (progress) => {
+            const zipFile = await fs.readFileBytes(zipFileUri.fsPath).catch((err) => {
                 throw new ToolkitError('Failed to read zip', { cause: err })
             })
             return await uploadZipBuffer(lambda, zipFile, progress)
@@ -457,7 +446,7 @@ async function zipAndUploadDirectory(
         const zip = new AdmZip()
         zip.addLocalFolder(path)
         zip.toBuffer(resolve, reject)
-    }).catch(err => {
+    }).catch((err) => {
         throw new ToolkitError('Failed to archive directory', { cause: err })
     })
 
@@ -473,7 +462,7 @@ async function zipAndUploadDirectory(
  */
 async function uploadZipBuffer(
     lambda: LambdaFunction,
-    zip: Buffer,
+    zip: Uint8Array,
     progress: vscode.Progress<{
         message?: string | undefined
         increment?: number | undefined
@@ -483,49 +472,44 @@ async function uploadZipBuffer(
     progress.report({
         message: localize('AWS.lambda.upload.progress.uploadingArchive', 'Uploading archive to Lambda...'),
     })
-    await lambdaClient.updateFunctionCode(lambda.name, zip).catch(err => {
+    await lambdaClient.updateFunctionCode(lambda.name, zip).catch((err) => {
         throw new ToolkitError('Failed to upload zip archive', { cause: err })
     })
 
-    Window.vscode().showInformationMessage(
+    void vscode.window.showInformationMessage(
         localize('AWS.lambda.upload.done', 'Uploaded Lambda function: {0}', lambda.name)
     )
 }
 
-export async function findApplicationJsonFile(
-    startPath: vscode.Uri,
-    cloud9 = isCloud9()
-): Promise {
-    if (!(await fileExists(startPath.fsPath))) {
+export async function findApplicationJsonFile(startPath: vscode.Uri): Promise {
+    if (!(await fs.exists(startPath.fsPath))) {
         getLogger().error(
             'findApplicationJsonFile() invalid path (not accessible or does not exist): "%s"',
             startPath.fsPath
         )
         return undefined
     }
-    const isdir = fs.statSync(startPath.fsPath).isDirectory()
+    const isdir = await fs.existsDir(startPath.fsPath)
     const parentDir = isdir ? startPath.fsPath : path.dirname(startPath.fsPath)
-    const found = cloud9
-        ? await cloud9Findfile(parentDir, '.application.json')
-        : await vscode.workspace.findFiles(
-              new vscode.RelativePattern(parentDir, '**/.application.json'),
-              // exclude:
-              // - null      = NO excludes apply
-              // - undefined = default excludes apply (e.g. the `files.exclude` setting but not `search.exclude`).
-              // eslint-disable-next-line no-null/no-null
-              null,
-              1
-          )
+    const found = await vscode.workspace.findFiles(
+        new vscode.RelativePattern(parentDir, '**/.application.json'),
+        // exclude:
+        // - null      = NO excludes apply
+        // - undefined = default excludes apply (e.g. the `files.exclude` setting but not `search.exclude`).
+        // eslint-disable-next-line unicorn/no-null
+        null,
+        1
+    )
     if (!found || found.length === 0) {
         getLogger().debug('uploadLambda: .application.json not found in: "%s"', parentDir)
     }
     return found[0]
 }
 
-export function getFunctionNames(file: vscode.Uri, region: string): string[] | undefined {
+export async function getFunctionNames(file: vscode.Uri, region: string): Promise {
     try {
         const names: string[] = []
-        const appData = JSON.parse(fs.readFileSync(file.fsPath, { encoding: 'utf-8' }).toString())
+        const appData = JSON.parse(await fs.readFileText(file.fsPath))
         if (appData['Functions']) {
             const functions = Object.keys(appData['Functions'])
             if (functions) {
@@ -550,14 +534,14 @@ async function listAllLambdaNames(region: string, path?: vscode.Uri) {
     // Get Lambda functions from .application.json #2588
     if (path) {
         const appFile = await findApplicationJsonFile(path)
-        const namesFromAppFile = appFile ? getFunctionNames(appFile, region) : undefined
+        const namesFromAppFile = appFile ? await getFunctionNames(appFile, region) : undefined
         if (!appFile) {
             getLogger().debug('lambda: .application.json not found')
         } else if (!namesFromAppFile) {
             getLogger().debug('lambda: no functions in .application.json for region: %s', region)
         } else {
             lambdaFunctionNames.push(
-                ...namesFromAppFile.map(n => {
+                ...namesFromAppFile.map((n) => {
                     return {
                         label: n,
                         description: localize('AWS.lambda.upload.fromAppJson', 'from .application.json'),
@@ -612,7 +596,7 @@ function createFunctionNamePrompter(region: string, path?: vscode.Uri) {
             'aws.lambda.upload.manualEntry.placeholder',
             'Filter or enter existing function name or ARN'
         ),
-        filterBoxInputSettings: { label: 'Existing lambda function: ', transform: input => input },
+        filterBoxInputSettings: { label: 'Existing lambda function: ', transform: (input) => input },
     })
 
     return prompter
diff --git a/src/lambda/config/configureParameterOverrides.ts b/packages/core/src/lambda/config/configureParameterOverrides.ts
similarity index 92%
rename from src/lambda/config/configureParameterOverrides.ts
rename to packages/core/src/lambda/config/configureParameterOverrides.ts
index 685ca8d6bc5..70e9d1936d8 100644
--- a/src/lambda/config/configureParameterOverrides.ts
+++ b/packages/core/src/lambda/config/configureParameterOverrides.ts
@@ -1,10 +1,10 @@
 /*!
- * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import * as vscode from 'vscode'
-import { getLogger } from '../../shared/logger'
+import { getLogger } from '../../shared/logger/logger'
 import { getNormalizedRelativePath } from '../../shared/utilities/pathUtils'
 import { getChildrenRange } from '../../shared/utilities/symbolUtilities'
 import { getTabSize } from '../../shared/utilities/textDocumentUtilities'
@@ -70,7 +70,7 @@ export async function configureParameterOverrides(
         const { json, isDirty } = populator.getResults()
 
         if (isDirty) {
-            await editor.edit(eb => {
+            await editor.edit((eb) => {
                 eb.replace(
                     new vscode.Range(
                         editor.document.positionAt(0),
@@ -124,21 +124,21 @@ async function getParameterOverridesRange(
         return defaultRange
     }
 
-    const templatesSymbol = symbols.find(c => c.name === 'templates')
+    const templatesSymbol = symbols.find((c) => c.name === 'templates')
     if (!templatesSymbol) {
         logger.warn(`Invalid format for document ${editor.document.uri}`)
 
         return defaultRange
     }
 
-    const templateSymbol = templatesSymbol.children.find(c => c.name === relativeTemplatePath)
+    const templateSymbol = templatesSymbol.children.find((c) => c.name === relativeTemplatePath)
     if (!templateSymbol) {
         logger.warn(`Cannot find template section '${relativeTemplatePath}' in: ${editor.document.uri}`)
 
         return defaultRange
     }
 
-    const parameterOverridesSymbol = templateSymbol.children.find(c => c.name === 'parameterOverrides')
+    const parameterOverridesSymbol = templateSymbol.children.find((c) => c.name === 'parameterOverrides')
     if (!parameterOverridesSymbol) {
         logger.warn(`Cannot find parameterOverrides section for '${relativeTemplatePath}' in: ${editor.document.uri}`)
 
diff --git a/src/lambda/config/parameterUtils.ts b/packages/core/src/lambda/config/parameterUtils.ts
similarity index 94%
rename from src/lambda/config/parameterUtils.ts
rename to packages/core/src/lambda/config/parameterUtils.ts
index 240ccf8b988..3849e466b05 100644
--- a/src/lambda/config/parameterUtils.ts
+++ b/packages/core/src/lambda/config/parameterUtils.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -8,7 +8,7 @@
 // "templates.json" support.
 //
 import * as vscode from 'vscode'
-import { CloudFormation } from '../../shared/cloudformation/cloudformation'
+import * as CloudFormation from '../../shared/cloudformation/cloudformation'
 import { getNormalizedRelativePath } from '../../shared/utilities/pathUtils'
 import { load as loadTemplatesConfig } from '../config/templates'
 
diff --git a/src/lambda/config/samParameterCompletionItemProvider.ts b/packages/core/src/lambda/config/samParameterCompletionItemProvider.ts
similarity index 86%
rename from src/lambda/config/samParameterCompletionItemProvider.ts
rename to packages/core/src/lambda/config/samParameterCompletionItemProvider.ts
index 60f2dcf4e99..f408733f886 100644
--- a/src/lambda/config/samParameterCompletionItemProvider.ts
+++ b/packages/core/src/lambda/config/samParameterCompletionItemProvider.ts
@@ -1,12 +1,12 @@
 /*!
- * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import * as path from 'path'
 import * as vscode from 'vscode'
-import { CloudFormation } from '../../shared/cloudformation/cloudformation'
-import { getLogger, Logger } from '../../shared/logger'
+import * as CloudFormation from '../../shared/cloudformation/cloudformation'
+import { getLogger, Logger } from '../../shared/logger/logger'
 import { getChildrenRange, loadSymbols, LoadSymbolsContext } from '../../shared/utilities/symbolUtilities'
 import { getParameterNames } from '../config/parameterUtils'
 
@@ -70,8 +70,8 @@ export class SamParameterCompletionItemProvider implements vscode.CompletionItem
         const templateParameterNames = await getParameterNames(templateUri, this.context)
 
         return templateParameterNames
-            .filter(name => !prefix || name.startsWith(prefix))
-            .map(name => {
+            .filter((name) => !prefix || name.startsWith(prefix))
+            .map((name) => {
                 const completionItem: vscode.CompletionItem = {
                     kind: vscode.CompletionItemKind.Reference,
                     label: name,
@@ -106,18 +106,18 @@ async function getTemplateUri({
     symbols: vscode.DocumentSymbol[]
     position: vscode.Position
 }): Promise {
-    const templates = symbols.find(symbol => symbol.name === 'templates')
+    const templates = symbols.find((symbol) => symbol.name === 'templates')
     if (!templates) {
         return undefined
     }
 
-    const template = templates.children.find(child => child.range.contains(position))
+    const template = templates.children.find((child) => child.range.contains(position))
     if (!template) {
         return undefined
     }
 
     // Only offer suggestions inside the 'parameterOverrides' property.
-    const parameterOverrides = template.children.find(child => child.name === 'parameterOverrides')
+    const parameterOverrides = template.children.find((child) => child.name === 'parameterOverrides')
     if (!parameterOverrides) {
         return undefined
     }
@@ -129,7 +129,7 @@ async function getTemplateUri({
 
     // Ensure that position is at a parameter name, not a value.
     if (parameterOverrides.children) {
-        const override = parameterOverrides.children.find(child => child.range.contains(position))
+        const override = parameterOverrides.children.find((child) => child.range.contains(position))
         if (override) {
             if (!override.selectionRange.contains(position)) {
                 return undefined
diff --git a/packages/core/src/lambda/config/templates.ts b/packages/core/src/lambda/config/templates.ts
new file mode 100644
index 00000000000..52beae0e953
--- /dev/null
+++ b/packages/core/src/lambda/config/templates.ts
@@ -0,0 +1,442 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// Use jsonc-parser.parse instead of JSON.parse, as JSONC can handle comments. VS Code uses jsonc-parser
+// under the hood to provide symbols for JSON documents, so this will keep us consistent with VS code.
+import * as jsonParser from 'jsonc-parser'
+import * as os from 'os'
+import * as _path from 'path'
+import * as vscode from 'vscode'
+import * as nls from 'vscode-nls'
+import * as fsUtils from '../../shared/filesystemUtilities'
+import { getLogger, Logger } from '../../shared/logger/logger'
+import { ReadonlyJsonObject } from '../../shared/sam/debugger/awsSamDebugConfiguration'
+import { getTabSizeSetting } from '../../shared/utilities/editorUtilities'
+import { getNormalizedRelativePath } from '../../shared/utilities/pathUtils'
+import { saveDocumentIfDirty } from '../../shared/utilities/textDocumentUtilities'
+import { access } from 'fs/promises'
+import { fs } from '../../shared/fs/fs'
+
+const localize = nls.loadMessageBundle()
+
+export interface TemplatesConfig {
+    templates: {
+        [relativePath: string]: TemplateConfig | undefined
+    }
+}
+
+export interface TemplateConfig {
+    parameterOverrides?: {
+        [key: string]: string | undefined
+    }
+    handlers?: {
+        [handler: string]: HandlerConfig | undefined
+    }
+}
+
+export interface HandlerConfig {
+    event: Record
+    environmentVariables: {
+        [name: string]: string
+    }
+    dockerNetwork?: string
+    useContainer?: boolean
+}
+
+export function generateDefaultHandlerConfig(): HandlerConfig {
+    return {
+        event: {},
+        environmentVariables: {},
+        dockerNetwork: undefined,
+        useContainer: undefined,
+    }
+}
+
+/**
+ * TODO: remove? still needed?
+ */
+export async function getHandlerConfig(params: {
+    handlerName: string
+    documentUri: vscode.Uri
+    samTemplate: vscode.Uri
+}): Promise {
+    const workspaceFolder = vscode.workspace.getWorkspaceFolder(params.documentUri)
+    if (!workspaceFolder) {
+        return generateDefaultHandlerConfig()
+    }
+
+    const config: HandlerConfig = await getLocalLambdaConfiguration(
+        workspaceFolder,
+        params.handlerName,
+        params.samTemplate
+    )
+
+    return config
+}
+
+async function getLocalLambdaConfiguration(
+    workspaceFolder: vscode.WorkspaceFolder,
+    handler: string,
+    samTemplate: vscode.Uri
+): Promise {
+    try {
+        const configPath: string = getTemplatesConfigPath(workspaceFolder.uri.fsPath)
+        const templateRelativePath = getNormalizedRelativePath(workspaceFolder.uri.fsPath, samTemplate.fsPath)
+
+        await saveDocumentIfDirty(configPath)
+
+        let rawConfig: string = '{}'
+        if (await fsUtils.fileExists(configPath)) {
+            rawConfig = await fsUtils.readFileAsString(configPath)
+        }
+
+        const configPopulationResult = new TemplatesConfigPopulator(rawConfig)
+            .ensureTemplateHandlerSectionExists(templateRelativePath, handler)
+            .getResults()
+
+        const config: TemplatesConfig = loadTemplatesConfigFromJson(configPopulationResult.json)
+
+        return config.templates[templateRelativePath]!.handlers![handler]!
+    } catch (e) {
+        if (e instanceof TemplatesConfigFieldTypeError) {
+            showTemplatesConfigurationError(e)
+        }
+
+        throw e
+    }
+}
+
+export interface LoadTemplatesConfigContext {
+    fileExists(path: string): Thenable
+    readFile(path: string): Thenable
+    saveDocumentIfDirty(editorPath: string): Thenable
+}
+
+export class DefaultLoadTemplatesConfigContext implements LoadTemplatesConfigContext {
+    public readonly fileExists = fsUtils.fileExists
+    public readonly readFile = fsUtils.readFileAsString
+
+    public readonly saveDocumentIfDirty = saveDocumentIfDirty
+}
+
+export function getTemplatesConfigPath(workspaceFolderPath: string): string {
+    return _path.join(workspaceFolderPath, '.aws', 'templates.json')
+}
+
+export async function load(
+    workspaceFolderPath: string,
+    context: LoadTemplatesConfigContext = new DefaultLoadTemplatesConfigContext()
+): Promise {
+    const templatesConfigPath = getTemplatesConfigPath(workspaceFolderPath)
+
+    return await loadTemplatesConfig(templatesConfigPath, context)
+}
+
+async function loadTemplatesConfig(
+    path: string,
+    context: LoadTemplatesConfigContext = new DefaultLoadTemplatesConfigContext()
+): Promise {
+    try {
+        await context.saveDocumentIfDirty(path)
+
+        if (!(await context.fileExists(path))) {
+            return {
+                templates: {},
+            }
+        }
+
+        const raw = await context.readFile(path)
+
+        return loadTemplatesConfigFromJson(raw)
+    } catch (err) {
+        if (Array.isArray(err) && (err as any[]).length === 1) {
+            throw new Error(`Could not load .aws/templates.json: ${(err as any[])[0]}`)
+        }
+
+        throw new Error(`Could not load .aws/templates.json: ${err}`)
+    }
+}
+
+export function loadTemplatesConfigFromJson(json: string): TemplatesConfig {
+    const errors: jsonParser.ParseError[] = []
+    const config = jsonParser.parse(json, errors) as TemplatesConfig
+    if (errors.length > 0) {
+        const message =
+            errors.length === 1
+                ? ` ${formatParseError(errors[0])}`
+                : `${os.EOL}${errors.map(formatParseError).join(os.EOL)}`
+
+        throw new Error(`Could not parse .aws/templates.json:${message}`)
+    }
+
+    return config
+}
+
+export function showTemplatesConfigurationError(
+    error: TemplatesConfigFieldTypeError,
+    showErrorMessage: typeof vscode.window.showErrorMessage = vscode.window.showErrorMessage
+): void {
+    const logger: Logger = getLogger()
+
+    void showErrorMessage(
+        localize(
+            'AWS.lambda.configure.error.fieldtype',
+            'Your templates.json file has an issue. {0} was detected as {1} instead of one of the following: [{2}]. Please change or remove this field, and try again.',
+            error.jsonPath.join('.'),
+            error.actualType,
+            error.expectedTypes.join(', ')
+        )
+    )
+
+    logger.error(
+        `Error detected in templates.json: ${error.message}. Field: ${error.jsonPath.join(
+            '.'
+        )}, expected one of: [${error.expectedTypes.join(', ')}], was: ${error.actualType}`
+    )
+}
+
+export async function ensureTemplatesConfigFileExists(path: string): Promise {
+    await fs.mkdir(_path.dirname(path))
+    try {
+        await access(path)
+    } catch {
+        await fs.writeFile(path, '{}')
+    }
+}
+
+function formatParseError(error: jsonParser.ParseError) {
+    return `${getParseErrorDescription(error.error)} at offset ${error.offset}, length ${error.length}`
+}
+
+// Reverse enum mappings are only generated for non-const numerical enums,
+// but ParseErrorCode is a const enum. So we have to reverse-map manually.
+function getParseErrorDescription(code: jsonParser.ParseErrorCode): string {
+    switch (code) {
+        case jsonParser.ParseErrorCode.CloseBraceExpected:
+            return 'close brace expected'
+        case jsonParser.ParseErrorCode.CloseBracketExpected:
+            return 'close bracket expected'
+        case jsonParser.ParseErrorCode.ColonExpected:
+            return 'colon expected'
+        case jsonParser.ParseErrorCode.CommaExpected:
+            return 'command expected'
+        case jsonParser.ParseErrorCode.EndOfFileExpected:
+            return 'end of file expected'
+        case jsonParser.ParseErrorCode.InvalidCharacter:
+            return 'invalid character'
+        case jsonParser.ParseErrorCode.InvalidCommentToken:
+            return 'invalid comment token'
+        case jsonParser.ParseErrorCode.InvalidEscapeCharacter:
+            return 'invalid escape character'
+        case jsonParser.ParseErrorCode.InvalidNumberFormat:
+            return 'invalid number format'
+        case jsonParser.ParseErrorCode.InvalidSymbol:
+            return 'invalid symbol'
+        case jsonParser.ParseErrorCode.InvalidUnicode:
+            return 'invalid unicode'
+        case jsonParser.ParseErrorCode.PropertyNameExpected:
+            return 'property name expected'
+        case jsonParser.ParseErrorCode.UnexpectedEndOfComment:
+            return 'unexpected end of comment'
+        case jsonParser.ParseErrorCode.UnexpectedEndOfNumber:
+            return 'unexpected end of number'
+        case jsonParser.ParseErrorCode.UnexpectedEndOfString:
+            return 'unexpected end of string'
+        case jsonParser.ParseErrorCode.ValueExpected:
+            return 'value expected'
+        // By omitting the default case, we force the compiler to yell at us
+        // if any enum members are added/removed/changed.
+    }
+}
+
+export class TemplatesConfigFieldTypeError extends Error {
+    public readonly jsonPath: jsonParser.JSONPath
+    public readonly expectedTypes: jsonParser.NodeType[]
+    public readonly actualType: jsonParser.NodeType
+
+    public constructor(params: {
+        message?: string
+        jsonPath: jsonParser.JSONPath
+        expectedTypes: jsonParser.NodeType[]
+        actualType: jsonParser.NodeType
+    }) {
+        super(params.message)
+
+        this.jsonPath = params.jsonPath
+        this.expectedTypes = params.expectedTypes
+        this.actualType = params.actualType
+    }
+}
+
+export class TemplatesConfigPopulator {
+    private isDirty: boolean = false
+
+    public constructor(
+        private json: string,
+        private readonly modificationOptions: jsonParser.ModificationOptions = {
+            formattingOptions: {
+                insertSpaces: true,
+                tabSize: getTabSizeSetting(),
+            },
+        }
+    ) {}
+
+    public ensureTemplateSectionExists(templateRelativePath: string): TemplatesConfigPopulator {
+        this.ensureTemplatesSectionExists()
+
+        this.ensureJsonPropertyExists(['templates', templateRelativePath], {})
+
+        return this
+    }
+
+    public ensureTemplateHandlerSectionExists(templateRelativePath: string, handler: string): TemplatesConfigPopulator {
+        this.ensureTemplateHandlersSectionExists(templateRelativePath)
+
+        this.ensureJsonPropertyExists(['templates', templateRelativePath, 'handlers', handler], {
+            event: {},
+            environmentVariables: {},
+        })
+
+        return this
+    }
+
+    public ensureTemplateHandlerPropertiesExist(
+        templateRelativePath: string,
+        handler: string
+    ): TemplatesConfigPopulator {
+        this.ensureTemplateHandlerSectionExists(templateRelativePath, handler)
+
+        this.ensureJsonPropertyExists(['templates', templateRelativePath, 'handlers', handler, 'event'], {})
+
+        this.ensureJsonPropertyExists(
+            ['templates', templateRelativePath, 'handlers', handler, 'environmentVariables'],
+            {}
+        )
+
+        return this
+    }
+
+    public ensureTemplateParameterOverrideExists(
+        templateRelativePath: string,
+        parameterName: string
+    ): TemplatesConfigPopulator {
+        this.ensureTemplateParamOverrideSectionExists(templateRelativePath)
+
+        this.ensureJsonPropertyExists(['templates', templateRelativePath, 'parameterOverrides', parameterName], '', [
+            'string',
+            'null',
+        ])
+
+        return this
+    }
+
+    public getResults(): {
+        isDirty: boolean
+        json: string
+    } {
+        return {
+            isDirty: this.isDirty,
+            json: this.json,
+        }
+    }
+
+    private ensureJsonPropertyExists(
+        jsonPath: jsonParser.JSONPath,
+        value: any,
+        allowedTypes: jsonParser.NodeType[] = ['object', 'null']
+    ) {
+        const root = jsonParser.parseTree(this.json)
+        const node = jsonParser.findNodeAtLocation(root!, jsonPath)
+        const allowedTypesSet = new Set(allowedTypes)
+
+        if (node && !allowedTypesSet.has(node.type)) {
+            throw new TemplatesConfigFieldTypeError({
+                message: 'Invalid configuration',
+                jsonPath: jsonPath,
+                actualType: node.type,
+                expectedTypes: allowedTypes,
+            })
+        }
+
+        if (!node || node.type === 'null') {
+            const edits = jsonParser.modify(this.json, jsonPath, value, this.modificationOptions)
+
+            if (edits.length > 0) {
+                this.json = jsonParser.applyEdits(this.json, edits)
+                this.isDirty = true
+            }
+        }
+    }
+
+    private ensureTemplatesSectionExists(): TemplatesConfigPopulator {
+        this.ensureJsonPropertyExists(['templates'], {})
+
+        return this
+    }
+
+    private ensureTemplateHandlersSectionExists(templateRelativePath: string): TemplatesConfigPopulator {
+        this.ensureTemplateSectionExists(templateRelativePath)
+
+        this.ensureJsonPropertyExists(['templates', templateRelativePath, 'handlers'], {})
+
+        return this
+    }
+
+    private ensureTemplateParamOverrideSectionExists(templateRelativePath: string): TemplatesConfigPopulator {
+        this.ensureTemplateSectionExists(templateRelativePath)
+
+        this.ensureJsonPropertyExists(['templates', templateRelativePath, 'parameterOverrides'], {})
+
+        return this
+    }
+}
+
+/**
+ * Returns a previously-configured event payload if the following are true (`undefined` if false):
+ * * old `.aws/templates.json` file exists and is valid JSON
+ * * handler exists in `.aws/templates.json`
+ * @param workspaceFolder Current workspace folder
+ * @param handler Function's handler
+ * @param samTemplate Originating SAM Template
+ */
+export async function getExistingConfiguration(
+    workspaceFolder: vscode.WorkspaceFolder,
+    handler: string,
+    samTemplate: vscode.Uri
+): Promise<
+    | {
+          eventJson?: ReadonlyJsonObject
+          environmentVariables?: { [key: string]: string }
+          dockerNetwork?: string
+          useContainer?: boolean
+      }
+    | undefined
+> {
+    const configPath: string = getTemplatesConfigPath(workspaceFolder.uri.fsPath)
+
+    if (await fsUtils.fileExists(configPath)) {
+        const templateRelativePath = getNormalizedRelativePath(workspaceFolder.uri.fsPath, samTemplate.fsPath)
+        try {
+            const json = JSON.parse(await fsUtils.readFileAsString(configPath)) as TemplatesConfig
+            const handlerData = json.templates[templateRelativePath]?.handlers?.[handler]
+            if (handlerData) {
+                return {
+                    eventJson: handlerData.event as any,
+                    environmentVariables: handlerData.environmentVariables,
+                    dockerNetwork: handlerData.dockerNetwork,
+                    useContainer: handlerData.useContainer,
+                }
+            }
+        } catch (e) {
+            // json was not parseable
+            const err = e as Error
+            getLogger().info(`Error parsing the legacy configuration file: ${err.message}`)
+
+            return undefined
+        }
+    }
+
+    return undefined
+}
diff --git a/packages/core/src/lambda/constants.ts b/packages/core/src/lambda/constants.ts
new file mode 100644
index 00000000000..95cca715d20
--- /dev/null
+++ b/packages/core/src/lambda/constants.ts
@@ -0,0 +1,9 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import { hostedFilesBaseUrl } from '../shared/constants'
+
+export const sampleRequestBase: string = 'LambdaSampleFunctions/SampleRequests'
+export const sampleRequestPath: string = `${hostedFilesBaseUrl}${sampleRequestBase}/`
+export const sampleRequestManifestPath: string = `${hostedFilesBaseUrl}${sampleRequestBase}/manifest.xml`
diff --git a/src/lambda/explorer/cloudFormationNodes.ts b/packages/core/src/lambda/explorer/cloudFormationNodes.ts
similarity index 76%
rename from src/lambda/explorer/cloudFormationNodes.ts
rename to packages/core/src/lambda/explorer/cloudFormationNodes.ts
index dbf05a026ae..4ef298a391e 100644
--- a/src/lambda/explorer/cloudFormationNodes.ts
+++ b/packages/core/src/lambda/explorer/cloudFormationNodes.ts
@@ -1,15 +1,15 @@
 /*!
- * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import * as nls from 'vscode-nls'
 const localize = nls.loadMessageBundle()
 
-import { CloudFormation, Lambda } from 'aws-sdk'
+import { FunctionConfiguration } from '@aws-sdk/client-lambda'
 import * as os from 'os'
 import * as vscode from 'vscode'
-import { DefaultCloudFormationClient } from '../../shared/clients/cloudFormationClient'
+import { CloudFormationClient, StackSummary } from '../../shared/clients/cloudFormation'
 import { DefaultLambdaClient } from '../../shared/clients/lambdaClient'
 
 import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode'
@@ -28,7 +28,7 @@ export class CloudFormationNode extends AWSTreeNodeBase {
 
     public constructor(
         public override readonly regionCode: string,
-        private readonly client = new DefaultCloudFormationClient(regionCode)
+        private readonly client = new CloudFormationClient(regionCode)
     ) {
         super('CloudFormation', vscode.TreeItemCollapsibleState.Collapsed)
         this.stackNodes = new Map()
@@ -49,13 +49,13 @@ export class CloudFormationNode extends AWSTreeNodeBase {
     }
 
     public async updateChildren(): Promise {
-        const stacks = await toMapAsync(listCloudFormationStacks(this.client), stack => stack.StackId)
+        const stacks = await toMapAsync(listCloudFormationStacks(this.client), (stack) => stack.StackId)
 
         updateInPlace(
             this.stackNodes,
             stacks.keys(),
-            key => this.stackNodes.get(key)!.update(stacks.get(key)!),
-            key => new CloudFormationStackNode(this, this.regionCode, stacks.get(key)!)
+            (key) => this.stackNodes.get(key)!.update(stacks.get(key)!),
+            (key) => new CloudFormationStackNode(this, this.regionCode, stacks.get(key)!)
         )
     }
 }
@@ -66,9 +66,9 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou
     public constructor(
         public readonly parent: AWSTreeNodeBase,
         public override readonly regionCode: string,
-        private stackSummary: CloudFormation.StackSummary,
+        private stackSummary: StackSummary,
         private readonly lambdaClient = new DefaultLambdaClient(regionCode),
-        private readonly cloudformationClient = new DefaultCloudFormationClient(regionCode)
+        private readonly cloudformationClient = new CloudFormationClient(regionCode)
     ) {
         super('', vscode.TreeItemCollapsibleState.Collapsed)
 
@@ -78,7 +78,7 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou
         this.iconPath = getIcon('aws-cloudformation-stack')
     }
 
-    public get stackId(): CloudFormation.StackId | undefined {
+    public get stackId(): string | undefined {
         return this.stackSummary.StackId
     }
 
@@ -94,7 +94,7 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou
         return this.stackName
     }
 
-    public get stackName(): CloudFormation.StackName {
+    public get stackName(): string {
         return this.stackSummary.StackName
     }
 
@@ -114,7 +114,7 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou
         })
     }
 
-    public update(stackSummary: CloudFormation.StackSummary): void {
+    public update(stackSummary: StackSummary): void {
         this.stackSummary = stackSummary
         this.label = `${this.stackName} [${stackSummary.StackStatus}]`
         this.tooltip = `${this.stackName}${os.EOL}${this.stackId}`
@@ -122,16 +122,16 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou
 
     private async updateChildren(): Promise {
         const resources: string[] = await this.resolveLambdaResources()
-        const functions: Map = toMap(
+        const functions: Map = toMap(
             await toArrayAsync(listLambdaFunctions(this.lambdaClient)),
-            functionInfo => functionInfo.FunctionName
+            (functionInfo) => functionInfo.FunctionName
         )
 
         updateInPlace(
             this.functionNodes,
             intersection(resources, functions.keys()),
-            key => this.functionNodes.get(key)!.update(functions.get(key)!),
-            key => makeCloudFormationLambdaFunctionNode(this, this.regionCode, functions.get(key)!)
+            (key) => this.functionNodes.get(key)!.update(functions.get(key)!),
+            (key) => makeCloudFormationLambdaFunctionNode(this, this.regionCode, functions.get(key)!)
         )
     }
 
@@ -139,8 +139,8 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou
         const response = await this.cloudformationClient.describeStackResources(this.stackSummary.StackName)
 
         if (response.StackResources) {
-            return response.StackResources.filter(it => it.ResourceType.includes('Lambda::Function')).map(
-                it => it.PhysicalResourceId || 'none'
+            return response.StackResources.filter((it) => it.ResourceType.includes('Lambda::Function')).map(
+                (it) => it.PhysicalResourceId || 'none'
             )
         }
 
@@ -151,10 +151,9 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou
 function makeCloudFormationLambdaFunctionNode(
     parent: AWSTreeNodeBase,
     regionCode: string,
-    configuration: Lambda.FunctionConfiguration
+    configuration: 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
new file mode 100644
index 00000000000..3d2f05a99fa
--- /dev/null
+++ b/packages/core/src/lambda/explorer/lambdaFunctionNode.ts
@@ -0,0 +1,114 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { FunctionConfiguration } from '@aws-sdk/client-lambda'
+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'
+// Without "Convert to SAM application"
+export const contextValueLambdaFunctionDownloadOnly = 'awsRegionFunctionNodeDownloadableOnly'
+
+function isLambdaFunctionDownloadable(contextValue?: string): boolean {
+    return (
+        contextValue === contextValueLambdaFunctionImportable || contextValue === contextValueLambdaFunctionDownloadOnly
+    )
+}
+
+export class LambdaFunctionNode extends AWSTreeNodeBase implements AWSResourceNode {
+    public constructor(
+        public readonly parent: AWSTreeNodeBase,
+        public override readonly regionCode: string,
+        public configuration: FunctionConfiguration,
+        public override readonly contextValue?: string,
+        public localDir?: string,
+        public projectRoot?: vscode.Uri,
+        public logicalId?: string
+    ) {
+        super(
+            `${configuration.FunctionArn}`,
+            isLambdaFunctionDownloadable(contextValue)
+                ? 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: 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 {
+        return this.configuration.FunctionName || ''
+    }
+
+    public get arn(): string {
+        if (this.configuration.FunctionArn === undefined) {
+            throw new Error('FunctionArn expected but not found')
+        }
+
+        return this.configuration.FunctionArn
+    }
+
+    public get name(): string {
+        if (this.configuration.FunctionName === undefined) {
+            throw new Error('FunctionName expected but not found')
+        }
+
+        return this.configuration.FunctionName
+    }
+
+    public override async getChildren(): Promise {
+        if (!isLambdaFunctionDownloadable(this.contextValue)) {
+            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
new file mode 100644
index 00000000000..dd753ce5594
--- /dev/null
+++ b/packages/core/src/lambda/explorer/lambdaNodes.ts
@@ -0,0 +1,88 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as nls from 'vscode-nls'
+const localize = nls.loadMessageBundle()
+
+import { FunctionConfiguration } from '@aws-sdk/client-lambda'
+import * as vscode from 'vscode'
+import { DefaultLambdaClient } from '../../shared/clients/lambdaClient'
+
+import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
+import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode'
+import { makeChildrenNodes } from '../../shared/treeview/utils'
+import { toArrayAsync, toMap, updateInPlace } from '../../shared/utilities/collectionUtils'
+import { listLambdaFunctions, isHotReloadingFunction } from '../utils'
+import {
+    contextValueLambdaFunction,
+    contextValueLambdaFunctionImportable,
+    contextValueLambdaFunctionDownloadOnly,
+    LambdaFunctionNode,
+} from './lambdaFunctionNode'
+import { samLambdaImportableRuntimes } from '../models/samLambdaRuntime'
+import { isLocalStackConnection } from '../../auth/utils'
+
+/**
+ * An AWS Explorer node representing the Lambda Service.
+ * Contains Lambda Functions for a specific region as child nodes.
+ */
+export class LambdaNode extends AWSTreeNodeBase {
+    private readonly functionNodes: Map
+
+    public constructor(
+        public override readonly regionCode: string,
+        private readonly client = new DefaultLambdaClient(regionCode)
+    ) {
+        super('Lambda', vscode.TreeItemCollapsibleState.Collapsed)
+        this.functionNodes = new Map()
+        this.contextValue = 'awsLambdaNode'
+    }
+
+    public override async getChildren(): Promise {
+        return await makeChildrenNodes({
+            getChildNodes: async () => {
+                await this.updateChildren()
+
+                return [...this.functionNodes.values()]
+            },
+            getNoChildrenPlaceholderNode: async () =>
+                new PlaceholderNode(this, localize('AWS.explorerNode.lambda.noFunctions', '[No Functions found]')),
+            sort: (nodeA, nodeB) => nodeA.functionName.localeCompare(nodeB.functionName),
+        })
+    }
+
+    public async updateChildren(): Promise {
+        const functions: Map = toMap(
+            await toArrayAsync(listLambdaFunctions(this.client)),
+            (configuration) => configuration.FunctionName
+        )
+
+        updateInPlace(
+            this.functionNodes,
+            functions.keys(),
+            (key) => this.functionNodes.get(key)!.update(functions.get(key)!),
+            (key) => makeLambdaFunctionNode(this, this.regionCode, functions.get(key)!)
+        )
+    }
+}
+
+function makeLambdaFunctionNode(
+    parent: AWSTreeNodeBase,
+    regionCode: string,
+    configuration: FunctionConfiguration
+): LambdaFunctionNode {
+    let contextValue = contextValueLambdaFunction
+    const isImportableRuntime = configuration.Runtime && samLambdaImportableRuntimes.contains(configuration.Runtime)
+    if (isLocalStackConnection()) {
+        if (isImportableRuntime && !isHotReloadingFunction(configuration?.CodeSha256)) {
+            contextValue = contextValueLambdaFunctionDownloadOnly
+        }
+    } else if (isImportableRuntime) {
+        contextValue = contextValueLambdaFunctionImportable
+    }
+    const node = new LambdaFunctionNode(parent, regionCode, configuration, contextValue)
+
+    return node
+}
diff --git a/src/lambda/local/debugConfiguration.ts b/packages/core/src/lambda/local/debugConfiguration.ts
similarity index 80%
rename from src/lambda/local/debugConfiguration.ts
rename to packages/core/src/lambda/local/debugConfiguration.ts
index 60db6e1ee95..bfccf03b070 100644
--- a/src/lambda/local/debugConfiguration.ts
+++ b/packages/core/src/lambda/local/debugConfiguration.ts
@@ -1,11 +1,11 @@
 /*!
- * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import * as path from 'path'
 import * as vscode from 'vscode'
-import { CloudFormation } from '../../shared/cloudformation/cloudformation'
+import * as CloudFormation from '../../shared/cloudformation/cloudformation'
 import {
     AwsSamDebuggerConfiguration,
     awsSamDebugTargetTypes,
@@ -18,14 +18,14 @@ import { tryGetAbsolutePath } from '../../shared/utilities/workspaceUtils'
 import { Architecture, RuntimeFamily } from '../models/samLambdaRuntime'
 import { SamLaunchRequestArgs } from '../../shared/sam/debugger/awsSamDebugger'
 
-import { getLogger } from '../../shared/logger'
+import { getLogger } from '../../shared/logger/logger'
 import globals from '../../shared/extensionGlobals'
 
 /**
  * Magic path on the Docker image.
  * https://github.com/aws/aws-sam-cli/blob/2201b17bff0a438b934abbb53f6c76eff9ccfa6d/samcli/local/docker/lambda_container.py#L25
  */
-export const dotnetCoreDebuggerPath = '/tmp/lambci_debug_files/vsdbg'
+export const dotnetDebuggerPath = '/tmp/lambci_debug_files/vsdbg'
 export const goDebuggerPath = '/tmp/lambci_debug_files'
 
 export interface NodejsDebugConfiguration extends SamLaunchRequestArgs {
@@ -54,22 +54,8 @@ export interface PythonDebugConfiguration extends SamLaunchRequestArgs {
     readonly pathMappings: PythonPathMapping[]
 }
 
-/** Alternative (Cloud9) Python debugger: ikp3db */
-export interface PythonCloud9DebugConfiguration extends SamLaunchRequestArgs {
-    readonly runtimeFamily: RuntimeFamily.Python
-    /** Passed to "sam build --manifest …" */
-    readonly manifestPath: string | undefined
-
-    // Fields expected by the Cloud9 debug adapter.
-    // (Cloud9 sourcefile: debugger-vscode-mainthread-adapter.ts)
-    readonly port: number
-    readonly address: string
-    readonly localRoot: string
-    readonly remoteRoot: string
-}
-
-export interface DotNetCoreDebugConfiguration extends SamLaunchRequestArgs {
-    readonly runtimeFamily: RuntimeFamily.DotNetCore
+export interface DotNetDebugConfiguration extends SamLaunchRequestArgs {
+    readonly runtimeFamily: RuntimeFamily.DotNet
     processName: string
     pipeTransport: PipeTransport
     windows: {
@@ -90,7 +76,7 @@ export interface GoDebugConfiguration extends SamLaunchRequestArgs {
 export interface PipeTransport {
     pipeProgram: 'sh' | 'powershell'
     pipeArgs: string[]
-    debuggerPath: typeof dotnetCoreDebuggerPath
+    debuggerPath: typeof dotnetDebuggerPath
     pipeCwd: string
 }
 
@@ -100,10 +86,10 @@ export interface PipeTransport {
  * - For "code" configs this is the `projectRoot` field.
  * - For "template" configs this is the `CodeUri` field in the template.
  */
-export function getCodeRoot(
+export async function getCodeRoot(
     folder: vscode.WorkspaceFolder | undefined,
     config: AwsSamDebuggerConfiguration
-): string | undefined {
+): Promise {
     switch (config.invokeTarget.target) {
         case 'code': {
             const codeInvoke = config.invokeTarget as CodeTargetProperties
@@ -112,11 +98,11 @@ export function getCodeRoot(
         case 'api':
         case 'template': {
             const templateInvoke = config.invokeTarget as TemplateTargetProperties
-            const template = getTemplate(folder, config)
+            const template = await getTemplate(folder, config)
             if (!template) {
                 return undefined
             }
-            const templateResource = getTemplateResource(folder, config)
+            const templateResource = await getTemplateResource(folder, config)
             if (!templateResource?.Properties) {
                 return undefined
             }
@@ -142,10 +128,10 @@ export function getCodeRoot(
 /**
  * Gets the lambda handler name specified in the given config.
  */
-export function getHandlerName(
+export async function getHandlerName(
     folder: vscode.WorkspaceFolder | undefined,
     config: AwsSamDebuggerConfiguration
-): string {
+): Promise {
     switch (config.invokeTarget.target) {
         case 'code': {
             const codeInvoke = config.invokeTarget as CodeTargetProperties
@@ -153,11 +139,11 @@ export function getHandlerName(
         }
         case 'api':
         case 'template': {
-            const template = getTemplate(folder, config)
+            const template = await getTemplate(folder, config)
             if (!template) {
                 return ''
             }
-            const templateResource = getTemplateResource(folder, config)
+            const templateResource = await getTemplateResource(folder, config)
             if (CloudFormation.isImageLambdaResource(templateResource?.Properties)) {
                 return config.invokeTarget.logicalId
             }
@@ -171,7 +157,7 @@ export function getHandlerName(
         }
         default: {
             // Should never happen.
-            vscode.window.showErrorMessage(
+            void vscode.window.showErrorMessage(
                 localize(
                     'AWS.sam.debugger.invalidTarget',
                     'Debug Configuration has an unsupported target type. Supported types: {0}',
@@ -184,16 +170,16 @@ export function getHandlerName(
 }
 
 /** Gets a template object from the given config. */
-export function getTemplate(
+export async function getTemplate(
     folder: vscode.WorkspaceFolder | undefined,
     config: AwsSamDebuggerConfiguration
-): CloudFormation.Template | undefined {
+): Promise {
     if (!['api', 'template'].includes(config.invokeTarget.target)) {
         return undefined
     }
     const templateInvoke = config.invokeTarget as TemplateTargetProperties
     const fullPath = tryGetAbsolutePath(folder, templateInvoke.templatePath)
-    const cfnTemplate = globals.templateRegistry.getRegisteredItem(fullPath)?.item
+    const cfnTemplate = (await globals.templateRegistry).getItem(fullPath)?.item
     return cfnTemplate
 }
 
@@ -201,15 +187,15 @@ export function getTemplate(
  * Gets the template resources object specified by the `logicalId`
  * field (if the config has `target=template` or `target=api`).
  */
-export function getTemplateResource(
+export async function getTemplateResource(
     folder: vscode.WorkspaceFolder | undefined,
     config: AwsSamDebuggerConfiguration
-): CloudFormation.Resource | undefined {
+): Promise {
     if (!['api', 'template'].includes(config.invokeTarget.target)) {
         return undefined
     }
     const templateInvoke = config.invokeTarget as TemplateTargetProperties
-    const cfnTemplate = getTemplate(folder, config)
+    const cfnTemplate = await getTemplate(folder, config)
     if (!cfnTemplate) {
         throw Error(`template not found (not registered?): ${templateInvoke.templatePath}`)
     }
@@ -231,8 +217,8 @@ export function getTemplateResource(
  *
  * Intended for use only by the language-specific `makeConfig` functions.
  */
-export function isImageLambdaConfig(config: SamLaunchRequestArgs): boolean {
-    const templateResource = getTemplateResource(config.workspaceFolder, config)
+export async function isImageLambdaConfig(config: SamLaunchRequestArgs): Promise {
+    const templateResource = await getTemplateResource(config.workspaceFolder, config)
 
     return CloudFormation.isImageLambdaResource(templateResource?.Properties)
 }
@@ -256,8 +242,8 @@ export function getArchitecture(
         const isArch = isArchitecture(arch)
 
         if (!isArch) {
-            getLogger('channel').warn('SAM Invoke: Invalid architecture. Defaulting to x86_64.')
-            vscode.window.showWarningMessage(
+            getLogger().warn('SAM Invoke: Invalid architecture. Defaulting to x86_64.')
+            void vscode.window.showWarningMessage(
                 localize(
                     'AWS.output.sam.invalidArchitecture',
                     'Invalid architecture specified in {0}. Defaulting to x86_64 architecture for invocation.',
diff --git a/src/lambda/models/samLambdaRuntime.ts b/packages/core/src/lambda/models/samLambdaRuntime.ts
similarity index 77%
rename from src/lambda/models/samLambdaRuntime.ts
rename to packages/core/src/lambda/models/samLambdaRuntime.ts
index 2a66ee2b4b2..985e947afc9 100644
--- a/src/lambda/models/samLambdaRuntime.ts
+++ b/packages/core/src/lambda/models/samLambdaRuntime.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -7,20 +7,22 @@ import * as nls from 'vscode-nls'
 const localize = nls.loadMessageBundle()
 
 import * as vscode from 'vscode'
-import { Runtime } from 'aws-sdk/clients/lambda'
+import { Runtime } from '@aws-sdk/client-lambda'
 import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable'
 import { isCloud9 } from '../../shared/extensionUtilities'
 import { PrompterButtons } from '../../shared/ui/buttons'
 import { createQuickPick, DataQuickPickItem, QuickPickPrompter } from '../../shared/ui/pickerPrompter'
 import { supportedLambdaRuntimesUrl } from '../../shared/constants'
+import { openUrl } from '../../shared/utilities/vsCodeUtils'
 
 export enum RuntimeFamily {
     Unknown,
     Python,
     NodeJS,
-    DotNetCore,
+    DotNet,
     Go,
     Java,
+    Ruby,
 }
 
 export type RuntimePackageType = 'Image' | 'Zip'
@@ -28,15 +30,48 @@ export type RuntimePackageType = 'Image' | 'Zip'
 // TODO: Consolidate all of the runtime constructs into a single > map
 //       We should be able to eliminate a fair amount of redundancy with that.
 export const nodeJsRuntimes: ImmutableSet = ImmutableSet([
+    'nodejs22.x' as Runtime,
+    'nodejs20.x',
     'nodejs18.x',
     'nodejs16.x',
     'nodejs14.x',
-    'nodejs12.x',
 ])
-export const pythonRuntimes: ImmutableSet = ImmutableSet(['python3.9', 'python3.8', 'python3.7'])
+export function getNodeMajorVersion(version?: string): number | undefined {
+    if (!version) {
+        return undefined
+    }
+
+    const match = version.match(/^nodejs(\d+)\./)
+
+    if (match) {
+        return Number(match[1])
+    } else {
+        return undefined
+    }
+}
+
+export const pythonRuntimes: ImmutableSet = ImmutableSet([
+    'python3.13' as Runtime,
+    'python3.12',
+    'python3.11',
+    'python3.10',
+    'python3.9',
+    'python3.8',
+    'python3.7',
+])
 export const goRuntimes: ImmutableSet = ImmutableSet(['go1.x'])
-export const javaRuntimes: ImmutableSet = ImmutableSet(['java11', 'java8', 'java8.al2'])
-export const dotNetRuntimes: ImmutableSet = ImmutableSet(['dotnetcore3.1', 'dotnet6'])
+export const javaRuntimes: ImmutableSet = ImmutableSet([
+    'java17',
+    'java11',
+    'java8',
+    'java8.al2',
+    'java21',
+])
+export const dotNetRuntimes: ImmutableSet = ImmutableSet(['dotnet6', 'dotnet8'])
+export const rubyRuntimes: ImmutableSet = ImmutableSet(['ruby3.2', 'ruby3.3', 'ruby3.4' as Runtime])
+
+// Image runtimes are not a direct subset of valid ZIP lambda types
+const dotnet50 = 'dotnet5.0' as Runtime
 
 /**
  * Deprecated runtimes can be found at https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html
@@ -54,13 +89,27 @@ export const deprecatedRuntimes: ImmutableSet = ImmutableSet([
     'nodejs6.10',
     'nodejs8.10',
     'nodejs10.x',
+    'nodejs12.x',
+    'ruby2.5',
+    'ruby2.7',
 ])
 const defaultRuntimes = ImmutableMap([
-    [RuntimeFamily.NodeJS, 'nodejs14.x'],
-    [RuntimeFamily.Python, 'python3.9'],
-    [RuntimeFamily.DotNetCore, 'dotnetcore3.1'],
+    [RuntimeFamily.NodeJS, 'nodejs22.x' as Runtime],
+    [RuntimeFamily.Python, 'python3.13' as Runtime],
+    [RuntimeFamily.DotNet, 'dotnet8'],
     [RuntimeFamily.Go, 'go1.x'],
-    [RuntimeFamily.Java, 'java11'],
+    [RuntimeFamily.Java, 'java21'],
+    [RuntimeFamily.Ruby, 'ruby3.3'],
+])
+
+export const mapFamilyToDebugType = ImmutableMap([
+    [RuntimeFamily.NodeJS, 'node'],
+    [RuntimeFamily.Python, 'python'],
+    [RuntimeFamily.DotNet, 'csharp'],
+    [RuntimeFamily.Go, 'go'],
+    [RuntimeFamily.Java, 'java'],
+    [RuntimeFamily.Ruby, 'ruby'],
+    [RuntimeFamily.Unknown, 'unknown'],
 ])
 
 export const samZipLambdaRuntimes: ImmutableSet = ImmutableSet.union([
@@ -74,10 +123,12 @@ export const samZipLambdaRuntimes: ImmutableSet = ImmutableSet.union([
 export const samArmLambdaRuntimes: ImmutableSet = ImmutableSet([
     'python3.9',
     'python3.8',
+    'nodejs22.x' as Runtime,
+    'nodejs20.x',
     'nodejs18.x',
     'nodejs16.x',
     'nodejs14.x',
-    'nodejs12.x',
+    'java17',
     'java11',
     'java8.al2',
 ])
@@ -87,14 +138,16 @@ 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
 }
 
-// Image runtimes are not a direct subset of valid ZIP lambda types
-const dotnet50 = 'dotnet5.0'
 export function samImageLambdaRuntimes(cloud9: boolean = isCloud9()): ImmutableSet {
     // Note: SAM also supports ruby, but Toolkit does not.
     return ImmutableSet([...samLambdaCreatableRuntimes(cloud9), ...(cloud9 ? [] : [dotnet50])])
@@ -120,7 +173,7 @@ export function getDependencyManager(runtime: Runtime): DependencyManager[] {
     throw new Error(`Runtime ${runtime} does not have an associated DependencyManager`)
 }
 
-export function getFamily(runtime: string): RuntimeFamily {
+export function getFamily(runtime: Runtime): RuntimeFamily {
     if (deprecatedRuntimes.has(runtime)) {
         handleDeprecatedRuntime(runtime)
     } else if (nodeJsRuntimes.has(runtime)) {
@@ -128,18 +181,20 @@ export function getFamily(runtime: string): RuntimeFamily {
     } else if (pythonRuntimes.has(runtime)) {
         return RuntimeFamily.Python
     } else if (dotNetRuntimes.has(runtime) || runtime === dotnet50) {
-        return RuntimeFamily.DotNetCore
+        return RuntimeFamily.DotNet
     } else if (goRuntimes.has(runtime)) {
         return RuntimeFamily.Go
     } else if (javaRuntimes.has(runtime)) {
         return RuntimeFamily.Java
+    } else if (rubyRuntimes.has(runtime)) {
+        return RuntimeFamily.Ruby
     }
     return RuntimeFamily.Unknown
 }
 
 function handleDeprecatedRuntime(runtime: Runtime) {
     const moreInfo = localize('AWS.generic.message.learnMore', 'Learn More')
-    vscode.window
+    void vscode.window
         .showErrorMessage(
             localize(
                 'AWS.samcli.deprecatedRuntime',
@@ -148,9 +203,9 @@ function handleDeprecatedRuntime(runtime: Runtime) {
             ),
             moreInfo
         )
-        .then(button => {
+        .then((button) => {
             if (button === moreInfo) {
-                vscode.env.openExternal(vscode.Uri.parse(supportedLambdaRuntimesUrl))
+                void openUrl(vscode.Uri.parse(supportedLambdaRuntimesUrl))
             }
         })
     throw new Error(`Runtime ${runtime} is deprecated, see: ${supportedLambdaRuntimesUrl}`)
@@ -177,11 +232,15 @@ export function getRuntimeFamily(langId: string): RuntimeFamily {
         case 'javascript':
             return RuntimeFamily.NodeJS
         case 'csharp':
-            return RuntimeFamily.DotNetCore
+            return RuntimeFamily.DotNet
         case 'python':
             return RuntimeFamily.Python
         case 'go':
             return RuntimeFamily.Go
+        case 'java':
+            return RuntimeFamily.Java
+        case 'ruby':
+            return RuntimeFamily.Ruby
         default:
             return RuntimeFamily.Unknown
     }
@@ -190,7 +249,7 @@ export function getRuntimeFamily(langId: string): RuntimeFamily {
 /**
  * Provides the default runtime for a given `RuntimeFamily` or undefined if the runtime is invalid.
  */
-export function getDefaultRuntime(runtime: RuntimeFamily): string | undefined {
+export function getDefaultRuntime(runtime: RuntimeFamily): Runtime | undefined {
     return defaultRuntimes.get(runtime)
 }
 
@@ -204,7 +263,7 @@ function getRuntimesForFamily(family: RuntimeFamily): ImmutableSet | un
             return nodeJsRuntimes
         case RuntimeFamily.Python:
             return pythonRuntimes
-        case RuntimeFamily.DotNetCore:
+        case RuntimeFamily.DotNet:
             return dotNetRuntimes
         case RuntimeFamily.Go:
             return goRuntimes
@@ -234,14 +293,14 @@ export function createRuntimeQuickPick(params: {
     totalSteps?: number
 }): QuickPickPrompter {
     const zipRuntimes = params.runtimeFamily
-        ? getRuntimesForFamily(params.runtimeFamily) ?? samLambdaCreatableRuntimes()
+        ? (getRuntimesForFamily(params.runtimeFamily) ?? samLambdaCreatableRuntimes())
         : samLambdaCreatableRuntimes()
 
     const zipRuntimeItems = zipRuntimes
         // remove uncreatable runtimes
-        .filter(value => samLambdaCreatableRuntimes().has(value))
+        .filter((value) => samLambdaCreatableRuntimes().has(value))
         .toArray()
-        .map(runtime => ({
+        .map((runtime) => ({
             data: { runtime, packageType: 'Zip' } as RuntimeAndPackage,
             label: runtime,
         }))
@@ -251,7 +310,7 @@ export function createRuntimeQuickPick(params: {
     let imageRuntimeItems: DataQuickPickItem[] = []
     if (params.showImageRuntimes) {
         imageRuntimeItems = samImageLambdaRuntimes()
-            .map(runtime => ({
+            .map((runtime) => ({
                 data: { runtime, packageType: 'Image' } as RuntimeAndPackage,
                 label: `${runtime} (Image)`,
             }))
diff --git a/packages/core/src/lambda/models/samTemplates.ts b/packages/core/src/lambda/models/samTemplates.ts
new file mode 100644
index 00000000000..ace9c93faf6
--- /dev/null
+++ b/packages/core/src/lambda/models/samTemplates.ts
@@ -0,0 +1,124 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import * as nls from 'vscode-nls'
+const localize = nls.loadMessageBundle()
+
+import * as semver from 'semver'
+import { Runtime } from '@aws-sdk/client-lambda'
+import { Set as ImmutableSet } from 'immutable'
+import { supportsEventBridgeTemplates } from '../../../src/eventSchemas/models/schemaCodeLangs'
+import { RuntimePackageType } from './samLambdaRuntime'
+import { getIdeProperties } from '../../shared/extensionUtilities'
+
+export const helloWorldTemplate = localize(
+    'AWS.samcli.initWizard.template.helloWorld.name',
+    '{0} SAM Hello World',
+    getIdeProperties().company
+)
+export const eventBridgeHelloWorldTemplate = localize(
+    'AWS.samcli.initWizard.template.helloWorld.name',
+    '{0} SAM EventBridge Hello World',
+    getIdeProperties().company
+)
+export const eventBridgeStarterAppTemplate = localize(
+    'AWS.samcli.initWizard.template.helloWorld.name',
+    '{0} SAM EventBridge App from Scratch',
+    getIdeProperties().company
+)
+export const stepFunctionsSampleApp = localize(
+    'AWS.samcli.initWizard.template.helloWorld.name',
+    '{0} Step Functions Sample App',
+    getIdeProperties().company
+)
+export const typeScriptBackendTemplate = 'App Backend using TypeScript'
+export const repromptUserForTemplate = 'REQUIRES_AWS_CREDENTIALS_REPROMPT_USER_FOR_TEMPLATE'
+
+export const cliVersionStepFunctionsTemplate = '0.52.0'
+
+export type SamTemplate = string
+
+export function getSamTemplateWizardOption(
+    runtime: Runtime,
+    packageType: RuntimePackageType,
+    samCliVersion: string
+): ImmutableSet {
+    const templateOptions = Array(helloWorldTemplate)
+
+    if (packageType === 'Image') {
+        // only supports hello world for now
+        return ImmutableSet(templateOptions)
+    }
+
+    if (supportsEventBridgeTemplates(runtime)) {
+        templateOptions.push(eventBridgeHelloWorldTemplate, eventBridgeStarterAppTemplate)
+    }
+
+    if (supportsStepFuntionsTemplate(samCliVersion)) {
+        templateOptions.push(stepFunctionsSampleApp)
+    }
+
+    if (supportsTypeScriptBackendTemplate(runtime)) {
+        templateOptions.push(typeScriptBackendTemplate)
+    }
+
+    return ImmutableSet(templateOptions)
+}
+
+export function getSamCliTemplateParameter(templateSelected: SamTemplate): string {
+    switch (templateSelected) {
+        case helloWorldTemplate:
+            return 'hello-world'
+        case eventBridgeHelloWorldTemplate:
+            return 'eventBridge-hello-world'
+        case eventBridgeStarterAppTemplate:
+            return 'eventBridge-schema-app'
+        case stepFunctionsSampleApp:
+            return 'step-functions-sample-app'
+        case typeScriptBackendTemplate:
+            return 'quick-start-typescript-app'
+        default:
+            throw new Error(`${templateSelected} is not valid sam template`)
+    }
+}
+
+export function getTemplateDescription(template: SamTemplate): string {
+    switch (template) {
+        case helloWorldTemplate:
+            return localize('AWS.samcli.initWizard.template.helloWorld.description', 'A basic SAM app')
+        case eventBridgeHelloWorldTemplate:
+            return localize(
+                'AWS.samcli.initWizard.template.eventBridge_helloWorld.description',
+                'Invokes a Lambda for every EC2 instance state change in your account'
+            )
+        case eventBridgeStarterAppTemplate:
+            return localize(
+                'AWS.samcli.initWizard.template.eventBridge_starterApp.description',
+                'Invokes a Lambda based on a dynamic event trigger for an EventBridge Schema of your choice'
+            )
+        case stepFunctionsSampleApp:
+            return localize(
+                'AWS.samcli.initWizard.template.stepFunctionsSampleApp.description',
+                'Orchestrates multiple Lambdas to execute a stock trading workflow on an hourly schedule'
+            )
+        case typeScriptBackendTemplate:
+            return localize(
+                'AWS.samcli.initWizard.template.typeScriptBackendTemplate.description',
+                'A sample TypeScript backend app with Lambda and DynamoDB'
+            )
+        default:
+            throw new Error(`No description found for template ${template}`)
+    }
+}
+
+export function supportsStepFuntionsTemplate(samCliVersion: string): boolean {
+    if (!samCliVersion) {
+        return false
+    }
+    return semver.gte(samCliVersion, cliVersionStepFunctionsTemplate)
+}
+
+export function supportsTypeScriptBackendTemplate(runtime: Runtime): boolean {
+    return runtime === 'nodejs16.x'
+}
diff --git a/packages/core/src/lambda/models/sampleRequest.ts b/packages/core/src/lambda/models/sampleRequest.ts
new file mode 100644
index 00000000000..7e90cf631e5
--- /dev/null
+++ b/packages/core/src/lambda/models/sampleRequest.ts
@@ -0,0 +1,9 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export class SampleRequest {
+    public name: string | undefined
+    public filename: string | undefined
+}
diff --git a/packages/core/src/lambda/remoteDebugging/lambdaDebugger.ts b/packages/core/src/lambda/remoteDebugging/lambdaDebugger.ts
new file mode 100644
index 00000000000..152d5913737
--- /dev/null
+++ b/packages/core/src/lambda/remoteDebugging/lambdaDebugger.ts
@@ -0,0 +1,75 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import globals from '../../shared/extensionGlobals'
+import { FunctionConfiguration } from '@aws-sdk/client-lambda'
+import { getLogger } from '../../shared/logger/logger'
+
+const logger = getLogger()
+
+export const remoteDebugSnapshotString = 'aws.lambda.remoteDebugSnapshot'
+
+export interface DebugConfig {
+    functionArn: string
+    functionName: string
+    port: number | undefined
+    localRoot: string
+    remoteRoot: string
+    skipFiles: string[]
+    shouldPublishVersion: boolean
+    lambdaRuntime?: string // Lambda runtime (e.g., nodejs18.x)
+    debuggerRuntime?: string // VS Code debugger runtime (e.g., node)
+    outFiles?: string[]
+    sourceMap?: boolean
+    justMyCode?: boolean
+    projectName?: string
+    otherDebugParams?: string
+    lambdaTimeout?: number
+    layerArn?: string
+    handlerFile?: string
+    samFunctionLogicalId?: string // SAM function logical ID for auto-detecting outFiles
+    samProjectRoot?: vscode.Uri // SAM project root for auto-detecting outFiles
+    isLambdaRemote: boolean // false if LocalStack connection
+}
+
+/**
+ * Interface for debugging AWS Lambda functions remotely.
+ *
+ * This interface defines the contract for implementing remote debugging
+ * for Lambda functions.
+ *
+ * Implementations of this interface handle the lifecycle of remote debugging sessions,
+ * including checking health, set up, necessary deployment, and later clean up
+ */
+export interface LambdaDebugger {
+    checkHealth(): Promise
+    setup(
+        progress: vscode.Progress<{ message?: string; increment?: number }>,
+        functionConfig: FunctionConfiguration,
+        region: string
+    ): Promise
+    waitForSetup(
+        progress: vscode.Progress<{ message?: string; increment?: number }>,
+        functionConfig: FunctionConfiguration,
+        region: string
+    ): Promise
+    waitForFunctionUpdates(progress: vscode.Progress<{ message?: string; increment?: number }>): Promise
+    cleanup(functionConfig: FunctionConfiguration): Promise
+}
+
+// this should be called when the debug session is started
+export async function persistLambdaSnapshot(config: FunctionConfiguration | undefined): Promise {
+    try {
+        await globals.globalState.update(remoteDebugSnapshotString, config)
+    } catch (error) {
+        // TODO raise toolkit error
+        logger.error(`Error persisting debug sessions: ${error}`)
+    }
+}
+
+export function getLambdaSnapshot(): FunctionConfiguration | undefined {
+    return globals.globalState.get(remoteDebugSnapshotString)
+}
diff --git a/packages/core/src/lambda/remoteDebugging/ldkClient.ts b/packages/core/src/lambda/remoteDebugging/ldkClient.ts
new file mode 100644
index 00000000000..020d13d5ef2
--- /dev/null
+++ b/packages/core/src/lambda/remoteDebugging/ldkClient.ts
@@ -0,0 +1,481 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { FunctionConfiguration } from '@aws-sdk/client-lambda'
+import {
+    CloseTunnelCommand,
+    IoTSecureTunnelingClient,
+    ListTunnelsCommand,
+    OpenTunnelCommand,
+    RotateTunnelAccessTokenCommand,
+} from '@aws-sdk/client-iotsecuretunneling'
+import { getClientId } from '../../shared/telemetry/util'
+import { DefaultLambdaClient } from '../../shared/clients/lambdaClient'
+import { LocalProxy } from './localProxy'
+import globals from '../../shared/extensionGlobals'
+import { getLogger } from '../../shared/logger/logger'
+import { getIoTSTClientWithAgent, getLambdaClientWithAgent, getLambdaDebugUserAgent } from './utils'
+import { ToolkitError } from '../../shared/errors'
+import * as nls from 'vscode-nls'
+
+const localize = nls.loadMessageBundle()
+
+export function isTunnelInfo(data: TunnelInfo): data is TunnelInfo {
+    return (
+        typeof data === 'object' &&
+        data !== null &&
+        typeof data.tunnelID === 'string' &&
+        typeof data.sourceToken === 'string' &&
+        typeof data.destinationToken === 'string'
+    )
+}
+
+export interface TunnelInfo {
+    tunnelID: string
+    sourceToken: string
+    destinationToken: string
+}
+
+async function callUpdateFunctionConfiguration(
+    lambda: DefaultLambdaClient,
+    config: FunctionConfiguration,
+    waitForUpdate: boolean
+): Promise {
+    // Update function configuration back to original values
+    return await lambda.updateFunctionConfiguration(
+        {
+            FunctionName: config.FunctionName!,
+            Timeout: config.Timeout,
+            Layers: config.Layers?.map((layer) => layer.Arn!).filter(Boolean) || [],
+            Environment: {
+                Variables: config.Environment?.Variables ?? {},
+            },
+        },
+        {
+            maxRetries: 5,
+            initialDelayMs: 2000,
+            backoffMultiplier: 2,
+            waitForUpdate: waitForUpdate,
+        }
+    )
+}
+
+export class LdkClient {
+    static #instance: LdkClient
+    private localProxy: LocalProxy | undefined
+    private static instanceCreating = false
+    private lambdaClientCache: Map = new Map()
+    private iotSTClientCache: Map = new Map()
+
+    constructor() {}
+
+    public static get instance() {
+        if (this.#instance !== undefined) {
+            return this.#instance
+        }
+        if (this.instanceCreating) {
+            getLogger().warn(
+                localize(
+                    'AWS.lambda.ldkClient.multipleInstancesError',
+                    'Attempt to create multiple LdkClient instances simultaneously'
+                )
+            )
+        }
+        // Set flag to prevent recursive instance creation
+        this.instanceCreating = true
+        try {
+            const self = (this.#instance = new this())
+            return self
+        } finally {
+            this.instanceCreating = false
+        }
+    }
+
+    /**
+     * Get or create a cached Lambda client for the specified region
+     */
+    private getLambdaClient(region: string): DefaultLambdaClient {
+        if (!this.lambdaClientCache.has(region)) {
+            this.lambdaClientCache.set(region, getLambdaClientWithAgent(region, getLambdaDebugUserAgent()))
+        }
+        return this.lambdaClientCache.get(region)!
+    }
+
+    private getIoTSTClient(region: string): IoTSecureTunnelingClient {
+        if (!this.iotSTClientCache.has(region)) {
+            this.iotSTClientCache.set(region, getIoTSTClientWithAgent(region))
+        }
+        return this.iotSTClientCache.get(region)!
+    }
+    /**
+     * Clean up all resources held by this client
+     * Should be called when the extension is deactivated
+     */
+    public dispose(): void {
+        if (this.localProxy) {
+            this.localProxy.stop()
+            this.localProxy = undefined
+        }
+        // Clear the Lambda client cache
+        this.iotSTClientCache.clear()
+        this.lambdaClientCache.clear()
+    }
+
+    // Create or reuse tunnel
+    async createOrReuseTunnel(region: string): Promise {
+        try {
+            // Get VSCode UUID using getClientId from telemetry.utils.ts
+            const vscodeUuid = getClientId(globals.globalState)
+
+            // Create IoTSecureTunneling client
+            const iotSecureTunneling = this.getIoTSTClient(region)
+
+            // Define tunnel identifier
+            const tunnelIdentifier = `RemoteDebugging+${vscodeUuid}`
+            const timeoutInMinutes = 720
+            // List existing tunnels
+            const listTunnelsResponse = await iotSecureTunneling.send(new ListTunnelsCommand({}))
+
+            // Find tunnel with our identifier
+            const existingTunnel = listTunnelsResponse.tunnelSummaries?.find(
+                (tunnel) => tunnel.description === tunnelIdentifier && tunnel.status?.toLowerCase() === 'open'
+            )
+
+            if (existingTunnel && existingTunnel.tunnelId) {
+                const timeCreated = existingTunnel?.createdAt ? new Date(existingTunnel.createdAt) : new Date()
+                const expiryTime = new Date(timeCreated.getTime() + timeoutInMinutes * 60 * 1000)
+                const currentTime = new Date()
+                const minutesRemaining = (expiryTime.getTime() - currentTime.getTime()) / (60 * 1000)
+
+                if (minutesRemaining >= 15) {
+                    // Rotate access tokens for the existing tunnel
+                    const rotateResponse = await this.refreshTunnelTokens(existingTunnel.tunnelId, region)
+
+                    return rotateResponse
+                } else {
+                    // Close tunnel if less than 15 minutes remaining
+                    await iotSecureTunneling.send(
+                        new CloseTunnelCommand({
+                            tunnelId: existingTunnel.tunnelId,
+                            delete: false,
+                        })
+                    )
+
+                    getLogger().info(`Closed tunnel ${existingTunnel.tunnelId} with less than 15 minutes remaining`)
+                }
+            }
+
+            // Create new tunnel
+            const openTunnelResponse = await iotSecureTunneling.send(
+                new OpenTunnelCommand({
+                    description: tunnelIdentifier,
+                    timeoutConfig: {
+                        maxLifetimeTimeoutMinutes: timeoutInMinutes, // 12 hours
+                    },
+                    destinationConfig: {
+                        services: ['WSS'],
+                    },
+                })
+            )
+
+            getLogger().info(`Created new tunnel with ID: ${openTunnelResponse.tunnelId}`)
+
+            return {
+                tunnelID: openTunnelResponse.tunnelId || '',
+                sourceToken: openTunnelResponse.sourceAccessToken || '',
+                destinationToken: openTunnelResponse.destinationAccessToken || '',
+            }
+        } catch (error) {
+            throw ToolkitError.chain(error, 'Error creating/reusing tunnel')
+        }
+    }
+
+    // Refresh tunnel tokens
+    async refreshTunnelTokens(tunnelId: string, region: string): Promise {
+        try {
+            const iotSecureTunneling = this.getIoTSTClient(region)
+            const rotateResponse = await iotSecureTunneling.send(
+                new RotateTunnelAccessTokenCommand({
+                    tunnelId: tunnelId,
+                    clientMode: 'ALL',
+                })
+            )
+
+            return {
+                tunnelID: tunnelId,
+                sourceToken: rotateResponse.sourceAccessToken || '',
+                destinationToken: rotateResponse.destinationAccessToken || '',
+            }
+        } catch (error) {
+            throw ToolkitError.chain(error, 'Error refreshing tunnel tokens')
+        }
+    }
+
+    async getFunctionDetail(functionArn: string): Promise {
+        try {
+            const region = getRegionFromArn(functionArn)
+            if (!region) {
+                getLogger().error(
+                    localize(
+                        'AWS.lambda.ldkClient.couldNotDetermineRegion',
+                        'Could not determine region from Lambda ARN'
+                    )
+                )
+                return undefined
+            }
+            const client = this.getLambdaClient(region)
+            const configuration = (await client.getFunction(functionArn)).Configuration as FunctionConfiguration
+            // get function detail
+            // return function detail
+            return configuration
+        } catch (error) {
+            getLogger().warn(`Error getting function detail:${error}`)
+            return undefined
+        }
+    }
+
+    // Create debug deployment to given lambda function
+    // save a snapshot of the current config to global : aws.lambda.remoteDebugContext
+    // we are 1: changing function timeout to 15 minute
+    // 2: adding the ldk layer LDK_LAYER_ARN_X86_64 or LDK_LAYER_ARN_ARM64 (ignore if already added, fail if 5 layer already there)
+    // 3: adding two param to lambda environment variable
+    // {AWS_LAMBDA_EXEC_WRAPPER:/opt/bin/ldk_wrapper, AWS_LDK_DESTINATION_TOKEN: destinationToken }
+    async createDebugDeployment(
+        config: FunctionConfiguration,
+        destinationToken: string,
+        lambdaTimeout: number,
+        shouldPublishVersion: boolean,
+        ldkLayerArn: string,
+        progress: vscode.Progress<{ message?: string | undefined; increment?: number | undefined }>
+    ): Promise {
+        try {
+            if (!config.FunctionArn || !config.FunctionName) {
+                throw new Error(localize('AWS.lambda.ldkClient.functionArnMissing', 'Function ARN is missing'))
+            }
+            const region = getRegionFromArn(config.FunctionArn ?? '')
+            if (!region) {
+                throw new Error(
+                    localize(
+                        'AWS.lambda.ldkClient.couldNotDetermineRegion',
+                        'Could not determine region from Lambda ARN'
+                    )
+                )
+            }
+
+            // fix out of bound timeout
+            if (lambdaTimeout && (lambdaTimeout > 900 || lambdaTimeout <= 0)) {
+                lambdaTimeout = 900
+            }
+
+            // Inform user about the changes that will be made
+
+            progress.report({ message: localize('AWS.lambda.ldkClient.applyingChanges', 'Applying changes...') })
+
+            // Determine architecture and select appropriate layer
+
+            const layers = config.Layers || []
+
+            // Check if LDK layer is already added
+            const ldkLayerExists = layers.some(
+                (layer) => layer.Arn?.includes('LDKLayerX86') || layer.Arn?.includes('LDKLayerArm64')
+            )
+
+            // Check if we have room to add a layer (max 5)
+            if (!ldkLayerExists && layers.length >= 5) {
+                throw new Error(
+                    localize(
+                        'AWS.lambda.ldkClient.cannotAddLdkLayer',
+                        'Cannot add LDK layer: Lambda function already has 5 layers'
+                    )
+                )
+            }
+            // Create updated layers list
+            const updatedLayers = ldkLayerExists
+                ? layers.map((layer) => layer.Arn!).filter(Boolean)
+                : [...layers.map((layer) => layer.Arn!).filter(Boolean), ldkLayerArn]
+
+            // Create updated environment variables
+            const currentEnv = config.Environment?.Variables || {}
+            const updatedEnv: { [key: string]: string } = {
+                ...currentEnv,
+                AWS_LAMBDA_EXEC_WRAPPER: '/opt/bin/ldk_wrapper',
+                AWS_LAMBDA_DEBUG_ON_LATEST: shouldPublishVersion ? 'false' : 'true',
+                AWS_LDK_DESTINATION_TOKEN: destinationToken,
+            }
+            if (currentEnv['AWS_LAMBDA_EXEC_WRAPPER']) {
+                updatedEnv.ORIGINAL_AWS_LAMBDA_EXEC_WRAPPER = currentEnv['AWS_LAMBDA_EXEC_WRAPPER']
+            }
+
+            if (getLogger().logLevelEnabled('debug')) {
+                updatedEnv.RUST_LOG = 'debug'
+            }
+
+            // Create Lambda client using AWS SDK
+            const lambda = this.getLambdaClient(region)
+
+            // Update function configuration
+            if (!config.FunctionArn || !config.FunctionName) {
+                throw new Error('Function ARN is missing')
+            }
+
+            // Create a temporary config for the update
+            const updateConfig: FunctionConfiguration = {
+                FunctionName: config.FunctionName,
+                Timeout: lambdaTimeout ?? 900, // 15 minutes
+                Layers: updatedLayers.map((arn) => ({ Arn: arn })),
+                Environment: {
+                    Variables: updatedEnv,
+                },
+            }
+
+            await callUpdateFunctionConfiguration(lambda, updateConfig, true)
+
+            // publish version
+            let version = '$Latest'
+            if (shouldPublishVersion) {
+                // should somehow return version for debugging
+                const versionResp = await lambda.publishVersion(config.FunctionName, { waitForUpdate: true })
+                version = versionResp.Version ?? ''
+                // remove debug deployment in a non-blocking way
+                void Promise.resolve(
+                    callUpdateFunctionConfiguration(lambda, config, false).then(() => {
+                        progress.report({
+                            message: localize(
+                                'AWS.lambda.ldkClient.debugDeploymentCompleted',
+                                'Debug deployment completed successfully'
+                            ),
+                        })
+                    })
+                )
+            }
+            return version
+        } catch (error) {
+            getLogger().error(`Error creating debug deployment: ${error}`)
+            if (error instanceof Error) {
+                throw new ToolkitError(`Failed to create debug deployment: ${error.message}`)
+            }
+            return 'Failed'
+        }
+    }
+
+    // Remove debug deployment from the given lambda function
+    // use the snapshot we took before create debug deployment
+    // we are 1: reverting timeout to it's original snapshot
+    // 2: reverting layer status according to it's original snapshot
+    // 3: reverting environment back to it's original snapshot
+    async removeDebugDeployment(config: FunctionConfiguration, check: boolean = true): Promise {
+        try {
+            if (!config.FunctionArn || !config.FunctionName) {
+                throw new Error('Function ARN is missing')
+            }
+            const region = getRegionFromArn(config.FunctionArn ?? '')
+            if (!region) {
+                throw new Error('Could not determine region from Lambda ARN')
+            }
+
+            if (check) {
+                const currentConfig = await this.getFunctionDetail(config.FunctionArn)
+                if (
+                    currentConfig?.Timeout === config?.Timeout &&
+                    currentConfig?.Layers?.length === config?.Layers?.length
+                ) {
+                    // nothing to remove
+                    return true
+                }
+            }
+
+            // Create Lambda client using AWS SDK
+            const lambda = this.getLambdaClient(region)
+
+            // Update function configuration back to original values
+            await callUpdateFunctionConfiguration(lambda, config, false)
+
+            return true
+        } catch (error) {
+            // no need to raise, even this failed we want the following to execute
+            throw ToolkitError.chain(error, 'Error removing debug deployment')
+        }
+    }
+
+    async deleteDebugVersion(functionArn: string, qualifier: string) {
+        try {
+            const region = getRegionFromArn(functionArn)
+            if (!region) {
+                throw new Error('Could not determine region from Lambda ARN')
+            }
+            const lambda = this.getLambdaClient(region)
+            await lambda.deleteFunction(functionArn, qualifier)
+            return true
+        } catch (error) {
+            getLogger().error('Error deleting debug version: %O', error)
+            return false
+        }
+    }
+
+    // Start proxy with better resource management
+    async startProxy(region: string, sourceToken: string, port: number = 0): Promise {
+        try {
+            getLogger().info(`Starting direct proxy for region:${region}`)
+
+            // Clean up any existing proxy thoroughly
+            if (this.localProxy) {
+                getLogger().info('Stopping existing proxy before starting a new one')
+                this.localProxy.stop()
+                this.localProxy = undefined
+
+                // Small delay to ensure resources are released
+                await new Promise((resolve) => setTimeout(resolve, 100))
+            }
+
+            // Create and start a new local proxy
+            this.localProxy = new LocalProxy()
+
+            // Start the proxy and get the assigned port
+            const localPort = await this.localProxy.start(region, sourceToken, port)
+            getLogger().info(`Local proxy started successfully on port ${localPort}`)
+            return true
+        } catch (error) {
+            getLogger().error(`Failed to start proxy: ${error}`)
+            if (this.localProxy) {
+                this.localProxy.stop()
+                this.localProxy = undefined
+            }
+            throw ToolkitError.chain(error, 'Failed to start proxy')
+        }
+    }
+
+    // Stop proxy with proper cleanup and reference handling
+    async stopProxy(): Promise {
+        try {
+            getLogger().info(`Stopping proxy`)
+
+            if (this.localProxy) {
+                // Ensure proper resource cleanup
+                this.localProxy.stop()
+
+                // Force delete the reference to allow GC
+                this.localProxy = undefined
+
+                getLogger().info('Local proxy stopped successfully')
+            } else {
+                getLogger().info('No active local proxy to stop')
+            }
+
+            return true
+        } catch (error) {
+            throw ToolkitError.chain(error, 'Error stopping proxy')
+        }
+    }
+}
+
+// Helper function to extract region from ARN
+export function getRegionFromArn(arn: string | undefined): string | undefined {
+    if (!arn) {
+        return undefined
+    }
+    const parts = arn.split(':')
+    return parts.length >= 4 ? parts[3] : undefined
+}
diff --git a/packages/core/src/lambda/remoteDebugging/ldkController.ts b/packages/core/src/lambda/remoteDebugging/ldkController.ts
new file mode 100644
index 00000000000..5dfbd653dc1
--- /dev/null
+++ b/packages/core/src/lambda/remoteDebugging/ldkController.ts
@@ -0,0 +1,811 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { getLogger } from '../../shared/logger/logger'
+import globals from '../../shared/extensionGlobals'
+import { FunctionConfiguration, Runtime } from '@aws-sdk/client-lambda'
+import { getRegionFromArn, LdkClient } from './ldkClient'
+import { getFamily, mapFamilyToDebugType } from '../models/samLambdaRuntime'
+import { findJavaPath } from '../../shared/utilities/pathFind'
+import { ToolkitError } from '../../shared/errors'
+import { showConfirmationMessage, showMessage } from '../../shared/utilities/messages'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import * as nls from 'vscode-nls'
+import path from 'path'
+import { glob } from 'glob'
+import { Commands } from '../../shared/vscode/commands2'
+import { getLambdaSnapshot, persistLambdaSnapshot, type LambdaDebugger, type DebugConfig } from './lambdaDebugger'
+import { RemoteLambdaDebugger } from './remoteLambdaDebugger'
+import { LocalStackLambdaDebugger } from './localStackLambdaDebugger'
+import { fs } from '../../shared/fs/fs'
+import { detectCdkProjects } from '../../awsService/cdk/explorer/detectCdkProjects'
+
+const localize = nls.loadMessageBundle()
+const logger = getLogger()
+
+// Map debug types to their corresponding VS Code extension IDs
+const mapDebugTypeToExtensionId = new Map([
+    ['python', ['ms-python.python']],
+    ['java', ['redhat.java', 'vscjava.vscode-java-debug']],
+    ['node', ['ms-vscode.js-debug']],
+])
+
+const mapExtensionToBackup = new Map([['ms-vscode.js-debug', 'ms-vscode.js-debug-nightly']])
+
+// Helper function to create a human-readable diff message
+function createDiffMessage(
+    config: FunctionConfiguration,
+    currentConfig: FunctionConfiguration,
+    isRevert: boolean = true
+): string {
+    let message = isRevert ? 'The following changes will be reverted:\n\n' : 'The following changes will be made:\n\n'
+
+    message +=
+        '1. Timeout: ' +
+        (currentConfig.Timeout || 'default') +
+        ' seconds → ' +
+        (config.Timeout || 'default') +
+        ' seconds\n'
+
+    message += '2. Layers: '
+    const hasLdkLayer = currentConfig.Layers?.some(
+        (layer) => layer.Arn?.includes('LDKLayerX86') || layer.Arn?.includes('LDKLayerArm64')
+    )
+
+    message += hasLdkLayer ? 'Remove LDK layer\n' : 'No Change\n'
+
+    message += '3. Environment Variables: Remove AWS_LAMBDA_EXEC_WRAPPER and AWS_LDK_DESTINATION_TOKEN\n'
+
+    return message
+}
+
+/**
+ * Attempts to revert an existing debug configuration if one exists
+ * @returns true if revert was successful or no config exists, false if revert failed or user chose not to revert
+ */
+export async function revertExistingConfig(): Promise {
+    try {
+        // Check if a debug context exists from a previous session
+        const savedConfig = getLambdaSnapshot()
+
+        if (!savedConfig) {
+            // No existing config to revert
+            return true
+        }
+
+        // clear the snapshot for it's corrupted
+        if (!savedConfig.FunctionArn || !savedConfig.FunctionName) {
+            logger.error('Function ARN or Function Name is missing, cannot revert')
+            void (await persistLambdaSnapshot(undefined))
+            return true
+        }
+
+        // compare with current config
+        const currentConfig = await LdkClient.instance.getFunctionDetail(savedConfig.FunctionArn)
+        // could be permission issues, or user has deleted previous function, we should remove the snapshot
+        if (!currentConfig) {
+            logger.error('Failed to get current function state, cannot revert')
+            void (await persistLambdaSnapshot(undefined))
+            return true
+        }
+
+        if (
+            currentConfig?.Timeout === savedConfig?.Timeout &&
+            currentConfig?.Layers?.length === savedConfig?.Layers?.length
+        ) {
+            // No changes needed, remove the snapshot
+            void (await persistLambdaSnapshot(undefined))
+            return true
+        }
+
+        // Create a diff message to show what will be changed
+        const diffMessage = currentConfig
+            ? createDiffMessage(savedConfig, currentConfig, true)
+            : 'Failed to get current function state'
+
+        const response = await showConfirmationMessage({
+            prompt: localize(
+                'AWS.lambda.remoteDebug.revertPreviousDeployment',
+                'A previous debug deployment was detected for {0}. Would you like to revert those changes before proceeding?\n\n{1}',
+                savedConfig.FunctionName,
+                diffMessage
+            ),
+            confirm: localize('AWS.lambda.remoteDebug.revert', 'Revert'),
+            cancel: localize('AWS.lambda.remoteDebug.dontShowAgain', "Don't show again"),
+            type: 'warning',
+        })
+
+        if (!response) {
+            // User chose not to revert, remove the snapshot
+            void (await persistLambdaSnapshot(undefined))
+            return true
+        }
+
+        await LdkClient.instance.removeDebugDeployment(savedConfig, false)
+        await persistLambdaSnapshot(undefined)
+        void showMessage(
+            'info',
+            localize(
+                'AWS.lambda.remoteDebug.successfullyReverted',
+                'Successfully reverted changes to {0}',
+                savedConfig.FunctionName
+            )
+        )
+
+        return true
+    } catch (error) {
+        throw ToolkitError.chain(error, `Error in revertExistingConfig`)
+    }
+}
+
+export async function activateRemoteDebugging(): Promise {
+    try {
+        globals.context.subscriptions.push(
+            Commands.register('aws.lambda.remoteDebugging.clearSnapshot', async () => {
+                void (await persistLambdaSnapshot(undefined))
+            })
+        )
+    } catch (error) {
+        logger.error(`Error in registering clearSnapshot command:${error}`)
+    }
+
+    try {
+        logger.info('Remote debugging is initiated')
+
+        // Use the revertExistingConfig function to handle any existing debug configurations
+        await revertExistingConfig()
+
+        // Initialize RemoteDebugController to ensure proper startup state
+        RemoteDebugController.instance.ensureCleanState()
+    } catch (error) {
+        // show warning
+        void vscode.window.showWarningMessage(`Error in activateRemoteDebugging: ${error}`)
+        logger.error(`Error in activateRemoteDebugging:${error}`)
+    }
+}
+
+/**
+ * Try to auto-detect outFile for TypeScript debugging (SAM or CDK)
+ * @param debugConfig Debug configuration
+ * @param functionConfig Lambda function configuration
+ * @returns The auto-detected outFile path or undefined
+ */
+export async function tryAutoDetectOutFile(
+    debugConfig: DebugConfig,
+    functionConfig: FunctionConfiguration
+): Promise {
+    // Only works for TypeScript files
+    if (
+        !debugConfig.handlerFile ||
+        (!debugConfig.handlerFile.endsWith('.ts') && !debugConfig.handlerFile.endsWith('.tsx'))
+    ) {
+        return undefined
+    }
+
+    // Try SAM detection first using the provided parameters
+    if (debugConfig.samFunctionLogicalId && debugConfig.samProjectRoot) {
+        // if proj root is ..../sam-proj/
+        // build dir will be ..../sam-proj/.aws-sam/build/{LogicalID}/
+        const samBuildPath = vscode.Uri.joinPath(
+            debugConfig.samProjectRoot,
+            '.aws-sam',
+            'build',
+            debugConfig.samFunctionLogicalId
+        )
+
+        if (await fs.exists(samBuildPath)) {
+            getLogger().info(`SAM outFile auto-detected: ${samBuildPath.fsPath}`)
+            return samBuildPath.fsPath
+        }
+    }
+
+    // If SAM detection didn't work, try CDK detection using the function name
+    if (!functionConfig.FunctionName) {
+        return undefined
+    }
+
+    try {
+        // Find which workspace contains the handler file
+        const workspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(debugConfig.handlerFile))
+        if (!workspaceFolder) {
+            return undefined
+        }
+
+        // Detect CDK projects in the workspace
+        const cdkProjects = await detectCdkProjects([workspaceFolder])
+
+        for (const project of cdkProjects) {
+            // Check if CDK project contains the handler file
+            const cdkProjectDir = vscode.Uri.joinPath(project.cdkJsonUri, '..')
+            // Normalize paths for comparison (handles Windows path separators and case)
+            const normalizedHandlerPath = path.normalize(debugConfig.handlerFile).toLowerCase()
+            const normalizedCdkPath = path.normalize(cdkProjectDir.fsPath).toLowerCase()
+            if (!normalizedHandlerPath.startsWith(normalizedCdkPath)) {
+                continue
+            }
+
+            // Get the cdk.out directory
+            const cdkOutDir = vscode.Uri.joinPath(project.treeUri, '..')
+
+            // Look for template.json files in cdk.out directory
+            const pattern = new vscode.RelativePattern(cdkOutDir.fsPath, '*.template.json')
+            const templateFiles = await vscode.workspace.findFiles(pattern)
+
+            for (const templateFile of templateFiles) {
+                try {
+                    // Read and parse the template.json file
+                    const templateContent = await fs.readFileText(templateFile)
+                    const template = JSON.parse(templateContent)
+
+                    // Search through resources for a Lambda function with matching FunctionName
+                    for (const [_, resource] of Object.entries(template.Resources || {})) {
+                        const res = resource as any
+                        if (
+                            res.Type === 'AWS::Lambda::Function' &&
+                            res.Properties?.FunctionName === functionConfig.FunctionName
+                        ) {
+                            // Found the matching function, extract the asset path from metadata
+                            const assetPath = res.Metadata?.['aws:asset:path']
+                            if (assetPath) {
+                                const assetDir = vscode.Uri.joinPath(cdkOutDir, assetPath)
+
+                                // Check if the asset directory exists
+                                if (await fs.exists(assetDir)) {
+                                    getLogger().info(`CDK outFile auto-detected from template.json: ${assetDir.fsPath}`)
+                                    return assetDir.fsPath
+                                }
+                            }
+                        }
+                    }
+                } catch (error) {
+                    getLogger().debug(`Failed to parse template file ${templateFile.fsPath}: ${error}`)
+                }
+            }
+        }
+    } catch (error) {
+        getLogger().warn(`Failed to auto-detect CDK outFile: ${error}`)
+    }
+
+    return undefined
+}
+
+/**
+ * Helper function to check if a string is a valid VSCode glob pattern
+ */
+function isVscodeGlob(pattern: string): boolean {
+    // Check for common glob patterns: *, **, ?, [], {}
+    return /[*?[\]{}]/.test(pattern)
+}
+
+/**
+ * Helper function to validate source map files exist for given outFiles patterns
+ */
+async function validateSourceMapFiles(outFiles: string[]): Promise {
+    const allAreGlobs = outFiles.every((pattern) => isVscodeGlob(pattern))
+    if (!allAreGlobs) {
+        return false
+    }
+
+    try {
+        let jsfileCount = 0
+        let mapfileCount = 0
+        const jsFiles = await glob(outFiles, { ignore: 'node_modules/**' })
+
+        for (const file of jsFiles) {
+            if (file.includes('js')) {
+                jsfileCount += 1
+            }
+            if (file.includes('.map')) {
+                mapfileCount += 1
+            }
+        }
+
+        return jsfileCount === 0 || mapfileCount === 0 ? false : true
+    } catch (error) {
+        getLogger().warn(`Error validating source map files: ${error}`)
+        return false
+    }
+}
+
+function processOutFiles(outFiles: string[], localRoot: string): string[] {
+    const processedOutFiles: string[] = []
+
+    for (let outFile of outFiles) {
+        if (!outFile.includes('*')) {
+            // add * in the end
+            outFile = path.join(outFile, '*')
+        }
+        if (!path.isAbsolute(outFile)) {
+            // Find which workspace contains the localRoot path
+            const workspaceFolders = vscode.workspace.workspaceFolders
+            if (workspaceFolders) {
+                let matchingWorkspace: vscode.WorkspaceFolder | undefined
+
+                // Check if localRoot is within any workspace
+                for (const workspace of workspaceFolders) {
+                    const absoluteLocalRoot = path.resolve(localRoot)
+                    const workspacePath = workspace.uri.fsPath
+
+                    if (absoluteLocalRoot.startsWith(workspacePath)) {
+                        matchingWorkspace = workspace
+                        break
+                    }
+                }
+
+                if (matchingWorkspace) {
+                    // Join workspace folder with the relative outFile path
+                    processedOutFiles.push(path.join(matchingWorkspace.uri.fsPath, outFile))
+                } else {
+                    // If no matching workspace found, use the original outFile
+                    processedOutFiles.push(outFile)
+                }
+            } else {
+                // No workspace folders, use the original outFile
+                processedOutFiles.push(outFile)
+            }
+        } else {
+            // Already absolute path, use as is
+            processedOutFiles.push(outFile)
+        }
+    }
+    return processedOutFiles
+}
+
+async function getVscodeDebugConfig(
+    functionConfig: FunctionConfiguration,
+    debugConfig: DebugConfig
+): Promise {
+    // Parse and validate otherDebugParams if provided
+    let additionalParams: Record = {}
+    if (debugConfig.otherDebugParams) {
+        try {
+            const parsed = JSON.parse(debugConfig.otherDebugParams)
+            if (typeof parsed === 'object' && !Array.isArray(parsed)) {
+                additionalParams = parsed
+                getLogger().info('Additional debug parameters parsed successfully: %O ', additionalParams)
+            } else {
+                void vscode.window.showWarningMessage(
+                    localize(
+                        'AWS.lambda.remoteDebug.invalidDebugParams',
+                        'Other Debug Parameters must be a valid JSON object. The parameter will be ignored.'
+                    )
+                )
+                getLogger().warn(`Invalid otherDebugParams format: expected object, got ${typeof parsed}`)
+            }
+        } catch (error) {
+            void vscode.window.showWarningMessage(
+                localize(
+                    'AWS.lambda.remoteDebug.failedToParseDebugParams',
+                    'Failed to parse Other Debug Parameters as JSON: {0}. The parameter will be ignored.',
+                    error instanceof Error ? error.message : 'Invalid JSON'
+                )
+            )
+            getLogger().warn(`Failed to parse otherDebugParams as JSON: ${error}`)
+        }
+    }
+
+    const debugSessionName = `Debug ${functionConfig.FunctionArn!.split(':').pop()}`
+
+    // Define debugConfig before the try block
+    const debugType = mapFamilyToDebugType.get(getFamily(functionConfig.Runtime!), 'unknown')
+    let vsCodeDebugConfig: vscode.DebugConfiguration
+    switch (debugType) {
+        case 'node':
+            // Try to auto-detect outFiles for TypeScript if not provided
+            if (debugConfig.sourceMap && !debugConfig.outFiles && debugConfig.handlerFile) {
+                const autoDetectedOutFile = await tryAutoDetectOutFile(debugConfig, functionConfig)
+                if (autoDetectedOutFile) {
+                    debugConfig.outFiles = [autoDetectedOutFile]
+                    getLogger().info(`outFile auto-detected: ${autoDetectedOutFile}`)
+                }
+            }
+
+            // source map support
+            if (debugConfig.sourceMap && debugConfig.outFiles) {
+                // process outFiles first, if they are relative path (not starting with /),
+                // check local root path is located in which workspace. Then join workspace Folder with outFiles
+
+                // Update debugConfig with processed outFiles
+                debugConfig.outFiles = processOutFiles(debugConfig.outFiles, debugConfig.localRoot)
+
+                // Use glob to search if there are any matching js file or source map file
+                const hasSourceMaps = await validateSourceMapFiles(debugConfig.outFiles)
+
+                if (hasSourceMaps) {
+                    // support mapping common sam cli location
+                    additionalParams['sourceMapPathOverrides'] = {
+                        ...additionalParams['sourceMapPathOverrides'],
+                        '?:*/T/?:*/*': path.join(debugConfig.localRoot, '*'),
+                    }
+                    debugConfig.localRoot = debugConfig.outFiles[0].split('*')[0]
+                } else {
+                    debugConfig.sourceMap = false
+                    debugConfig.outFiles = undefined
+                    await showMessage(
+                        'warn',
+                        localize(
+                            'AWS.lambda.remoteDebug.outFileNotFound',
+                            'outFiles not valid or no js and map file found in outFiles, debug will continue without sourceMap support'
+                        )
+                    )
+                }
+            }
+            vsCodeDebugConfig = {
+                type: debugType,
+                request: 'attach',
+                name: debugSessionName,
+                address: 'localhost',
+                port: debugConfig.port,
+                localRoot: debugConfig.localRoot,
+                remoteRoot: debugConfig.remoteRoot,
+                skipFiles: debugConfig.skipFiles,
+                sourceMaps: debugConfig.sourceMap,
+                outFiles: debugConfig.outFiles,
+                continueOnAttach: debugConfig.outFiles ? false : true,
+                stopOnEntry: false,
+                timeout: 60000,
+                ...additionalParams, // Merge additional debug parameters
+            }
+            break
+        case 'python':
+            vsCodeDebugConfig = {
+                type: debugType,
+                request: 'attach',
+                name: debugSessionName,
+                port: debugConfig.port,
+                cwd: debugConfig.localRoot,
+                pathMappings: [
+                    {
+                        localRoot: debugConfig.localRoot,
+                        remoteRoot: debugConfig.remoteRoot,
+                    },
+                ],
+                justMyCode: debugConfig.justMyCode ?? true,
+                ...additionalParams, // Merge additional debug parameters
+            }
+            break
+        case 'java':
+            vsCodeDebugConfig = {
+                type: debugType,
+                request: 'attach',
+                name: debugSessionName,
+                hostName: 'localhost',
+                port: debugConfig.port,
+                sourcePaths: [debugConfig.localRoot],
+                projectName: debugConfig.projectName,
+                timeout: 60000,
+                ...additionalParams, // Merge additional debug parameters
+            }
+            break
+        default:
+            throw new ToolkitError(`Unsupported debug type: ${debugType}`)
+    }
+    getLogger().info('VS Code debug configuration: %O', vsCodeDebugConfig)
+    return vsCodeDebugConfig
+}
+
+export class RemoteDebugController {
+    static #instance: RemoteDebugController
+    isDebugging: boolean = false
+    qualifier: string | undefined = undefined
+    debugger: LambdaDebugger | undefined = undefined
+    private lastDebugStartTime: number = 0
+    // private debugSession: DebugSession | undefined
+    private debugSessionDisposables: Map = new Map()
+    private debugTypeSource: 'remoteDebug' | 'LocalStackDebug' = 'remoteDebug'
+
+    public static get instance() {
+        if (this.#instance !== undefined) {
+            return this.#instance
+        }
+
+        const self = (this.#instance = new this())
+        return self
+    }
+
+    constructor() {}
+
+    /**
+     * Ensures the controller is in a clean state at startup or before a new operation
+     */
+    public ensureCleanState(): void {
+        this.isDebugging = false
+        this.qualifier = undefined
+
+        // Clean up any leftover disposables
+        for (const [key, disposable] of this.debugSessionDisposables.entries()) {
+            try {
+                disposable.dispose()
+            } catch (e) {
+                // Ignore errors during startup cleanup
+            }
+            this.debugSessionDisposables.delete(key)
+        }
+    }
+
+    public supportCodeDownload(runtime: Runtime | undefined, codeSha256: string | undefined = ''): boolean {
+        if (!runtime) {
+            return false
+        }
+        // Incompatible with LocalStack hot-reloading
+        if (codeSha256?.startsWith('hot-reloading')) {
+            return false
+        }
+        try {
+            return ['node', 'python'].includes(mapFamilyToDebugType.get(getFamily(runtime)) ?? '')
+        } catch {
+            // deprecated runtime
+            return false
+        }
+    }
+
+    public supportRuntimeRemoteDebug(runtime: Runtime | undefined): boolean {
+        if (!runtime) {
+            return false
+        }
+        try {
+            return ['node', 'python', 'java'].includes(mapFamilyToDebugType.get(getFamily(runtime)) ?? '')
+        } catch {
+            return false
+        }
+    }
+
+    public async installDebugExtension(runtime: Runtime | undefined): Promise {
+        if (!runtime) {
+            throw new ToolkitError('Runtime is undefined')
+        }
+
+        const debugType = mapFamilyToDebugType.get(getFamily(runtime))
+        if (!debugType) {
+            throw new ToolkitError(`Debug type is undefined for runtime ${runtime}`)
+        }
+        // Install needed debug extension based on runtime
+        const extensions = mapDebugTypeToExtensionId.get(debugType)
+        if (extensions) {
+            for (const extension of extensions) {
+                const extensionObj = vscode.extensions.getExtension(extension)
+                const backupExtensionObj = vscode.extensions.getExtension(mapExtensionToBackup.get(extension) ?? '')
+
+                if (!extensionObj && !backupExtensionObj) {
+                    // Extension is not installed, install it
+                    const choice = await showConfirmationMessage({
+                        prompt: localize(
+                            'AWS.lambda.remoteDebug.extensionNotInstalled',
+                            'You need to install the {0} extension to debug {1} functions. Would you like to install it now?',
+                            extension,
+                            debugType
+                        ),
+                        confirm: localize('AWS.lambda.remoteDebug.install', 'Install'),
+                        cancel: localize('AWS.lambda.remoteDebug.cancel', 'Cancel'),
+                        type: 'warning',
+                    })
+                    if (!choice) {
+                        return false
+                    }
+                    await vscode.commands.executeCommand('workbench.extensions.installExtension', extension)
+                    if (vscode.extensions.getExtension(extension) === undefined) {
+                        return false
+                    }
+                }
+            }
+        }
+
+        if (debugType === 'java' && !(await findJavaPath())) {
+            // jvm not available
+            const choice = await showConfirmationMessage({
+                prompt: localize(
+                    'AWS.lambda.remoteDebug.jvmNotInstalled',
+                    'You need to install a JVM to debug Java functions. Would you like to install it now?'
+                ),
+                confirm: localize('AWS.lambda.remoteDebug.install', 'Install'),
+                cancel: localize('AWS.lambda.remoteDebug.continueAnyway', 'Continue Anyway'),
+                type: 'warning',
+            })
+            // open https://developers.redhat.com/products/openjdk/download
+            if (choice) {
+                await vscode.env.openExternal(
+                    vscode.Uri.parse('https://developers.redhat.com/products/openjdk/download')
+                )
+                return false
+            }
+        }
+        // passed all checks
+        return true
+    }
+
+    public async startDebugging(functionArn: string, runtime: string, debugConfig: DebugConfig): Promise {
+        if (debugConfig.isLambdaRemote) {
+            this.debugTypeSource = 'remoteDebug'
+            this.debugger = new RemoteLambdaDebugger(debugConfig, {
+                getQualifier: () => {
+                    return this.qualifier
+                },
+                setQualifier: (qualifier) => {
+                    this.qualifier = qualifier
+                },
+            })
+        } else {
+            this.debugTypeSource = 'LocalStackDebug'
+            this.debugger = new LocalStackLambdaDebugger(debugConfig)
+        }
+        if (this.isDebugging) {
+            getLogger().error('Debug already in progress, remove debug setup to restart')
+            return
+        }
+
+        await telemetry.lambda_remoteDebugStart.run(async (span) => {
+            // Create a copy of debugConfig without functionName and functionArn for telemetry
+            const debugConfigForTelemetry: Partial = { ...debugConfig }
+            debugConfigForTelemetry.functionName = undefined
+            debugConfigForTelemetry.functionArn = undefined
+            debugConfigForTelemetry.localRoot = undefined
+
+            span.record({
+                source: this.debugTypeSource,
+                passive: false,
+                action: JSON.stringify(debugConfigForTelemetry),
+            })
+            this.lastDebugStartTime = Date.now()
+            await vscode.window.withProgress(
+                {
+                    location: vscode.ProgressLocation.Notification,
+                    title: 'Setting up debug session',
+                    cancellable: false,
+                },
+                async (progress) => {
+                    // Reset state before starting
+                    this.ensureCleanState()
+
+                    getLogger().info(`Starting debugger for ${functionArn}`)
+
+                    const region = getRegionFromArn(functionArn)
+                    if (!region) {
+                        throw new ToolkitError('Could not determine region from Lambda ARN')
+                    }
+
+                    // Check if runtime / region is supported for remote debugging
+                    if (!this.supportRuntimeRemoteDebug(runtime as Runtime)) {
+                        throw new ToolkitError(
+                            `Runtime ${runtime} is not supported for remote debugging. ` +
+                                `Only Python, Node.js, and Java runtimes are supported.`
+                        )
+                    }
+
+                    // Ensure the remote connection is reachable before calling lambda.GetFunction in revertExistingConfig()
+                    await this.debugger?.checkHealth()
+
+                    // Check if a snapshot already exists and revert if needed
+                    // Use the revertExistingConfig function from ldkController
+                    progress.report({ message: 'Checking if snapshot exists...' })
+                    const revertResult = await revertExistingConfig()
+
+                    // If revert failed and user didn't choose to ignore, abort the deployment
+                    if (revertResult === false) {
+                        return
+                    }
+                    try {
+                        // Anything fails before this point doesn't requires reverting
+                        this.isDebugging = true
+
+                        // the following will contain changes that requires reverting.
+                        // Create a snapshot of lambda config before debug
+                        // let's preserve this config to a global variable at here
+                        // we will use this config to revert the changes back to it once was, once confirm it's success, update the global to undefined
+                        // if somehow the changes failed to revert, in init phase(activate remote debugging), we will detect this config and prompt user to revert the changes
+                        // get function config again in case anything changed
+                        const functionConfig = await LdkClient.instance.getFunctionDetail(functionArn)
+                        if (!functionConfig?.Runtime || !functionConfig?.FunctionArn) {
+                            throw new ToolkitError('Could not retrieve Lambda function configuration')
+                        }
+                        await persistLambdaSnapshot(functionConfig)
+
+                        // Record runtime in telemetry
+                        span.record({
+                            runtimeString: functionConfig.Runtime as any,
+                        })
+
+                        await this.debugger?.setup(progress, functionConfig, region)
+
+                        const vscodeDebugConfig = await getVscodeDebugConfig(functionConfig, debugConfig)
+                        // show every field in debugConfig
+                        // getLogger().info(`Debug configuration created successfully ${JSON.stringify(debugConfig)}`)
+
+                        await this.debugger?.waitForSetup(progress, functionConfig, region)
+
+                        progress.report({ message: 'Starting debugger...' })
+                        // Start debugging in a non-blocking way
+                        void Promise.resolve(vscode.debug.startDebugging(undefined, vscodeDebugConfig)).then(
+                            async (debugStarted) => {
+                                if (!debugStarted) {
+                                    // this could be triggered by another stop debugging, let's check state before stopping.
+                                    throw new ToolkitError('Failed to start debug session')
+                                }
+                            }
+                        )
+
+                        const debugSessionEndDisposable = vscode.debug.onDidTerminateDebugSession(async (session) => {
+                            if (session.name === vscodeDebugConfig.name) {
+                                void (await this.stopDebugging())
+                            }
+                        })
+
+                        await this.debugger?.waitForFunctionUpdates(progress)
+
+                        // Store the disposable
+                        this.debugSessionDisposables.set(functionConfig.FunctionArn, debugSessionEndDisposable)
+                        progress.report({
+                            message: `Debug session setup completed for ${functionConfig.FunctionArn.split(':').pop()}`,
+                        })
+                    } catch (error) {
+                        try {
+                            await this.stopDebugging()
+                        } catch (errStop) {
+                            getLogger().error(
+                                'encountered following error when stopping debug for failed debug session:'
+                            )
+                            getLogger().error(errStop as Error)
+                        }
+
+                        throw ToolkitError.chain(error, 'Error StartDebugging')
+                    }
+                }
+            )
+        })
+    }
+
+    public async stopDebugging(): Promise {
+        await telemetry.lambda_remoteDebugStop.run(async (span) => {
+            if (!this.isDebugging) {
+                void showMessage(
+                    'info',
+                    localize('AWS.lambda.remoteDebug.debugNotInProgress', 'Debug is not in progress')
+                )
+                return
+            }
+            // use sessionDuration to record debug duration
+            span.record({
+                sessionDuration: this.lastDebugStartTime === 0 ? 0 : Date.now() - this.lastDebugStartTime,
+                source: this.debugTypeSource,
+            })
+            try {
+                await vscode.window.withProgress(
+                    {
+                        location: vscode.ProgressLocation.Notification,
+                        title: 'Stopping debug session',
+                        cancellable: false,
+                    },
+                    async (progress) => {
+                        progress.report({ message: 'Stopping debugging...' })
+
+                        // First attempt to clean up resources from Lambda
+                        const savedConfig = getLambdaSnapshot()
+                        if (!savedConfig?.FunctionArn) {
+                            getLogger().error('No saved configuration found during cleanup')
+                            throw new ToolkitError('No saved configuration found during cleanup')
+                        }
+
+                        const disposable = this.debugSessionDisposables.get(savedConfig.FunctionArn)
+                        if (disposable) {
+                            disposable.dispose()
+                            this.debugSessionDisposables.delete(savedConfig.FunctionArn)
+                        }
+                        await this.debugger?.cleanup(savedConfig)
+
+                        progress.report({ message: `Debug session stopped` })
+                    }
+                )
+                void showMessage(
+                    'info',
+                    localize('AWS.lambda.remoteDebug.debugSessionStopped', 'Debug session stopped')
+                )
+            } catch (error) {
+                throw ToolkitError.chain(error, 'error when stopping remote debug')
+            } finally {
+                this.isDebugging = false
+            }
+        })
+    }
+}
diff --git a/packages/core/src/lambda/remoteDebugging/ldkLayers.ts b/packages/core/src/lambda/remoteDebugging/ldkLayers.ts
new file mode 100644
index 00000000000..f0c5dff2c02
--- /dev/null
+++ b/packages/core/src/lambda/remoteDebugging/ldkLayers.ts
@@ -0,0 +1,46 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+interface RegionAccountMapping {
+    [region: string]: string
+}
+
+// Map region to account ID
+export const regionToAccount: RegionAccountMapping = {
+    'us-east-1': '166855510987',
+    'ap-northeast-1': '435951944084',
+    'us-west-1': '397974708477',
+    'us-west-2': '116489046076',
+    'us-east-2': '372632330791',
+    'ca-central-1': '816313119386',
+    'eu-west-1': '020236748984',
+    'eu-west-2': '199003954714',
+    'eu-west-3': '490913546906',
+    'eu-central-1': '944487268028',
+    'eu-north-1': '351516301086',
+    'ap-southeast-1': '812073016575',
+    'ap-southeast-2': '185226997092',
+    'ap-northeast-2': '241511115815',
+    'ap-south-1': '926022987530',
+    'sa-east-1': '313162186107',
+    'ap-east-1': '416298298123',
+    'me-south-1': '511027370648',
+    'me-central-1': '766358817862',
+}
+
+// Global layer version
+const globalLayerVersion = 2
+
+export function getRemoteDebugLayerForArch(region: string, arch: string): string | undefined {
+    const account = regionToAccount[region]
+
+    if (!account) {
+        return undefined
+    }
+
+    const layerName = arch === 'x86_64' ? 'LDKLayerX86' : 'LDKLayerArm64'
+
+    return `arn:aws:lambda:${region}:${account}:layer:${layerName}:${globalLayerVersion}`
+}
diff --git a/packages/core/src/lambda/remoteDebugging/localProxy.ts b/packages/core/src/lambda/remoteDebugging/localProxy.ts
new file mode 100644
index 00000000000..8b228deeb1a
--- /dev/null
+++ b/packages/core/src/lambda/remoteDebugging/localProxy.ts
@@ -0,0 +1,901 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as net from 'net'
+import WebSocket from 'ws'
+import * as crypto from 'crypto'
+import { getLogger } from '../../shared/logger/logger'
+import { v4 as uuidv4 } from 'uuid'
+import * as protobuf from 'protobufjs'
+
+const logger = getLogger()
+
+// Define the message types from the protocol
+enum MessageType {
+    UNKNOWN = 0,
+    DATA = 1,
+    STREAM_START = 2,
+    STREAM_RESET = 3,
+    SESSION_RESET = 4,
+    SERVICE_IDS = 5,
+    CONNECTION_START = 6,
+    CONNECTION_RESET = 7,
+}
+
+// Interface for tunnel info
+export interface TunnelInfo {
+    tunnelId: string
+    sourceToken: string
+    destinationToken: string
+}
+
+// Interface for TCP connection
+interface TcpConnection {
+    socket: net.Socket
+    streamId: number
+    connectionId: number
+}
+
+/**
+ * LocalProxy class that handles WebSocket connection to IoT secure tunneling
+ * and sets up a TCP adapter as a local proxy
+ */
+export class LocalProxy {
+    private ws: WebSocket.WebSocket | undefined = undefined
+    private tcpServer: net.Server | undefined = undefined
+    private tcpConnections: Map = new Map()
+    private isConnected: boolean = false
+    private reconnectAttempts: number = 0
+    private maxReconnectAttempts: number = 10
+    private reconnectInterval: number = 2500 // 2.5 seconds
+    private pingInterval: NodeJS.Timeout | undefined = undefined
+    private serviceId: string = 'WSS'
+    private currentStreamId: number = 1
+    private nextConnectionId: number = 1
+    private localPort: number = 0
+    private region: string = ''
+    private accessToken: string = ''
+    private Message: protobuf.Type | undefined = undefined
+    private clientToken: string = ''
+    private eventHandlers: { [key: string]: any[] } = {}
+    private isDisposed: boolean = false
+
+    constructor() {
+        void this.loadProtobufDefinition()
+    }
+
+    // Define the protobuf schema as a string constant
+    private static readonly protobufSchema = `
+    syntax = "proto3";
+
+    package com.amazonaws.iot.securedtunneling;
+
+    message Message {
+        Type    type         = 1;
+        int32   streamId     = 2;
+        bool    ignorable    = 3;
+        bytes   payload      = 4;
+        string  serviceId = 5;
+        repeated string availableServiceIds = 6;
+        uint32 connectionId = 7;
+
+        enum Type {
+            UNKNOWN = 0;
+            DATA = 1;
+            STREAM_START = 2;
+            STREAM_RESET = 3;
+            SESSION_RESET = 4;
+            SERVICE_IDS = 5;
+            CONNECTION_START = 6;
+            CONNECTION_RESET = 7;
+        }
+    }`
+
+    /**
+     * Load the protobuf definition from the embedded schema string
+     */
+    private async loadProtobufDefinition(): Promise {
+        try {
+            if (this.Message) {
+                // Already loaded, don't parse again
+                return
+            }
+
+            const root = protobuf.parse(LocalProxy.protobufSchema).root
+            this.Message = root.lookupType('com.amazonaws.iot.securedtunneling.Message')
+
+            if (!this.Message) {
+                throw new Error('Failed to load Message type from protobuf definition')
+            }
+
+            logger.debug('Protobuf definition loaded successfully')
+        } catch (error) {
+            logger.error(`Error loading protobuf definition:${error}`)
+            throw error
+        }
+    }
+
+    /**
+     * Start the local proxy
+     * @param region AWS region
+     * @param sourceToken Source token for the tunnel
+     * @param port Local port to listen on
+     */
+    public async start(region: string, sourceToken: string, port: number = 0): Promise {
+        // Reset disposal state when starting
+        this.isDisposed = false
+
+        this.region = region
+        this.accessToken = sourceToken
+
+        try {
+            // Start TCP server first
+            this.localPort = await this.startTcpServer(port)
+
+            // Then connect to WebSocket
+            await this.connectWebSocket()
+
+            return this.localPort
+        } catch (error) {
+            logger.error(`Failed to start local proxy:${error}`)
+            this.stop()
+            throw error
+        }
+    }
+
+    /**
+     * Stop the local proxy and clean up all resources
+     */
+    public stop(): void {
+        if (this.isDisposed) {
+            logger.debug('LocalProxy already stopped, skipping duplicate stop call')
+            return
+        }
+
+        logger.debug('Stopping LocalProxy and cleaning up resources')
+
+        // Cancel any pending reconnect timeouts
+        if (this.eventHandlers['reconnectTimeouts']) {
+            for (const timeoutId of this.eventHandlers['reconnectTimeouts']) {
+                clearTimeout(timeoutId as NodeJS.Timeout)
+            }
+        }
+
+        this.stopPingInterval()
+        this.closeWebSocket()
+        this.closeTcpServer()
+
+        // Reset all state
+        this.clientToken = ''
+        this.isConnected = false
+        this.reconnectAttempts = 0
+        this.currentStreamId = 1
+        this.nextConnectionId = 1
+        this.localPort = 0
+        this.region = ''
+        this.accessToken = ''
+
+        // Mark as disposed to prevent duplicate stop calls
+        this.isDisposed = true
+
+        // Clear any remaining event handlers reference
+        this.eventHandlers = {}
+    }
+
+    /**
+     * Start the TCP server
+     * @param port Port to listen on (0 for random port)
+     * @returns The port the server is listening on
+     */
+    private startTcpServer(port: number): Promise {
+        return new Promise((resolve, reject) => {
+            try {
+                this.tcpServer = net.createServer((socket) => {
+                    this.handleNewTcpConnection(socket)
+                })
+
+                this.tcpServer.on('error', (err) => {
+                    logger.error(`TCP server error:${err}`)
+                })
+
+                this.tcpServer.listen(port, '127.0.0.1', () => {
+                    const address = this.tcpServer?.address() as net.AddressInfo
+                    this.localPort = address.port
+                    logger.debug(`TCP server listening on port ${this.localPort}`)
+                    resolve(this.localPort)
+                })
+            } catch (error) {
+                logger.error(`Failed to start TCP server:${error}`)
+                reject(error)
+            }
+        })
+    }
+
+    /**
+     * Close the TCP server and all connections
+     */
+    private closeTcpServer(): void {
+        if (this.tcpServer) {
+            logger.debug('Closing TCP server and connections')
+
+            // Remove all listeners from the server
+            this.tcpServer.removeAllListeners('error')
+            this.tcpServer.removeAllListeners('connection')
+            this.tcpServer.removeAllListeners('listening')
+
+            // Close all TCP connections with proper error handling
+            for (const connection of this.tcpConnections.values()) {
+                try {
+                    // Remove all listeners before destroying
+                    connection.socket.removeAllListeners('data')
+                    connection.socket.removeAllListeners('error')
+                    connection.socket.removeAllListeners('close')
+                    connection.socket.destroy()
+                } catch (err) {
+                    logger.error(`Error closing TCP connection: ${err}`)
+                }
+            }
+            this.tcpConnections.clear()
+
+            // Close the server with proper error handling and timeout
+            try {
+                // Set a timeout in case server.close() hangs
+                const serverCloseTimeout = setTimeout(() => {
+                    logger.warn('TCP server close timed out, forcing closure')
+                    this.tcpServer = undefined
+                }, 5000)
+
+                this.tcpServer.close(() => {
+                    clearTimeout(serverCloseTimeout)
+                    logger.debug('TCP server closed successfully')
+                    this.tcpServer = undefined
+                })
+            } catch (err) {
+                logger.error(`Error closing TCP server: ${err}`)
+                this.tcpServer = undefined
+            }
+        }
+    }
+
+    /**
+     * Handle a new TCP connection with proper resource management
+     * @param socket The TCP socket
+     */
+    private handleNewTcpConnection(socket: net.Socket): void {
+        if (!this.isConnected || this.isDisposed) {
+            logger.warn('WebSocket not connected or proxy disposed, rejecting TCP connection')
+            socket.destroy()
+            return
+        }
+
+        const connectionId = this.nextConnectionId++
+        const streamId = this.currentStreamId
+
+        logger.debug(`New TCP connection: ${connectionId}`)
+
+        // Track event handlers for this connection
+        const handlers: { [event: string]: (...args: any[]) => void } = {}
+
+        // Data handler
+        const dataHandler = (data: Buffer) => {
+            this.sendData(streamId, connectionId, data)
+        }
+        socket.on('data', dataHandler)
+        handlers.data = dataHandler
+
+        // Error handler
+        const errorHandler = (err: Error) => {
+            logger.error(`TCP connection ${connectionId} error: ${err}`)
+            this.sendConnectionReset(streamId, connectionId)
+
+            // Cleanup handlers on error
+            this.cleanupSocketHandlers(socket, handlers)
+        }
+        socket.on('error', errorHandler)
+        handlers.error = errorHandler
+
+        // Close handler
+        const closeHandler = () => {
+            logger.debug(`TCP connection ${connectionId} closed`)
+
+            // Remove from connections map and send reset
+            this.tcpConnections.delete(connectionId)
+            this.sendConnectionReset(streamId, connectionId)
+
+            // Cleanup handlers on close
+            this.cleanupSocketHandlers(socket, handlers)
+        }
+        socket.on('close', closeHandler)
+        handlers.close = closeHandler
+
+        // Set a timeout to close idle connections after 10 minutes
+        const idleTimeout = setTimeout(
+            () => {
+                if (this.tcpConnections.has(connectionId)) {
+                    logger.debug(`Closing idle TCP connection ${connectionId}`)
+                    socket.destroy()
+                }
+            },
+            10 * 60 * 1000
+        )
+
+        // Clear timeout on socket close
+        socket.once('close', () => {
+            clearTimeout(idleTimeout)
+        })
+
+        // Store the connection
+        const connection: TcpConnection = {
+            socket,
+            streamId,
+            connectionId,
+        }
+        this.tcpConnections.set(connectionId, connection)
+
+        // Send StreamStart for the first connection, ConnectionStart for subsequent ones
+        if (connectionId === 1) {
+            this.sendStreamStart(streamId, connectionId)
+        } else {
+            this.sendConnectionStart(streamId, connectionId)
+        }
+    }
+
+    /**
+     * Helper method to clean up socket event handlers
+     * @param socket The socket to clean up
+     * @param handlers The handlers to remove
+     */
+    private cleanupSocketHandlers(socket: net.Socket, handlers: { [event: string]: (...args: any[]) => void }): void {
+        try {
+            if (handlers.data) {
+                socket.removeListener('data', handlers.data as (...args: any[]) => void)
+            }
+            if (handlers.error) {
+                socket.removeListener('error', handlers.error as (...args: any[]) => void)
+            }
+            if (handlers.close) {
+                socket.removeListener('close', handlers.close as (...args: any[]) => void)
+            }
+        } catch (error) {
+            logger.error(`Error cleaning up socket handlers: ${error}`)
+        }
+    }
+
+    /**
+     * Connect to the WebSocket server with proper event tracking
+     */
+    private async connectWebSocket(): Promise {
+        if (this.ws) {
+            this.closeWebSocket()
+        }
+
+        // Reset for new connection
+        this.isDisposed = false
+
+        return new Promise((resolve, reject) => {
+            try {
+                const url = `wss://data.tunneling.iot.${this.region}.amazonaws.com:443/tunnel?local-proxy-mode=source`
+
+                if (!this.clientToken) {
+                    this.clientToken = uuidv4().replace(/-/g, '')
+                }
+
+                this.ws = new WebSocket.WebSocket(url, ['aws.iot.securetunneling-3.0'], {
+                    headers: {
+                        'access-token': this.accessToken,
+                        'client-token': this.clientToken,
+                    },
+                    handshakeTimeout: 30000, // 30 seconds
+                })
+
+                // Track event listeners for proper cleanup
+                this.eventHandlers['wsOpen'] = []
+                this.eventHandlers['wsMessage'] = []
+                this.eventHandlers['wsClose'] = []
+                this.eventHandlers['wsError'] = []
+                this.eventHandlers['wsPing'] = []
+                this.eventHandlers['wsPong'] = []
+
+                // Open handler
+                const openHandler = () => {
+                    logger.debug('WebSocket connected')
+                    this.isConnected = true
+                    this.reconnectAttempts = 0
+                    this.startPingInterval()
+                    resolve()
+                }
+                this.ws.on('open', openHandler)
+                this.eventHandlers['wsOpen'].push(openHandler)
+
+                // Message handler
+                const messageHandler = (data: WebSocket.RawData) => {
+                    this.handleWebSocketMessage(data)
+                }
+                this.ws.on('message', messageHandler)
+                this.eventHandlers['wsMessage'].push(messageHandler)
+
+                // Close handler
+                const closeHandler = (code: number, reason: Buffer) => {
+                    logger.debug(`WebSocket closed: ${code} ${reason.toString()}`)
+                    this.isConnected = false
+                    this.stopPingInterval()
+
+                    // Only attempt reconnect if we haven't explicitly stopped
+                    if (!this.isDisposed) {
+                        void this.attemptReconnect()
+                    }
+                }
+                this.ws.on('close', closeHandler)
+                this.eventHandlers['wsClose'].push(closeHandler)
+
+                // Error handler
+                const errorHandler = (err: Error) => {
+                    logger.error(`WebSocket error: ${err}`)
+                    reject(err)
+                }
+                this.ws.on('error', errorHandler)
+                this.eventHandlers['wsError'].push(errorHandler)
+
+                // Ping handler
+                const pingHandler = (data: Buffer) => {
+                    // Respond to ping with pong
+                    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+                        this.ws.pong(data)
+                    }
+                }
+                this.ws.on('ping', pingHandler)
+                this.eventHandlers['wsPing'].push(pingHandler)
+
+                // Pong handler
+                const pongHandler = () => {
+                    logger.debug('Received pong')
+                }
+                this.ws.on('pong', pongHandler)
+                this.eventHandlers['wsPong'].push(pongHandler)
+
+                // Set connection timeout
+                const connectionTimeout = setTimeout(() => {
+                    if (this.ws && this.ws.readyState !== WebSocket.OPEN) {
+                        logger.error('WebSocket connection timed out')
+                        this.closeWebSocket()
+                        reject(new Error('WebSocket connection timed out'))
+                    }
+                }, 35000) // 35 seconds (slightly longer than handshake timeout)
+
+                // Add a handler to clear the timeout on successful connection
+                this.ws.once('open', () => {
+                    clearTimeout(connectionTimeout)
+                })
+            } catch (error) {
+                logger.error(`Failed to connect WebSocket: ${error}`)
+                this.isConnected = false
+                reject(error)
+            }
+        })
+    }
+
+    /**
+     * Close the WebSocket connection with proper cleanup
+     */
+    private closeWebSocket(): void {
+        if (this.ws) {
+            try {
+                logger.debug('Closing WebSocket connection')
+
+                // Remove all event listeners before closing
+                this.ws.removeAllListeners('open')
+                this.ws.removeAllListeners('message')
+                this.ws.removeAllListeners('close')
+                this.ws.removeAllListeners('error')
+                this.ws.removeAllListeners('ping')
+                this.ws.removeAllListeners('pong')
+
+                // Try to close gracefully first
+                if (this.ws.readyState === WebSocket.OPEN) {
+                    // Set timeout in case close hangs
+                    const closeTimeout = setTimeout(() => {
+                        logger.warn('WebSocket close timed out, forcing termination')
+                        if (this.ws) {
+                            try {
+                                this.ws.terminate()
+                            } catch (e) {
+                                // Ignore errors on terminate after timeout
+                            }
+                            this.ws = undefined
+                        }
+                    }, 1000)
+
+                    // Try graceful closure first
+                    this.ws.close(1000, 'Normal Closure')
+
+                    // Set up a handler to clear the timeout if close works normally
+                    this.ws.once('close', () => {
+                        clearTimeout(closeTimeout)
+                    })
+                } else {
+                    // If not open, just terminate
+                    this.ws.terminate()
+                }
+            } catch (error) {
+                logger.error(`Error closing WebSocket: ${error}`)
+            } finally {
+                this.ws = undefined
+            }
+        }
+    }
+
+    /**
+     * Start the ping interval to keep the connection alive
+     */
+    private startPingInterval(): void {
+        this.stopPingInterval()
+
+        // Send ping every 30 seconds to keep the connection alive
+        this.pingInterval = setInterval(() => {
+            if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+                logger.debug('Sending ping')
+                try {
+                    this.ws.ping(crypto.randomBytes(16))
+                } catch (error) {
+                    logger.error(`Error sending ping: ${error}`)
+                }
+            } else {
+                // If websocket is no longer open, stop the interval
+                this.stopPingInterval()
+            }
+        }, 30000)
+    }
+
+    /**
+     * Stop the ping interval with better error handling
+     */
+    private stopPingInterval(): void {
+        try {
+            if (this.pingInterval) {
+                clearInterval(this.pingInterval)
+                this.pingInterval = undefined
+                logger.debug('Ping interval stopped')
+            }
+        } catch (error) {
+            logger.error(`Error stopping ping interval: ${error}`)
+            this.pingInterval = undefined
+        }
+    }
+
+    /**
+     * Attempt to reconnect to the WebSocket server with better resource management
+     */
+    private async attemptReconnect(): Promise {
+        if (this.isDisposed) {
+            logger.debug('LocalProxy is disposed, not attempting reconnect')
+            return
+        }
+
+        if (!this.clientToken) {
+            logger.debug('stop retrying, ws closed manually')
+            return
+        }
+
+        if (this.reconnectAttempts >= this.maxReconnectAttempts) {
+            logger.error('Max reconnect attempts reached')
+            // Clean up resources when max attempts reached
+            this.stop()
+            return
+        }
+
+        this.reconnectAttempts++
+        const delay = this.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1)
+
+        logger.debug(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`)
+
+        // Use a tracked timeout that we can clear if needed
+        const reconnectTimeoutId = setTimeout(() => {
+            if (!this.isDisposed) {
+                void this.connectWebSocket().catch((err) => {
+                    logger.error(`Reconnect failed: ${err}`)
+                })
+            } else {
+                logger.debug('Reconnect cancelled because LocalProxy was disposed')
+            }
+        }, delay)
+
+        // Store the timeout ID so it can be cleared if stop() is called
+        if (!this.eventHandlers['reconnectTimeouts']) {
+            this.eventHandlers['reconnectTimeouts'] = []
+        }
+        this.eventHandlers['reconnectTimeouts'].push(reconnectTimeoutId)
+    }
+
+    /**
+     * Handle a WebSocket message
+     * @param data The message data
+     */
+    private handleWebSocketMessage(data: WebSocket.RawData): void {
+        try {
+            // Handle binary data
+            if (Buffer.isBuffer(data)) {
+                let offset = 0
+
+                // Process all messages in the buffer
+                while (offset < data.length) {
+                    // Read the 2-byte length prefix
+                    if (offset + 2 > data.length) {
+                        logger.error('Incomplete message length prefix')
+                        break
+                    }
+
+                    const messageLength = data.readUInt16BE(offset)
+                    offset += 2
+
+                    // Check if we have the complete message
+                    if (offset + messageLength > data.length) {
+                        logger.error('Incomplete message data')
+                        break
+                    }
+
+                    // Extract the message data
+                    const messageData = data.slice(offset, offset + messageLength)
+                    offset += messageLength
+
+                    // Decode and process the message
+                    this.processMessage(messageData)
+                }
+            } else {
+                logger.warn('Received non-buffer WebSocket message')
+            }
+        } catch (error) {
+            logger.error(`Error handling WebSocket message:${error}`)
+        }
+    }
+
+    /**
+     * Process a decoded message
+     * @param messageData The message data
+     */
+    private processMessage(messageData: Buffer): void {
+        try {
+            if (!this.Message) {
+                logger.error('Protobuf Message type not loaded')
+                return
+            }
+
+            // Decode the message
+            const message = this.Message.decode(messageData)
+
+            // Process based on message type
+            const typedMessage = message as any
+            switch (typedMessage.type) {
+                case MessageType.DATA:
+                    this.handleDataMessage(message)
+                    break
+
+                case MessageType.STREAM_RESET:
+                    this.handleStreamReset(message)
+                    break
+
+                case MessageType.CONNECTION_RESET:
+                    this.handleConnectionReset(message)
+                    break
+
+                case MessageType.SESSION_RESET:
+                    this.handleSessionReset()
+                    break
+
+                case MessageType.SERVICE_IDS:
+                    this.handleServiceIds(message)
+                    break
+
+                default:
+                    logger.debug(`Received message of type ${typedMessage.type}`)
+                    break
+            }
+        } catch (error) {
+            logger.error(`Error processing message:${error}`)
+        }
+    }
+
+    /**
+     * Handle a DATA message
+     * @param message The message
+     */
+    private handleDataMessage(message: any): void {
+        const { streamId, connectionId, payload } = message
+
+        // Validate stream ID
+        if (streamId !== this.currentStreamId) {
+            logger.warn(`Received data for invalid stream ID: ${streamId}, current: ${this.currentStreamId}`)
+            return
+        }
+
+        // Find the connection
+        const connection = this.tcpConnections.get(connectionId || 1)
+        if (!connection) {
+            logger.warn(`Received data for unknown connection ID: ${connectionId}`)
+            return
+        }
+
+        logger.debug(`Received data for connection ${connectionId} in stream ${streamId}`)
+
+        // Write data to the TCP socket
+        if (connection.socket.writable) {
+            connection.socket.write(Buffer.from(payload))
+        }
+    }
+
+    /**
+     * Handle a STREAM_RESET message
+     * @param message The message
+     */
+    private handleStreamReset(message: any): void {
+        const { streamId } = message
+
+        logger.debug(`Received STREAM_RESET for stream ${streamId}`)
+
+        // Close all connections for this stream
+        for (const [connectionId, connection] of this.tcpConnections.entries()) {
+            if (connection.streamId === streamId) {
+                connection.socket.destroy()
+                this.tcpConnections.delete(connectionId)
+            }
+        }
+    }
+
+    /**
+     * Handle a CONNECTION_RESET message
+     * @param message The message
+     */
+    private handleConnectionReset(message: any): void {
+        const { streamId, connectionId } = message
+
+        logger.debug(`Received CONNECTION_RESET for connection ${connectionId} in stream ${streamId}`)
+
+        // Close the specific connection
+        const connection = this.tcpConnections.get(connectionId)
+        if (connection) {
+            connection.socket.destroy()
+            this.tcpConnections.delete(connectionId)
+        }
+    }
+
+    /**
+     * Handle a SESSION_RESET message
+     */
+    private handleSessionReset(): void {
+        logger.debug('Received SESSION_RESET')
+
+        // Close all connections
+        for (const connection of this.tcpConnections.values()) {
+            connection.socket.destroy()
+        }
+        this.tcpConnections.clear()
+
+        // Increment stream ID for new connections
+        this.currentStreamId++
+    }
+
+    /**
+     * Handle a SERVICE_IDS message
+     * @param message The message
+     */
+    private handleServiceIds(message: any): void {
+        const { availableServiceIds } = message
+
+        logger.debug(`Received SERVICE_IDS: ${availableServiceIds}`)
+
+        // Validate service IDs
+        if (Array.isArray(availableServiceIds) && availableServiceIds.length > 0) {
+            // Use the first service ID
+            this.serviceId = availableServiceIds[0]
+        }
+    }
+
+    /**
+     * Send a message over the WebSocket
+     * @param messageType The message type
+     * @param streamId The stream ID
+     * @param connectionId The connection ID
+     * @param payload The payload
+     */
+    private sendMessage(messageType: MessageType, streamId: number, connectionId: number, payload?: Buffer): void {
+        if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
+            logger.warn('WebSocket not connected, cannot send message')
+            return
+        }
+
+        if (!this.Message) {
+            logger.error('Protobuf Message type not loaded')
+            return
+        }
+
+        try {
+            // Create the message
+            const message = {
+                type: messageType,
+                streamId,
+                connectionId,
+                serviceId: this.serviceId,
+            }
+
+            // Add payload if provided
+            const typedMessage: any = message
+            if (payload) {
+                typedMessage.payload = payload
+            }
+
+            // Verify and encode the message
+            const err = this.Message.verify(message)
+            if (err) {
+                throw new Error(`Invalid message: ${err}`)
+            }
+
+            const encodedMessage = this.Message.encode(this.Message.create(message)).finish()
+
+            // Create the frame with 2-byte length prefix
+            const frameLength = encodedMessage.length
+            const frame = Buffer.alloc(2 + frameLength)
+
+            // Write the length prefix
+            frame.writeUInt16BE(frameLength, 0)
+
+            // Copy the encoded message
+            Buffer.from(encodedMessage).copy(frame, 2)
+
+            // Send the frame
+            if (this.ws?.readyState === WebSocket.OPEN) {
+                this.ws.send(frame)
+            } else {
+                logger.warn('WebSocket connection lost before sending message')
+            }
+        } catch (error) {
+            logger.error(`Error sending message: ${error}`)
+        }
+    }
+
+    /**
+     * Send a STREAM_START message
+     * @param streamId The stream ID
+     * @param connectionId The connection ID
+     */
+    private sendStreamStart(streamId: number, connectionId: number): void {
+        logger.debug(`Sending STREAM_START for stream ${streamId}, connection ${connectionId}`)
+        this.sendMessage(MessageType.STREAM_START, streamId, connectionId)
+    }
+
+    /**
+     * Send a CONNECTION_START message
+     * @param streamId The stream ID
+     * @param connectionId The connection ID
+     */
+    private sendConnectionStart(streamId: number, connectionId: number): void {
+        logger.debug(`Sending CONNECTION_START for stream ${streamId}, connection ${connectionId}`)
+        this.sendMessage(MessageType.CONNECTION_START, streamId, connectionId)
+    }
+
+    /**
+     * Send a CONNECTION_RESET message
+     * @param streamId The stream ID
+     * @param connectionId The connection ID
+     */
+    private sendConnectionReset(streamId: number, connectionId: number): void {
+        logger.debug(`Sending CONNECTION_RESET for stream ${streamId}, connection ${connectionId}`)
+        this.sendMessage(MessageType.CONNECTION_RESET, streamId, connectionId)
+    }
+
+    /**
+     * Send data over the WebSocket
+     * @param streamId The stream ID
+     * @param connectionId The connection ID
+     * @param data The data to send
+     */
+    private sendData(streamId: number, connectionId: number, data: Buffer): void {
+        // Split data into chunks if it exceeds the maximum payload size (63kb)
+        const maxChunkSize = 63 * 1024 // 63kb
+
+        for (let offset = 0; offset < data.length; offset += maxChunkSize) {
+            const chunk = data.slice(offset, offset + maxChunkSize)
+            this.sendMessage(MessageType.DATA, streamId, connectionId, chunk)
+        }
+    }
+}
diff --git a/packages/core/src/lambda/remoteDebugging/localStackLambdaDebugger.ts b/packages/core/src/lambda/remoteDebugging/localStackLambdaDebugger.ts
new file mode 100644
index 00000000000..d74b6ac3471
--- /dev/null
+++ b/packages/core/src/lambda/remoteDebugging/localStackLambdaDebugger.ts
@@ -0,0 +1,164 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { FunctionConfiguration } from '@aws-sdk/client-lambda'
+import globals from '../../shared/extensionGlobals'
+import { persistLambdaSnapshot, type LambdaDebugger, type DebugConfig } from './lambdaDebugger'
+import { getLambdaClientWithAgent, getLambdaDebugUserAgent } from './utils'
+import { getLogger } from '../../shared/logger/logger'
+import { ToolkitError } from '../../shared/errors'
+
+export class LocalStackLambdaDebugger implements LambdaDebugger {
+    private debugConfig: DebugConfig
+
+    constructor(debugConfig: DebugConfig) {
+        this.debugConfig = debugConfig
+    }
+
+    public async checkHealth(): Promise {
+        const endpointUrl = globals.awsContext.getCredentialEndpointUrl()
+        const localStackHealthUrl = `${endpointUrl}/_localstack/health`
+        const localStackNotRunningMessage = 'LocalStack is not reachable. Ensure LocalStack is running!'
+        try {
+            const response = await fetch(localStackHealthUrl)
+            if (!response.ok) {
+                getLogger().error(`LocalStack health check failed with status ${response.status}`)
+                throw new ToolkitError(localStackNotRunningMessage)
+            }
+        } catch (error) {
+            throw ToolkitError.chain(error, localStackNotRunningMessage)
+        }
+    }
+
+    public async setup(
+        progress: vscode.Progress<{ message?: string; increment?: number }>,
+        functionConfig: FunctionConfiguration,
+        region: string
+    ): Promise {
+        // No function update and version publishing needed for LocalStack
+        this.debugConfig.shouldPublishVersion = false
+
+        progress.report({ message: 'Creating LocalStack debug configuration...' })
+        const endpointUrl = globals.awsContext.getCredentialEndpointUrl()
+        const localStackLDMUrl = `${endpointUrl}/_aws/lambda/debug_configs/${functionConfig.FunctionArn}:$LATEST`
+        const response = await fetch(localStackLDMUrl, {
+            method: 'PUT',
+            body: JSON.stringify({
+                port: this.debugConfig.port,
+                user_agent: getLambdaDebugUserAgent(),
+            }),
+        })
+
+        if (!response.ok) {
+            const error = await this.errorFromResponse(response)
+            if (error.startsWith('UnsupportedLocalStackVersion')) {
+                void vscode.window.showErrorMessage(`${error}`, 'Update LocalStack Docker image').then((selection) => {
+                    if (selection) {
+                        const terminal = vscode.window.createTerminal('Update LocalStack Docker image')
+                        terminal.show()
+                        terminal.sendText('localstack update docker-images')
+                    }
+                })
+            } else {
+                void vscode.window.showErrorMessage(error)
+            }
+
+            throw ToolkitError.chain(
+                error,
+                `Failed to create LocalStack debug configuration for Lambda function ${functionConfig.FunctionName}.`
+            )
+        }
+
+        const json = await response.json()
+        this.debugConfig.port = json.port
+    }
+
+    private async errorFromResponse(response: Response): Promise {
+        const isXml = response.headers.get('content-type') === 'application/xml'
+        if (isXml) {
+            return 'UnsupportedLocalStackVersion: Your current LocalStack version does not support Lambda remote debugging. Update LocalStack and check your license.'
+        }
+
+        const isJson = response.headers.get('content-type') === 'application/json'
+        if (isJson) {
+            const json = await response.json()
+            if (json.error.type !== undefined && json.error.message !== undefined) {
+                return `${json.error.type}: ${json.error.message}`
+            }
+        }
+
+        return 'Unknown error'
+    }
+
+    public async waitForSetup(
+        progress: vscode.Progress<{ message?: string; increment?: number }>,
+        functionConfig: FunctionConfiguration,
+        region: string
+    ): Promise {
+        if (!functionConfig?.FunctionArn) {
+            throw new ToolkitError('Could not retrieve Lambda function configuration')
+        }
+
+        progress.report({ message: 'Waiting for Lambda function to become Active...' })
+        getLogger().info(`Waiting for ${functionConfig.FunctionArn} to become Active...`)
+        try {
+            await getLambdaClientWithAgent(region).waitForActive(functionConfig.FunctionArn)
+        } catch (error) {
+            throw ToolkitError.chain(error, 'Lambda function failed to become Active.')
+        }
+
+        progress.report({ message: 'Waiting for startup of execution environment and debugger...' })
+        getLogger().info(`Waiting for ${functionConfig.FunctionArn} to startup execution environment and debugger...`)
+        const endpointUrl = globals.awsContext.getCredentialEndpointUrl()
+        const localStackLDMUrl = `${endpointUrl}/_aws/lambda/debug_configs/${functionConfig.FunctionArn}:$LATEST?debug_server_ready_timeout=300`
+        // Blocking call to wait for the Lambda function debug server to be running. LocalStack probes the debug server.
+        const response = await fetch(localStackLDMUrl, { method: 'GET' })
+        if (!response.ok) {
+            const error = await this.errorFromResponse(response)
+            throw ToolkitError.chain(
+                new Error(error),
+                `Failed to startup execution environment or debugger for Lambda function ${functionConfig.FunctionName}.`
+            )
+        }
+
+        const json = await response.json()
+        if (json.is_debug_server_running !== true) {
+            throw new ToolkitError(
+                `Debug server on port ${this.debugConfig.port} is not running for Lambda function ${functionConfig.FunctionName}.`
+            )
+        }
+
+        getLogger().info(`${functionConfig.FunctionArn} is ready for debugging on port ${this.debugConfig.port}.`)
+    }
+
+    public async waitForFunctionUpdates(
+        progress: vscode.Progress<{ message?: string; increment?: number }>
+    ): Promise {
+        // No additional steps needed for LocalStack:
+        // a) Port probing ensures the debug server is ready
+        // b) Invokes for debug-enabled await being served until the debugger is connected
+    }
+
+    public async cleanup(functionConfig: FunctionConfiguration): Promise {
+        await vscode.commands.executeCommand('workbench.action.debug.stop')
+
+        const endpointUrl = globals.awsContext.getCredentialEndpointUrl()
+        const localStackLDMUrl = `${endpointUrl}/_aws/lambda/debug_configs/${functionConfig.FunctionArn}:$LATEST`
+        const response = await fetch(localStackLDMUrl, { method: 'DELETE' })
+        if (!response.ok) {
+            const error = await this.errorFromResponse(response)
+            getLogger().warn(
+                `Failed to remove LocalStack debug configuration for ${functionConfig.FunctionArn}. ${error}`
+            )
+            throw new ToolkitError(
+                `Failed to remove LocalStack debug configuration for Lambda function ${functionConfig.FunctionName}.`
+            )
+        }
+
+        await persistLambdaSnapshot(undefined)
+        getLogger().info(`Removed LocalStack debug configuration for ${functionConfig.FunctionArn}`)
+    }
+}
diff --git a/packages/core/src/lambda/remoteDebugging/remoteLambdaDebugger.ts b/packages/core/src/lambda/remoteDebugging/remoteLambdaDebugger.ts
new file mode 100644
index 00000000000..afc9f83abcd
--- /dev/null
+++ b/packages/core/src/lambda/remoteDebugging/remoteLambdaDebugger.ts
@@ -0,0 +1,155 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as vscode from 'vscode'
+import { Architecture, FunctionConfiguration } from '@aws-sdk/client-lambda'
+import { persistLambdaSnapshot, type LambdaDebugger, type DebugConfig } from './lambdaDebugger'
+import { getLogger } from '../../shared/logger/logger'
+import { isTunnelInfo, LdkClient } from './ldkClient'
+import type { TunnelInfo } from './ldkClient'
+import { ToolkitError } from '../../shared/errors'
+import { getRemoteDebugLayerForArch } from './ldkLayers'
+
+export function getRemoteDebugLayer(
+    region: string | undefined,
+    architectures: Architecture[] | undefined
+): string | undefined {
+    if (!region || !architectures) {
+        return undefined
+    }
+    if (architectures.includes('x86_64')) {
+        return getRemoteDebugLayerForArch(region, 'x86_64')
+    }
+    if (architectures.includes('arm64')) {
+        return getRemoteDebugLayerForArch(region, 'arm64')
+    }
+    return undefined
+}
+
+export interface QualifierProxy {
+    setQualifier(qualifier: string): void
+    getQualifier(): string | undefined
+}
+
+export class RemoteLambdaDebugger implements LambdaDebugger {
+    private debugConfig: DebugConfig
+    private debugDeployPromise: Promise | undefined
+    private tunnelInfo: TunnelInfo | undefined
+    private qualifierProxy: QualifierProxy
+
+    constructor(debugConfig: DebugConfig, qualifierProxy: QualifierProxy) {
+        this.debugConfig = debugConfig
+        this.qualifierProxy = qualifierProxy
+    }
+
+    public async checkHealth(): Promise {
+        // We assume AWS is always available
+    }
+
+    public async setup(
+        progress: vscode.Progress<{ message?: string; increment?: number }>,
+        functionConfig: FunctionConfiguration,
+        region: string
+    ): Promise {
+        const ldkClient = LdkClient.instance
+        // Create or reuse tunnel
+        progress.report({ message: 'Creating secure tunnel...' })
+        getLogger().info('Creating secure tunnel...')
+        this.tunnelInfo = await ldkClient.createOrReuseTunnel(region)
+        if (!this.tunnelInfo) {
+            throw new ToolkitError(`Empty tunnel info response, please retry: ${this.tunnelInfo}`)
+        }
+
+        if (!isTunnelInfo(this.tunnelInfo)) {
+            throw new ToolkitError(`Invalid tunnel info response: ${this.tunnelInfo}`)
+        }
+        // start update lambda function, await in the end
+        // Create debug deployment
+        progress.report({ message: 'Configuring Lambda function for debugging...' })
+        getLogger().info('Configuring Lambda function for debugging...')
+
+        const layerArn = this.debugConfig.layerArn ?? getRemoteDebugLayer(region, functionConfig.Architectures)
+        if (!layerArn) {
+            throw new ToolkitError(`No Layer Arn is provided`)
+        }
+        // start this request and await in the end
+        this.debugDeployPromise = ldkClient.createDebugDeployment(
+            functionConfig,
+            this.tunnelInfo.destinationToken,
+            this.debugConfig.lambdaTimeout ?? 900,
+            this.debugConfig.shouldPublishVersion,
+            layerArn,
+            progress
+        )
+    }
+
+    public async waitForSetup(
+        progress: vscode.Progress<{ message?: string; increment?: number }>,
+        functionConfig: FunctionConfiguration,
+        region: string
+    ): Promise {
+        if (!this.tunnelInfo) {
+            throw new ToolkitError(`Empty tunnel info response, please retry: ${this.tunnelInfo}`)
+        }
+
+        // Start local proxy with timeout and better error handling
+        progress.report({ message: 'Starting local proxy...' })
+
+        const proxyStartTimeout = new Promise((_, reject) => {
+            setTimeout(() => reject(new Error('Local proxy start timed out')), 30000)
+        })
+
+        const proxyStartAttempt = LdkClient.instance.startProxy(
+            region,
+            this.tunnelInfo.sourceToken,
+            this.debugConfig.port
+        )
+
+        const proxyStarted = await Promise.race([proxyStartAttempt, proxyStartTimeout])
+
+        if (!proxyStarted) {
+            throw new ToolkitError('Failed to start local proxy')
+        }
+        getLogger().info('Local proxy started successfully')
+    }
+
+    public async waitForFunctionUpdates(
+        progress: vscode.Progress<{ message?: string; increment?: number }>
+    ): Promise {
+        // wait until lambda function update is completed
+        progress.report({ message: 'Waiting for function update...' })
+        const qualifier = await this.debugDeployPromise
+        if (!qualifier || qualifier === 'Failed') {
+            throw new ToolkitError('Failed to configure Lambda function for debugging')
+        }
+        // store the published version for debugging in version
+        if (this.debugConfig.shouldPublishVersion) {
+            // we already reverted
+            this.qualifierProxy.setQualifier(qualifier)
+        }
+    }
+
+    public async cleanup(functionConfig: FunctionConfiguration): Promise {
+        const ldkClient = LdkClient.instance
+        if (!functionConfig?.FunctionArn) {
+            throw new ToolkitError('No saved configuration found during cleanup')
+        }
+
+        getLogger().info(`Removing debug deployment for function: ${functionConfig.FunctionName}`)
+
+        await vscode.commands.executeCommand('workbench.action.debug.stop')
+        // Then stop the proxy (with more reliable error handling)
+        getLogger().info('Stopping proxy during cleanup')
+        await ldkClient.stopProxy()
+        // Ensure our resources are properly cleaned up
+        const qualifier = this.qualifierProxy.getQualifier()
+        if (qualifier) {
+            await ldkClient.deleteDebugVersion(functionConfig?.FunctionArn, qualifier)
+        }
+        if (await ldkClient.removeDebugDeployment(functionConfig, true)) {
+            await persistLambdaSnapshot(undefined)
+        }
+    }
+}
diff --git a/packages/core/src/lambda/remoteDebugging/utils.ts b/packages/core/src/lambda/remoteDebugging/utils.ts
new file mode 100644
index 00000000000..8f2ea862556
--- /dev/null
+++ b/packages/core/src/lambda/remoteDebugging/utils.ts
@@ -0,0 +1,42 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { IoTSecureTunnelingClient } from '@aws-sdk/client-iotsecuretunneling'
+import { DefaultLambdaClient } from '../../shared/clients/lambdaClient'
+import { getUserAgent } from '../../shared/telemetry/util'
+import globals from '../../shared/extensionGlobals'
+
+const customUserAgentBase = 'LAMBDA-DEBUG/1.0.0'
+
+export function getLambdaClientWithAgent(region: string, customUserAgent?: string): DefaultLambdaClient {
+    if (!customUserAgent) {
+        customUserAgent = getLambdaUserAgent()
+    }
+    return new DefaultLambdaClient(region, customUserAgent)
+}
+
+// Example user agent:
+// LAMBDA-DEBUG/1.0.0 AWS-Toolkit-For-VSCode/testPluginVersion Visual-Studio-Code/1.102.2 ClientId/11111111-1111-1111-1111-111111111111
+export function getLambdaDebugUserAgent(): string {
+    return `${customUserAgentBase} ${getLambdaUserAgent()}`
+}
+
+// Example user agent:
+// AWS-Toolkit-For-VSCode/testPluginVersion Visual-Studio-Code/1.102.2 ClientId/11111111-1111-1111-1111-111111111111
+export function getLambdaUserAgent(): string {
+    return `${getUserAgent({ includePlatform: true, includeClientId: true })}`
+}
+
+export function getIoTSTClientWithAgent(region: string): IoTSecureTunnelingClient {
+    const customUserAgent = `${customUserAgentBase} ${getUserAgent({ includePlatform: true, includeClientId: true })}`
+    return globals.sdkClientBuilderV3.createAwsService({
+        serviceClient: IoTSecureTunnelingClient,
+        clientOptions: {
+            userAgent: [[customUserAgent]],
+            region,
+        },
+        userAgent: false,
+    })
+}
diff --git a/packages/core/src/lambda/uriHandlers.ts b/packages/core/src/lambda/uriHandlers.ts
new file mode 100644
index 00000000000..8ae1d7b8c35
--- /dev/null
+++ b/packages/core/src/lambda/uriHandlers.ts
@@ -0,0 +1,58 @@
+/*!
+ * 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 { 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 {
+                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
new file mode 100644
index 00000000000..9b8ffcb884a
--- /dev/null
+++ b/packages/core/src/lambda/utils.ts
@@ -0,0 +1,214 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as nls from 'vscode-nls'
+const localize = nls.loadMessageBundle()
+
+import path from 'path'
+import xml2js = require('xml2js')
+import { FunctionConfiguration, LayerVersionsListItem } from '@aws-sdk/client-lambda'
+import * as vscode from 'vscode'
+import { CloudFormationClient, StackSummary } from '../shared/clients/cloudFormation'
+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
+    // in the explorer, and be removed when that region's query completes
+    const status = vscode.window.setStatusBarMessage(
+        localize('AWS.message.statusBar.loading.cloudFormation', 'Loading CloudFormation Stacks...')
+    )
+
+    try {
+        yield* client.listStacks()
+    } finally {
+        status.dispose()
+    }
+}
+
+export async function* listLambdaFunctions(client: LambdaClient): AsyncIterableIterator {
+    const status = vscode.window.setStatusBarMessage(
+        localize('AWS.message.statusBar.loading.lambda', 'Loading Lambdas...')
+    )
+
+    try {
+        yield* client.listFunctions()
+    } finally {
+        if (status) {
+            status.dispose()
+        }
+    }
+}
+
+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)
+ * @param configuration Lambda configuration object from getFunction
+ */
+export function getLambdaDetails(configuration: FunctionConfiguration): {
+    fileName: string
+    functionName: string
+} {
+    let runtimeExtension: string
+    switch (getFamily(configuration.Runtime!)) {
+        case RuntimeFamily.Python:
+            runtimeExtension = 'py'
+            break
+        case RuntimeFamily.NodeJS: {
+            const nodeVersion = getNodeMajorVersion(configuration.Runtime)
+            if (nodeVersion && nodeVersion >= 18) {
+                // node18+ defaults to using the .mjs extension
+                runtimeExtension = 'mjs'
+            } else {
+                runtimeExtension = 'js'
+            }
+            break
+        }
+        case RuntimeFamily.Ruby:
+            runtimeExtension = 'rb'
+            break
+        default:
+            throw new Error(`Toolkit does not currently support imports for runtime: ${configuration.Runtime}`)
+    }
+
+    const handlerArr = configuration.Handler!.split('.')
+
+    return {
+        fileName: `${handlerArr.slice(0, handlerArr.length - 1).join('.')}.${runtimeExtension}`,
+        functionName: handlerArr[handlerArr.length - 1]!,
+    }
+}
+
+export interface SampleRequest {
+    name: string | undefined
+    filename: string | undefined
+}
+
+interface SampleRequestManifest {
+    requests: {
+        request: SampleRequest[]
+    }
+}
+
+export async function getSampleLambdaPayloads(): Promise {
+    const logger = getLogger()
+    const sampleInput = await getSampleRequestManifest()
+
+    if (!sampleInput) {
+        throw new Error('Unable to retrieve Sample Request manifest')
+    }
+
+    logger.debug(`Loaded: ${sampleInput}`)
+
+    const inputs: SampleRequest[] = []
+
+    xml2js.parseString(sampleInput, { explicitArray: false }, (err: Error | null, result: SampleRequestManifest) => {
+        if (err) {
+            return
+        }
+
+        inputs.push(...result.requests.request)
+    })
+
+    return inputs
+}
+
+async function getSampleRequestManifest(): Promise {
+    const httpResp = await new HttpResourceFetcher(sampleRequestManifestPath, { showUrl: true }).get()
+    if (!httpResp) {
+        const fileResp = new FileResourceFetcher(globals.manifestPaths.lambdaSampleRequests)
+        return fileResp.get()
+    }
+    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 interface FunctionInfo {
+    lastDeployed?: number
+    undeployed?: boolean
+    sha?: string
+    handlerFile?: string
+}
+
+export async function getFunctionInfo(lambda: LambdaFunction, field?: K) {
+    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: Partial) {
+    try {
+        const existing = await getFunctionInfo(lambda)
+        const updated: FunctionInfo = {
+            lastDeployed: info.lastDeployed ?? existing.lastDeployed,
+            undeployed: info.undeployed ?? true,
+            sha: info.sha ?? (await getCodeShaLive(lambda)),
+            handlerFile: info.handlerFile ?? existing.handlerFile,
+        }
+        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)
+}
+
+// LocalStack hot-reloading: https://docs.localstack.cloud/aws/tooling/lambda-tools/hot-reloading/
+export function isHotReloadingFunction(codeSha256: string | undefined): boolean {
+    return codeSha256?.startsWith('hot-reloading') ?? false
+}
diff --git a/packages/core/src/lambda/vue/configEditor/index.ts b/packages/core/src/lambda/vue/configEditor/index.ts
new file mode 100644
index 00000000000..270392b1f65
--- /dev/null
+++ b/packages/core/src/lambda/vue/configEditor/index.ts
@@ -0,0 +1,16 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { createApp } from 'vue'
+import component from './samInvokeComponent.vue'
+
+const create = () => createApp(component)
+const app = create()
+app.mount('#vue-app')
+
+window.addEventListener('remount', () => {
+    app.unmount()
+    create().mount('#vue-app')
+})
diff --git a/packages/core/src/lambda/vue/configEditor/samInvoke.css b/packages/core/src/lambda/vue/configEditor/samInvoke.css
new file mode 100644
index 00000000000..9ca2c8ef452
--- /dev/null
+++ b/packages/core/src/lambda/vue/configEditor/samInvoke.css
@@ -0,0 +1,177 @@
+.section-header {
+    margin: 0px;
+    margin-bottom: 10px;
+    display: flex;
+    justify-content: flex-start;
+}
+
+textarea {
+    color: var(--vscode-settings-textInputForeground);
+    background: var(--vscode-settings-textInputBackground);
+    border: 1px solid var(--vscode-settings-textInputBorder);
+}
+
+.config-item {
+    border-bottom: 1px solid var(--vscode-menubar-selectionBackground);
+    display: grid;
+    grid-template-columns: 1fr 3fr;
+    padding: 8px 0px;
+}
+
+.col2 {
+    grid-column: 2;
+}
+
+.data-view {
+    display: none;
+    border: dashed rgb(218, 31, 31) 1px;
+    color: rgb(218, 31, 31);
+}
+
+.required {
+    color: red;
+}
+
+#form-title {
+    font-size: large;
+    font-weight: bold;
+}
+
+.form-buttons {
+    margin-left: 20px;
+}
+
+.margin-bottom-16 {
+    margin-bottom: 16px;
+}
+
+.header-buttons {
+    display: flex;
+    align-items: center;
+    margin-bottom: 20px;
+}
+
+#target-type-selector {
+    margin-bottom: 15px;
+    margin-left: 8px;
+}
+
+.form-row {
+    display: grid;
+    grid-template-columns: 150px 1fr;
+    margin-bottom: 10px;
+}
+
+.form-control {
+    min-width: 170%; /* Set a minimum width */
+    width: 100%; /* Allow the width to adjust based on content */
+    display: inline-block;
+    flex-grow: 1;
+    margin-right: 0.5rem;
+}
+
+.payload-options-button {
+    display: grid;
+    align-items: center;
+    border: none;
+    padding: 5px 10px;
+    cursor: pointer;
+    font-size: 0.9em;
+    margin-bottom: 10px;
+}
+
+.payload-options-buttons {
+    display: flex;
+    align-items: center;
+    margin-top: 10px;
+    margin-bottom: 10px;
+}
+
+.Icontainer {
+    margin-inline: auto;
+    margin-top: 5rem;
+}
+
+.container {
+    width: 574px;
+    height: 824px;
+    top: 18px;
+    gap: 20px;
+    margin: auto;
+    left: 688px;
+    background-color: var(--vscode-editor-background);
+}
+
+.container em {
+    display: block;
+    text-align: justify;
+}
+
+.button-theme-primary {
+    color: var(--vscode-button-foreground);
+    background: var(--vscode-button-background);
+    border: 1px solid var(--vscode-button-border);
+    padding: 8px 12px;
+}
+.button-theme-primary:hover:not(:disabled) {
+    background: var(--vscode-button-hoverBackground);
+    cursor: pointer;
+}
+.button-theme-secondary {
+    color: var(--vscode-button-secondaryForeground);
+    background: var(--vscode-button-secondaryBackground);
+    border: 1px solid var(--vscode-button-border);
+    padding: 8px 12px;
+}
+.button-theme-secondary:hover:not(:disabled) {
+    background: var(--vscode-button-secondaryHoverBackground);
+    cursor: pointer;
+}
+
+.formfield {
+    display: flex;
+    align-items: center;
+    margin-bottom: 0.5rem;
+}
+
+.payload-options-buttons {
+    display: flex;
+    align-items: center;
+    margin-top: 10px;
+    margin-bottom: 10px;
+}
+
+.radio-selector {
+    width: 15px;
+    height: 15px;
+    border-radius: 50%;
+}
+
+.label-selector {
+    padding-left: 7px;
+    font-weight: 500;
+    font-size: 13px;
+    line-height: 15.51px;
+    text-align: center;
+}
+
+.form-row-select {
+    width: 387px;
+    height: 28px;
+    border: 1px;
+    border-radius: 5px;
+    gap: 4px;
+    padding: 2px 8px;
+}
+
+.form-row-event-select {
+    width: 244px;
+    height: 28px;
+    margin-bottom: 15px;
+    margin-left: 8px;
+}
+
+.runtime-description {
+    font-size: 12px;
+    margin-top: 5px;
+}
diff --git a/packages/core/src/lambda/vue/configEditor/samInvokeBackend.ts b/packages/core/src/lambda/vue/configEditor/samInvokeBackend.ts
new file mode 100644
index 00000000000..2084ebe82fe
--- /dev/null
+++ b/packages/core/src/lambda/vue/configEditor/samInvokeBackend.ts
@@ -0,0 +1,512 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as path from 'path'
+import * as vscode from 'vscode'
+import * as nls from 'vscode-nls'
+import { LaunchConfiguration } from '../../../shared/debug/launchConfiguration'
+import { getLogger } from '../../../shared/logger/logger'
+import { HttpResourceFetcher } from '../../../shared/resourcefetcher/httpResourceFetcher'
+import {
+    AwsSamDebuggerConfiguration,
+    isCodeTargetProperties,
+    isTemplateTargetProperties,
+    TemplateTargetProperties,
+} from '../../../shared/sam/debugger/awsSamDebugConfiguration'
+import {
+    DefaultAwsSamDebugConfigurationValidator,
+    resolveWorkspaceFolderVariable,
+} from '../../../shared/sam/debugger/awsSamDebugConfigurationValidator'
+import * as input from '../../../shared/ui/input'
+import * as picker from '../../../shared/ui/picker'
+import { addCodiconToString } from '../../../shared/utilities/textUtilities'
+import { sampleRequestPath } from '../../constants'
+import { tryGetAbsolutePath } from '../../../shared/utilities/workspaceUtils'
+import * as CloudFormation from '../../../shared/cloudformation/cloudformation'
+import { openLaunchJsonFile } from '../../../shared/sam/debugger/commands/addSamDebugConfiguration'
+import { getSampleLambdaPayloads } from '../../utils'
+import { samLambdaCreatableRuntimes } from '../../models/samLambdaRuntime'
+import globals from '../../../shared/extensionGlobals'
+import { VueWebview } from '../../../webviews/main'
+import { Commands } from '../../../shared/vscode/commands2'
+import { telemetry } from '../../../shared/telemetry/telemetry'
+import { fs } from '../../../shared/fs/fs'
+import { ToolkitError } from '../../../shared/errors'
+import { ResourceNode } from '../../../awsService/appBuilder/explorer/nodes/resourceNode'
+
+const localize = nls.loadMessageBundle()
+
+export interface ResourceData {
+    logicalId: string
+    region: string
+    arn: string
+    location: string
+    handler: string
+    runtime: string
+    stackName: string
+    source: string
+    environment?: {
+        Variables: Record
+    }
+}
+
+export type AwsSamDebuggerConfigurationLoose = AwsSamDebuggerConfiguration & {
+    invokeTarget: Omit<
+        AwsSamDebuggerConfiguration['invokeTarget'],
+        'templatePath' | 'logicalId' | 'lambdaHandler' | 'projectRoot'
+    > & {
+        templatePath: string
+        logicalId: string
+        lambdaHandler: string
+        projectRoot: string
+    }
+}
+
+interface SampleQuickPickItem extends vscode.QuickPickItem {
+    filename: string
+}
+
+export interface LaunchConfigPickItem extends vscode.QuickPickItem {
+    index: number
+    config?: AwsSamDebuggerConfiguration
+}
+
+export class SamInvokeWebview extends VueWebview {
+    public static readonly sourcePath: string = 'src/lambda/vue/configEditor/index.js'
+    public readonly id = 'createLambda'
+
+    public constructor(
+        private readonly config?: AwsSamDebuggerConfiguration,
+        private readonly data?: ResourceData
+    ) {
+        super(SamInvokeWebview.sourcePath)
+    }
+
+    public getRuntimes() {
+        return samLambdaCreatableRuntimes().toArray().sort()
+    }
+
+    public init() {
+        return this.config
+    }
+
+    public getResourceData() {
+        return this.data
+    }
+
+    public async getSamLaunchConfigs(): Promise {
+        // TODO: Find a better way to infer this. Might need another arg from the frontend (depends on the context in which the launch config is made?)
+        const workspaceFolder = vscode.workspace.workspaceFolders?.length
+            ? vscode.workspace.workspaceFolders[0]
+            : undefined
+        if (!workspaceFolder) {
+            void vscode.window.showErrorMessage(localize('AWS.lambda.form.noFolder', 'No workspace folder found.'))
+            return
+        }
+        const uri = workspaceFolder.uri
+        const launchConfig = new LaunchConfiguration(uri)
+        const pickerItems = await this.getLaunchConfigQuickPickItems(launchConfig, uri)
+        return pickerItems
+    }
+
+    /**
+     * Open a quick pick containing the names of launch configs in the `launch.json` array.
+     * Filter out non-supported launch configs.
+     */
+    public async loadSamLaunchConfig(): Promise {
+        const pickerItems: LaunchConfigPickItem[] = (await this.getSamLaunchConfigs()) || []
+
+        if (pickerItems.length === 0) {
+            pickerItems.push({
+                index: -1,
+                label: localize('AWS.lambda.form.noDebugConfigs', 'No aws-sam debug configurations found.'),
+                detail: localize('AWS.picker.dynamic.noItemsFound.detail', 'Click here to go back'),
+            })
+        }
+        const qp = picker.createQuickPick({
+            items: pickerItems,
+            options: {
+                title: localize('AWS.lambda.form.selectDebugConfig', 'Select Debug Configuration'),
+            },
+        })
+
+        const choices = await picker.promptUser({
+            picker: qp,
+        })
+        const pickerResponse = picker.verifySinglePickerOutput(choices)
+
+        if (!pickerResponse || pickerResponse.index === -1) {
+            return
+        }
+        return pickerResponse.config!
+    }
+
+    /**
+     * Open a quick pick containing upstream sample payloads.
+     * Call back into the webview with the contents of the payload to add to the JSON field.
+     */
+    public async getSamplePayload(): Promise {
+        try {
+            const inputs: SampleQuickPickItem[] = (await getSampleLambdaPayloads()).map((entry) => {
+                return { label: entry.name ?? '', filename: entry.filename ?? '' }
+            })
+
+            const qp = picker.createQuickPick({
+                items: inputs,
+                options: {
+                    title: localize('AWS.lambda.form.pickSampleInput', 'Choose Sample Input'),
+                },
+            })
+
+            const choices = await picker.promptUser({
+                picker: qp,
+            })
+            const pickerResponse = picker.verifySinglePickerOutput(choices)
+
+            if (!pickerResponse) {
+                return
+            }
+            const sampleUrl = `${sampleRequestPath}${pickerResponse.filename}`
+            const resp = await new HttpResourceFetcher(sampleUrl, { showUrl: true }).get()
+            const sample = (await resp?.text()) ?? ''
+
+            return sample
+        } catch (err) {
+            getLogger().error('Error getting manifest data..: %O', err as Error)
+            throw ToolkitError.chain(err, 'getting manifest data')
+        }
+    }
+
+    protected getTemplateRegistry() {
+        return globals.templateRegistry
+    }
+
+    /**
+     * Get all templates in the registry.
+     * Call back into the webview with the registry contents.
+     */
+    public async getTemplate() {
+        const items: (vscode.QuickPickItem & { templatePath: string })[] = []
+        const noTemplate = 'NOTEMPLATEFOUND'
+        for (const template of (await this.getTemplateRegistry()).items) {
+            const resources = template.item.Resources
+            if (resources) {
+                for (const resource of Object.keys(resources)) {
+                    if (
+                        resources[resource]?.Type === CloudFormation.LAMBDA_FUNCTION_TYPE ||
+                        resources[resource]?.Type === CloudFormation.SERVERLESS_FUNCTION_TYPE ||
+                        resources[resource]?.Type === CloudFormation.SERVERLESS_API_TYPE
+                    ) {
+                        items.push({
+                            label: resource,
+                            detail: localize('AWS.lambda.form.selectResource.detail', 'Template: {0}', template.path),
+                            templatePath: template.path,
+                        })
+                    }
+                }
+            }
+        }
+
+        if (items.length === 0) {
+            items.push({
+                label: localize(
+                    'AWS.lambda.form.selectResource.noTemplates',
+                    'No templates with valid SAM functions found.'
+                ),
+                detail: localize('AWS.picker.dynamic.noItemsFound.detail', 'Click here to go back'),
+                templatePath: noTemplate,
+            })
+        }
+
+        const qp = picker.createQuickPick({
+            items,
+            options: {
+                title: localize('AWS.lambda.form.selectResource', 'Select Resource'),
+            },
+        })
+
+        const choices = await picker.promptUser({
+            picker: qp,
+        })
+        const selectedTemplate = picker.verifySinglePickerOutput(choices)
+
+        if (!selectedTemplate || selectedTemplate.templatePath === noTemplate) {
+            return
+        }
+
+        return {
+            logicalId: selectedTemplate.label,
+            template: selectedTemplate.templatePath,
+        }
+    }
+
+    // This method serves as a wrapper around the backend function `openLaunchJsonFile`.
+    // The frontend cannot directly import and invoke backend functions like `openLaunchJsonFile`
+    // because doing so would break the webview environment by introducing server-side logic
+    // into client-side code. Instead, this method acts as an interface or bridge, allowing
+    // the frontend to request the backend to open the launch configuration file without
+    // directly coupling the frontend to backend-specific implementations.
+    public async openLaunchConfig() {
+        await openLaunchJsonFile()
+    }
+
+    public async promptFile() {
+        const fileLocations = await vscode.window.showOpenDialog({
+            openLabel: 'Open',
+        })
+
+        if (!fileLocations || fileLocations.length === 0) {
+            return undefined
+        }
+
+        try {
+            const fileContent = await fs.readFileBytes(fileLocations[0].fsPath)
+            return {
+                sample: fileContent,
+                selectedFilePath: fileLocations[0].fsPath,
+                selectedFile: this.getFileName(fileLocations[0].fsPath),
+            }
+        } catch (e) {
+            getLogger().error('readFileSync: Failed to read file at path %s %O', fileLocations[0].fsPath, e)
+            throw ToolkitError.chain(e, 'Failed to read selected file')
+        }
+    }
+
+    public getFileName(filePath: string): string {
+        return path.basename(filePath)
+    }
+    /**
+     * Open a quick pick containing the names of launch configs in the `launch.json` array, plus a "Create New Entry" entry.
+     * On selecting a name, overwrite the existing entry in the `launch.json` array and resave the file.
+     * On selecting "Create New Entry", prompt the user for a name and save the contents to the end of the `launch.json` array.
+     * @param config Config to save
+     */
+    public async saveLaunchConfig(config: AwsSamDebuggerConfiguration): Promise {
+        const uri = await this.getUriFromLaunchConfig(config)
+        if (!uri) {
+            // TODO Localize
+            void vscode.window.showErrorMessage(
+                'Toolkit requires a target resource in order to save a debug configuration'
+            )
+            return
+        }
+
+        const launchConfig = new LaunchConfiguration(uri)
+        const launchConfigItems = await this.getLaunchConfigQuickPickItems(launchConfig, uri)
+        const pickerItems = [
+            {
+                label: addCodiconToString(
+                    'add',
+                    localize('AWS.command.addSamDebugConfiguration', 'Add Local Invoke and Debug Configuration')
+                ),
+                index: -1,
+                alwaysShow: true,
+            },
+            ...launchConfigItems,
+        ]
+
+        const qp = picker.createQuickPick({
+            items: pickerItems,
+            options: {
+                title: localize('AWS.lambda.form.selectDebugConfig', 'Select Debug Configuration'),
+            },
+        })
+
+        const choices = await picker.promptUser({
+            picker: qp,
+        })
+        const pickerResponse = picker.verifySinglePickerOutput(choices)
+
+        if (!pickerResponse) {
+            return
+        }
+
+        if (pickerResponse.index === -1) {
+            const ib = input.createInputBox({
+                options: {
+                    prompt: localize('AWS.lambda.form.debugConfigName', 'Input Name For Debug Configuration'),
+                },
+            })
+            const response = await input.promptUser({ inputBox: ib })
+            if (response) {
+                await launchConfig.addDebugConfiguration(finalizeConfig(config, response))
+                await this.openLaunchConfig()
+            }
+        } else {
+            // use existing label
+            await launchConfig.editDebugConfiguration(
+                finalizeConfig(config, pickerResponse.label),
+                pickerResponse.index
+            )
+            await this.openLaunchConfig()
+        }
+    }
+
+    /**
+     * Validate and execute the provided launch config.
+     * TODO: Post validation failures back to webview?
+     * @param config Config to invoke
+     */
+    public async invokeLaunchConfig(config: AwsSamDebuggerConfiguration, source?: string): Promise {
+        const finalConfig = finalizeConfig(
+            resolveWorkspaceFolderVariable(undefined, config),
+            'Editor-Created Debug Config'
+        )
+        const targetUri = await this.getUriFromLaunchConfig(finalConfig)
+        const folder = targetUri ? vscode.workspace.getWorkspaceFolder(targetUri) : undefined
+
+        // startDebugging on VS Code goes through the whole resolution chain
+        await vscode.debug.startDebugging(folder, finalConfig)
+    }
+    public async getLaunchConfigQuickPickItems(
+        launchConfig: LaunchConfiguration,
+        uri: vscode.Uri
+    ): Promise {
+        const existingConfigs = launchConfig.getDebugConfigurations()
+        const samValidator = new DefaultAwsSamDebugConfigurationValidator(vscode.workspace.getWorkspaceFolder(uri))
+        const registry = await globals.templateRegistry
+        const mapped = existingConfigs.map((val, index) => {
+            return {
+                config: val as AwsSamDebuggerConfiguration,
+                index: index,
+                label: val.name,
+            }
+        })
+        // XXX: can't use filter() with async predicate.
+        const filtered: LaunchConfigPickItem[] = []
+        for (const c of mapped) {
+            const valid = await samValidator.validate(c.config, registry, true)
+            if (valid?.isValid) {
+                filtered.push(c)
+            }
+        }
+        return filtered
+    }
+
+    public async getUriFromLaunchConfig(config: AwsSamDebuggerConfiguration): Promise {
+        let targetPath: string
+        if (isTemplateTargetProperties(config.invokeTarget)) {
+            targetPath = config.invokeTarget.templatePath
+        } else if (isCodeTargetProperties(config.invokeTarget)) {
+            targetPath = config.invokeTarget.projectRoot
+        } else {
+            // error
+            return undefined
+        }
+        if (path.isAbsolute(targetPath)) {
+            return vscode.Uri.file(targetPath)
+        }
+        // TODO: rework this logic (and config variables in general)
+        // we have too many places where we try to resolve these paths when it realistically can be
+        // in a single place. Much less bug-prone when it's centralized.
+        // the following line is a quick-fix for a very narrow edge-case
+        targetPath = targetPath.replace('${workspaceFolder}/', '')
+        const workspaceFolders = vscode.workspace.workspaceFolders || []
+        for (const workspaceFolder of workspaceFolders) {
+            const absolutePath = tryGetAbsolutePath(workspaceFolder, targetPath)
+            if (await fs.exists(absolutePath)) {
+                return vscode.Uri.file(absolutePath)
+            }
+        }
+
+        return undefined
+    }
+}
+
+const WebviewPanel = VueWebview.compilePanel(SamInvokeWebview)
+
+export function registerSamInvokeVueCommand(context: vscode.ExtensionContext): vscode.Disposable {
+    return Commands.register('aws.launchConfigForm', async (launchConfig?: AwsSamDebuggerConfiguration) => {
+        const webview = new WebviewPanel(context, launchConfig)
+        await telemetry.sam_openConfigUi.run(async (span) => {
+            await webview.show({
+                title: localize('AWS.command.launchConfigForm.title', 'Local Invoke and Debug Configuration'),
+                // TODO: make this only open `Beside` when executed via CodeLens
+                viewColumn: vscode.ViewColumn.Beside,
+            })
+        })
+    })
+}
+
+export async function registerSamDebugInvokeVueCommand(
+    context: vscode.ExtensionContext,
+    params: { resource: ResourceNode }
+) {
+    const resource = params?.resource.resource
+    const source = 'AppBuilderLocalInvoke'
+    const launchConfigs = await new LaunchConfiguration(resource.location).getSamDebugConfigurations()
+    const launchConfig = launchConfigs.find(
+        (config) => (config.invokeTarget as TemplateTargetProperties).logicalId === resource.resource.Id
+    )
+
+    const webview = new WebviewPanel(context, launchConfig, {
+        logicalId: resource.resource.Id ?? '',
+        region: resource.region ?? '',
+        location: resource.location.fsPath,
+        handler: resource.resource.Handler!,
+        runtime: launchConfig?.lambda?.runtime ?? resource.resource.Runtime!,
+        arn: resource.functionArn ?? '',
+        stackName: resource.stackName ?? '',
+        environment: resource.resource.Environment,
+        source: source,
+    })
+    await telemetry.sam_openConfigUi.run(async (span) => {
+        telemetry.record({ source: 'AppBuilderDebugger' }),
+            await webview.show({
+                title: localize('AWS.command.launchConfigForm.title', 'Local Invoke and Debug Configuration'),
+                // TODO: make this only open `Beside` when executed via CodeLens
+                viewColumn: vscode.ViewColumn.Beside,
+            })
+    })
+}
+
+export function finalizeConfig(config: AwsSamDebuggerConfiguration, name: string): AwsSamDebuggerConfiguration {
+    const newConfig = doTraverseAndPrune(config)
+    newConfig.name = name
+
+    if (isTemplateTargetProperties(config.invokeTarget)) {
+        newConfig.invokeTarget = {
+            target: config.invokeTarget.target,
+            logicalId: config.invokeTarget.logicalId,
+            templatePath: config.invokeTarget.templatePath,
+        }
+    } else if (isCodeTargetProperties(config.invokeTarget)) {
+        newConfig.invokeTarget = {
+            target: config.invokeTarget.target,
+            lambdaHandler: config.invokeTarget.lambdaHandler,
+            projectRoot: config.invokeTarget.projectRoot,
+        }
+    }
+
+    return newConfig
+}
+
+/**
+ * Removes empty objects, strings, fields, and arrays from a given object.
+ * Use when writing JSON to a file.
+ * @param object
+ * @returns Pruned object
+ */
+function doTraverseAndPrune(object: { [key: string]: any }): any | undefined {
+    const keys = Object.keys(object)
+    const final = JSON.parse(JSON.stringify(object))
+    for (const key of keys) {
+        const val = object[key]
+        if (val === undefined || val === '' || (Array.isArray(val) && val.length === 0)) {
+            delete final[key]
+        } else if (typeof val === 'object') {
+            const pruned = doTraverseAndPrune(val)
+            if (pruned) {
+                final[key] = pruned
+            } else {
+                delete final[key]
+            }
+        }
+    }
+    if (Object.keys(final).length === 0) {
+        return undefined
+    }
+    return final
+}
diff --git a/packages/core/src/lambda/vue/configEditor/samInvokeComponent.vue b/packages/core/src/lambda/vue/configEditor/samInvokeComponent.vue
new file mode 100644
index 00000000000..e7d09c584c9
--- /dev/null
+++ b/packages/core/src/lambda/vue/configEditor/samInvokeComponent.vue
@@ -0,0 +1,400 @@
+/*! * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */
+
+
+
+
+
diff --git a/packages/core/src/lambda/vue/configEditor/samInvokeFrontend.ts b/packages/core/src/lambda/vue/configEditor/samInvokeFrontend.ts
new file mode 100644
index 00000000000..375e22f4878
--- /dev/null
+++ b/packages/core/src/lambda/vue/configEditor/samInvokeFrontend.ts
@@ -0,0 +1,464 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// Disable because it is a front-end file.
+/* eslint-disable aws-toolkits/no-console-log */
+
+import { defineComponent } from 'vue'
+import { AwsSamDebuggerConfiguration } from '../../../shared/sam/debugger/awsSamDebugConfiguration'
+import {
+    AwsSamDebuggerConfigurationLoose,
+    LaunchConfigPickItem,
+    ResourceData,
+    SamInvokeWebview,
+} from './samInvokeBackend'
+import settingsPanel from '../../../webviews/components/settingsPanel.vue'
+import { WebviewClientFactory } from '../../../webviews/client'
+import saveData from '../../../webviews/mixins/saveData'
+
+const client = WebviewClientFactory.create()
+
+interface VueDataLaunchPropertyObject {
+    value: string
+    errorMsg: string
+}
+interface SamInvokeVueData {
+    msg: any
+    targetTypes: { [k: string]: string }[]
+    runtimes: string[]
+    company: string
+    httpMethods: string[]
+    launchConfig: AwsSamDebuggerConfigurationLoose
+    payload: VueDataLaunchPropertyObject
+    apiPayload: VueDataLaunchPropertyObject
+    environmentVariables: VueDataLaunchPropertyObject
+    headers: VueDataLaunchPropertyObject
+    stageVariables: VueDataLaunchPropertyObject
+    parameters: VueDataLaunchPropertyObject
+    containerBuild: boolean
+    skipNewImageCheck: boolean
+    selectedConfig: LaunchConfigPickItem
+    payloadOption: string
+    selectedFile: string
+    selectedFilePath: string
+    selectedTestEvent: string
+    TestEvents: string[]
+    showNameInput: boolean
+    newTestEventName: string
+    resourceData: ResourceData | undefined
+    useDebugger: boolean
+}
+
+function newLaunchConfig(existingConfig?: AwsSamDebuggerConfiguration): AwsSamDebuggerConfigurationLoose {
+    return {
+        type: 'aws-sam',
+        request: 'direct-invoke',
+        name: '',
+        aws: {
+            credentials: '',
+            region: '',
+            ...(existingConfig?.aws ? existingConfig.aws : {}),
+        },
+        invokeTarget: {
+            target: 'template',
+            templatePath: '',
+            logicalId: '',
+            lambdaHandler: '',
+            projectRoot: '',
+            ...existingConfig?.invokeTarget,
+        },
+        lambda: {
+            runtime: existingConfig?.lambda?.runtime,
+            memoryMb: undefined,
+            timeoutSec: undefined,
+            environmentVariables: {},
+            ...existingConfig?.lambda,
+            payload: {
+                json: existingConfig?.lambda?.payload?.json ? existingConfig.lambda.payload.json : {},
+                path: existingConfig?.lambda?.payload?.path ? existingConfig.lambda.payload.path : '',
+            },
+
+            // pathMappings: undefined
+        },
+        sam: {
+            buildArguments: undefined,
+            containerBuild: false,
+            dockerNetwork: '',
+            localArguments: undefined,
+            skipNewImageCheck: false,
+            ...(existingConfig?.sam ? existingConfig.sam : {}),
+            template: {
+                parameters: existingConfig?.sam?.template?.parameters ? existingConfig.sam.template.parameters : {},
+            },
+        },
+        api: {
+            path: '',
+            httpMethod: 'get',
+            clientCertificateId: '',
+            querystring: '',
+            headers: {},
+            stageVariables: {},
+            ...(existingConfig?.api ? existingConfig.api : {}),
+            payload: {
+                json: existingConfig?.api?.payload?.json ? existingConfig.api.payload.json : {},
+                path: existingConfig?.api?.payload?.path ? existingConfig.api.payload.path : '',
+            },
+        },
+    }
+}
+
+function initData() {
+    return {
+        containerBuild: false,
+        skipNewImageCheck: false,
+        useDebugger: true,
+        launchConfig: newLaunchConfig(),
+        payload: { value: '', errorMsg: '' },
+        apiPayload: { value: '', errorMsg: '' },
+        environmentVariables: { value: '', errorMsg: '' },
+        parameters: { value: '', errorMsg: '' },
+        headers: { value: '', errorMsg: '' },
+        stageVariables: { value: '', errorMsg: '' },
+        selectedConfig: { index: 0, config: undefined, label: 'new-config' },
+        selectedTestEvent: '',
+        TestEvents: [],
+        showNameInput: false,
+        newTestEventName: '',
+    }
+}
+
+export default defineComponent({
+    name: 'sam-invoke',
+    components: {
+        settingsPanel,
+    },
+    created() {
+        this.setUpWebView()
+    },
+    mixins: [saveData],
+    data(): SamInvokeVueData {
+        return {
+            ...initData(),
+            msg: 'Hello',
+            company: '',
+            targetTypes: [
+                { name: 'Code', value: 'code' },
+                { name: 'Template', value: 'template' },
+                { name: 'API Gateway (Template)', value: 'api' },
+            ],
+            runtimes: [],
+            httpMethods: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH'],
+            payloadOption: 'sampleEvents',
+            selectedFile: '',
+            selectedFilePath: '',
+            resourceData: undefined,
+        }
+    },
+    methods: {
+        resetJsonErrors() {
+            this.payload.errorMsg = ''
+            this.environmentVariables.errorMsg = ''
+            this.headers.errorMsg = ''
+            this.stageVariables.errorMsg = ''
+        },
+        async launch() {
+            const config = this.formatConfig()
+
+            if (!config) {
+                return // Exit early if config is not available
+            }
+
+            const source = this.resourceData?.source
+
+            client.invokeLaunchConfig(config, source).catch((e: Error) => {
+                console.error(`invokeLaunchConfig failed: ${e.message}`)
+            })
+        },
+        save() {
+            const config = this.formatConfig()
+            config &&
+                client.saveLaunchConfig(config).catch((e) => {
+                    console.error('saveLaunchConfig failed: %s', (e as Error).message)
+                })
+        },
+        loadConfig() {
+            client.loadSamLaunchConfig().then(
+                (config) => this.parseConfig(config),
+                (e) => {
+                    console.error('client.loadSamLaunchConfig failed: %s', (e as Error).message)
+                }
+            )
+        },
+        async parseConfig(config?: AwsSamDebuggerConfiguration) {
+            if (!config) {
+                return
+            }
+            const company = this.company
+            this.clearForm()
+            this.launchConfig = newLaunchConfig(config)
+
+            if (config.lambda?.payload) {
+                this.payload.value = JSON.stringify(config.lambda.payload.json, undefined, 4)
+            }
+
+            const localArgs = config.sam?.localArguments
+
+            if (!localArgs && this.payload.value) {
+                this.payloadOption = 'sampleEvents'
+                this.selectedFile = ''
+            }
+
+            if (localArgs?.includes('-e') || localArgs?.includes('--event')) {
+                const index = localArgs.findIndex((arg) => arg === '-e' || arg === '--event')
+
+                if (index !== -1 && localArgs[index + 1]) {
+                    this.payloadOption = 'localFile'
+                    this.selectedFile = await client.getFileName(localArgs[index + 1])
+                }
+            }
+
+            if (config.lambda?.environmentVariables) {
+                this.environmentVariables.value = JSON.stringify(config.lambda?.environmentVariables)
+            }
+            if (config.sam?.template?.parameters) {
+                this.parameters.value = JSON.stringify(config.sam?.template?.parameters)
+            }
+            if (config.api?.headers) {
+                this.headers.value = JSON.stringify(config.api?.headers)
+            }
+            if (config.api?.stageVariables) {
+                this.stageVariables.value = JSON.stringify(config.api?.stageVariables)
+            }
+            this.containerBuild = config.sam?.containerBuild ?? false
+            this.skipNewImageCheck = config.sam?.skipNewImageCheck ?? false
+            this.msg = `Loaded config: ${config.name}`
+            this.company = company
+        },
+        loadPayload() {
+            this.resetJsonErrors()
+            client.getSamplePayload().then(
+                (sample) => {
+                    if (!sample) {
+                        return
+                    }
+                    this.payload.value = JSON.stringify(JSON.parse(sample), undefined, 4)
+                },
+                (e) => {
+                    console.error('client.getSamplePayload failed: %s', (e as Error).message)
+                }
+            )
+        },
+        loadResource() {
+            this.resetJsonErrors()
+            client.getTemplate().then(
+                (template) => {
+                    if (!template) {
+                        return
+                    }
+                    this.launchConfig.invokeTarget.target = 'template'
+                    this.launchConfig.invokeTarget.logicalId = template.logicalId
+                    this.launchConfig.invokeTarget.templatePath = template.template
+                },
+                (e) => {
+                    console.error('client.getTemplate failed: %s', (e as Error).message)
+                }
+            )
+        },
+        formatFieldToStringArray(field: string | undefined) {
+            if (!field) {
+                return undefined
+            }
+            // Reg ex for a comma with 0 or more whitespace before and/or after
+            const re = /\s*,\s*/g
+            return field.trim().split(re)
+        },
+        formatStringtoJSON(field: VueDataLaunchPropertyObject) {
+            field.errorMsg = ''
+            if (field.value) {
+                try {
+                    return JSON.parse(field.value)
+                } catch (e) {
+                    field.errorMsg = (e as Error).message
+                    throw e
+                }
+            }
+        },
+        async openLaunchJson() {
+            await client.openLaunchConfig()
+        },
+        onFileChange(event: Event) {
+            const input = event.target as HTMLInputElement
+            if (input.files && input.files.length > 0) {
+                const file = input.files[0]
+                this.selectedFile = file.name
+
+                // Use Blob.text() to read the file as text
+                file.text()
+                    .then((text) => {
+                        this.payload.value = text
+                    })
+                    .catch((error) => {
+                        console.error('Error reading file:', error)
+                    })
+            }
+        },
+        async promptForFileLocation() {
+            const resp = await client.promptFile()
+
+            if (resp) {
+                this.selectedFile = resp.selectedFile
+                this.launchConfig.sam = this.launchConfig.sam || {}
+                this.launchConfig.sam.localArguments = this.launchConfig.sam.localArguments || []
+
+                // Ensure only one '-e ' or '--event ' exists
+                const eventArgIndex = this.launchConfig.sam.localArguments.findIndex(
+                    (arg) => arg === '-e' || arg === '--event'
+                )
+
+                if (eventArgIndex !== -1 && this.launchConfig.sam.localArguments[eventArgIndex + 1]) {
+                    // Replace the existing file path for either '-e' or '--event'
+                    this.launchConfig.sam.localArguments[eventArgIndex + 1] = resp.selectedFilePath
+                } else {
+                    // Add '-e ' if not already present
+                    this.launchConfig.sam.localArguments.push('-e', resp.selectedFilePath)
+                }
+            }
+        },
+        showNameField() {
+            this.showNameInput = true
+        },
+        setUpWebView() {
+            client.init().then(
+                (config) => this.parseConfig(config),
+                (e) => {
+                    console.error('client.init failed: %s', (e as Error).message)
+                }
+            )
+
+            if (this.launchConfig.invokeTarget.templatePath === '') {
+                client.getResourceData().then(
+                    (data) => {
+                        this.resourceData = data
+                        if (this.launchConfig && this.resourceData) {
+                            this.launchConfig.invokeTarget.logicalId = this.resourceData.logicalId
+                            this.launchConfig.invokeTarget.templatePath = this.resourceData.location
+                            this.launchConfig.invokeTarget.lambdaHandler = this.resourceData.handler
+                            if (this.launchConfig.lambda) {
+                                this.launchConfig.lambda.runtime = this.resourceData.runtime
+                                if (this.resourceData.environment?.Variables !== undefined) {
+                                    this.environmentVariables.value = JSON.stringify(
+                                        this.resourceData.environment?.Variables
+                                    )
+                                }
+                            }
+                        }
+                    },
+                    (e) => {
+                        console.error('client.getResourceData failed: %s', (e as Error).message)
+                    }
+                )
+            }
+
+            client.getRuntimes().then(
+                (runtimes) => {
+                    this.runtimes = runtimes
+                },
+                (e) => {
+                    console.error('client.getRuntimes failed: %s', (e as Error).message)
+                }
+            )
+
+            client.getCompanyName().then(
+                (o) => {
+                    this.company = o
+                },
+                (e) => {
+                    console.error('client.getCompanyName failed: %s', (e as Error).message)
+                }
+            )
+        },
+        formatConfig() {
+            this.resetJsonErrors()
+
+            let payloadJson, environmentVariablesJson, headersJson, stageVariablesJson, parametersJson, apiPayloadJson
+
+            try {
+                payloadJson = this.formatStringtoJSON(this.payload)
+                environmentVariablesJson = this.formatStringtoJSON(this.environmentVariables)
+                headersJson = this.formatStringtoJSON(this.headers)
+                stageVariablesJson = this.formatStringtoJSON(this.stageVariables)
+                parametersJson = this.formatStringtoJSON(this.parameters)
+                apiPayloadJson = this.formatStringtoJSON(this.apiPayload)
+            } catch {
+                return
+            }
+
+            // Vue internally stores a Proxy for all object-like fields, so the spread operator can
+            // propagate those through to the `postMessage` command, causing an error. We can stop
+            // this by recursively accessing all primitive fields (which is what this line does)
+            const launchConfig: AwsSamDebuggerConfigurationLoose = JSON.parse(JSON.stringify(this.launchConfig))
+            const localArgs = launchConfig.sam?.localArguments
+
+            const removeEventArg = () => {
+                if (localArgs) {
+                    const eventArgIndex = localArgs?.findIndex((arg) => arg === '-e' || arg === '--event')
+                    if (eventArgIndex !== -1) {
+                        // Remove the event argument and its value
+                        localArgs?.splice(eventArgIndex, 2)
+                    }
+                }
+            }
+
+            if (localArgs) {
+                if (this.payload && this.payloadOption !== 'localFile') {
+                    removeEventArg()
+                } else if (this.payloadOption === 'localFile' && this.selectedFile) {
+                    payloadJson = undefined
+                } else {
+                    removeEventArg()
+                }
+            }
+
+            return {
+                ...launchConfig,
+                lambda: {
+                    ...launchConfig.lambda,
+                    payload: {
+                        ...launchConfig.payload,
+                        json: payloadJson,
+                    },
+                    environmentVariables: environmentVariablesJson,
+                },
+                sam: {
+                    ...launchConfig.sam,
+                    buildArguments: this.formatFieldToStringArray(launchConfig.sam?.buildArguments?.toString()),
+                    localArguments: this.formatFieldToStringArray(launchConfig.sam?.localArguments?.toString()),
+                    containerBuild: this.containerBuild,
+                    skipNewImageCheck: this.skipNewImageCheck,
+                    template: {
+                        parameters: parametersJson,
+                    },
+                },
+                api: launchConfig.api
+                    ? {
+                          ...launchConfig.api,
+                          headers: headersJson,
+                          stageVariables: stageVariablesJson,
+                          payload: {
+                              json: apiPayloadJson,
+                          },
+                      }
+                    : undefined,
+                noDebug: !this.useDebugger,
+            }
+        },
+        clearForm() {
+            const init = initData()
+            for (const k of Object.keys(init)) {
+                ;(this as any)[k] = init[k as keyof typeof init]
+            }
+        },
+    },
+})
diff --git a/packages/core/src/lambda/vue/remoteInvoke/index.ts b/packages/core/src/lambda/vue/remoteInvoke/index.ts
new file mode 100644
index 00000000000..a44142fd527
--- /dev/null
+++ b/packages/core/src/lambda/vue/remoteInvoke/index.ts
@@ -0,0 +1,16 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { createApp } from 'vue'
+import component from './remoteInvoke.vue'
+
+const create = () => createApp(component)
+const app = create()
+app.mount('#vue-app')
+
+window.addEventListener('remount', () => {
+    app.unmount()
+    create().mount('#vue-app')
+})
diff --git a/packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts b/packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts
new file mode 100644
index 00000000000..c56f43ae199
--- /dev/null
+++ b/packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts
@@ -0,0 +1,949 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { readFileSync } from 'fs' // eslint-disable-line no-restricted-imports
+import * as _ from 'lodash'
+import * as vscode from 'vscode'
+import { LambdaClient } from '../../../shared/clients/lambdaClient'
+import * as picker from '../../../shared/ui/picker'
+import { ExtContext } from '../../../shared/extensions'
+
+import { getLogger } from '../../../shared/logger/logger'
+import { HttpResourceFetcher } from '../../../shared/resourcefetcher/httpResourceFetcher'
+import { sampleRequestPath } from '../../constants'
+import { LambdaFunctionNode } from '../../explorer/lambdaFunctionNode'
+import { getSampleLambdaPayloads, SampleRequest, isHotReloadingFunction } from '../../utils'
+
+import * as nls from 'vscode-nls'
+import { VueWebview } from '../../../webviews/main'
+import { telemetry, Runtime as TelemetryRuntime } from '../../../shared/telemetry/telemetry'
+import { Runtime } from '@aws-sdk/client-lambda'
+import {
+    runSamCliRemoteTestEvents,
+    SamCliRemoteTestEventsParameters,
+    TestEventsOperation,
+} from '../../../shared/sam/cli/samCliRemoteTestEvent'
+import { getSamCliContext } from '../../../shared/sam/cli/samCliContext'
+import { ToolkitError } from '../../../shared/errors'
+import { basename } from 'path'
+import { decodeBase64 } from '../../../shared/utilities/textUtilities'
+import { RemoteDebugController, revertExistingConfig } from '../../remoteDebugging/ldkController'
+import type { DebugConfig } from '../../remoteDebugging/lambdaDebugger'
+import { getCachedLocalPath, openLambdaFile, runDownloadLambda } from '../../commands/downloadLambda'
+import { getLambdaHandlerFile } from '../../../awsService/appBuilder/utils'
+import { runUploadDirectory } from '../../commands/uploadLambda'
+import fs from '../../../shared/fs/fs'
+import { showConfirmationMessage, showMessage } from '../../../shared/utilities/messages'
+import { getLambdaClientWithAgent, getLambdaDebugUserAgent } from '../../remoteDebugging/utils'
+import { isLocalStackConnection } from '../../../auth/utils'
+import { getRemoteDebugLayer } from '../../remoteDebugging/remoteLambdaDebugger'
+
+const localize = nls.loadMessageBundle()
+
+type Event = {
+    name: string
+    region: string
+    arn: string
+    event?: string
+}
+
+export interface InitialData {
+    FunctionName: string
+    FunctionArn: string
+    FunctionRegion: string
+    InputSamples: SampleRequest[]
+    TestEvents?: string[]
+    Source?: string
+    StackName?: string
+    LogicalId?: string
+    Runtime?: Runtime
+    LocalRootPath?: string
+    LambdaFunctionNode?: LambdaFunctionNode
+    supportCodeDownload?: boolean
+    runtimeSupportsRemoteDebug?: boolean
+    remoteDebugLayer?: string | undefined
+    isLambdaRemote?: boolean
+}
+
+// Debug configuration sub-interface
+export interface DebugConfiguration {
+    debugPort: number | undefined
+    localRootPath: string
+    remoteRootPath: string
+    shouldPublishVersion: boolean
+    lambdaTimeout: number
+    otherDebugParams: string
+}
+
+// Debug state sub-interface
+export interface DebugState {
+    isDebugging: boolean
+    debugTimer: number | undefined
+    debugTimeRemaining: number
+    showDebugTimer: boolean
+    handlerFileAvailable: boolean
+    remoteDebuggingEnabled: boolean
+}
+
+// Runtime-specific debug settings sub-interface
+export interface RuntimeDebugSettings {
+    // Node.js specific
+    sourceMapEnabled: boolean
+    skipFiles: string
+    outFiles: string | undefined
+    // Python specific
+    justMyCode: boolean
+    // Java specific
+    projectName: string
+}
+
+// UI state sub-interface
+export interface UIState {
+    isCollapsed: boolean
+    extraRegionInfo: string
+}
+
+// Payload/Event handling sub-interface
+export interface PayloadData {
+    sampleText: string
+}
+
+export interface RemoteInvokeData {
+    initialData: InitialData
+    debugConfig: DebugConfiguration
+    debugState: DebugState
+    runtimeSettings: RuntimeDebugSettings
+    uiState: UIState
+    payloadData: PayloadData
+}
+
+// Event types for communicating state between backend and frontend
+export type StateChangeEvent = {
+    isDebugging?: boolean
+}
+interface SampleQuickPickItem extends vscode.QuickPickItem {
+    filename: string
+}
+
+export class RemoteInvokeWebview extends VueWebview {
+    public static readonly sourcePath: string = 'src/lambda/vue/remoteInvoke/index.js'
+    public readonly id = 'remoteInvoke'
+
+    // Event emitter for state changes that need to be synchronized with the frontend
+    public readonly onStateChange = new vscode.EventEmitter()
+
+    // Backend timer that will continue running even when the webview loses focus
+    private debugTimerHandle: NodeJS.Timeout | undefined
+    private debugTimeRemaining: number = 0
+    private isInvoking: boolean = false
+    private debugging: boolean = false
+    private watcherDisposable: vscode.Disposable | undefined
+    private fileWatcherDisposable: vscode.Disposable | undefined
+    private handlerFileAvailable: boolean = false
+    private isStartingDebug: boolean = false
+    private handlerFile: string | undefined
+    public constructor(
+        private readonly channel: vscode.OutputChannel,
+        private readonly client: LambdaClient,
+        private readonly clientDebug: LambdaClient,
+        private readonly data: InitialData
+    ) {
+        super(RemoteInvokeWebview.sourcePath)
+    }
+
+    public init() {
+        this.watcherDisposable = vscode.debug.onDidTerminateDebugSession(async (session: vscode.DebugSession) => {
+            this.resetServerState()
+        })
+        return this.data
+    }
+
+    public resetServerState() {
+        this.stopDebugTimer()
+        this.debugging = false
+        this.isInvoking = false
+        this.isStartingDebug = false
+        this.onStateChange.fire({
+            isDebugging: false,
+        })
+    }
+
+    public async disposeServer() {
+        this.watcherDisposable?.dispose()
+        this.fileWatcherDisposable?.dispose()
+        if (this.debugging && RemoteDebugController.instance.isDebugging) {
+            await this.stopDebugging()
+        }
+        this.dispose()
+    }
+
+    private setupFileWatcher() {
+        // Dispose existing watcher if any
+        this.fileWatcherDisposable?.dispose()
+
+        if (!this.data.LocalRootPath) {
+            return
+        }
+
+        // Create a file system watcher for the local root path
+        const pattern = new vscode.RelativePattern(this.data.LocalRootPath, '**/*')
+        const watcher = vscode.workspace.createFileSystemWatcher(pattern)
+
+        // Set up event handlers for file changes
+        const handleFileChange = async () => {
+            const result = await showMessage(
+                'info',
+                localize(
+                    'AWS.lambda.remoteInvoke.codeChangesDetected',
+                    'Code changes detected in the local directory. Would you like to update the Lambda function {0}@{1}?',
+                    this.data.FunctionName,
+                    this.data.FunctionRegion
+                ),
+                ['Yes', 'No']
+            )
+
+            if (result === 'Yes') {
+                try {
+                    if (this.data.LambdaFunctionNode && this.data.LocalRootPath) {
+                        const lambdaFunction = {
+                            name: this.data.FunctionName,
+                            region: this.data.FunctionRegion,
+                            configuration: this.data.LambdaFunctionNode.configuration,
+                        }
+                        await runUploadDirectory(lambdaFunction, 'zip', vscode.Uri.file(this.data.LocalRootPath))
+                    }
+                } catch (error) {
+                    throw ToolkitError.chain(
+                        error,
+                        localize('AWS.lambda.remoteInvoke.updateFailed', 'Failed to update Lambda function')
+                    )
+                }
+            }
+        }
+
+        // Listen for file changes, creations, and deletions
+        watcher.onDidChange(handleFileChange)
+        watcher.onDidCreate(handleFileChange)
+        watcher.onDidDelete(handleFileChange)
+
+        // Store the disposable so we can clean it up later
+        this.fileWatcherDisposable = watcher
+    }
+
+    // Method to start the backend timer
+    public startDebugTimer() {
+        // Clear any existing timer
+        this.stopDebugTimer()
+
+        this.debugTimeRemaining = 60
+
+        // Create a new timer that ticks every second
+        this.debugTimerHandle = setInterval(async () => {
+            this.debugTimeRemaining--
+
+            // When timer reaches zero, stop debugging
+            if (this.debugTimeRemaining <= 0) {
+                await this.handleTimerExpired()
+            }
+        }, 1000)
+    }
+
+    // Method to stop the timer
+    public stopDebugTimer() {
+        if (this.debugTimerHandle) {
+            clearInterval(this.debugTimerHandle)
+            this.debugTimerHandle = undefined
+            this.debugTimeRemaining = 0
+        }
+    }
+
+    // Handler for timer expiration
+    private async handleTimerExpired() {
+        await this.stopDebugging()
+    }
+
+    public async invokeLambda(input: string, source?: string, remoteDebugEnabled: boolean = false): Promise {
+        let qualifier: string | undefined = undefined
+        // if debugging, focus on the first editor
+        if (remoteDebugEnabled && RemoteDebugController.instance.isDebugging) {
+            await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup')
+            qualifier = RemoteDebugController.instance.qualifier
+        }
+
+        this.isInvoking = true
+
+        // If debugging is active, reset the timer during invoke
+        if (RemoteDebugController.instance.isDebugging) {
+            this.stopDebugTimer()
+        }
+
+        this.channel.show()
+        this.channel.appendLine('Loading response...')
+        await telemetry.lambda_invokeRemote.run(async (span) => {
+            try {
+                const funcResponse = remoteDebugEnabled
+                    ? await this.clientDebug.invoke(this.data.FunctionArn, input, qualifier)
+                    : await this.client.invoke(this.data.FunctionArn, input, qualifier)
+                const logs = funcResponse.LogResult ? decodeBase64(funcResponse.LogResult) : ''
+                const decodedPayload = funcResponse.Payload ? new TextDecoder().decode(funcResponse.Payload) : ''
+                const payload = decodedPayload || JSON.stringify({})
+
+                this.channel.appendLine(`Invocation result for ${this.data.FunctionArn}`)
+                this.channel.appendLine('Logs:')
+                this.channel.appendLine(logs)
+                this.channel.appendLine('')
+                this.channel.appendLine('Payload:')
+                this.channel.appendLine(String(payload))
+                this.channel.appendLine('')
+            } catch (e) {
+                const error = e as Error
+                this.channel.appendLine(`There was an error invoking ${this.data.FunctionArn}`)
+                this.channel.appendLine(error.toString())
+                this.channel.appendLine('')
+            } finally {
+                let action = remoteDebugEnabled ? 'debug' : 'invoke'
+                if (!this.data.isLambdaRemote) {
+                    action = `${action}LocalStack`
+                }
+                span.record({
+                    passive: false,
+                    source: source,
+                    runtimeString: this.data.Runtime,
+                    action: action,
+                })
+
+                // Update the session state to indicate we've finished invoking
+                this.isInvoking = false
+
+                // If debugging is active, restart the timer
+                if (RemoteDebugController.instance.isDebugging) {
+                    this.startDebugTimer()
+                }
+                this.channel.show()
+            }
+        })
+    }
+
+    public async promptFile() {
+        const fileLocations = await vscode.window.showOpenDialog({
+            openLabel: localize('AWS.lambda.remoteInvoke.open', 'Open'),
+        })
+
+        if (!fileLocations || fileLocations.length === 0) {
+            return undefined
+        }
+
+        try {
+            const fileContent = readFileSync(fileLocations[0].fsPath, { encoding: 'utf8' })
+            return {
+                sample: fileContent,
+                selectedFilePath: fileLocations[0].fsPath,
+                selectedFile: this.getFileName(fileLocations[0].fsPath),
+            }
+        } catch (e) {
+            getLogger().error('readFileSync: Failed to read file at path %s %O', fileLocations[0].fsPath, e)
+            throw ToolkitError.chain(
+                e,
+                localize('AWS.lambda.remoteInvoke.failedToReadFile', 'Failed to read selected file')
+            )
+        }
+    }
+
+    public async promptFolder(): Promise {
+        const fileLocations = await vscode.window.showOpenDialog({
+            openLabel: localize('AWS.lambda.remoteInvoke.open', 'Open'),
+            canSelectFolders: true,
+            canSelectFiles: false,
+            canSelectMany: false,
+        })
+
+        if (!fileLocations || fileLocations.length === 0) {
+            return undefined
+        }
+        this.data.LocalRootPath = fileLocations[0].fsPath
+        // try to find the handler file in this folder, open it if not opened already
+        if (!(await this.tryOpenHandlerFile())) {
+            const warning = localize(
+                'AWS.lambda.remoteInvoke.handlerFileNotFound',
+                'Handler {0} not found in selected location. Please select the folder that contains the copy of your handler file',
+                this.data.LambdaFunctionNode?.configuration.Handler
+            )
+            getLogger().warn(warning)
+            void showMessage('warn', warning)
+        }
+        return fileLocations[0].fsPath
+    }
+
+    public async tryOpenHandlerFile(path?: string, watchForUpdates: boolean = true): Promise {
+        this.handlerFile = undefined
+        if (this.data.LocalRootPath) {
+            // don't watch in appbuilder
+            watchForUpdates = false
+        }
+        if (path) {
+            // path is provided, override init path
+            this.data.LocalRootPath = path
+        }
+        // init path or node not available
+        if (!this.data.LocalRootPath || !this.data.LambdaFunctionNode) {
+            return false
+        }
+
+        const handlerFile = this.data.Runtime
+            ? await getLambdaHandlerFile(
+                  vscode.Uri.file(this.data.LocalRootPath),
+                  '',
+                  this.data.LambdaFunctionNode?.configuration.Handler ?? '',
+                  this.data.Runtime
+              )
+            : undefined
+        if (!handlerFile || !(await fs.exists(handlerFile))) {
+            this.handlerFileAvailable = false
+            return false
+        }
+        this.handlerFileAvailable = true
+        if (watchForUpdates && !isHotReloadingFunction(this.data.LambdaFunctionNode?.configuration.CodeSha256)) {
+            this.setupFileWatcher()
+        }
+        await openLambdaFile(handlerFile.fsPath)
+        this.handlerFile = handlerFile.fsPath
+        return true
+    }
+
+    public async loadFile(fileLocations: string) {
+        return await this.readFile(fileLocations)
+    }
+
+    private async readFile(filePath: string) {
+        if (!filePath) {
+            return undefined
+        }
+        const fileLocation = vscode.Uri.file(filePath)
+        try {
+            const fileContent = readFileSync(fileLocation.fsPath, { encoding: 'utf8' })
+
+            return {
+                sample: fileContent,
+                selectedFilePath: fileLocation.fsPath,
+                selectedFile: this.getFileName(fileLocation.fsPath),
+            }
+        } catch (e) {
+            getLogger().error('readFileSync: Failed to read file at path %s %O', fileLocation.fsPath, e)
+            throw ToolkitError.chain(
+                e,
+                localize('AWS.lambda.remoteInvoke.failedToReadFile', 'Failed to read selected file')
+            )
+        }
+    }
+
+    private getFileName(filePath: string): string {
+        return basename(filePath)
+    }
+
+    public async listRemoteTestEvents(functionArn: string, region: string): Promise {
+        try {
+            const params: SamCliRemoteTestEventsParameters = {
+                functionArn: functionArn,
+                operation: TestEventsOperation.List,
+                region: region,
+            }
+            const result = await this.remoteTestEvents(params)
+            return result.split('\n').filter((event) => event.trim() !== '')
+        } catch (error) {
+            // Suppress "lambda-testevent-schemas registry not found" error - this is normal when no test events exist
+            const errorMessage = error instanceof Error ? error.message : String(error)
+            if (
+                errorMessage.includes('lambda-testevent-schemas registry not found') ||
+                errorMessage.includes('There are no saved events')
+            ) {
+                getLogger().debug('No remote test events found for function: %s', functionArn)
+                return []
+            }
+            // Re-throw other errors
+            throw error
+        }
+    }
+
+    public async selectRemoteTestEvent(functionArn: string, region: string): Promise {
+        let events: string[] = []
+
+        try {
+            events = await this.listRemoteTestEvents(functionArn, region)
+        } catch (error) {
+            getLogger().error('Failed to list remote test events: %O', error)
+            void showMessage(
+                'error',
+                localize('AWS.lambda.remoteInvoke.failedToListEvents', 'Failed to list remote test events')
+            )
+            return undefined
+        }
+
+        if (events.length === 0) {
+            void showMessage(
+                'info',
+                localize(
+                    'AWS.lambda.remoteInvoke.noRemoteEvents',
+                    'No remote test events found. You can create one using "Save as remote event".'
+                )
+            )
+            return undefined
+        }
+
+        const selected = await vscode.window.showQuickPick(events, {
+            placeHolder: localize('AWS.lambda.remoteInvoke.selectRemoteEvent', 'Select a remote test event'),
+            title: localize('AWS.lambda.remoteInvoke.loadRemoteEvent', 'Load Remote Test Event'),
+        })
+
+        if (selected) {
+            const eventData = {
+                name: selected,
+                region: region,
+                arn: functionArn,
+            }
+            const resp = await this.getRemoteTestEvents(eventData)
+            return resp
+        }
+
+        return undefined
+    }
+
+    public async saveRemoteTestEvent(
+        functionArn: string,
+        region: string,
+        eventContent: string
+    ): Promise {
+        let events: string[] = []
+
+        try {
+            events = await this.listRemoteTestEvents(functionArn, region)
+        } catch (error) {
+            // Log error but continue - user can still create new events
+            getLogger().debug('Failed to list existing remote test events (may not exist yet): %O', error)
+        }
+
+        // Create options for quickpick
+        const createNewOption = '$(add) Create new test event'
+        const options = events.length > 0 ? [createNewOption, ...events] : [createNewOption]
+
+        const selected = await vscode.window.showQuickPick(options, {
+            placeHolder: localize(
+                'AWS.lambda.remoteInvoke.saveEventChoice',
+                'Create new or overwrite existing test event'
+            ),
+            title: localize('AWS.lambda.remoteInvoke.saveRemoteEvent', 'Save as Remote Event'),
+        })
+
+        if (!selected) {
+            return undefined
+        }
+
+        let eventName: string | undefined
+
+        if (selected === createNewOption) {
+            // Prompt for new event name
+            eventName = await vscode.window.showInputBox({
+                prompt: localize('AWS.lambda.remoteInvoke.enterEventName', 'Enter a name for the test event'),
+                placeHolder: localize('AWS.lambda.remoteInvoke.eventNamePlaceholder', 'MyTestEvent'),
+                validateInput: (value) => {
+                    if (!value || value.trim() === '') {
+                        return localize('AWS.lambda.remoteInvoke.eventNameRequired', 'Event name is required')
+                    }
+                    if (events.includes(value)) {
+                        return localize(
+                            'AWS.lambda.remoteInvoke.eventNameExists',
+                            'An event with this name already exists'
+                        )
+                    }
+                    return undefined
+                },
+            })
+        } else {
+            // Use selected existing event name
+            const confirm = await showConfirmationMessage({
+                prompt: localize(
+                    'AWS.lambda.remoteInvoke.overwriteEvent',
+                    'Overwrite existing test event "{0}"?',
+                    selected
+                ),
+                confirm: localize('AWS.lambda.remoteInvoke.overwrite', 'Overwrite'),
+                cancel: 'Cancel',
+                type: 'warning',
+            })
+
+            if (confirm) {
+                eventName = selected
+            }
+        }
+
+        if (eventName) {
+            // Use force flag when overwriting existing events
+            const isOverwriting = selected !== createNewOption
+            const params: SamCliRemoteTestEventsParameters = {
+                functionArn: functionArn,
+                operation: TestEventsOperation.Put,
+                name: eventName,
+                eventSample: eventContent,
+                region: region,
+                force: isOverwriting,
+            }
+            await this.remoteTestEvents(params)
+            return eventName
+        }
+
+        return undefined
+    }
+
+    public async createRemoteTestEvents(putEvent: Event, force: boolean = false) {
+        const params: SamCliRemoteTestEventsParameters = {
+            functionArn: putEvent.arn,
+            operation: TestEventsOperation.Put,
+            name: putEvent.name,
+            eventSample: putEvent.event,
+            region: putEvent.region,
+            force: force,
+        }
+        return await this.remoteTestEvents(params)
+    }
+    public async getRemoteTestEvents(getEvents: Event) {
+        const params: SamCliRemoteTestEventsParameters = {
+            name: getEvents.name,
+            operation: TestEventsOperation.Get,
+            functionArn: getEvents.arn,
+            region: getEvents.region,
+        }
+        return await this.remoteTestEvents(params)
+    }
+
+    private async remoteTestEvents(params: SamCliRemoteTestEventsParameters) {
+        return await runSamCliRemoteTestEvents(params, getSamCliContext().invoker)
+    }
+
+    public async getSamplePayload(): Promise {
+        try {
+            const inputs: SampleQuickPickItem[] = (await getSampleLambdaPayloads()).map((entry) => {
+                return { label: entry.name ?? '', filename: entry.filename ?? '' }
+            })
+
+            const qp = picker.createQuickPick({
+                items: inputs,
+                options: {
+                    title: localize(
+                        'AWS.lambda.form.pickSampleInput',
+                        'Enter keywords to filter the list of sample events'
+                    ),
+                },
+            })
+
+            const choices = await picker.promptUser({
+                picker: qp,
+            })
+            const pickerResponse = picker.verifySinglePickerOutput(choices)
+
+            if (!pickerResponse) {
+                return
+            }
+            const sampleUrl = `${sampleRequestPath}${pickerResponse.filename}`
+            const resp = await new HttpResourceFetcher(sampleUrl, { showUrl: true }).get()
+            const sample = (await resp?.text()) ?? ''
+
+            return sample
+        } catch (err) {
+            getLogger().error('Error getting manifest data..: %O', err as Error)
+            throw ToolkitError.chain(
+                err,
+                localize('AWS.lambda.remoteInvoke.gettingManifestData', 'getting manifest data')
+            )
+        }
+    }
+
+    // Download lambda code and update the local root path
+    public async downloadRemoteCode(): Promise {
+        return await telemetry.lambda_import.run(async (span) => {
+            span.record({ runtime: this.data.Runtime as TelemetryRuntime | undefined, source: 'RemoteDebug' })
+            try {
+                if (this.data.LambdaFunctionNode) {
+                    const output = await runDownloadLambda(this.data.LambdaFunctionNode, true)
+                    if (output instanceof vscode.Uri) {
+                        this.data.LocalRootPath = output.fsPath
+                        this.handlerFileAvailable = true
+                        this.setupFileWatcher()
+
+                        return output.fsPath
+                    }
+                } else {
+                    getLogger().error(
+                        localize(
+                            'AWS.lambda.remoteInvoke.lambdaFunctionNodeUndefined',
+                            'LambdaFunctionNode is undefined'
+                        )
+                    )
+                }
+                return undefined
+            } catch (error) {
+                throw ToolkitError.chain(
+                    error,
+                    localize('AWS.lambda.remoteInvoke.failedToDownloadCode', 'Failed to download remote code')
+                )
+            }
+        })
+    }
+
+    // this serves as a lock for invoke
+    public checkReadyToInvoke(): boolean {
+        if (this.isInvoking) {
+            void showMessage(
+                'warn',
+                localize(
+                    'AWS.lambda.remoteInvoke.invokeInProgress',
+                    'A remote invoke is already in progress, please wait for previous invoke, or remove debug setup'
+                )
+            )
+            return false
+        }
+        if (this.isStartingDebug) {
+            void showMessage(
+                'warn',
+                localize(
+                    'AWS.lambda.remoteInvoke.debugSetupInProgress',
+                    'A debugger setup is already in progress, please wait for previous setup to complete, or remove debug setup'
+                )
+            )
+            return false
+        }
+        return true
+    }
+
+    // this check is run when user click remote invoke with remote debugging checked
+    public async checkReadyToDebug(config: DebugConfig): Promise {
+        if (!this.data.LambdaFunctionNode) {
+            return false
+        }
+
+        if (!this.handlerFileAvailable) {
+            const result = await showConfirmationMessage({
+                prompt: localize(
+                    'AWS.lambda.remoteInvoke.handlerFileNotLocated',
+                    'The handler file cannot be located in the specified Local Root Path. As a result, remote debugging will not pause at breakpoints.'
+                ),
+                confirm: 'Continue Anyway',
+                cancel: 'Cancel',
+                type: 'warning',
+            })
+            if (!result) {
+                return false
+            }
+        }
+        // check if snapstart is on and we are publishing a version
+        if (
+            config.shouldPublishVersion &&
+            this.data.LambdaFunctionNode.configuration.SnapStart?.ApplyOn === 'PublishedVersions'
+        ) {
+            const result = await showConfirmationMessage({
+                prompt: localize(
+                    'AWS.lambda.remoteInvoke.snapstartWarning',
+                    "This function has Snapstart enabled. If you use Remote Debug with the 'publish version' setting, you'll experience notable delays. For faster debugging, consider disabling the 'publish version' option."
+                ),
+                confirm: 'Continue Anyway',
+                cancel: 'Cancel',
+                type: 'warning',
+            })
+            if (!result) {
+                // didn't confirm
+                getLogger().warn(
+                    localize('AWS.lambda.remoteInvoke.userCanceledSnapstart', 'User canceled Snapstart confirm')
+                )
+                return false
+            }
+        }
+
+        // ready to debug
+        return true
+    }
+
+    public async startDebugging(config: DebugConfig): Promise {
+        if (!this.data.LambdaFunctionNode) {
+            return false
+        }
+        if (!(await this.checkReadyToDebug(config))) {
+            return false
+        }
+        this.isStartingDebug = true
+        try {
+            await RemoteDebugController.instance.startDebugging(this.data.FunctionArn, this.data.Runtime ?? 'unknown', {
+                ...config,
+                handlerFile: this.handlerFile,
+                samFunctionLogicalId: this.data.LambdaFunctionNode.logicalId,
+                samProjectRoot: this.data.LambdaFunctionNode.projectRoot,
+            })
+        } catch (e) {
+            throw ToolkitError.chain(
+                e,
+                localize('AWS.lambda.remoteInvoke.failedToStartDebugging', 'Failed to start debugging')
+            )
+        } finally {
+            this.isStartingDebug = false
+        }
+
+        this.startDebugTimer()
+        this.debugging = this.isLDKDebugging()
+        return this.debugging
+    }
+
+    public async stopDebugging(): Promise {
+        if (this.isLDKDebugging()) {
+            this.resetServerState()
+            await RemoteDebugController.instance.stopDebugging()
+        }
+        this.debugging = this.isLDKDebugging()
+        return this.debugging
+    }
+
+    public isLDKDebugging(): boolean {
+        return RemoteDebugController.instance.isDebugging
+    }
+
+    public isWebViewDebugging(): boolean {
+        return this.debugging
+    }
+
+    public getIsInvoking(): boolean {
+        return this.isInvoking
+    }
+
+    public getDebugTimeRemaining(): number {
+        return this.debugTimeRemaining
+    }
+
+    public getLocalPath(): string {
+        return this.data.LocalRootPath ?? ''
+    }
+
+    public getHandlerAvailable(): boolean {
+        return this.handlerFileAvailable
+    }
+
+    // prestatus check run at checkbox click
+    public async debugPreCheck(): Promise {
+        return await telemetry.lambda_remoteDebugPrecheck.run(async (span) => {
+            span.record({
+                runtimeString: this.data.Runtime,
+                source: this.data.isLambdaRemote ? 'webview' : 'webviewLocalStack',
+            })
+            if (!this.debugging && RemoteDebugController.instance.isDebugging) {
+                // another debug session in progress
+                const result = await showConfirmationMessage({
+                    prompt: localize(
+                        'AWS.lambda.remoteInvoke.debugSessionActive',
+                        'A remote debug session is already active. Stop that for this new session?'
+                    ),
+                    confirm: 'Stop Previous Session',
+                    cancel: 'Cancel',
+                    type: 'warning',
+                })
+
+                if (result) {
+                    // Stop the previous session
+                    if (await this.stopDebugging()) {
+                        getLogger().error(
+                            localize(
+                                'AWS.lambda.remoteInvoke.failedToStopPreviousSession',
+                                'Failed to stop previous debug session.'
+                            )
+                        )
+                        return false
+                    }
+                } else {
+                    // user canceled, Do nothing
+                    return false
+                }
+            }
+
+            const result = await RemoteDebugController.instance.installDebugExtension(this.data.Runtime)
+            if (!result && result === false) {
+                // install failed
+                return false
+            }
+
+            await revertExistingConfig()
+
+            // Check if the function ARN is in the cache and try to open handler file
+            const cachedPath = getCachedLocalPath(this.data.FunctionArn)
+            // only check cache if not comming from appbuilder
+            if (cachedPath && !this.data.LambdaFunctionNode?.localDir) {
+                getLogger().debug(
+                    `lambda: found cached local path for function ARN: ${this.data.FunctionArn} -> ${cachedPath}`
+                )
+                await this.tryOpenHandlerFile(cachedPath, true)
+            }
+
+            // this is comming from appbuilder
+            if (this.data.LambdaFunctionNode?.localDir) {
+                await this.tryOpenHandlerFile(undefined, false)
+            }
+
+            return true
+        })
+    }
+}
+
+export async function invokeRemoteLambda(
+    context: ExtContext,
+    params: {
+        /* TODO: Instead of vague scope-leaking objects: awsContext & element, it would be cleaner if this took:
+         *  {
+         *      lambdaClient: LambdaClient,         // or just invoke/invokeAsync interface of AWS.Lambda (see: lambda.d.ts)
+         *      invokeParams: {functionArn: string} // or Lambda.Types.InvocationRequest (see: lambda.d.ts)
+         *  }
+         */
+        outputChannel: vscode.OutputChannel
+        functionNode: LambdaFunctionNode
+        source?: string
+    }
+) {
+    const inputs = await getSampleLambdaPayloads()
+    const resource: LambdaFunctionNode = params.functionNode
+    const source: string = params.source || 'AwsExplorerRemoteInvoke'
+    const client = getLambdaClientWithAgent(resource.regionCode)
+    const clientDebug = getLambdaClientWithAgent(resource.regionCode, getLambdaDebugUserAgent())
+
+    const Panel = VueWebview.compilePanel(RemoteInvokeWebview)
+
+    // Initialize support and debugging capabilities
+    const runtime = resource.configuration.Runtime
+    const region = resource.regionCode
+    const supportCodeDownload = RemoteDebugController.instance.supportCodeDownload(
+        runtime,
+        resource.configuration.CodeSha256
+    )
+    const runtimeSupportsRemoteDebug = RemoteDebugController.instance.supportRuntimeRemoteDebug(runtime)
+    const remoteDebugLayer = getRemoteDebugLayer(region, resource.configuration.Architectures)
+
+    const wv = new Panel(context.extensionContext, context.outputChannel, client, clientDebug, {
+        FunctionName: resource.configuration.FunctionName ?? '',
+        FunctionArn: resource.configuration.FunctionArn ?? '',
+        FunctionRegion: resource.regionCode,
+        InputSamples: inputs,
+        TestEvents: [],
+        Source: source,
+        Runtime: runtime,
+        LocalRootPath: params.functionNode.localDir,
+        LambdaFunctionNode: params.functionNode,
+        supportCodeDownload: supportCodeDownload,
+        runtimeSupportsRemoteDebug: runtimeSupportsRemoteDebug,
+        remoteDebugLayer: remoteDebugLayer,
+        isLambdaRemote: !isLocalStackConnection(),
+    })
+    // focus on first group so wv will show up in the side
+    await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup')
+
+    const activePanel = await wv.show({
+        title: localize('AWS.invokeLambda.title', 'Invoke Lambda {0}', resource.configuration.FunctionName),
+        viewColumn: vscode.ViewColumn.Beside,
+    })
+
+    activePanel.onDidDispose(async () => {
+        await wv.server.disposeServer()
+    })
+}
diff --git a/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.css b/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.css
new file mode 100644
index 00000000000..c96291b26ae
--- /dev/null
+++ b/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.css
@@ -0,0 +1,238 @@
+/* Container and Layout */
+.Icontainer {
+    margin-inline: auto;
+    margin-top: 2rem;
+    width: 100%;
+    min-width: 650px;
+}
+
+h1 {
+    text-align: left;
+    margin-bottom: 20px;
+}
+
+/* Remove fixed width for divs to allow responsive behavior */
+div {
+    width: 100%;
+}
+
+/* VSCode Settings Style Layout */
+.vscode-setting-item {
+    margin-bottom: 10px;
+    padding: 5px 0;
+}
+
+.setting-header {
+    display: flex;
+    align-items: center;
+    margin-bottom: 8px;
+}
+
+.setting-title {
+    font-weight: 600;
+    font-size: 14px;
+    margin: 0;
+}
+
+.setting-body {
+    display: flex;
+    align-items: flex-start;
+    gap: 8px;
+}
+
+.setting-description {
+    flex: 1;
+}
+
+.setting-description info-wrap,
+.setting-description info {
+    display: block;
+    margin-bottom: 4px;
+}
+
+.setting-description-full {
+    margin-bottom: 8px;
+}
+
+.setting-description-full info-wrap {
+    display: block;
+    margin-bottom: 4px;
+}
+
+.setting-input-group-full {
+    display: flex;
+    align-items: center;
+    gap: 5px;
+}
+
+.setting-input {
+    flex-grow: 1;
+    margin-right: 2px;
+}
+
+/* Form Layout Classes - Base grid layout shared by multiple classes */
+.form-row,
+.form-row-no-align {
+    display: grid;
+    grid-template-columns: 150px 1fr;
+    margin-bottom: 10px;
+}
+
+.form-row {
+    align-items: center;
+}
+
+.form-double-row {
+    display: grid;
+    grid-template-rows: 20px 1fr;
+    align-items: center;
+}
+
+/* Typography and Text Elements */
+label {
+    font-weight: 500;
+    font-size: 14px;
+    margin-right: 10px;
+}
+
+/* Merge info and info-wrap as they share most properties */
+info,
+info-wrap {
+    color: var(--vscode-descriptionForeground);
+    font-weight: 500;
+    font-size: 13px;
+    margin-right: 10px;
+}
+
+info {
+    text-wrap-mode: nowrap;
+}
+
+/* Form Elements */
+span,
+select {
+    display: block;
+}
+
+textarea {
+    color: var(--vscode-settings-textInputForeground);
+    background: var(--vscode-settings-textInputBackground);
+    border: 1px solid var(--vscode-settings-textInputBorder);
+    width: 100%;
+    box-sizing: border-box;
+    resize: none;
+}
+
+/* Button Styles */
+.button-theme-primary,
+.button-theme-inline {
+    border: 1px solid var(--vscode-button-border);
+}
+
+.button-theme-primary {
+    padding: 8px 12px;
+    color: var(--vscode-button-foreground);
+    background: var(--vscode-button-background);
+}
+
+.button-theme-primary:hover:not(:disabled) {
+    background: var(--vscode-button-hoverBackground);
+    cursor: pointer;
+}
+
+.button-theme-inline {
+    padding: 4px 6px;
+    color: var(--vscode-button-secondaryForeground);
+    background: var(--vscode-button-secondaryBackground);
+}
+
+.button-theme-inline:hover:not(:disabled) {
+    background: var(--vscode-button-secondaryHoverBackground);
+    cursor: pointer;
+}
+
+button:disabled {
+    opacity: 0.5;
+    cursor: not-allowed;
+}
+
+/* Payload Section Styles */
+.payload-button-group {
+    display: flex;
+    gap: 5px;
+    margin-bottom: 10px;
+}
+
+.payload-textarea {
+    width: 100%;
+    min-height: 200px;
+    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
+    font-size: 13px;
+    line-height: 1.5;
+}
+
+/* Collapsible Section */
+.collapsible-section {
+    margin: 15px 0;
+    border: 1px solid var(--vscode-widget-border);
+    border-radius: 4px;
+}
+
+.collapsible-header,
+.collapsible-content {
+    max-width: 96%;
+}
+
+.collapsible-header {
+    padding: 8px 12px;
+    background-color: var(--vscode-sideBarSectionHeader-background);
+    cursor: pointer;
+    font-weight: 500;
+}
+
+.collapsible-content {
+    padding: 10px;
+    border-top: 1px solid var(--vscode-widget-border);
+}
+
+/* Validation and Error Styles */
+.input-error {
+    border: 1px solid var(--vscode-inputValidation-errorBorder) !important;
+    background-color: var(--vscode-inputValidation-errorBackground) !important;
+}
+
+.error-message {
+    color: var(--vscode-inputValidation-errorForeground);
+    font-size: 12px;
+    margin-top: 4px;
+    font-weight: 400;
+    line-height: 1.2;
+}
+
+/* Checkbox and Status Styles */
+.remote-debug-checkbox {
+    width: 18px !important;
+    height: 18px !important;
+    accent-color: var(--vscode-checkbox-foreground);
+    border: 2px solid var(--vscode-checkbox-border) !important;
+    border-radius: 3px !important;
+    background-color: var(--vscode-checkbox-background) !important;
+    cursor: pointer;
+}
+
+.remote-debug-checkbox:checked {
+    background-color: var(--vscode-checkbox-selectBackground) !important;
+    border-color: var(--vscode-checkbox-selectBorder) !important;
+}
+
+.remote-debug-checkbox:disabled {
+    opacity: 0.6;
+    cursor: not-allowed;
+    border-color: var(--vscode-checkbox-border) !important;
+    background-color: var(--vscode-input-background) !important;
+}
+
+.remote-debug-checkbox:focus {
+    outline: 2px solid var(--vscode-focusBorder);
+    outline-offset: 2px;
+}
diff --git a/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.vue b/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.vue
new file mode 100644
index 00000000000..62a3de7ce09
--- /dev/null
+++ b/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.vue
@@ -0,0 +1,338 @@
+/*! * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */
+
+
+
+
+
diff --git a/packages/core/src/lambda/vue/remoteInvoke/remoteInvokeFrontend.ts b/packages/core/src/lambda/vue/remoteInvoke/remoteInvokeFrontend.ts
new file mode 100644
index 00000000000..a99b6ac075e
--- /dev/null
+++ b/packages/core/src/lambda/vue/remoteInvoke/remoteInvokeFrontend.ts
@@ -0,0 +1,397 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// Disable because it is a front-end file.
+/* eslint-disable aws-toolkits/no-console-log */
+
+import { defineComponent } from 'vue'
+import { WebviewClientFactory } from '../../../webviews/client'
+import saveData from '../../../webviews/mixins/saveData'
+import type { RemoteInvokeData, RemoteInvokeWebview } from './invokeLambda'
+
+const client = WebviewClientFactory.create()
+const defaultInitialData = {
+    FunctionName: '',
+    FunctionArn: '',
+    FunctionRegion: '',
+    InputSamples: [],
+    FunctionMap: [],
+    TestEvents: [],
+    FunctionStack: '',
+    Source: '',
+    LambdaFunctionNode: undefined,
+    supportCodeDownload: true,
+    runtimeSupportsRemoteDebug: true,
+    remoteDebugLayer: '',
+    isLambdaRemote: true,
+}
+
+export default defineComponent({
+    data(): RemoteInvokeData {
+        return {
+            initialData: { ...defaultInitialData },
+            debugConfig: {
+                debugPort: undefined,
+                localRootPath: '',
+                remoteRootPath: '/var/task',
+                shouldPublishVersion: true,
+                lambdaTimeout: 900,
+                otherDebugParams: '',
+            },
+            debugState: {
+                isDebugging: false,
+                debugTimer: undefined,
+                debugTimeRemaining: 60,
+                showDebugTimer: false,
+                handlerFileAvailable: false,
+                remoteDebuggingEnabled: false,
+            },
+            runtimeSettings: {
+                sourceMapEnabled: true,
+                skipFiles: '/var/runtime/node_modules/**/*.js,/**',
+                justMyCode: true,
+                projectName: '',
+                outFiles: undefined,
+            },
+            uiState: {
+                isCollapsed: true,
+                extraRegionInfo: '',
+            },
+            payloadData: {
+                sampleText: '{}',
+            },
+        }
+    },
+
+    async created() {
+        // Initialize data from backend
+        this.initialData = (await client.init()) ?? this.initialData
+        this.debugConfig.localRootPath = this.initialData.LocalRootPath ?? ''
+
+        // Register for state change events from the backend
+        void client.onStateChange(async () => {
+            await this.syncStateFromWorkspace()
+        })
+
+        // Check for existing session state and load it
+        await this.syncStateFromWorkspace()
+    },
+
+    computed: {
+        // Auto-adjust textarea rows based on content
+        textareaRows(): number {
+            if (!this.payloadData.sampleText) {
+                return 5 // Default minimum rows
+            }
+
+            // Count line breaks to determine basic row count
+            const lineCount = this.payloadData.sampleText.split('\n').length
+            let additionalLine = 0
+            for (const line of this.payloadData.sampleText.split('\n')) {
+                if (line.length > 60) {
+                    additionalLine += Math.floor(line.length / 60)
+                }
+            }
+
+            // Use the larger of line count or estimated lines, with min 5 and max 20
+            const calculatedRows = lineCount + additionalLine
+            return Math.max(5, Math.min(50, calculatedRows))
+        },
+
+        // Validation computed properties
+        debugPortError(): string {
+            if (this.debugConfig.debugPort !== null && this.debugConfig.debugPort !== undefined) {
+                const port = Number(this.debugConfig.debugPort)
+                if (isNaN(port) || port < 1 || port > 65535) {
+                    return 'Debug port must be between 1 and 65535'
+                }
+            }
+            return ''
+        },
+
+        otherDebugParamsError(): string {
+            if (this.debugConfig.otherDebugParams && this.debugConfig.otherDebugParams.trim() !== '') {
+                try {
+                    JSON.parse(this.debugConfig.otherDebugParams)
+                } catch (error) {
+                    return 'Other Debug Params must be a valid JSON object'
+                }
+            }
+            return ''
+        },
+
+        lambdaTimeoutError(): string {
+            if (this.debugConfig.lambdaTimeout !== undefined) {
+                const timeout = Number(this.debugConfig.lambdaTimeout)
+                if (isNaN(timeout) || timeout < 1 || timeout > 900) {
+                    return 'Timeout override must be between 1 and 900 seconds'
+                }
+            }
+            return ''
+        },
+
+        // user can override the default provided layer and bring their own layer
+        // this is useful to support function with code signing config
+        lambdaLayerError(): string {
+            if (this.initialData.remoteDebugLayer && this.initialData.remoteDebugLayer.trim() !== '') {
+                const layerArn = this.initialData.remoteDebugLayer.trim()
+
+                // Validate Lambda layer ARN format
+                // Expected format: arn:aws:lambda:region:account-id:layer:layer-name:version
+                const layerArnRegex = /^arn:aws:lambda:[a-z0-9-]+:\d{12}:layer:[a-zA-Z0-9-_]+:\d+$/
+
+                if (!layerArnRegex.test(layerArn)) {
+                    return 'Layer ARN must be in the format: arn:aws:lambda:::layer::'
+                }
+
+                // Extract region from ARN to validate it matches the function region
+                const arnParts = layerArn.split(':')
+                if (arnParts.length >= 4) {
+                    const layerRegion = arnParts[3]
+                    if (this.initialData.FunctionRegion && layerRegion !== this.initialData.FunctionRegion) {
+                        return `Layer region (${layerRegion}) must match function region (${this.initialData.FunctionRegion})`
+                    }
+                }
+            }
+            return ''
+        },
+    },
+
+    methods: {
+        // Runtime detection computed properties based on the runtime string
+        hasRuntimePrefix(prefix: string): boolean {
+            const runtime = this.initialData.Runtime || ''
+            return runtime.startsWith(prefix)
+        },
+        // Sync state from workspace storage
+        async syncStateFromWorkspace() {
+            try {
+                // Detect Lambda remote debugging connection
+                this.uiState.extraRegionInfo = this.initialData.isLambdaRemote ? '' : '(LocalStack running)'
+
+                // Update debugging state
+                this.debugState.isDebugging = await client.isWebViewDebugging()
+                this.debugConfig.localRootPath = await client.getLocalPath()
+                this.debugState.handlerFileAvailable = await client.getHandlerAvailable()
+                // Get current session state
+
+                if (this.debugState.isDebugging) {
+                    // Update invoke button state based on session
+                    const isInvoking = await client.getIsInvoking()
+
+                    // If debugging is active and we're not showing the timer,
+                    // calculate and show remaining time
+                    this.clearDebugTimer()
+                    if (this.debugState.isDebugging && !isInvoking) {
+                        await this.startDebugTimer()
+                    }
+                } else {
+                    this.clearDebugTimer()
+                    // no debug session
+                }
+            } catch (error) {
+                console.error('Failed to sync state from workspace:', error)
+            }
+        },
+        async saveEvent() {
+            if (this.initialData.FunctionArn && this.initialData.FunctionRegion) {
+                // Use the backend method that shows a quickpick for save
+                await client.saveRemoteTestEvent(
+                    this.initialData.FunctionArn,
+                    this.initialData.FunctionRegion,
+                    this.payloadData.sampleText
+                )
+            }
+        },
+        async promptForFileLocation() {
+            const resp = await client.promptFile()
+            if (resp) {
+                // Populate the textarea with file content
+                this.payloadData.sampleText = resp.sample
+            }
+        },
+        async promptForFolderLocation() {
+            const resp = await client.promptFolder()
+            if (resp) {
+                this.debugConfig.localRootPath = resp
+                this.debugState.handlerFileAvailable = await client.getHandlerAvailable()
+            }
+        },
+        async debugPreCheck() {
+            if (!this.debugState.remoteDebuggingEnabled) {
+                // don't check if unchecking
+                this.debugState.remoteDebuggingEnabled = false
+                if (this.debugState.isDebugging) {
+                    await client.stopDebugging()
+                }
+            } else {
+                // check when user is checking box
+                this.debugState.remoteDebuggingEnabled = await client.debugPreCheck()
+                this.debugConfig.localRootPath = await client.getLocalPath()
+                this.debugState.handlerFileAvailable = await client.getHandlerAvailable()
+            }
+        },
+
+        async sendInput() {
+            // Tell the backend to set the button state. This state is maintained even if webview loses focus
+            if (this.debugState.remoteDebuggingEnabled) {
+                // check few outof bound issue
+                if (
+                    this.debugConfig.lambdaTimeout &&
+                    (this.debugConfig.lambdaTimeout > 900 || this.debugConfig.lambdaTimeout < 0)
+                ) {
+                    this.debugConfig.lambdaTimeout = 900
+                }
+                if (
+                    this.debugConfig.debugPort &&
+                    (this.debugConfig.debugPort > 65535 || this.debugConfig.debugPort <= 0)
+                ) {
+                    this.debugConfig.debugPort = 9229
+                }
+
+                // acquire invoke lock
+                if (this.debugState.remoteDebuggingEnabled && !(await client.checkReadyToInvoke())) {
+                    return
+                }
+
+                const defaultPort = this.initialData.isLambdaRemote ? 9229 : undefined
+
+                if (!this.debugState.isDebugging) {
+                    this.debugState.isDebugging = await client.startDebugging({
+                        functionArn: this.initialData.FunctionArn,
+                        functionName: this.initialData.FunctionName,
+                        port: this.debugConfig.debugPort ?? defaultPort,
+                        sourceMap: this.runtimeSettings.sourceMapEnabled,
+                        localRoot: this.debugConfig.localRootPath,
+                        shouldPublishVersion: this.debugConfig.shouldPublishVersion,
+                        remoteRoot:
+                            this.debugConfig.remoteRootPath !== '' ? this.debugConfig.remoteRootPath : '/var/task',
+                        skipFiles: (this.runtimeSettings.skipFiles !== ''
+                            ? this.runtimeSettings.skipFiles
+                            : '/**'
+                        ).split(','),
+                        justMyCode: this.runtimeSettings.justMyCode,
+                        projectName: this.runtimeSettings.projectName,
+                        otherDebugParams: this.debugConfig.otherDebugParams,
+                        layerArn: this.initialData.remoteDebugLayer,
+                        lambdaTimeout: this.debugConfig.lambdaTimeout ?? 900,
+                        outFiles: this.runtimeSettings.outFiles?.split(','),
+                        isLambdaRemote: this.initialData.isLambdaRemote ?? true,
+                    })
+                    if (!this.debugState.isDebugging) {
+                        // user cancel or failed to start debugging
+                        return
+                    }
+                }
+                this.debugState.showDebugTimer = false
+            }
+
+            await client.invokeLambda(
+                this.payloadData.sampleText,
+                this.initialData.Source,
+                this.debugState.remoteDebuggingEnabled
+            )
+            await this.syncStateFromWorkspace()
+        },
+
+        async removeDebugSetup() {
+            this.debugState.isDebugging = await client.stopDebugging()
+        },
+
+        async startDebugTimer() {
+            this.debugState.debugTimeRemaining = await client.getDebugTimeRemaining()
+            if (this.debugState.debugTimeRemaining <= 0) {
+                return
+            }
+            this.debugState.showDebugTimer = true
+            this.debugState.debugTimer = window.setInterval(() => {
+                this.debugState.debugTimeRemaining--
+                if (this.debugState.debugTimeRemaining <= 0) {
+                    this.clearDebugTimer()
+                }
+            }, 1000) as number | undefined
+        },
+
+        clearDebugTimer() {
+            if (this.debugState.debugTimer) {
+                window.clearInterval(this.debugState.debugTimer)
+                this.debugState.debugTimeRemaining = 0
+                this.debugState.debugTimer = undefined
+                this.debugState.showDebugTimer = false
+            }
+        },
+
+        toggleCollapsible() {
+            this.uiState.isCollapsed = !this.uiState.isCollapsed
+        },
+
+        async openHandler() {
+            await client.tryOpenHandlerFile()
+        },
+
+        async openHandlerWithDelay() {
+            const preValue = this.debugConfig.localRootPath
+            // user is inputting the dir, only try to open dir if user stopped typing for 1 second
+            await new Promise((resolve) => setTimeout(resolve, 1000))
+            if (preValue !== this.debugConfig.localRootPath) {
+                return
+            }
+            // try open if user stop input for 1 second
+            await client.tryOpenHandlerFile(this.debugConfig.localRootPath)
+            this.debugState.handlerFileAvailable = await client.getHandlerAvailable()
+        },
+
+        async downloadRemoteCode() {
+            try {
+                const path = await client.downloadRemoteCode()
+                if (path) {
+                    this.debugConfig.localRootPath = path
+                    this.debugState.handlerFileAvailable = await client.getHandlerAvailable()
+                }
+            } catch (error) {
+                console.error('Failed to download remote code:', error)
+            }
+        },
+
+        loadSampleEvent() {
+            client.getSamplePayload().then(
+                (sample) => {
+                    if (!sample) {
+                        return
+                    }
+                    this.payloadData.sampleText = JSON.stringify(JSON.parse(sample), undefined, 4)
+                },
+                (e) => {
+                    console.error('client.getSamplePayload failed: %s', (e as Error).message)
+                }
+            )
+        },
+
+        async loadRemoteTestEvents() {
+            if (this.initialData.FunctionArn && this.initialData.FunctionRegion) {
+                // Use the backend method that shows a quickpick
+                const eventContent = await client.selectRemoteTestEvent(
+                    this.initialData.FunctionArn,
+                    this.initialData.FunctionRegion
+                )
+
+                if (eventContent) {
+                    // Populate the textarea with the selected event
+                    this.payloadData.sampleText = JSON.stringify(JSON.parse(eventContent), undefined, 4)
+                }
+            }
+        },
+        onDebugPortChange(event: Event) {
+            const value = (event.target as HTMLInputElement).value
+            if (value === '') {
+                this.debugConfig.debugPort = undefined
+            } else {
+                this.debugConfig.debugPort = Number(value)
+            }
+        },
+    },
+
+    mixins: [saveData],
+})
diff --git a/src/lambda/wizards/samDeployWizard.ts b/packages/core/src/lambda/wizards/samDeployWizard.ts
similarity index 92%
rename from src/lambda/wizards/samDeployWizard.ts
rename to packages/core/src/lambda/wizards/samDeployWizard.ts
index d407b877a0f..365d395e532 100644
--- a/src/lambda/wizards/samDeployWizard.ts
+++ b/packages/core/src/lambda/wizards/samDeployWizard.ts
@@ -1,5 +1,5 @@
 /*!
- * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -10,9 +10,8 @@ const localize = nls.loadMessageBundle()
 
 import * as path from 'path'
 import * as vscode from 'vscode'
-import { samDeployDocUrl } from '../../shared/constants'
 import * as localizedText from '../../shared/localizedText'
-import { getLogger } from '../../shared/logger'
+import { getLogger } from '../../shared/logger/logger'
 import { createHelpButton } from '../../shared/ui/buttons'
 import * as input from '../../shared/ui/input'
 import * as picker from '../../shared/ui/picker'
@@ -33,15 +32,16 @@ import { getSamCliVersion } from '../../shared/sam/cli/samCliContext'
 import * as semver from 'semver'
 import { minSamCliVersionForImageSupport } from '../../shared/sam/cli/samCliValidator'
 import { ExtContext } from '../../shared/extensions'
-import { validateBucketName } from '../../s3/util'
+import { validateBucketName } from '../../awsService/s3/util'
 import { showViewLogsMessage } from '../../shared/utilities/messages'
-import { getIdeProperties, isCloud9 } from '../../shared/extensionUtilities'
+import { getIdeProperties, getSamDeployDocUrl, isCloud9 } from '../../shared/extensionUtilities'
 import { recentlyUsed } from '../../shared/localizedText'
 import globals from '../../shared/extensionGlobals'
 import { SamCliSettings } from '../../shared/sam/cli/samCliSettings'
 import { getIcon } from '../../shared/icons'
-import { DefaultS3Client } from '../../shared/clients/s3Client'
+import { S3Client } from '../../shared/clients/s3'
 import { telemetry } from '../../shared/telemetry/telemetry'
+import { openUrl } from '../../shared/utilities/vsCodeUtils'
 
 // eslint-disable-next-line @typescript-eslint/naming-convention
 const CREATE_NEW_BUCKET = localize('AWS.command.s3.createBucket', 'Create Bucket...')
@@ -206,19 +206,19 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
     public constructor(readonly extContext: ExtContext) {}
 
     public get workspaceFolders(): vscode.Uri[] | undefined {
-        return (vscode.workspace.workspaceFolders || []).map(f => f.uri)
+        return (vscode.workspace.workspaceFolders || []).map((f) => f.uri)
     }
 
     public async determineIfTemplateHasImages(templatePath: vscode.Uri): Promise {
-        const template = globals.templateRegistry.getRegisteredItem(templatePath.fsPath)
+        const template = (await globals.templateRegistry).getItem(templatePath.fsPath)
         const resources = template?.item?.Resources
         if (resources === undefined) {
             return false
         } else {
             return Object.keys(resources)
-                .filter(key => resources[key]?.Type === 'AWS::Serverless::Function')
-                .map(key => resources[key]?.Properties?.PackageType)
-                .some(it => it === 'Image')
+                .filter((key) => resources[key]?.Type === 'AWS::Serverless::Function')
+                .map((key) => resources[key]?.Properties?.PackageType)
+                .includes('Image')
         }
     }
 
@@ -251,7 +251,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
                 if (button === vscode.QuickInputButtons.Back) {
                     resolve(undefined)
                 } else if (button === this.helpButton) {
-                    vscode.env.openExternal(vscode.Uri.parse(samDeployDocUrl))
+                    void openUrl(getSamDeployDocUrl())
                 }
             },
         })
@@ -291,7 +291,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
                         if (button === vscode.QuickInputButtons.Back) {
                             resolve(undefined)
                         } else if (button === this.helpButton) {
-                            vscode.env.openExternal(vscode.Uri.parse(samDeployDocUrl))
+                            void openUrl(getSamDeployDocUrl())
                         }
                     },
                 })
@@ -335,7 +335,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
                         if (button === vscode.QuickInputButtons.Back) {
                             resolve(undefined)
                         } else if (button === this.helpButton) {
-                            vscode.env.openExternal(vscode.Uri.parse(samDeployDocUrl))
+                            void openUrl(getSamDeployDocUrl())
                         }
                     },
                 })
@@ -367,7 +367,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
                 step: step,
                 totalSteps: this.totalSteps + this.additionalSteps,
             },
-            items: partitionRegions.map(region => ({
+            items: partitionRegions.map((region) => ({
                 label: region.name,
                 detail: region.id,
                 // this is the only way to get this to show on going back
@@ -384,7 +384,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
                 if (button === vscode.QuickInputButtons.Back) {
                     resolve(undefined)
                 } else if (button === this.helpButton) {
-                    vscode.env.openExternal(vscode.Uri.parse(samDeployDocUrl))
+                    void openUrl(getSamDeployDocUrl())
                 }
             },
         })
@@ -446,7 +446,11 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
         // This will background load the S3 buckets and load them all (in one chunk) when the operation completes.
         // Not awaiting lets us display a "loading" quick pick for immediate feedback.
         // Does not use an IteratingQuickPick because listing S3 buckets by region is not a paginated operation.
-        populateS3QuickPick(quickPick, selectedRegion, SamCliSettings.instance, messages, profile, accountId)
+        populateS3QuickPick(quickPick, selectedRegion, SamCliSettings.instance, messages, profile, accountId).catch(
+            (e) => {
+                getLogger().error('populateS3QuickPick: %s', (e as Error).message)
+            }
+        )
 
         const choices = await picker.promptUser({
             picker: quickPick,
@@ -454,7 +458,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
                 if (button === vscode.QuickInputButtons.Back) {
                     resolve(undefined)
                 } else if (button === this.helpButton) {
-                    vscode.env.openExternal(vscode.Uri.parse(samDeployDocUrl))
+                    void openUrl(getSamDeployDocUrl())
                 } else if (button === createBucket) {
                     resolve([{ label: CREATE_NEW_BUCKET }])
                 } else if (button === enterBucket) {
@@ -510,7 +514,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
                 if (button === vscode.QuickInputButtons.Back) {
                     resolve(undefined)
                 } else if (button === this.helpButton) {
-                    vscode.env.openExternal(vscode.Uri.parse(samDeployDocUrl))
+                    void openUrl(getSamDeployDocUrl())
                 } else if (bucketProps.buttonHandler) {
                     bucketProps.buttonHandler(button, inputBox, resolve, reject)
                 }
@@ -544,7 +548,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
 
         const populator = new IteratorTransformer(
             () => new DefaultEcrClient(selectedRegion).describeRepositories(),
-            response => (response === undefined ? [] : [{ label: response.repositoryName, repository: response }])
+            (response) => (response === undefined ? [] : [{ label: response.repositoryName, repository: response }])
         )
         const controller = new picker.IteratingQuickPickController(quickPick, populator)
         controller.startRequests()
@@ -554,7 +558,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
                 if (button === vscode.QuickInputButtons.Back) {
                     resolve(undefined)
                 } else if (button === this.helpButton) {
-                    vscode.env.openExternal(vscode.Uri.parse(samDeployDocUrl))
+                    void openUrl(getSamDeployDocUrl())
                 }
             },
         })
@@ -607,7 +611,7 @@ export class DefaultSamDeployWizardContext implements SamDeployWizardContext {
                 if (button === vscode.QuickInputButtons.Back) {
                     resolve(undefined)
                 } else if (button === this.helpButton) {
-                    vscode.env.openExternal(vscode.Uri.parse(samDeployDocUrl))
+                    void openUrl(getSamDeployDocUrl())
                 }
             },
         })
@@ -638,7 +642,10 @@ export class SamDeployWizard extends MultiStepWizard {
      * @param context
      * @param commandArg Argument given by VSCode when the "Deploy" command was invoked from a context-menu.
      */
-    public constructor(private readonly context: SamDeployWizardContext, commandArg?: any) {
+    public constructor(
+        private readonly context: SamDeployWizardContext,
+        commandArg?: any
+    ) {
         super()
         if (commandArg && commandArg.path) {
             // "Deploy" command was invoked on a template.yaml file.
@@ -709,7 +716,7 @@ export class SamDeployWizard extends MultiStepWizard {
             // TODO: remove check when min version is high enough
             const samCliVersion = await getSamCliVersion(this.context.extContext.samCliContext())
             if (semver.lt(samCliVersion, minSamCliVersionForImageSupport)) {
-                vscode.window.showErrorMessage(
+                void vscode.window.showErrorMessage(
                     localize(
                         'AWS.output.sam.no.image.support',
                         'Support for Image-based Lambdas requires a minimum SAM CLI version of 1.13.0.'
@@ -729,7 +736,7 @@ export class SamDeployWizard extends MultiStepWizard {
         }
 
         const requiredParameterNames = new Set(
-            filter(parameters.keys(), name => parameters.get(name)!.required)
+            filter(parameters.keys(), (name) => parameters.get(name)!.required)
         )
         const overriddenParameters = await this.context.getOverriddenParameters(this.response.template)
         if (!overriddenParameters) {
@@ -765,13 +772,13 @@ export class SamDeployWizard extends MultiStepWizard {
         return wizardContinue(this.skipOrPromptRegion(this.S3_BUCKET))
     }
 
-    private readonly REGION: WizardStep = async step => {
+    private readonly REGION: WizardStep = async (step) => {
         this.response.region = await this.context.promptUserForRegion(step, this.response.region)
 
         return this.response.region ? wizardContinue(this.S3_BUCKET) : WIZARD_GOBACK
     }
 
-    private readonly S3_BUCKET: WizardStep = async step => {
+    private readonly S3_BUCKET: WizardStep = async (step) => {
         const profile = this.context.extContext.awsContext.getCredentialProfileName() || ''
         const accountId = this.context.extContext.awsContext.getCredentialAccountId() || ''
         const response = await this.context.promptUserForS3Bucket(
@@ -795,18 +802,17 @@ export class SamDeployWizard extends MultiStepWizard {
             }
 
             try {
-                const s3Client = new DefaultS3Client(this.response.region!)
-                const newBucketName = (await s3Client.createBucket({ bucketName: newBucketRequest })).bucket.name
+                const s3Client = new S3Client(this.response.region!)
+                const newBucketName = (await s3Client.createBucket({ bucketName: newBucketRequest })).bucket.Name
                 this.response.s3Bucket = newBucketName
                 getLogger().info('Created bucket: %O', newBucketName)
-                vscode.window.showInformationMessage(
+                void vscode.window.showInformationMessage(
                     localize('AWS.s3.createBucket.success', 'Created bucket: {0}', newBucketName)
                 )
                 telemetry.s3_createBucket.emit({ result: 'Succeeded' })
             } catch (e) {
-                showViewLogsMessage(
-                    localize('AWS.s3.createBucket.error.general', 'Failed to create bucket: {0}', newBucketRequest),
-                    vscode.window
+                void showViewLogsMessage(
+                    localize('AWS.s3.createBucket.error.general', 'Failed to create bucket: {0}', newBucketRequest)
                 )
                 telemetry.s3_createBucket.emit({ result: 'Failed' })
                 return WIZARD_RETRY
@@ -829,7 +835,7 @@ export class SamDeployWizard extends MultiStepWizard {
         return this.hasImages ? wizardContinue(this.ECR_REPO) : wizardContinue(this.STACK_NAME)
     }
 
-    private readonly ECR_REPO: WizardStep = async step => {
+    private readonly ECR_REPO: WizardStep = async (step) => {
         const response = await this.context.promptUserForEcrRepo(step, this.response.region, this.response.ecrRepo)
 
         this.response.ecrRepo = response
@@ -859,7 +865,10 @@ class SamTemplateQuickPickItem implements vscode.QuickPickItem {
     public description?: string
     public detail?: string
 
-    public constructor(public readonly uri: vscode.Uri, showWorkspaceFolderDetails: boolean) {
+    public constructor(
+        public readonly uri: vscode.Uri,
+        showWorkspaceFolderDetails: boolean
+    ) {
         this.label = SamTemplateQuickPickItem.getLabel(uri)
 
         if (showWorkspaceFolderDetails) {
@@ -933,15 +942,15 @@ function validateStackName(value: string): string | undefined {
 }
 
 async function getTemplateChoices(...workspaceFolders: vscode.Uri[]): Promise {
-    const templateUris = globals.templateRegistry.registeredItems.map(o => vscode.Uri.file(o.path))
+    const templateUris = (await globals.templateRegistry).items.map((o) => vscode.Uri.file(o.path))
     const uriToLabel: Map = new Map()
     const labelCounts: Map = new Map()
 
-    templateUris.forEach(uri => {
+    for (const uri of templateUris) {
         const label = SamTemplateQuickPickItem.getLabel(uri)
         uriToLabel.set(uri, label)
         labelCounts.set(label, 1 + (labelCounts.get(label) || 0))
-    })
+    }
 
     return Array.from(uriToLabel, ([uri, label]) => {
         const showWorkspaceFolderDetails: boolean = (labelCounts.get(label) || 0) > 1
@@ -967,7 +976,7 @@ async function populateS3QuickPick(
     profile?: string,
     accountId?: string
 ): Promise {
-    return new Promise(async resolve => {
+    return new Promise(async (resolve) => {
         const goBack: string = localize('AWS.picker.dynamic.noItemsFound.detail', 'Click here to go back')
         const baseItems: vscode.QuickPickItem[] = []
         const cloud9Bucket = `cloud9-${accountId}-sam-deployments-${selectedRegion}`
@@ -983,7 +992,7 @@ async function populateS3QuickPick(
                 })
             }
         } catch (e) {
-            getLogger().error('Recent bucket JSON not parseable.', e)
+            getLogger().error('Recent bucket JSON not parseable: %O', e)
         }
 
         if (isCloud9() && recent !== cloud9Bucket) {
@@ -998,7 +1007,7 @@ async function populateS3QuickPick(
         }
 
         try {
-            const s3Client = new DefaultS3Client(selectedRegion)
+            const s3Client = new S3Client(selectedRegion)
 
             quickPick.items = [...baseItems]
 
@@ -1016,10 +1025,10 @@ async function populateS3QuickPick(
                 ]
             } else {
                 const bucketItems = buckets
-                    .filter(bucket => bucket.name !== recent && !(isCloud9() && bucket.name === cloud9Bucket))
-                    .map(bucket => {
+                    .filter((bucket) => bucket.Name !== recent && !(isCloud9() && bucket.Name === cloud9Bucket))
+                    .map((bucket) => {
                         return {
-                            label: bucket.name,
+                            label: bucket.Name,
                         }
                     })
 
diff --git a/src/lambda/wizards/samInitWizard.ts b/packages/core/src/lambda/wizards/samInitWizard.ts
similarity index 77%
rename from src/lambda/wizards/samInitWizard.ts
rename to packages/core/src/lambda/wizards/samInitWizard.ts
index 1dca950a6dc..9bd3a1b72fb 100644
--- a/src/lambda/wizards/samInitWizard.ts
+++ b/packages/core/src/lambda/wizards/samInitWizard.ts
@@ -1,16 +1,16 @@
 /*!
- * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
 
 import * as nls from 'vscode-nls'
 import * as AWS from '@aws-sdk/types'
-import { Runtime } from 'aws-sdk/clients/lambda'
+import { Runtime } from '@aws-sdk/client-lambda'
 import * as path from 'path'
 import * as vscode from 'vscode'
 import { SchemasDataProvider } from '../../eventSchemas/providers/schemasDataProvider'
 import { DefaultSchemaClient } from '../../shared/clients/schemaClient'
-import { eventBridgeSchemasDocUrl, samInitDocUrl } from '../../shared/constants'
+import { eventBridgeSchemasDocUrl } from '../../shared/constants'
 import {
     Architecture,
     createRuntimeQuickPick,
@@ -28,7 +28,6 @@ import {
 } from '../models/samTemplates'
 import * as semver from 'semver'
 import { minSamCliVersionForArmSupport, minSamCliVersionForImageSupport } from '../../shared/sam/cli/samCliValidator'
-import * as fsutil from '../../shared/filesystemUtilities'
 import { Wizard } from '../../shared/wizards/wizard'
 import { createFolderPrompt } from '../../shared/ui/common/location'
 import { createInputBox, InputBoxPrompter } from '../../shared/ui/inputPrompter'
@@ -37,6 +36,8 @@ import { createRegionPrompter } from '../../shared/ui/common/region'
 import { Region } from '../../shared/regions/endpoints'
 import { createCommonButtons } from '../../shared/ui/buttons'
 import { createExitPrompter } from '../../shared/ui/common/exitPrompter'
+import { getNonexistentFilename } from '../../shared/filesystemUtilities'
+import { getSamInitDocUrl } from '../../shared/extensionUtilities'
 
 const localize = nls.loadMessageBundle()
 
@@ -55,7 +56,7 @@ export interface CreateNewSamAppWizardForm {
 function createRuntimePrompter(samCliVersion: string): QuickPickPrompter {
     return createRuntimeQuickPick({
         showImageRuntimes: semver.gte(samCliVersion, minSamCliVersionForImageSupport),
-        buttons: createCommonButtons(samInitDocUrl),
+        buttons: createCommonButtons(getSamInitDocUrl()),
     })
 }
 
@@ -65,7 +66,7 @@ function createSamTemplatePrompter(
     samCliVersion: string
 ): QuickPickPrompter {
     const templates = getSamTemplateWizardOption(currRuntime, packageType, samCliVersion)
-    const items = templates.toArray().map(template => ({
+    const items = templates.toArray().map((template) => ({
         label: template,
         data: template,
         detail: getTemplateDescription(template),
@@ -73,7 +74,7 @@ function createSamTemplatePrompter(
 
     return createQuickPick(items, {
         title: localize('AWS.samcli.initWizard.template.prompt', 'Select a SAM Application Template'),
-        buttons: createCommonButtons(samInitDocUrl),
+        buttons: createCommonButtons(getSamInitDocUrl()),
     })
 }
 
@@ -82,16 +83,16 @@ function createSchemaRegionPrompter(regions: Region[], defaultRegion?: string):
         title: localize('AWS.samcli.initWizard.schemas.region.prompt', 'Select an EventBridge Schemas Region'),
         buttons: createCommonButtons(eventBridgeSchemasDocUrl),
         defaultRegion,
-    }).transform(r => r.id)
+    }).transform((r) => r.id)
 }
 
 function createDependencyPrompter(currRuntime: Runtime): QuickPickPrompter {
     const dependencyManagers = getDependencyManager(currRuntime)
-    const items = dependencyManagers.map(dependencyManager => ({ label: dependencyManager }))
+    const items = dependencyManagers.map((dependencyManager) => ({ label: dependencyManager }))
 
     return createLabelQuickPick(items, {
         title: localize('AWS.samcli.initWizard.dependencyManager.prompt', 'Select a Dependency Manager'),
-        buttons: createCommonButtons(samInitDocUrl),
+        buttons: createCommonButtons(getSamInitDocUrl()),
     })
 }
 
@@ -99,9 +100,9 @@ function createRegistryPrompter(region: string, credentials?: AWS.Credentials):
     const client = new DefaultSchemaClient(region)
     const items = SchemasDataProvider.getInstance()
         .getRegistries(region, client, credentials!)
-        .then(registryNames => {
+        .then((registryNames) => {
             if (!registryNames) {
-                vscode.window.showInformationMessage(
+                void vscode.window.showInformationMessage(
                     localize(
                         'AWS.samcli.initWizard.schemas.registry.failed_to_load_resources',
                         'Error loading registries.'
@@ -110,14 +111,14 @@ function createRegistryPrompter(region: string, credentials?: AWS.Credentials):
                 return []
             }
 
-            return registryNames.map(registry => ({
+            return registryNames.map((registry) => ({
                 label: registry,
             }))
         })
 
     return createLabelQuickPick(items, {
         title: localize('AWS.samcli.initWizard.schemas.registry.prompt', 'Select a Registry'),
-        buttons: createCommonButtons(samInitDocUrl),
+        buttons: createCommonButtons(getSamInitDocUrl()),
     })
 }
 
@@ -129,9 +130,9 @@ function createSchemaPrompter(
     const client = new DefaultSchemaClient(region)
     const items = SchemasDataProvider.getInstance()
         .getSchemas(region, registry, client, credentials!)
-        .then(schemas => {
+        .then((schemas) => {
             if (!schemas) {
-                vscode.window.showInformationMessage(
+                void vscode.window.showInformationMessage(
                     localize(
                         'AWS.samcli.initWizard.schemas.failed_to_load_resources',
                         'Error loading schemas in registry {0}.',
@@ -142,13 +143,13 @@ function createSchemaPrompter(
             }
 
             if (schemas.length === 0) {
-                vscode.window.showInformationMessage(
+                void vscode.window.showInformationMessage(
                     localize('AWS.samcli.initWizard.schemas.notFound"', 'No schemas found in registry {0}.', registry)
                 )
                 return []
             }
 
-            return schemas.map(schema => ({
+            return schemas.map((schema) => ({
                 label: schema.SchemaName!,
             }))
         })
@@ -179,7 +180,7 @@ function createNamePrompter(defaultValue: string): InputBoxPrompter {
     return createInputBox({
         value: defaultValue,
         title: localize('AWS.samcli.initWizard.name.prompt', 'Enter a name for your new application'),
-        buttons: createCommonButtons(samInitDocUrl),
+        buttons: createCommonButtons(getSamInitDocUrl()),
         validateInput: validateName,
     })
 }
@@ -187,7 +188,7 @@ function createNamePrompter(defaultValue: string): InputBoxPrompter {
 function createArchitecturePrompter(): QuickPickPrompter {
     return createLabelQuickPick([{ label: 'x86_64' }, { label: 'arm64' }], {
         title: localize('AWS.samcli.initWizard.architecture.prompt', 'Select an Architecture'),
-        buttons: createCommonButtons(samInitDocUrl),
+        buttons: createCommonButtons(getSamInitDocUrl()),
     })
 }
 
@@ -204,15 +205,18 @@ export class CreateNewSamAppWizard extends Wizard {
 
         this.form.runtimeAndPackage.bindPrompter(() => createRuntimePrompter(context.samCliVersion))
 
-        this.form.dependencyManager.bindPrompter(state => createDependencyPrompter(state.runtimeAndPackage!.runtime!), {
-            showWhen: state =>
-                state.runtimeAndPackage?.runtime !== undefined &&
-                getDependencyManager(state.runtimeAndPackage.runtime).length > 1,
-            setDefault: state =>
-                state.runtimeAndPackage?.runtime !== undefined
-                    ? getDependencyManager(state.runtimeAndPackage.runtime)[0]
-                    : undefined,
-        })
+        this.form.dependencyManager.bindPrompter(
+            (state) => createDependencyPrompter(state.runtimeAndPackage!.runtime!),
+            {
+                showWhen: (state) =>
+                    state.runtimeAndPackage?.runtime !== undefined &&
+                    getDependencyManager(state.runtimeAndPackage.runtime).length > 1,
+                setDefault: (state) =>
+                    state.runtimeAndPackage?.runtime !== undefined
+                        ? getDependencyManager(state.runtimeAndPackage.runtime)[0]
+                        : undefined,
+            }
+        )
 
         // TODO: remove `partial` when updated wizard types gets merged (need to make the PR first of course...)
         function canShowArchitecture(
@@ -227,14 +231,14 @@ export class CreateNewSamAppWizard extends Wizard {
                 return false
             }
 
-            return samArmLambdaRuntimes.has(state.runtimeAndPackage?.runtime ?? 'unknown')
+            return state.runtimeAndPackage ? samArmLambdaRuntimes.has(state.runtimeAndPackage.runtime) : false
         }
 
         this.form.architecture.bindPrompter(createArchitecturePrompter, {
             showWhen: canShowArchitecture,
         })
 
-        this.form.template.bindPrompter(state =>
+        this.form.template.bindPrompter((state) =>
             createSamTemplatePrompter(
                 state.runtimeAndPackage!.runtime!,
                 state.runtimeAndPackage!.packageType!,
@@ -250,12 +254,12 @@ export class CreateNewSamAppWizard extends Wizard {
             showWhen: isStarterTemplate,
         })
 
-        this.form.registryName.bindPrompter(form => createRegistryPrompter(form.region!, context.credentials), {
+        this.form.registryName.bindPrompter((form) => createRegistryPrompter(form.region!, context.credentials), {
             showWhen: isStarterTemplate,
         })
 
         this.form.schemaName.bindPrompter(
-            state => createSchemaPrompter(state.region!, state.registryName!, context.credentials),
+            (state) => createSchemaPrompter(state.region!, state.registryName!, context.credentials),
             {
                 showWhen: isStarterTemplate,
             }
@@ -263,7 +267,7 @@ export class CreateNewSamAppWizard extends Wizard {
 
         this.form.location.bindPrompter(() =>
             createFolderPrompt(vscode.workspace.workspaceFolders ?? [], {
-                buttons: createCommonButtons(samInitDocUrl),
+                buttons: createCommonButtons(getSamInitDocUrl()),
                 title: localize('AWS.samInit.location.title', 'Select the folder for your new SAM application'),
                 browseFolderDetail: localize(
                     'AWS.samInit.location.detail',
@@ -272,15 +276,14 @@ export class CreateNewSamAppWizard extends Wizard {
             })
         )
 
-        this.form.name.bindPrompter(state =>
-            createNamePrompter(
-                fsutil.getNonexistentFilename(
-                    state.location!.fsPath,
-                    `lambda-${state.runtimeAndPackage!.runtime}`,
-                    '',
-                    99
-                )
+        this.form.name.bindPrompter(async (state) => {
+            const fname = await getNonexistentFilename(
+                state.location!.fsPath,
+                `lambda-${state.runtimeAndPackage!.runtime}`,
+                '',
+                99
             )
-        )
+            return createNamePrompter(fname)
+        })
     }
 }
diff --git a/packages/core/src/login/webview/commonAuthViewProvider.ts b/packages/core/src/login/webview/commonAuthViewProvider.ts
new file mode 100644
index 00000000000..8ad5c71bcd1
--- /dev/null
+++ b/packages/core/src/login/webview/commonAuthViewProvider.ts
@@ -0,0 +1,176 @@
+/*!
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * This is the webview provider of login ux. It should be created per webview.
+ * Usage:
+ * 1. Create a view in package.json
+ * {
+"type": "webview",
+"id": "aws.AmazonCommonAuth",
+"name": "%AWS.amazonq.login%",
+"when": "!aws.isSageMaker && !aws.amazonq.showView"
+},
+
+* 2. Assign when clause context to this view. Manage the state of when clause context.
+* 3. Init this provider at activation
+* const provider2 = new CommonAuthViewProvider(context, appInitContext.onDidChangeAmazonQVisibility)
+*     context.subscriptions.push(
+window.registerWebviewViewProvider(CommonAuthViewProvider.viewType, provider2, {
+    webviewOptions: {
+        retainContextWhenHidden: true,
+    },
+}),
+* 
+*/
+
+import * as vscode from 'vscode'
+import {
+    WebviewViewProvider,
+    ExtensionContext,
+    WebviewView,
+    WebviewViewResolveContext,
+    CancellationToken,
+    Uri,
+    EventEmitter,
+} from 'vscode'
+import { registerAssetsHttpsFileSystem } from '../../amazonq/webview/assets/assetsHandler'
+import { VueWebview, VueWebviewPanel } from '../../webviews/main'
+import { AmazonQLoginWebview } from './vue/amazonq/backend_amazonq'
+import { ToolkitLoginWebview } from './vue/toolkit/backend_toolkit'
+import { CodeCatalystAuthenticationProvider } from '../../codecatalyst/auth'
+import { telemetry } from '../../shared/telemetry/telemetry'
+import { AuthSources } from './util'
+import { AuthFlowStates } from './vue/types'
+import { getTelemetryMetadataForConn } from '../../auth/connection'
+import { AuthUtil } from '../../codewhisperer/util/authUtil'
+import { ExtensionUse } from '../../auth/utils'
+
+export class CommonAuthViewProvider implements WebviewViewProvider {
+    public readonly viewType: string
+
+    webView: VueWebviewPanel | undefined
+    source: string = ''
+
+    constructor(
+        private readonly extensionContext: ExtensionContext,
+        readonly app: string,
+        private readonly onDidChangeVisibility?: EventEmitter
+    ) {
+        this.viewType = `aws.${app}.AmazonCommonAuth`
+
+        registerAssetsHttpsFileSystem(extensionContext)
+        if (app === 'toolkit') {
+            // Create panel bindings using our class
+            const Panel = VueWebview.compilePanel(ToolkitLoginWebview)
+            // `context` is `ExtContext` provided on activation
+            this.webView = new Panel(extensionContext, CodeCatalystAuthenticationProvider.fromContext(extensionContext))
+            this.source = ToolkitLoginWebview.sourcePath
+        } else if (app === 'amazonq') {
+            const Panel = VueWebview.compilePanel(AmazonQLoginWebview)
+            this.webView = new Panel(extensionContext)
+            this.source = AmazonQLoginWebview.sourcePath
+        } else {
+            throw new Error(`invalid app provided to common auth view: ${app}`)
+        }
+    }
+
+    public async resolveWebviewView(
+        webviewView: WebviewView,
+        context: WebviewViewResolveContext,
+        _token: CancellationToken
+    ) {
+        // Our callback won't fire on the first view.
+        if (webviewView.visible) {
+            telemetry.auth_signInPageOpened.emit({
+                result: 'Succeeded',
+                passive: true,
+                source: ExtensionUse.instance.sourceForTelemetry(),
+            })
+        }
+
+        // This will fire whenever the user opens or closes the login page from 'somewhere else'
+        // i.e. NOT when switching from/to the chat window, which uses the same view area.
+        webviewView.onDidChangeVisibility(async () => {
+            if (webviewView.visible) {
+                telemetry.auth_signInPageOpened.emit({
+                    result: 'Succeeded',
+                    passive: true,
+                    source: ExtensionUse.instance.sourceForTelemetry(),
+                })
+            } else {
+                telemetry.auth_signInPageClosed.emit({ result: 'Succeeded', passive: true })
+
+                // Count leaving the webview as a user cancellation.
+                const authState = await this.webView!.server.getAuthState()
+                this.webView!.server.storeMetricMetadata({ result: 'Cancelled' })
+                if (authState === AuthFlowStates.REAUTHNEEDED || authState === AuthFlowStates.REAUTHENTICATING) {
+                    this.webView!.server.storeMetricMetadata({
+                        isReAuth: true,
+                        ...(await getTelemetryMetadataForConn(AuthUtil.instance.conn)),
+                    })
+                } else {
+                    this.webView!.server.storeMetricMetadata({ isReAuth: false })
+                }
+                this.webView!.server.emitAuthMetric()
+                this.webView!.server.cancelAuthFlow()
+
+                // Set after emitting. If users use side bar to return to login, this source is correct
+                // for the next iteration. Otherwise, other sources will be set accordingly by whatever
+                // shows the login page.
+                this.webView!.server.authSource = AuthSources.vscodeComponent
+            }
+
+            this.onDidChangeVisibility?.fire(webviewView.visible)
+        })
+
+        const dist = Uri.joinPath(this.extensionContext.extensionUri, 'dist')
+        const resources = Uri.joinPath(this.extensionContext.extensionUri, 'resources')
+        webviewView.webview.options = {
+            enableScripts: true,
+            enableCommandUris: true,
+            localResourceRoots: [dist, resources],
+        }
+        // register the webview server
+        await this.webView?.setup(webviewView.webview)
+
+        webviewView.webview.html = this._getHtmlForWebview(this.extensionContext.extensionUri, webviewView.webview)
+    }
+
+    private _getHtmlForWebview(extensionURI: Uri, webview: vscode.Webview) {
+        const assetsPath = Uri.joinPath(extensionURI)
+        const javascriptUri = Uri.joinPath(assetsPath, 'dist', this.source)
+        // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview.
+        const scriptUri = webview.asWebviewUri(javascriptUri)
+        const serverHostname = process.env.WEBPACK_DEVELOPER_SERVER
+        const entrypoint =
+            serverHostname !== undefined ? Uri.parse(serverHostname).with({ path: `/${this.source}` }) : scriptUri
+
+        // Get Vue.js from dist/libs directory
+        const vueUri = Uri.joinPath(assetsPath, 'dist', 'libs', 'vue.min.js')
+        const vueScript = webview.asWebviewUri(vueUri)
+
+        return `
+            
+            
+                
+                    
+                    
+
+                    Base View Extension
+                
+                
+                      
+                    
+
+                    
+ + + + ` + } +} diff --git a/packages/core/src/login/webview/index.ts b/packages/core/src/login/webview/index.ts new file mode 100644 index 00000000000..80abcc4fd79 --- /dev/null +++ b/packages/core/src/login/webview/index.ts @@ -0,0 +1,7 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export { CommonAuthViewProvider } from './commonAuthViewProvider' +export { CommonAuthWebview } from './vue/backend' diff --git a/packages/core/src/login/webview/util.ts b/packages/core/src/login/webview/util.ts new file mode 100644 index 00000000000..c526e387d31 --- /dev/null +++ b/packages/core/src/login/webview/util.ts @@ -0,0 +1,28 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { cwQuickPickSource, cwTreeNodeSource, amazonQChatSource } from '../../codewhisperer/commands/types' +import { ExtStartUpSources } from '../../shared/telemetry/util' +import { vscodeComponent } from '../../shared/vscode/commands2' + +/** + * Different places the Add Connection command could be executed from. + * + * Useful for telemetry. + */ +export const AuthSources = { + addConnectionQuickPick: 'addConnectionQuickPick', + firstStartUp: ExtStartUpSources.firstStartUp, + codecatalystDeveloperTools: 'codecatalystDeveloperTools', + vscodeComponent: vscodeComponent, + cwQuickPick: cwQuickPickSource, + cwTreeNode: cwTreeNodeSource, + amazonQChat: amazonQChatSource, + authNode: 'authNode', // deprecated? + appBuilderWalkthrough: 'AppBuilderWalkthrough', + unknown: 'unknown', +} as const + +export type AuthSource = (typeof AuthSources)[keyof typeof AuthSources] diff --git a/packages/core/src/login/webview/vue/amazonq/backend_amazonq.ts b/packages/core/src/login/webview/vue/amazonq/backend_amazonq.ts new file mode 100644 index 00000000000..a83e99d04b7 --- /dev/null +++ b/packages/core/src/login/webview/vue/amazonq/backend_amazonq.ts @@ -0,0 +1,265 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { + AwsConnection, + Connection, + SsoConnection, + getTelemetryMetadataForConn, + isSsoConnection, +} from '../../../../auth/connection' +import { AuthUtil } from '../../../../codewhisperer/util/authUtil' +import { CommonAuthWebview } from '../backend' +import { awsIdSignIn } from '../../../../codewhisperer/util/showSsoPrompt' +import { connectToEnterpriseSso } from '../../../../codewhisperer/util/getStartUrl' +import { activateExtension, isExtensionInstalled } from '../../../../shared/utilities/vsCodeUtils' +import { VSCODE_EXTENSION_ID } from '../../../../shared/extensions' +import { getLogger } from '../../../../shared/logger/logger' +import { debounce } from 'lodash' +import { AuthError, AuthFlowState, userCancelled } from '../types' +import { ToolkitError } from '../../../../shared/errors' +import { withTelemetryContext } from '../../../../shared/telemetry/util' +import { builderIdStartUrl } from '../../../../auth/sso/constants' +import { RegionProfile } from '../../../../codewhisperer/models/model' +import { randomUUID } from '../../../../shared/crypto' +import globals from '../../../../shared/extensionGlobals' +import { telemetry } from '../../../../shared/telemetry/telemetry' +import { ProfileSwitchIntent } from '../../../../codewhisperer/region/regionProfileManager' + +const className = 'AmazonQLoginWebview' +export class AmazonQLoginWebview extends CommonAuthWebview { + public override id: string = 'aws.amazonq.AmazonCommonAuth' + public static sourcePath: string = 'vue/src/login/webview/vue/amazonq/index.js' + public override supportsLoadTelemetry: boolean = true + + override onActiveConnectionModified = new vscode.EventEmitter() + + constructor() { + super(AmazonQLoginWebview.sourcePath) + + this.setupConnectionEventEmitter() + } + + /** + * Returns list of connections that are pushed from Toolkit to Amazon Q + */ + async fetchConnections(): Promise { + if (!isExtensionInstalled(VSCODE_EXTENSION_ID.awstoolkit)) { + return undefined + } + await activateExtension(VSCODE_EXTENSION_ID.awstoolkit) + const toolkitExt = vscode.extensions.getExtension(VSCODE_EXTENSION_ID.awstoolkit) + const importedApi = toolkitExt?.exports?.getApi(VSCODE_EXTENSION_ID.amazonq) + if (importedApi && 'listConnections' in importedApi) { + return ((await importedApi?.listConnections()) as AwsConnection[]).filter( + // No need to display Builder ID as an existing connection, + // users can just select the Builder ID login option and it would have the same effect. + (conn) => conn.startUrl !== builderIdStartUrl + ) + } + return [] + } + + async startBuilderIdSetup(): Promise { + getLogger().debug(`called startBuilderIdSetup()`) + return await this.ssoSetup('startCodeWhispererBuilderIdSetup', async () => { + this.storeMetricMetadata({ + credentialSourceId: 'awsId', + authEnabledFeatures: 'codewhisperer', + isReAuth: false, + }) + + const conn = await awsIdSignIn() + this.storeMetricMetadata(await getTelemetryMetadataForConn(conn)) + + void vscode.window.showInformationMessage('AmazonQ: Successfully connected to AWS Builder ID') + }) + } + + async startEnterpriseSetup(startUrl: string, region: string): Promise { + getLogger().debug(`called startEnterpriseSetup() with startUrl: '${startUrl}', region: '${region}'`) + await globals.globalState.update('recentSso', { + startUrl: startUrl, + region: region, + }) + return await this.ssoSetup('startCodeWhispererEnterpriseSetup', async () => { + this.storeMetricMetadata({ + credentialStartUrl: startUrl, + credentialSourceId: 'iamIdentityCenter', + authEnabledFeatures: 'codewhisperer', + isReAuth: false, + }) + + const conn = await connectToEnterpriseSso(startUrl, region) + this.storeMetricMetadata(await getTelemetryMetadataForConn(conn)) + + void vscode.window.showInformationMessage('AmazonQ: Successfully connected to AWS IAM Identity Center') + }) + } + + async reauthenticateConnection(): Promise { + this.isReauthenticating = true + this.reauthError = undefined + + try { + // Sanity checks + if (!AuthUtil.instance.isConnected()) { + getLogger().error('amazon Q reauthenticate called on a non-existant connection') + throw new ToolkitError('Cannot reauthenticate non-existant connection.') + } + + const conn = AuthUtil.instance.conn + if (!isSsoConnection(conn)) { + getLogger().error('amazon Q reauthenticate called, but the connection is not SSO') + throw new ToolkitError('Cannot reauthenticate non-SSO connection.') + } + + /** + * IMPORTANT: During this process {@link this.onActiveConnectionModified} is triggered. This + * causes the reauth page to refresh before the user is actually done the whole reauth flow. + */ + this.reauthError = await this.ssoSetup('reauthenticateAmazonQ', async () => { + this.storeMetricMetadata({ + authEnabledFeatures: this.getAuthEnabledFeatures(conn), + isReAuth: true, + ...(await getTelemetryMetadataForConn(conn)), + }) + await AuthUtil.instance.reauthenticate() + this.storeMetricMetadata({ + ...(await getTelemetryMetadataForConn(conn)), + }) + }) + } finally { + this.isReauthenticating = false + } + + if (this.reauthError?.id === userCancelled) { + // Since reauth was not successful it did not trigger an update in the connection. + // We need to pretend it changed so our frontend triggers an update. + this.onActiveConnectionModified.fire() + } + } + + private reauthError: AuthError | undefined = undefined + override async getReauthError(): Promise { + return this.reauthError + } + + async getActiveConnection(): Promise { + return AuthUtil.instance.conn + } + + /** + * `true` if the actual reauth flow is in progress. + * + * We need this state since the reauth process triggers + * {@link this.onActiveConnectionModified} before it is actually done. + * This causes the UI to refresh, and we need to remember that we are + * still in the process of reauthenticating. + */ + isReauthenticating: boolean = false + private authState: AuthFlowState = 'LOGIN' + override async refreshAuthState(): Promise { + const featureAuthStates = await AuthUtil.instance.getChatAuthState() + if (featureAuthStates.amazonQ === 'expired') { + this.authState = this.isReauthenticating ? 'REAUTHENTICATING' : 'REAUTHNEEDED' + return + } else if (featureAuthStates.amazonQ === 'pendingProfileSelection') { + this.authState = 'PENDING_PROFILE_SELECTION' + // possible that user starts with "profile selection" state therefore the timeout for auth flow should be disposed otherwise will emit failure + this.loadMetadata?.loadTimeout?.dispose() + this.loadMetadata = { + traceId: randomUUID(), + loadTimeout: undefined, + start: globals.clock.Date.now(), + } + return + } + this.authState = 'LOGIN' + } + + override async getAuthState(): Promise { + return this.authState + } + + @withTelemetryContext({ name: 'signout', class: className }) + override async signout(): Promise { + const conn = AuthUtil.instance.secondaryAuth.activeConnection + if (!isSsoConnection(conn)) { + throw new ToolkitError(`Cannot signout non-SSO connection, type is: ${conn?.type}`) + } + + this.storeMetricMetadata({ + authEnabledFeatures: this.getAuthEnabledFeatures(conn), + isReAuth: true, + ...(await getTelemetryMetadataForConn(conn)), + result: 'Cancelled', + }) + + await AuthUtil.instance.secondaryAuth.deleteConnection() + this.reauthError = undefined + + this.emitAuthMetric() + } + + async listSsoConnections(): Promise { + // Amazon Q only supports 1 connection at a time, + // so there isn't a need to de-duplicate connections. + return [] + } + + override startIamCredentialSetup( + profileName: string, + accessKey: string, + secretKey: string + ): Promise { + throw new Error('Method not implemented.') + } + + /** If users are unauthenticated in Q/CW, we should always display the auth screen. */ + async quitLoginScreen() {} + + /** + * The purpose of returning Error.message is to notify vue frontend that API call fails and to render corresponding error message to users + * @returns ProfileList when API call succeeds, otherwise Error.message + */ + override async listRegionProfiles(): Promise { + try { + return await AuthUtil.instance.regionProfileManager.getProfiles() + } catch (e) { + const conn = AuthUtil.instance.conn as SsoConnection | undefined + telemetry.amazonq_didSelectProfile.emit({ + source: 'auth', + amazonQProfileRegion: AuthUtil.instance.regionProfileManager.activeRegionProfile?.region ?? 'not-set', + ssoRegion: conn?.ssoRegion, + result: 'Failed', + credentialStartUrl: conn?.startUrl, + reason: (e as Error).message, + }) + + return (e as Error).message + } + } + + override selectRegionProfile(profile: RegionProfile, source: ProfileSwitchIntent) { + return AuthUtil.instance.regionProfileManager.switchRegionProfile(profile, source) + } + + private setupConnectionEventEmitter(): void { + // allows the frontend to listen to Amazon Q auth events from the backend + const codeWhispererConnectionChanged = createThrottle(() => this.onActiveConnectionModified.fire()) + AuthUtil.instance.secondaryAuth.onDidChangeActiveConnection(codeWhispererConnectionChanged) + AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(codeWhispererConnectionChanged) + + /** + * Multiple events can be received in rapid succession and if + * we execute on the first one it is possible to get a stale + * state. + */ + function createThrottle(callback: () => void) { + return debounce(callback, 500) + } + } +} diff --git a/packages/core/src/login/webview/vue/amazonq/index.ts b/packages/core/src/login/webview/vue/amazonq/index.ts new file mode 100644 index 00000000000..0f27e4ff74f --- /dev/null +++ b/packages/core/src/login/webview/vue/amazonq/index.ts @@ -0,0 +1,21 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * This module is run within the webview, and will mount the Vue app. + */ + +import { createApp } from 'vue' +import component from '../root.vue' + +const create = () => + createApp(component, { + app: 'AMAZONQ', + }) +const app = create() + +app.mount('#vue-app') +window.addEventListener('remount', () => { + app.unmount() + create().mount('#vue-app') +}) diff --git a/packages/core/src/login/webview/vue/backend.ts b/packages/core/src/login/webview/vue/backend.ts new file mode 100644 index 00000000000..c8f1f38d4d7 --- /dev/null +++ b/packages/core/src/login/webview/vue/backend.ts @@ -0,0 +1,309 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import globals from '../../../shared/extensionGlobals' +import { VueWebview } from '../../../webviews/main' +import { Region } from '../../../shared/regions/endpoints' +import { getTelemetryReasonDesc, ToolkitError } from '../../../shared/errors' +import { CancellationError } from '../../../shared/utilities/timeoutUtils' +import { trustedDomainCancellation } from '../../../auth/sso/model' +import { handleWebviewError } from '../../../webviews/server' +import { InvalidGrantException } from '@aws-sdk/client-sso-oidc' +import { + AwsConnection, + Connection, + hasScopes, + scopesCodeCatalyst, + scopesCodeWhispererChat, + scopesSsoAccountAccess, + SsoConnection, + TelemetryMetadata, +} from '../../../auth/connection' +import { Auth } from '../../../auth/auth' +import { StaticProfile, StaticProfileKeyErrorMessage } from '../../../auth/credentials/types' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { AuthAddConnection } from '../../../shared/telemetry/telemetry' +import { AuthSources } from '../util' +import { AuthEnabledFeatures, AuthError, AuthFlowState, AuthUiClick, userCancelled } from './types' +import { DevSettings } from '../../../shared/settings' +import { AuthSSOServer } from '../../../auth/sso/server' +import { getLogger } from '../../../shared/logger/logger' +import { isValidUrl } from '../../../shared/utilities/uriUtils' +import { RegionProfile } from '../../../codewhisperer/models/model' +import { ProfileSwitchIntent } from '../../../codewhisperer/region/regionProfileManager' + +export abstract class CommonAuthWebview extends VueWebview { + private readonly className = 'CommonAuthWebview' + private metricMetadata: TelemetryMetadata = {} + + // authSource should be set by whatever triggers the auth page flow. + // It will be reported in telemetry. + static #authSource: string = AuthSources.vscodeComponent + + public static get authSource() { + return CommonAuthWebview.#authSource + } + + public static set authSource(source: string) { + CommonAuthWebview.#authSource = source + } + + public get authSource() { + return CommonAuthWebview.#authSource + } + + public set authSource(source: string) { + CommonAuthWebview.#authSource = source + } + + public getRegions(): Region[] { + return globals.regionProvider.getRegions().reverse() + } + + /** + * Called when the UI load process is completed, regardless of success or failure + * + * @param errorMessage IF an error is caught on the frontend, this is the message. It will result in a failure metric. + * Otherwise we assume success. + */ + public setUiReady(state: 'login' | 'reauth' | 'selectProfile', errorMessage?: string) { + if (errorMessage) { + this.setLoadFailure(state, errorMessage) + } else { + this.setDidLoad(state) + } + } + + /** + * This wraps the execution of the given setupFunc() and handles common errors from the SSO setup process. + * + * @param methodName A value that will help identify which high level function called this method. + * @param setupFunc The function which will be executed in a try/catch so that we can handle common errors. + * @param postMetrics Whether to emit telemetry. + * @returns + */ + async ssoSetup(methodName: string, setupFunc: () => Promise, postMetrics: boolean = true) { + const runSetup = async () => { + try { + await setupFunc() + return + } catch (e) { + getLogger().error('ssoSetup encountered an error: %s', e) + + if (e instanceof ToolkitError && e.code === 'NotOnboarded') { + /** + * Connection is fine, they just skipped onboarding so not an actual error. + * + * The error comes from user cancelling prompt by {@link CodeCatalystAuthenticationProvider.promptOnboarding()} + */ + return + } + + if ( + CancellationError.isUserCancelled(e) || + (e instanceof ToolkitError && (CancellationError.isUserCancelled(e.cause) || e.cancelled === true)) + ) { + return { id: userCancelled, text: 'Setup cancelled.' } + } + + if (e instanceof ToolkitError && e.cause instanceof InvalidGrantException) { + return { + id: 'invalidGrantException', + text: 'Permissions for this service may not be enabled by your SSO Admin, or the selected region may not be supported.', + } + } + + if ( + e instanceof ToolkitError && + (e.code === trustedDomainCancellation || e.cause?.name === trustedDomainCancellation) + ) { + return { + id: 'trustedDomainCancellation', + text: `Must 'Open' or 'Configure Trusted Domains', unless you cancelled.`, + } + } + + const invalidRequestException = 'InvalidRequestException' + if ( + (e instanceof Error && e.name === invalidRequestException) || + (e instanceof ToolkitError && e.cause?.name === invalidRequestException) + ) { + return { id: 'badStartUrl', text: `Connection failed. Please verify your start URL.` } + } + + // If SSO setup fails we want to be able to show the user an error in the UI, due to this we cannot + // throw an error here. So instead this will additionally show an error message that provides more + // detailed information. + handleWebviewError(e, this.id, methodName) + + return { id: 'defaultFailure', text: 'Failed to setup.' } + } + } + + // Add context to our telemetry by adding the methodName argument to the function stack + const result = await telemetry.function_call.run( + async () => { + return runSetup() + }, + { emit: false, functionId: { name: methodName, class: this.className } } + ) + + if (postMetrics) { + this.storeMetricMetadata(this.getResultForMetrics(result)) + this.emitAuthMetric() + } + this.authSource = AuthSources.vscodeComponent + + return result + } + + /** Allows the frontend to subscribe to events emitted by the backend regarding the ACTIVE auth connection changing in some way. */ + abstract onActiveConnectionModified: vscode.EventEmitter + + abstract startBuilderIdSetup(app: string): Promise + + abstract startEnterpriseSetup(startUrl: string, region: string, app: string): Promise + + async getAuthenticatedCredentialsError(data: StaticProfile): Promise { + return Auth.instance.authenticateData(data) + } + + abstract startIamCredentialSetup( + profileName: string, + accessKey: string, + secretKey: string + ): Promise + + async showResourceExplorer(): Promise { + await vscode.commands.executeCommand('aws.explorer.focus') + } + + abstract fetchConnections(): Promise + + async errorNotification(e: AuthError) { + void vscode.window.showInformationMessage(`${e.text}`) + } + + abstract quitLoginScreen(): Promise + + /** + * NOTE: If we eventually need to be able to specify the connection to reauth, it should + * be an arg in this function + */ + abstract reauthenticateConnection(): Promise + abstract getReauthError(): Promise + + abstract getActiveConnection(): Promise + + /** Refreshes the current state of the auth flow, determining what you see in the UI */ + abstract refreshAuthState(): Promise + /** Use {@link refreshAuthState} first to ensure this returns the latest state */ + abstract getAuthState(): Promise + + abstract signout(): Promise + + /** List current connections known by the extension for the purpose of preventing duplicates. */ + abstract listSsoConnections(): Promise + + abstract listRegionProfiles(): Promise + + abstract selectRegionProfile(profile: RegionProfile, source: ProfileSwitchIntent): Promise + + /** + * Emit stored metric metadata. Does not reset the stored metric metadata, because it + * may be used for additional emits (e.g. user cancels multiple times, user cancels then logs in) + */ + emitAuthMetric() { + // We shouldn't report startUrl or region if we aren't reporting IdC + if (this.metricMetadata.credentialSourceId !== 'iamIdentityCenter') { + delete this.metricMetadata.awsRegion + delete this.metricMetadata.credentialStartUrl + } + telemetry.auth_addConnection.emit({ + ...this.metricMetadata, + source: this.authSource, + } as AuthAddConnection) + } + + /** + * Incrementally store auth metric data during vue, backend sign in logic, + * and cancellation flows. + */ + storeMetricMetadata(data: TelemetryMetadata) { + this.metricMetadata = { ...this.metricMetadata, ...data } + } + + /** + * Reset metadata stored by the auth form. + */ + resetStoredMetricMetadata() { + this.metricMetadata = {} + } + + /** + * Determines the status of the metric to report. + */ + getResultForMetrics(error?: AuthError) { + const metadata: Partial = {} + if (error) { + if (error.id === userCancelled) { + metadata.result = 'Cancelled' + } else { + metadata.result = 'Failed' + metadata.reason = error.id + metadata.reasonDesc = getTelemetryReasonDesc(error.text) + } + } else { + metadata.result = 'Succeeded' + } + + return metadata + } + + /** + * The metric when certain elements in the webview are clicked. + */ + emitUiClick(id: AuthUiClick) { + telemetry.ui_click.emit({ + elementId: id, + }) + } + + /** + * Return a comma-delimited list of features for which the connection has access to. + */ + getAuthEnabledFeatures(conn: SsoConnection | AwsConnection) { + const authEnabledFeatures: AuthEnabledFeatures[] = [] + if (hasScopes(conn.scopes!, scopesCodeWhispererChat)) { + authEnabledFeatures.push('codewhisperer') + } + if (hasScopes(conn.scopes!, scopesCodeCatalyst)) { + authEnabledFeatures.push('codecatalyst') + } + if (hasScopes(conn.scopes!, scopesSsoAccountAccess)) { + authEnabledFeatures.push('awsExplorer') + } + + return authEnabledFeatures.join(',') + } + + getDefaultSsoProfile(): { startUrl: string; region: string } { + const devSettings = DevSettings.instance.get('autofillStartUrl', '') + if (devSettings) { + return { startUrl: devSettings, region: 'us-east-1' } + } + + return globals.globalState.tryGet('recentSso', Object, { startUrl: '', region: 'us-east-1' }) + } + + cancelAuthFlow() { + AuthSSOServer.lastInstance?.cancelCurrentFlow() + } + + validateUrl(url: string) { + return isValidUrl(url) + } +} diff --git a/packages/core/src/login/webview/vue/base.css b/packages/core/src/login/webview/vue/base.css new file mode 100644 index 00000000000..1a4a6297c26 --- /dev/null +++ b/packages/core/src/login/webview/vue/base.css @@ -0,0 +1,11 @@ +:root { + /* Font-sizes */ + --font-size-sm: calc(0.75 * var(--font-size-base)); + --font-size-base: var(--vscode-font-size); + --font-size-md: calc(1.25 * var(--font-size-base)); + --font-size-lg: calc(1.5 * var(--font-size-base)); + --font-size-xl: calc(1.75 * var(--font-size-base)); + + /* Alignment */ + --auth-container-top: 15%; +} diff --git a/packages/core/src/login/webview/vue/login.vue b/packages/core/src/login/webview/vue/login.vue new file mode 100644 index 00000000000..7973844f4d4 --- /dev/null +++ b/packages/core/src/login/webview/vue/login.vue @@ -0,0 +1,845 @@ + + + + + diff --git a/packages/core/src/login/webview/vue/reauthenticate.vue b/packages/core/src/login/webview/vue/reauthenticate.vue new file mode 100644 index 00000000000..dbd0c16f6cb --- /dev/null +++ b/packages/core/src/login/webview/vue/reauthenticate.vue @@ -0,0 +1,257 @@ + + + + diff --git a/packages/core/src/login/webview/vue/regionProfileSelector.vue b/packages/core/src/login/webview/vue/regionProfileSelector.vue new file mode 100644 index 00000000000..a793e314a64 --- /dev/null +++ b/packages/core/src/login/webview/vue/regionProfileSelector.vue @@ -0,0 +1,317 @@ + + + diff --git a/packages/core/src/login/webview/vue/root.vue b/packages/core/src/login/webview/vue/root.vue new file mode 100644 index 00000000000..a4afc07f150 --- /dev/null +++ b/packages/core/src/login/webview/vue/root.vue @@ -0,0 +1,173 @@ + + + + diff --git a/packages/core/src/login/webview/vue/selectableItem.vue b/packages/core/src/login/webview/vue/selectableItem.vue new file mode 100644 index 00000000000..4e545cb5cea --- /dev/null +++ b/packages/core/src/login/webview/vue/selectableItem.vue @@ -0,0 +1,211 @@ + + + + + diff --git a/packages/core/src/login/webview/vue/toolkit/backend_toolkit.ts b/packages/core/src/login/webview/vue/toolkit/backend_toolkit.ts new file mode 100644 index 00000000000..f6a80d8c3c2 --- /dev/null +++ b/packages/core/src/login/webview/vue/toolkit/backend_toolkit.ts @@ -0,0 +1,195 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { tryAddCredentials } from '../../../../auth/utils' +import { getLogger } from '../../../../shared/logger/logger' +import { CommonAuthWebview } from '../backend' +import { + AwsConnection, + Connection, + SsoConnection, + TelemetryMetadata, + createSsoProfile, + getTelemetryMetadataForConn, + isSsoConnection, +} from '../../../../auth/connection' +import { Auth } from '../../../../auth/auth' +import { CodeCatalystAuthenticationProvider } from '../../../../codecatalyst/auth' +import { AuthError, AuthFlowState } from '../types' +import { setContext } from '../../../../shared/vscode/setContext' +import { builderIdStartUrl } from '../../../../auth/sso/constants' +import { RegionProfile } from '../../../../codewhisperer/models/model' +import { ProfileSwitchIntent } from '../../../../codewhisperer/region/regionProfileManager' +import globals from '../../../../shared/extensionGlobals' + +export class ToolkitLoginWebview extends CommonAuthWebview { + public override id: string = 'aws.toolkit.AmazonCommonAuth' + public static sourcePath: string = 'vue/src/login/webview/vue/toolkit/index.js' + public override supportsLoadTelemetry: boolean = true + private isCodeCatalystLogin = false + + override onActiveConnectionModified: vscode.EventEmitter = new vscode.EventEmitter() + + constructor(private readonly codeCatalystAuth: CodeCatalystAuthenticationProvider) { + super(ToolkitLoginWebview.sourcePath) + } + + setLoginService(serviceToShow?: string) { + this.isCodeCatalystLogin = serviceToShow === 'codecatalyst' + } + + async startEnterpriseSetup(startUrl: string, region: string): Promise { + getLogger().debug(`called startEnterpriseSetup() with startUrl: '${startUrl}', region: '${region}'`) + const metadata: TelemetryMetadata = { + credentialSourceId: 'iamIdentityCenter', + credentialStartUrl: startUrl, + isReAuth: false, + } + await globals.globalState.update('recentSso', { + startUrl: startUrl, + region: region, + }) + + if (this.isCodeCatalystLogin) { + return this.ssoSetup('startCodeCatalystSSOSetup', async () => { + this.storeMetricMetadata({ ...metadata }) + + const conn = await this.codeCatalystAuth.connectToEnterpriseSso(startUrl, region) + this.storeMetricMetadata({ + authEnabledFeatures: this.getAuthEnabledFeatures(conn), + ...(await getTelemetryMetadataForConn(conn)), + }) + + await setContext('aws.explorer.showAuthView', false) + await this.showResourceExplorer() + }) + } + + return this.ssoSetup('createIdentityCenterConnection', async () => { + this.storeMetricMetadata({ ...metadata }) + + const ssoProfile = createSsoProfile(startUrl, region) + const conn = await Auth.instance.createConnection(ssoProfile) + await Auth.instance.useConnection(conn) + + this.storeMetricMetadata({ + authEnabledFeatures: this.getAuthEnabledFeatures(conn), + ...(await getTelemetryMetadataForConn(conn)), + }) + + await setContext('aws.explorer.showAuthView', false) + void vscode.window.showInformationMessage('Toolkit: Successfully connected to AWS IAM Identity Center') + void this.showResourceExplorer() + }) + } + + async startIamCredentialSetup( + profileName: string, + accessKey: string, + secretKey: string + ): Promise { + getLogger().debug(`called startIamCredentialSetup()`) + // See submitData() in manageCredentials.vue + const runAuth = async () => { + const data = { aws_access_key_id: accessKey, aws_secret_access_key: secretKey } + const error = await this.getAuthenticatedCredentialsError(data) + if (error) { + return { id: this.id, text: error.error } + } + try { + await tryAddCredentials(profileName, data, true) + await setContext('aws.explorer.showAuthView', false) + await this.showResourceExplorer() + } catch (e) { + getLogger().error('Failed submitting credentials %O', e) + return { id: this.id, text: e as string } + } + } + + const result = await runAuth() + this.storeMetricMetadata({ + credentialSourceId: 'sharedCredentials', + authEnabledFeatures: 'awsExplorer', + ...this.getResultForMetrics(result), + }) + this.emitAuthMetric() + + return result + } + + async startBuilderIdSetup(): Promise { + getLogger().debug(`called startBuilderIdSetup()`) + return this.ssoSetup('startCodeCatalystBuilderIdSetup', async () => { + this.storeMetricMetadata({ + credentialSourceId: 'awsId', + authEnabledFeatures: 'codecatalyst', + isReAuth: false, + }) + + const conn = await this.codeCatalystAuth.connectToAwsBuilderId() + this.storeMetricMetadata(await getTelemetryMetadataForConn(conn)) + + await setContext('aws.explorer.showAuthView', false) + await this.showResourceExplorer() + }) + } + + /** + * Returns list of connections that are pushed from other extensions to Toolkit + */ + async fetchConnections(): Promise { + const connections: AwsConnection[] = [] + for (const conn of Auth.instance.declaredConnections) { + // No need to display Builder ID as an existing connection, + // users can just select the Builder ID login option and it would have the same effect. + if (conn.startUrl !== builderIdStartUrl) { + connections.push({ + ssoRegion: conn.ssoRegion, + startUrl: conn.startUrl, + } as AwsConnection) + } + } + return connections + } + + async listSsoConnections(): Promise { + return (await Auth.instance.listConnections()).filter((conn) => isSsoConnection(conn)) as SsoConnection[] + } + + override reauthenticateConnection(): Promise { + throw new Error('Method not implemented.') + } + override getActiveConnection(): Promise { + throw new Error('Method not implemented.') + } + + override async refreshAuthState(): Promise {} + override async getAuthState(): Promise { + // No need for a reauth page yet, so always show login + return 'LOGIN' + } + + override signout(): Promise { + throw new Error('Method not implemented.') + } + + override getReauthError(): Promise { + throw new Error('Method not implemented.') + } + + async quitLoginScreen() { + await setContext('aws.explorer.showAuthView', false) + await this.showResourceExplorer() + } + + override listRegionProfiles(): Promise { + throw new Error('Method not implemented') + } + + override selectRegionProfile(profile: RegionProfile, source: ProfileSwitchIntent): Promise { + throw new Error('Method not implemented') + } +} diff --git a/packages/core/src/login/webview/vue/toolkit/index.ts b/packages/core/src/login/webview/vue/toolkit/index.ts new file mode 100644 index 00000000000..34fbbedb8c9 --- /dev/null +++ b/packages/core/src/login/webview/vue/toolkit/index.ts @@ -0,0 +1,21 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * This module is run within the webview, and will mount the Vue app. + */ + +import { createApp } from 'vue' +import component from '../root.vue' + +const create = () => + createApp(component, { + app: 'TOOLKIT', + }) +const app = create() + +app.mount('#vue-app') +window.addEventListener('remount', () => { + app.unmount() + create().mount('#vue-app') +}) diff --git a/packages/core/src/login/webview/vue/types.ts b/packages/core/src/login/webview/vue/types.ts new file mode 100644 index 00000000000..c4203dcbe18 --- /dev/null +++ b/packages/core/src/login/webview/vue/types.ts @@ -0,0 +1,85 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Types that can be used by both the backend and frontend files + */ + +/** + * The identifiers for the different features that use Auth. + * + * These are important as they represent the specific feature for all parts of the + * auth sign setup flows. + */ +export const FeatureIds = { + TOOLKIT: 'TOOLKIT', + AMAZONQ: 'AMAZONQ', +} as const +export type FeatureId = (typeof FeatureIds)[keyof typeof FeatureIds] + +/** + * The type of Auth flows that the user could see. + */ +export const AuthFlowStates = { + /** User needs to select/setup a connection */ + LOGIN: 'LOGIN', + /** User has a connection but just needs to reauthenticate it */ + REAUTHNEEDED: 'REAUTHNEEDED', + /** Reauthentication is currently in progress */ + REAUTHENTICATING: 'REAUTHENTICATING', + PENDING_PROFILE_SELECTION: 'PENDING_PROFILE_SELECTION', +} as const +export type AuthFlowState = (typeof AuthFlowStates)[keyof typeof AuthFlowStates] + +export enum LoginOption { + NONE, + BUILDER_ID, + ENTERPRISE_SSO, + IAM_CREDENTIAL, + IMPORTED_LOGINS, +} + +/** + * 'elementId' for auth telemetry + */ +export type AuthUiClick = + | 'auth_backButton' + | 'auth_cancelButton' + | 'auth_reauthCancelButton' + | 'auth_continueButton' + | 'auth_idcOption' + | 'auth_builderIdOption' + | 'auth_credentialsOption' + | 'auth_codecatalystOption' + | 'auth_existingAuthOption' + | 'auth_regionSelection' + | 'auth_codeCatalystSignIn' + | 'auth_toolkitCloseButton' + | 'auth_reauthenticate' + | 'auth_signout' + | 'auth_helpLink' + | 'amazonq_switchToQSignIn' + +export const userCancelled = 'userCancelled' + +export type AuthEnabledFeatures = 'awsExplorer' | 'codewhisperer' | 'codecatalyst' + +export type AuthError = { id: string; text: string } + +export type ServiceItemId = 'awsExplorer' | 'codewhisperer' | 'codecatalyst' +export function isServiceItemId(value: unknown): value is ServiceItemId { + return ( + typeof value === 'string' && (value === 'awsExplorer' || value === 'codewhisperer' || value === 'codecatalyst') + ) +} + +export type AuthFormId = + | 'credentials' + | 'builderIdCodeWhisperer' + | 'builderIdCodeCatalyst' + | 'identityCenterCodeWhisperer' + | 'identityCenterCodeCatalyst' + | 'identityCenterExplorer' + | 'unknown' diff --git a/packages/core/src/notifications/activation.ts b/packages/core/src/notifications/activation.ts new file mode 100644 index 00000000000..152f6e1ab62 --- /dev/null +++ b/packages/core/src/notifications/activation.ts @@ -0,0 +1,53 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { DevSettings } from '../shared/settings' +import { DevFetcher, NotificationsController, RemoteFetcher } from './controller' +import { NotificationsNode } from './panelNode' +import { getRuleContext } from './rules' +import globals from '../shared/extensionGlobals' +import { AuthState } from './types' +import { getLogger } from '../shared/logger/logger' +import { oneMinute } from '../shared/datetime' + +const logger = getLogger('notifications') + +/** Time in MS to poll for emergency notifications */ +const emergencyPollTime = oneMinute * 10 + +/** + * Activate the in-IDE notifications module and begin receiving notifications. + * + * @param context extension context + * @param initialState initial auth state + * @param authStateFn fn to get current auth state + */ +export async function activate(context: vscode.ExtensionContext, authStateFn: () => Promise) { + try { + const panelNode = NotificationsNode.instance + panelNode.registerView(context) + + const controller = new NotificationsController( + panelNode, + async () => await getRuleContext(context, await authStateFn()), + DevSettings.instance.isDevMode() ? new DevFetcher() : new RemoteFetcher() + ) + + await controller.pollForStartUp() + await controller.pollForEmergencies() + + globals.clock.setInterval( + async () => { + await controller.pollForEmergencies() + }, + DevSettings.instance.get('notificationsPollInterval', emergencyPollTime) + ) + + logger.debug('Activated in-IDE notifications polling module') + } catch (err) { + logger.error('Failed to activate in-IDE notifications module.') + } +} diff --git a/packages/core/src/notifications/controller.ts b/packages/core/src/notifications/controller.ts new file mode 100644 index 00000000000..41ec81f8500 --- /dev/null +++ b/packages/core/src/notifications/controller.ts @@ -0,0 +1,386 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { ToolkitError } from '../shared/errors' +import globals from '../shared/extensionGlobals' +import { globalKey } from '../shared/globalState' +import { + defaultNotificationsState, + DevNotificationsState, + getNotificationTelemetryId, + Notifications, + NotificationsState, + NotificationsStateConstructor, + NotificationType, + RuleContext, + ToolkitNotification, +} from './types' +import { HttpResourceFetcher } from '../shared/resourcefetcher/httpResourceFetcher' +import { getLogger } from '../shared/logger/logger' +import { NotificationsNode } from './panelNode' +import { Commands } from '../shared/vscode/commands2' +import { RuleEngine } from './rules' +import { TreeNode } from '../shared/treeview/resourceTreeDataProvider' +import { FileResourceFetcher } from '../shared/resourcefetcher/fileResourceFetcher' +import { isAmazonQ } from '../shared/extensionUtilities' +import { telemetry } from '../shared/telemetry/telemetry' +import { randomUUID } from '../shared/crypto' +import { waitUntil } from '../shared/utilities/timeoutUtils' + +const logger = getLogger('notifications') + +/** + * Handles fetching and maintaining the state of in-IDE notifications. + * Notifications are constantly polled from a known endpoint and then stored in global state. + * The global state is used to compare if there are a change in notifications on the endpoint + * or if the endpoint is not reachable. + * + * This class will send any notifications to {@link NotificationsNode} for display. + * Notifications can be dismissed. + * + * Startup notifications - fetched each start up. + * Emergency notifications - fetched at a regular interval. + */ +export class NotificationsController { + /** Internal memory state that is written to global state upon modification. */ + private state: NotificationsState + + static #instance: NotificationsController | undefined + + constructor( + private readonly notificationsNode: NotificationsNode, + private readonly ruleContextFn: () => Promise, + private readonly fetcher: NotificationFetcher = new RemoteFetcher(), + public readonly storageKey: globalKey = 'aws.notifications' + ) { + if (!NotificationsController.#instance) { + // Register on first creation only. + registerDismissCommand() + } + NotificationsController.#instance = this + + this.state = this.getDefaultState() + } + + public reset() { + this.state = defaultNotificationsState() + return this.writeState().then(() => this.displayNotifications()) + } + + public pollForStartUp() { + return this.poll('startUp') + } + + public pollForEmergencies() { + return this.poll('emergency') + } + + private async poll(category: NotificationType) { + try { + // Get latest state in case it was modified by other windows. + // It is a minimal read to avoid race conditions. + this.readState() + await this.fetchNotifications(category) + } catch (err: any) { + logger.error(`Unable to fetch %s notifications: %s`, category, err) + } + + await this.displayNotifications() + } + + private async displayNotifications() { + const ruleEngine = new RuleEngine(await this.ruleContextFn()) + const dismissed = new Set(this.state.dismissed) + const startUp = + this.state.startUp.payload?.notifications.filter( + (n) => !dismissed.has(n.id) && ruleEngine.shouldDisplayNotification(n) + ) ?? [] + const emergency = (this.state.emergency.payload?.notifications ?? []).filter((n) => + ruleEngine.shouldDisplayNotification(n) + ) + + await NotificationsNode.instance.setNotifications(startUp, emergency) + + // Process on-receive behavior for newly received notifications that passes rule engine + const wasNewlyReceived = (n: ToolkitNotification) => this.state.newlyReceived.includes(n.id) + const newStartUp = startUp.filter(wasNewlyReceived) + const newEmergency = emergency.filter(wasNewlyReceived) + const newlyReceived = [...newStartUp, ...newEmergency] + + if (newlyReceived.length > 0) { + await this.notificationsNode.onReceiveNotifications(newlyReceived) + // remove displayed notifications from newlyReceived + this.state.newlyReceived = this.state.newlyReceived.filter((id) => !newlyReceived.some((n) => n.id === id)) + await this.writeState() + if (newEmergency.length > 0) { + void this.notificationsNode.focusPanel() + } + } + } + + /** + * Permanently hides a notification from view. Only 'startUp' notifications can be dismissed. + * Users are able to collapse or hide the notifications panel in native VSC if they want to + * hide all notifications. + */ + public async dismissNotification(notificationId: string) { + logger.debug('Dismissing notification: %s', notificationId) + + this.readState() // Don't overwrite dismissals from other windows + this.state.dismissed.push(notificationId) + await this.writeState() + + await NotificationsNode.instance.dismissStartUpNotification(notificationId) + } + + /** + * Fetch notifications from the endpoint and store them in the global state. + */ + private async fetchNotifications(category: NotificationType) { + const response = await this.fetcher.fetch(category, this.state[category].eTag) + if (!response.content) { + logger.verbose('No new notifications for category: %s', category) + return + } + // Parse the notifications + const newPayload: Notifications = JSON.parse(response.content) + const newNotifications = newPayload.notifications ?? [] + + // Get the current notifications + const currentNotifications = this.state[category].payload?.notifications ?? [] + const currentNotificationIds = new Set(currentNotifications.map((n: any) => n.id)) + + // Compare and find if there's any notifications newly added + const addedNotifications = newNotifications.filter((n: any) => !currentNotificationIds.has(n.id)) + + if (addedNotifications.length > 0) { + logger.verbose( + 'New notifications received for category %s, ids: %s', + category, + addedNotifications.map((n: any) => n.id).join(', ') + ) + this.state.newlyReceived.push(...addedNotifications.map((n: any) => n.id)) + } + + this.state[category].payload = newPayload + this.state[category].eTag = response.eTag + await this.writeState() + + logger.verbose( + "Fetched notifications JSON for category '%s' with schema version: %s. There were %d notifications.", + category, + this.state[category].payload?.schemaVersion, + this.state[category].payload?.notifications?.length + ) + } + + /** + * Write the latest memory state to global state. + */ + private async writeState() { + logger.debug('NotificationsController: Updating notifications state at %s', this.storageKey) + + // Clean out anything in 'dismissed' that doesn't exist anymore. + const notifications = new Set( + [ + ...(this.state.startUp.payload?.notifications ?? []), + ...(this.state.emergency.payload?.notifications ?? []), + ].map((n) => n.id) + ) + this.state.dismissed = this.state.dismissed.filter((id) => notifications.has(id)) + + await globals.globalState.update(this.storageKey, this.state) + } + + /** + * Read relevant values from the latest global state to memory. Useful to bring changes from other windows. + * + * Currently only brings dismissed, so users with multiple vscode instances open do not have issues with + * dismissing notifications multiple times. Otherwise, each instance has an independent session for + * displaying the notifications (e.g. multiple windows can be blocked in critical emergencies). + * + * Note: This sort of pattern (reading back and forth from global state in async functions) is prone to + * race conditions, which is why we limit the read to the fairly inconsequential `dismissed` property. + */ + private readState() { + const state = this.getDefaultState() + this.state.dismissed = [...new Set([...this.state.dismissed, ...state.dismissed])] + } + + /** + * Returns stored notification state, or a default state object if it is invalid or undefined. + */ + private getDefaultState() { + return globals.globalState.tryGet( + this.storageKey, + NotificationsStateConstructor, + defaultNotificationsState() + ) + } + + static get instance() { + if (this.#instance === undefined) { + throw new ToolkitError('NotificationsController was accessed before it has been initialized.') + } + + return this.#instance + } +} + +function registerDismissCommand() { + const name = isAmazonQ() ? '_aws.amazonq.notifications.dismiss' : '_aws.toolkit.notifications.dismiss' + + globals.context.subscriptions.push( + Commands.register(name, async (node: TreeNode) => { + const item = node?.getTreeItem() + if (item instanceof vscode.TreeItem && item.command?.arguments) { + // The command used to build the TreeNode contains the notification as an argument. + /** See {@link NotificationsNode} for more info. */ + const notification = item.command?.arguments[0] as ToolkitNotification + + await telemetry.ui_click.run(async (span) => { + span.record({ elementId: `${getNotificationTelemetryId(notification)}:DISMISS` }) + await NotificationsController.instance.dismissNotification(notification.id) + }) + } else { + logger.error(`${name}: Cannot dismiss notification: item is not a vscode.TreeItem`) + } + }) + ) +} + +export type ResourceResponse = Awaited> + +export interface NotificationFetcher { + /** + * Fetch notifications from some source. If there is no (new) data to fetch, then the response's + * content value will be undefined. + * + * @param type typeof NotificationType + * @param versionTag last known version of the data aka ETAG. Can be used to determine if the data changed. + */ + fetch(type: NotificationType, versionTag?: string): Promise +} + +export class RemoteFetcher implements NotificationFetcher { + public static readonly retryIntervalMs = 30000 + public static readonly retryTimeout = RemoteFetcher.retryIntervalMs * 5 + + private readonly startUpEndpoint: string = + 'https://idetoolkits-hostedfiles.amazonaws.com/Notifications/VSCode/startup/1.x.json' + private readonly emergencyEndpoint: string = + 'https://idetoolkits-hostedfiles.amazonaws.com/Notifications/VSCode/emergency/1.x.json' + + constructor(startUpPath?: string, emergencyPath?: string) { + this.startUpEndpoint = startUpPath ?? this.startUpEndpoint + this.emergencyEndpoint = emergencyPath ?? this.emergencyEndpoint + } + + fetch(category: NotificationType, versionTag?: string): Promise { + const endpoint = category === 'startUp' ? this.startUpEndpoint : this.emergencyEndpoint + const fetcher = new HttpResourceFetcher(endpoint, { + showUrl: true, + }) + logger.verbose('Attempting to fetch notifications for category: %s at endpoint: %s', category, endpoint) + + return waitUntil( + async () => { + try { + return await fetcher.getNewETagContent(versionTag) + } catch (err) { + logger.error('Failed to fetch at endpoint: %s, err: %s', endpoint, err) + throw err + } + }, + { + interval: RemoteFetcher.retryIntervalMs, + timeout: RemoteFetcher.retryTimeout, + retryOnFail: true, + // No exponential backoff - necessary? + } + ) + } +} + +/** + * Can be used when developing locally. This may be expanded at some point to allow notifications + * to be published via github rather than internally. + * + * versionTag (ETAG) is ignored. + */ +export class LocalFetcher implements NotificationFetcher { + // Paths relative to running extension root folder (e.g. packages/amazonq/). + private readonly startUpLocalPath: string = '../core/src/test/notifications/resources/startup/1.x.json' + private readonly emergencyLocalPath: string = '../core/src/test/notifications/resources/emergency/1.x.json' + + constructor(startUpPath?: string, emergencyPath?: string) { + this.startUpLocalPath = startUpPath ?? this.startUpLocalPath + this.emergencyLocalPath = emergencyPath ?? this.emergencyLocalPath + } + + async fetch(category: NotificationType, versionTag?: string): Promise { + const uri = category === 'startUp' ? this.startUpLocalPath : this.emergencyLocalPath + logger.verbose('Attempting to fetch notifications locally for category: %s at path: %s', category, uri) + + return { + content: await new FileResourceFetcher(globals.context.asAbsolutePath(uri)).get(), + eTag: 'LOCAL_PATH', + } + } +} + +/** + * Fetches notifications from global state for local testing and verification. It is + * an extension of the RemoteFetcher so that devs can still see public notifications + * while testing (or while not testing, but they left dev mode on). + * + * Usage: + * 1. Enable dev mode: set `"aws.dev.forceDevMode": true` in your settings. + * 2. Open the extension Developer menu. + * 3. (optional) Reset the current notification state via the "Reset feature state" option. + * 4. Add notifications to the payload via the `Notifications: Edit Notifications` option. + * 5. Save to send the notification. + * + * Save to receive these notifications AND ALSO fetch from the public endpoint. + */ +export class DevFetcher extends RemoteFetcher implements NotificationFetcher { + constructor(public readonly storageKey: globalKey = 'aws.notifications.dev') { + super() + } + + override async fetch(category: NotificationType, versionTag?: string): Promise { + logger.debug('DevFetcher: Fetching notifications in dev mode for category: %s', category) + + // Fetch the notifications that are currently released to users. Ignore eTag so that the + // complete current set of notifications is always returned to devs. + const data = await super.fetch(category, undefined) + + const devNotifications: DevNotificationsState | undefined = await globals.globalState.getStrict( + this.storageKey, + Object + ) + + if (devNotifications && devNotifications[category] && devNotifications[category].length > 0) { + const content: Notifications = data.content + ? JSON.parse(data.content) + : { notifications: [], schemaVersion: 'devmode' } + + // Give each dev notification a unique ID so the dev doesn't have to keep track of them. + content.notifications.push( + ...devNotifications[category].map((n) => { + return { ...n, id: `${n.id ?? 'no-id'}_${randomUUID()}` } + }) + ) + + return { + content: JSON.stringify(content), + eTag: data.eTag, + } + } + + return data + } +} diff --git a/packages/core/src/notifications/index.ts b/packages/core/src/notifications/index.ts new file mode 100644 index 00000000000..8e3c6066f88 --- /dev/null +++ b/packages/core/src/notifications/index.ts @@ -0,0 +1,7 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export { activate } from './activation' +export * from './controller' diff --git a/packages/core/src/notifications/panelNode.ts b/packages/core/src/notifications/panelNode.ts new file mode 100644 index 00000000000..70a69873ee3 --- /dev/null +++ b/packages/core/src/notifications/panelNode.ts @@ -0,0 +1,321 @@ +/*! + * 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 { ResourceTreeDataProvider, TreeNode } from '../shared/treeview/resourceTreeDataProvider' +import { Command, Commands } from '../shared/vscode/commands2' +import { Icon, getIcon } from '../shared/icons' +import { contextKey, setContext } from '../shared/vscode/setContext' +import { NotificationType, OnReceiveType, ToolkitNotification, getNotificationTelemetryId } from './types' +import { ToolkitError } from '../shared/errors' +import { isAmazonQ } from '../shared/extensionUtilities' +import { getLogger } from '../shared/logger/logger' +import { registerToolView } from '../awsexplorer/activationShared' +import { readonlyDocument } from '../shared/utilities/textDocumentUtilities' +import { openUrl } from '../shared/utilities/vsCodeUtils' +import { telemetry } from '../shared/telemetry/telemetry' +import globals from '../shared/extensionGlobals' + +const localize = nls.loadMessageBundle() +const logger = getLogger('notifications') + +/** + * Controls the "Notifications" side panel/tree in each extension. It takes purely UX actions + * and does not determine what notifications to dispaly or how to fetch and store them. + */ +export class NotificationsNode implements TreeNode { + public static readonly title = localize('AWS.notifications.title', 'Notifications') + + public readonly id = 'notifications' + public readonly resource = this + public provider?: ResourceTreeDataProvider + public startUpNotifications: ToolkitNotification[] = [] + public emergencyNotifications: ToolkitNotification[] = [] + + /** Command executed when a notification item is clicked on in the panel. */ + private readonly openNotificationCmd: Command + private readonly focusCmdStr: string + private readonly showContextStr: contextKey + private readonly startUpNodeContext: string + private readonly emergencyNodeContext: string + private view: vscode.TreeView | undefined + + static #instance: NotificationsNode + + private constructor() { + this.openNotificationCmd = Commands.register( + isAmazonQ() ? '_aws.amazonq.notifications.open' : '_aws.toolkit.notifications.open', + (n: ToolkitNotification) => { + return telemetry.ui_click.run((span) => { + span.record({ elementId: getNotificationTelemetryId(n) }) + return this.openNotification(n) + }) + } + ) + + if (isAmazonQ()) { + this.focusCmdStr = 'aws.amazonq.notifications.focus' + this.showContextStr = 'aws.amazonq.notifications.show' + this.startUpNodeContext = 'amazonqNotificationStartUp' + this.emergencyNodeContext = 'amazonqNotificationEmergency' + } else { + this.focusCmdStr = 'aws.toolkit.notifications.focus' + this.showContextStr = 'aws.toolkit.notifications.show' + this.startUpNodeContext = 'toolkitNotificationStartUp' + this.emergencyNodeContext = 'toolkitNotificationEmergency' + } + } + + public getTreeItem() { + const item = new vscode.TreeItem(NotificationsNode.title) + item.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed + item.contextValue = 'notifications' + + return item + } + + public refresh() { + const totalNotifications = this.notificationCount() + if (this.view) { + if (totalNotifications > 0) { + this.view.badge = { + tooltip: `${totalNotifications} notification${totalNotifications > 1 ? 's' : ''}`, + value: totalNotifications, + } + this.view.title = `${NotificationsNode.title} (${totalNotifications})` + } else { + this.view.badge = undefined + this.view.title = NotificationsNode.title + } + } else { + logger.warn('NotificationsNode was refreshed but the view was not initialized!') + } + + this.provider?.refresh() + return setContext(this.showContextStr, totalNotifications > 0) + } + + public getChildren() { + const buildNode = (n: ToolkitNotification, type: NotificationType) => { + const icon: Icon = + type === 'emergency' + ? Object.assign(getIcon('vscode-alert') as Icon, { + color: new vscode.ThemeColor('errorForeground'), + }) + : (getIcon('vscode-question') as Icon) + + const title = n.uiRenderInstructions.content['en-US'].title + return this.openNotificationCmd.build(n).asTreeNode({ + label: title, + tooltip: title, + iconPath: icon, + contextValue: type === 'startUp' ? this.startUpNodeContext : this.emergencyNodeContext, + }) + } + + return [ + ...this.emergencyNotifications.map((n) => buildNode(n, 'emergency')), + ...this.startUpNotifications.map((n) => buildNode(n, 'startUp')), + ] + } + + /** + * Sets the current list of notifications. Nodes are generated for each notification. + * No other processing is done, see NotificationController. + */ + public async setNotifications(startUp: ToolkitNotification[], emergency: ToolkitNotification[]) { + this.startUpNotifications = startUp + this.emergencyNotifications = emergency + await this.refresh() + } + + /** + * Deletes a notification node from the panel. This is purely a UX action - nothing happens + * to the notification on the backend via this function. + * + * Only dismisses startup notifications. + */ + public async dismissStartUpNotification(id: string) { + this.startUpNotifications = this.startUpNotifications.filter((n) => n.id !== id) + await this.refresh() + } + + /** + * Will uncollapse/unhide the notifications panel from view and focus it. + */ + public focusPanel() { + return vscode.commands.executeCommand(this.focusCmdStr) + } + + private notificationCount() { + return this.startUpNotifications.length + this.emergencyNotifications.length + } + + /** + * Fired when a notification is clicked on in the panel. It will run any rendering + * instructions included in the notification. See {@link ToolkitNotification.uiRenderInstructions}. + */ + public async openNotification(notification: ToolkitNotification) { + const onClickType = notification.uiRenderInstructions.onClick.type + switch (onClickType) { + case 'modal': + // Render blocking modal + logger.verbose(`rendering modal for notificaiton: ${notification.id} ...`) + await this.showInformationWindow(notification, 'modal', false) + break + case 'openUrl': + // Show open url option + if (!notification.uiRenderInstructions.onClick.url) { + throw new ToolkitError('No url provided for onclick open url') + } + logger.verbose(`opening url for notification: ${notification.id} ...`) + await openUrl( + vscode.Uri.parse(notification.uiRenderInstructions.onClick.url), + getNotificationTelemetryId(notification) + ) + break + case 'openTextDocument': + // Display read-only txt document + logger.verbose(`showing txt document for notification: ${notification.id} ...`) + await telemetry.toolkit_invokeAction.run(async () => { + telemetry.record({ + id: getNotificationTelemetryId(notification), + source: getNotificationTelemetryId(notification), + action: onClickType, + }) + await readonlyDocument.show( + notification.uiRenderInstructions.content['en-US'].description, + `Notification: ${notification.id}` + ) + }) + break + } + } + + /** + * Renders information window with the notification's content and buttons. + * Can be either a blocking modal or a bottom-right corner toast + * Handles the button click actions based on the button type. + */ + private showInformationWindow( + notification: ToolkitNotification, + type: OnReceiveType = 'toast', + passive: boolean = false + ) { + const isModal = type === 'modal' + + // modal has to have defined actions (buttons) + const buttons = notification.uiRenderInstructions.actions ?? [] + const buttonLabels = buttons.map((actions) => actions.displayText['en-US']) + const detail = notification.uiRenderInstructions.content['en-US'].description + + // we use toastPreview to display as title for toast, since detail won't be shown + const title = isModal + ? notification.uiRenderInstructions.content['en-US'].title + : (notification.uiRenderInstructions.content['en-US'].toastPreview ?? + notification.uiRenderInstructions.content['en-US'].title) + + telemetry.toolkit_showNotification.emit({ + id: getNotificationTelemetryId(notification), + passive, + component: 'editor', + result: 'Succeeded', + }) + + return vscode.window + .showInformationMessage(title, { modal: isModal, detail }, ...buttonLabels) + .then((response) => { + return telemetry.toolkit_invokeAction.run(async (span) => { + span.record({ + id: getNotificationTelemetryId(notification), + source: getNotificationTelemetryId(notification), + action: response ?? 'OK', + }) + if (response) { + const selectedButton = buttons.find((actions) => actions.displayText['en-US'] === response) + // Different button options + if (selectedButton) { + span.record({ action: selectedButton.type }) + switch (selectedButton.type) { + case 'openTextDocument': + await readonlyDocument.show( + notification.uiRenderInstructions.content['en-US'].description, + `Notification: ${notification.id}` + ) + break + case 'updateAndReload': + // Give things time to finish executing. + globals.clock.setTimeout(() => { + return this.updateAndReload(notification.displayIf.extensionId) + }, 1000) + break + case 'openUrl': + if (selectedButton.url) { + await openUrl(vscode.Uri.parse(selectedButton.url)) + } else { + throw new ToolkitError('url not provided') + } + break + default: + throw new ToolkitError('button action not defined') + } + } + } + }) + }) + } + + public async onReceiveNotifications(notifications: ToolkitNotification[]) { + for (const notification of notifications) { + void this.showInformationWindow(notification, notification.uiRenderInstructions.onReceive, true) + } + } + + private async updateAndReload(id: string) { + logger.verbose('Updating and reloading the extension...') + + // Publish pending telemetry before it is lost to the window reload. + await globals.telemetry.flushRecords() + + await vscode.commands.executeCommand('workbench.extensions.installExtension', id) + await vscode.commands.executeCommand('workbench.action.reloadWindow') + } + + /** + * HACK: Since this is assumed to be an immediate child of the + * root, we return undefined. + * + * TODO: Look to have a base root class to extend so we do not + * need to implement this here. + * @returns + */ + getParent(): TreeNode | undefined { + return undefined + } + + static get instance() { + if (this.#instance === undefined) { + this.#instance = new NotificationsNode() + } + + return this.#instance + } + + registerProvider(provider: ResourceTreeDataProvider) { + this.provider = provider + } + + registerView(context: vscode.ExtensionContext) { + this.view = registerToolView( + { + nodes: [this], + view: isAmazonQ() ? 'aws.amazonq.notifications' : 'aws.toolkit.notifications', + refreshCommands: [(provider: ResourceTreeDataProvider) => this.registerProvider(provider)], + }, + context + ) + } +} diff --git a/packages/core/src/notifications/rules.ts b/packages/core/src/notifications/rules.ts new file mode 100644 index 00000000000..ddce795a39e --- /dev/null +++ b/packages/core/src/notifications/rules.ts @@ -0,0 +1,208 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as semver from 'semver' +import globals from '../shared/extensionGlobals' +import { ConditionalClause, RuleContext, DisplayIf, CriteriaCondition, ToolkitNotification, AuthState } from './types' +import { getComputeEnvType, getOperatingSystem } from '../shared/telemetry/util' +import { isAutomation } from '../shared/vscode/env' +import { AuthFormId } from '../login/webview/vue/types' +import { getLogger } from '../shared/logger/logger' +import { ToolkitError } from '../shared/errors' + +const logger = getLogger('notifications') +/** + * Evaluates if a given version fits into the parameters specified by a notification, e.g: + * + * extensionVersion: { + * type: 'range', + * lowerInclusive: '1.21.0' + * } + * + * will match all versions 1.21.0 and up. + * + * @param version the version to check + * @param condition the condition to check against + * @returns true if the version satisfies the condition + */ +function isValidVersion(version: string, condition: ConditionalClause): boolean { + const cleanVersion = version.split('-')[0] // remove any pre-release tags + switch (condition.type) { + case 'range': { + const lowerConstraint = + !condition.lowerInclusive || + condition.lowerInclusive === '-inf' || + semver.gte(cleanVersion, condition.lowerInclusive) + const upperConstraint = + !condition.upperExclusive || + condition.upperExclusive === '+inf' || + semver.lt(cleanVersion, condition.upperExclusive) + return lowerConstraint && upperConstraint + } + case 'exactMatch': + return condition.values.some((v) => semver.eq(v, cleanVersion)) + case 'or': + /** Check case where any of the subconditions are true, i.e. one of multiple range or exactMatch conditions */ + return condition.clauses.some((clause) => isValidVersion(cleanVersion, clause)) + default: + throw new Error(`Unknown clause type: ${(condition as any).type}`) + } +} + +/** + * Determine whether or not to display a given notification based on whether the + * notification requirements fit the extension context provided on initialization. + * + * Usage: + * const myContext = { + * extensionVersion: '4.5.6', + * ... + * } + * + * const ruleEngine = new RuleEngine(myContext) + * + * notifications.forEach(n => { + * if (ruleEngine.shouldDisplayNotification(n)) { + * // process notification + * ... + * } + * }) + * + */ +export class RuleEngine { + constructor(private readonly context: RuleContext) {} + + public shouldDisplayNotification(payload: ToolkitNotification) { + return this.evaluate(payload.id, payload.displayIf) + } + + private evaluate(id: string, condition: DisplayIf): boolean { + const currentExt = globals.context.extension.id + // if in test, skip the extension id check since its fake + if (condition.extensionId !== currentExt && !isAutomation()) { + logger.verbose( + 'notification id: (%s) did NOT pass extension id check, actual ext id: (%s), expected ext id: (%s)', + id, + currentExt, + condition.extensionId + ) + return false + } + + if (condition.ideVersion) { + if (!isValidVersion(this.context.ideVersion, condition.ideVersion)) { + logger.verbose( + 'notification id: (%s) did NOT pass IDE version check, actual version: (%s), expected version: (%s)', + id, + this.context.ideVersion, + condition.ideVersion + ) + return false + } + } + if (condition.extensionVersion) { + if (!isValidVersion(this.context.extensionVersion, condition.extensionVersion)) { + logger.verbose( + 'notification id: (%s) did NOT pass extension version check, actual ext version: (%s), expected ext version: (%s)', + id, + this.context.extensionVersion, + condition.extensionVersion + ) + return false + } + } + + if (condition.additionalCriteria) { + for (const criteria of condition.additionalCriteria) { + if (!this.evaluateRule(criteria)) { + logger.verbose('notification id: (%s) did NOT pass criteria check: %O', id, criteria) + return false + } + logger.debug('notification id: (%s) passed criteria check: %O', id, criteria) + } + } + + return true + } + + private evaluateRule(criteria: CriteriaCondition) { + const expected = criteria.values + const expectedSet = new Set(expected) + + const hasAnyOfExpected = (i: string[]) => i.some((v) => expectedSet.has(v)) + const isSuperSetOfExpected = (i: string[]) => { + const s = new Set(i) + return expected.every((v) => s.has(v)) + } + const isEqualSetToExpected = (i: string[]) => { + const s = new Set(i) + return expected.every((v) => s.has(v)) && i.every((v) => expectedSet.has(v)) + } + + // Maybe we could abstract these out into some strategy pattern with classes. + // But this list is short and its unclear if we need to expand it further. + // Also, we might replace this with a common implementation amongst the toolkits. + // So... YAGNI + switch (criteria.type) { + case 'OS': + return hasAnyOfExpected([this.context.os]) + case 'ComputeEnv': + return hasAnyOfExpected([this.context.computeEnv]) + case 'AuthType': + return hasAnyOfExpected(this.context.authTypes) + case 'AuthRegion': + return hasAnyOfExpected(this.context.authRegions) + case 'AuthState': + return hasAnyOfExpected(this.context.authStates) + case 'AuthScopes': + return isEqualSetToExpected(this.context.authScopes) + case 'ActiveExtensions': + return isSuperSetOfExpected(this.context.activeExtensions) + default: + logger.error('Unknown criteria passed to RuleEngine: %O', criteria) + throw new ToolkitError(`Unknown criteria type: ${(criteria as any).type}`) + } + } +} + +export async function getRuleContext(context: vscode.ExtensionContext, authState: AuthState): Promise { + const authTypes = + authState.authEnabledConnections === '' + ? [] + : // TODO: There is a large disconnect in the codebase with how auth "types" are stored, displayed, sent to telemetry, etc. + // For now we have this "hack" (more of an inefficiency) until auth code can be properly refactored. + (authState.authEnabledConnections.split(',') as AuthFormId[]).map( + (id): RuleContext['authTypes'][number] => { + if (id.includes('builderId')) { + return 'builderId' + } else if (id.includes('identityCenter')) { + return 'identityCenter' + } else if (id.includes('credentials')) { + return 'credentials' + } + return 'unknown' + } + ) + const ruleContext = { + ideVersion: vscode.version, + extensionVersion: context.extension.packageJSON.version, + os: getOperatingSystem(), + computeEnv: await getComputeEnvType(), + authTypes: [...new Set(authTypes)], + authScopes: authState.authScopes ? authState.authScopes?.split(',') : [], + activeExtensions: vscode.extensions.all.filter((e) => e.isActive).map((e) => e.id), + + // Toolkit (and eventually Q?) may have multiple connections with different regions and states. + // However, this granularity does not seem useful at this time- only the active connection is considered. + authRegions: authState.awsRegion ? [authState.awsRegion] : [], + authStates: [authState.authStatus], + } + + const { activeExtensions, ...loggableRuleContext } = ruleContext + logger.debug('getRuleContext() determined rule context: %O', loggableRuleContext) + + return ruleContext +} diff --git a/packages/core/src/notifications/types.ts b/packages/core/src/notifications/types.ts new file mode 100644 index 00000000000..66d5d2e54ea --- /dev/null +++ b/packages/core/src/notifications/types.ts @@ -0,0 +1,186 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { EnvType, OperatingSystem } from '../shared/telemetry/util' +import { TypeConstructor } from '../shared/utilities/typeConstructors' +import { AuthUserState, AuthStatus } from '../shared/telemetry/telemetry.gen' +import { AuthType } from '../auth/connection' + +/** Types of information that we can use to determine whether to show a notification or not. */ + +type OsCriteria = { + type: 'OS' + values: OperatingSystem[] +} + +type ComputeEnvCriteria = { + type: 'ComputeEnv' + values: EnvType[] +} + +type AuthTypeCriteria = { + type: 'AuthType' + values: AuthType[] +} + +type AuthRegionCriteria = { + type: 'AuthRegion' + values: string[] +} + +type AuthStateCriteria = { + type: 'AuthState' + values: AuthStatus[] +} + +type AuthScopesCriteria = { + type: 'AuthScopes' + values: string[] // TODO: Scopes should be typed. Could import here, but don't want to import too much. +} + +type ActiveExtensionsCriteria = { + type: 'ActiveExtensions' + values: string[] +} + +/** Generic condition where the type determines how the values are evaluated. */ +export type CriteriaCondition = + | OsCriteria + | ComputeEnvCriteria + | AuthTypeCriteria + | AuthRegionCriteria + | AuthStateCriteria + | AuthScopesCriteria + | ActiveExtensionsCriteria + +/** One of the subconditions (clauses) must match to be valid. */ +export interface OR { + readonly type: 'or' + readonly clauses: (Range | ExactMatch)[] +} + +/** Version must be within the bounds to be valid. Missing bound indicates that bound is open-ended. */ +export interface Range { + readonly type: 'range' + readonly lowerInclusive?: string // null means "-inf" + readonly upperExclusive?: string // null means "+inf" +} + +/** Version must be equal. */ +export interface ExactMatch { + readonly type: 'exactMatch' + readonly values: string[] +} + +export type ConditionalClause = Range | ExactMatch | OR + +export type OnReceiveType = 'toast' | 'modal' +export type OnClickType = { type: 'modal' } | { type: 'openTextDocument' } | { type: 'openUrl'; url: string } +export type ActionType = 'openUrl' | 'updateAndReload' | 'openTextDocument' + +/** How to display the notification. */ +export interface UIRenderInstructions { + content: { + [`en-US`]: { + title: string + description: string + toastPreview?: string // optional property for toast + } + } + onReceive: OnReceiveType + onClick: OnClickType + actions?: Array<{ + type: ActionType + displayText: { + [`en-US`]: string + } + url?: string // optional property for 'openUrl' + }> +} + +/** Condition/criteria section of a notification. */ +export interface DisplayIf { + extensionId: string + ideVersion?: ConditionalClause + extensionVersion?: ConditionalClause + additionalCriteria?: CriteriaCondition[] +} + +export interface ToolkitNotification { + id: string + displayIf: DisplayIf + uiRenderInstructions: UIRenderInstructions +} + +export interface Notifications { + schemaVersion: string + notifications: ToolkitNotification[] +} + +export type NotificationData = { + payload?: Notifications + eTag?: string +} + +export type NotificationsState = { + // Categories + startUp: NotificationData + emergency: NotificationData + + // Util + dismissed: string[] + newlyReceived: string[] +} + +export const defaultNotificationsState: () => NotificationsState = () => { + return { + startUp: {}, + emergency: {}, + dismissed: [], + newlyReceived: [], + } +} + +export const NotificationsStateConstructor: TypeConstructor = (v: unknown): NotificationsState => { + const isNotificationsState = (v: Partial): v is NotificationsState => { + const requiredKeys: (keyof NotificationsState)[] = ['startUp', 'emergency', 'dismissed', 'newlyReceived'] + return ( + requiredKeys.every((key) => key in v) && + Array.isArray(v.dismissed) && + Array.isArray(v.newlyReceived) && + typeof v.startUp === 'object' && + typeof v.emergency === 'object' + ) + } + + if (v && typeof v === 'object' && isNotificationsState(v)) { + return v + } + throw new Error('Cannot cast to NotificationsState.') +} + +export type NotificationType = keyof Omit + +export interface RuleContext { + readonly ideVersion: typeof vscode.version + readonly extensionVersion: string + readonly os: OperatingSystem + readonly computeEnv: EnvType + readonly authTypes: AuthType[] + readonly authRegions: string[] + readonly authStates: AuthStatus[] + readonly authScopes: string[] + readonly activeExtensions: string[] +} + +/** Type expected by things that build (or help build) {@link RuleContext} */ +export type AuthState = Omit + +export function getNotificationTelemetryId(n: ToolkitNotification): string { + return `TARGETED_NOTIFICATION:${n.id}` +} + +export type DevNotificationsState = { startUp: ToolkitNotification[]; emergency: ToolkitNotification[] } diff --git a/packages/core/src/sagemakerunifiedstudio/activation.ts b/packages/core/src/sagemakerunifiedstudio/activation.ts new file mode 100644 index 00000000000..7fefd2eb44a --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/activation.ts @@ -0,0 +1,23 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { activate as activateConnectionMagicsSelector } from './connectionMagicsSelector/activation' +import { activate as activateExplorer } from './explorer/activation' +import { isSageMaker } from '../shared/extensionUtilities' +import { initializeResourceMetadata } from './shared/utils/resourceMetadataUtils' +import { setContext } from '../shared/vscode/setContext' +import { SmusUtils } from './shared/smusUtils' + +export async function activate(extensionContext: vscode.ExtensionContext): Promise { + // Only run when environment is a SageMaker Unified Studio space + if (isSageMaker('SMUS') || isSageMaker('SMUS-SPACE-REMOTE-ACCESS')) { + await initializeResourceMetadata() + // Setting context before any getContext calls to avoid potential race conditions. + await setContext('aws.smus.inSmusSpaceEnvironment', SmusUtils.isInSmusSpaceEnvironment()) + await activateConnectionMagicsSelector(extensionContext) + } + await activateExplorer(extensionContext) +} diff --git a/packages/core/src/sagemakerunifiedstudio/auth/model.ts b/packages/core/src/sagemakerunifiedstudio/auth/model.ts new file mode 100644 index 00000000000..6e60fa20e96 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/auth/model.ts @@ -0,0 +1,68 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SsoProfile, SsoConnection } from '../../auth/connection' + +/** + * Scope for SageMaker Unified Studio authentication + */ +export const scopeSmus = 'datazone:domain:access' + +/** + * SageMaker Unified Studio profile extending the base SSO profile + */ +export interface SmusProfile extends SsoProfile { + readonly domainUrl: string + readonly domainId: string +} + +/** + * SageMaker Unified Studio connection extending the base SSO connection + */ +export interface SmusConnection extends SmusProfile, SsoConnection { + readonly id: string + readonly label: string +} + +/** + * Creates a SageMaker Unified Studio profile + * @param domainUrl The SageMaker Unified Studio domain URL + * @param domainId The SageMaker Unified Studio domain ID + * @param startUrl The SSO start URL (issuer URL) + * @param region The AWS region + * @returns A SageMaker Unified Studio profile + */ +export function createSmusProfile( + domainUrl: string, + domainId: string, + startUrl: string, + region: string, + scopes = [scopeSmus] +): SmusProfile & { readonly scopes: string[] } { + return { + scopes, + type: 'sso', + startUrl, + ssoRegion: region, + domainUrl, + domainId, + } +} + +/** + * Checks if a connection is a valid SageMaker Unified Studio connection + * @param conn Connection to check + * @returns True if the connection is a valid SMUS connection + */ +export function isValidSmusConnection(conn?: any): conn is SmusConnection { + if (!conn || conn.type !== 'sso') { + return false + } + // Check if the connection has the required SMUS scope + const hasScope = Array.isArray(conn.scopes) && conn.scopes.includes(scopeSmus) + // Check if the connection has the required SMUS properties + const hasSmusProps = 'domainUrl' in conn && 'domainId' in conn + return !!hasScope && !!hasSmusProps +} diff --git a/packages/core/src/sagemakerunifiedstudio/auth/providers/connectionCredentialsProvider.ts b/packages/core/src/sagemakerunifiedstudio/auth/providers/connectionCredentialsProvider.ts new file mode 100644 index 00000000000..f060e6477ab --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/auth/providers/connectionCredentialsProvider.ts @@ -0,0 +1,243 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getLogger } from '../../../shared/logger/logger' +import { ToolkitError } from '../../../shared/errors' +import * as AWS from '@aws-sdk/types' +import { CredentialsId, CredentialsProvider, CredentialsProviderType } from '../../../auth/providers/credentials' + +import { DataZoneClient } from '../../shared/client/datazoneClient' +import { SmusAuthenticationProvider } from './smusAuthenticationProvider' +import { CredentialType } from '../../../shared/telemetry/telemetry' +import { SmusCredentialExpiry, validateCredentialFields } from '../../shared/smusUtils' + +/** + * Credentials provider for SageMaker Unified Studio Connection credentials + * Uses DataZone API to get connection credentials for a specific connection * + * This provider implements independent caching with 10-minute expiry + */ +export class ConnectionCredentialsProvider implements CredentialsProvider { + private readonly logger = getLogger() + private credentialCache?: { + credentials: AWS.Credentials + expiresAt: Date + } + + constructor( + private readonly smusAuthProvider: SmusAuthenticationProvider, + private readonly connectionId: string + ) {} + + /** + * Gets the connection ID + * @returns Connection ID + */ + public getConnectionId(): string { + return this.connectionId + } + + /** + * Gets the credentials ID + * @returns Credentials ID + */ + public getCredentialsId(): CredentialsId { + return { + credentialSource: 'temp', + credentialTypeId: `${this.smusAuthProvider.getDomainId()}:${this.connectionId}`, + } + } + + /** + * Gets the provider type + * @returns Provider type + */ + public getProviderType(): CredentialsProviderType { + return 'temp' + } + + /** + * Gets the telemetry type + * @returns Telemetry type + */ + public getTelemetryType(): CredentialType { + return 'other' + } + + /** + * Gets the default region + * @returns Default region + */ + public getDefaultRegion(): string | undefined { + return this.smusAuthProvider.getDomainRegion() + } + + /** + * Gets the domain AWS account ID + * @returns Promise resolving to the domain account ID + */ + public async getDomainAccountId(): Promise { + return this.smusAuthProvider.getDomainAccountId() + } + + /** + * Gets the hash code + * @returns Hash code + */ + public getHashCode(): string { + const hashCode = `smus-connection:${this.smusAuthProvider.getDomainId()}:${this.connectionId}` + return hashCode + } + + /** + * Determines if the provider can auto-connect + * @returns Promise resolving to boolean + */ + public async canAutoConnect(): Promise { + return false // SMUS requires manual authentication + } + + /** + * Determines if the provider is available + * @returns Promise resolving to boolean + */ + public async isAvailable(): Promise { + try { + return this.smusAuthProvider.isConnected() + } catch (err) { + this.logger.error('SMUS Connection: Error checking if auth provider is connected: %s', err) + return false + } + } + + /** + * Gets Connection credentials with independent caching + * @returns Promise resolving to credentials + */ + public async getCredentials(): Promise { + this.logger.debug(`SMUS Connection: Getting credentials for connection ${this.connectionId}`) + + // Check cache first (10-minute expiry) + if (this.credentialCache && this.credentialCache.expiresAt > new Date()) { + this.logger.debug( + `SMUS Connection: Using cached connection credentials for connection ${this.connectionId}` + ) + return this.credentialCache.credentials + } + + this.logger.debug( + `SMUS Connection: Calling GetConnection to fetch credentials for connection ${this.connectionId}` + ) + + try { + const datazoneClient = await DataZoneClient.getInstance(this.smusAuthProvider) + const getConnectionResponse = await datazoneClient.getConnection({ + domainIdentifier: this.smusAuthProvider.getDomainId(), + identifier: this.connectionId, + withSecret: true, + }) + + this.logger.debug(`SMUS Connection: Successfully retrieved connection details for ${this.connectionId}`) + + // Extract connection credentials + const connectionCredentials = getConnectionResponse.connectionCredentials + if (!connectionCredentials) { + throw new ToolkitError( + `No connection credentials available in response for connection ${this.connectionId}`, + { + code: 'NoConnectionCredentials', + } + ) + } + + // Validate credential fields + validateCredentialFields( + connectionCredentials, + 'InvalidConnectionCredentials', + 'connection credential response', + true + ) + + // Create AWS credentials with expiration + // Use the expiration from the response if available, otherwise default to 10 minutes + let expiresAt: Date + if (connectionCredentials.expiration) { + // The API returns expiration as a string or Date, handle both cases + expiresAt = + connectionCredentials.expiration instanceof Date + ? connectionCredentials.expiration + : new Date(connectionCredentials.expiration) + } else { + expiresAt = new Date(Date.now() + SmusCredentialExpiry.connectionExpiryMs) + } + + const awsCredentials: AWS.Credentials = { + accessKeyId: connectionCredentials.accessKeyId as string, + secretAccessKey: connectionCredentials.secretAccessKey as string, + sessionToken: connectionCredentials.sessionToken as string, + expiration: expiresAt, + } + + // Cache connection credentials (10-minute expiry) + const cacheExpiresAt = new Date(Date.now() + SmusCredentialExpiry.connectionExpiryMs) + this.credentialCache = { + credentials: awsCredentials, + expiresAt: cacheExpiresAt, + } + + this.logger.debug( + `SMUS Connection: Successfully cached connection credentials for connection ${this.connectionId}, expires in %s minutes`, + Math.round((cacheExpiresAt.getTime() - Date.now()) / 60000) + ) + + return awsCredentials + } catch (err) { + this.logger.error( + `SMUS Connection: Failed to get connection credentials for connection ${this.connectionId}: %s`, + err + ) + + // Re-throw ToolkitErrors with specific codes (NoConnectionCredentials, InvalidConnectionCredentials) + if ( + err instanceof ToolkitError && + (err.code === 'NoConnectionCredentials' || err.code === 'InvalidConnectionCredentials') + ) { + throw err + } + + // Wrap other errors in ConnectionCredentialsFetchFailed + throw new ToolkitError(`Failed to get connection credentials for ${this.connectionId}: ${err}`, { + code: 'ConnectionCredentialsFetchFailed', + cause: err instanceof Error ? err : undefined, + }) + } + } + + /** + * Invalidates cached connection credentials + * Clears the internal cache without fetching new credentials + */ + public invalidate(): void { + this.logger.debug(`SMUS Connection: Invalidating cached credentials for connection ${this.connectionId}`) + // Clear cache to force fresh fetch on next getCredentials() call + this.credentialCache = undefined + this.logger.debug( + `SMUS Connection: Successfully invalidated connection credentials cache for connection ${this.connectionId}` + ) + } + + /** + * Disposes of the provider and cleans up resources + */ + public dispose(): void { + this.logger.debug( + `SMUS Connection: Disposing connection credentials provider for connection ${this.connectionId}` + ) + // Clear cache to clean up resources + this.invalidate() + this.logger.debug( + `SMUS Connection: Successfully disposed connection credentials provider for connection ${this.connectionId}` + ) + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/auth/providers/domainExecRoleCredentialsProvider.ts b/packages/core/src/sagemakerunifiedstudio/auth/providers/domainExecRoleCredentialsProvider.ts new file mode 100644 index 00000000000..968749a9c9c --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/auth/providers/domainExecRoleCredentialsProvider.ts @@ -0,0 +1,325 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getLogger } from '../../../shared/logger/logger' +import { ToolkitError } from '../../../shared/errors' +import * as AWS from '@aws-sdk/types' +import { CredentialsId, CredentialsProvider, CredentialsProviderType } from '../../../auth/providers/credentials' +import fetch from 'node-fetch' +import globals from '../../../shared/extensionGlobals' +import { CredentialType } from '../../../shared/telemetry/telemetry' +import { SmusCredentialExpiry, SmusTimeouts, SmusErrorCodes, validateCredentialFields } from '../../shared/smusUtils' + +/** + * Credentials provider for SageMaker Unified Studio Domain Execution Role (DER) + * Uses SSO tokens to get DER credentials via the /sso/redeem-token endpoint + * + * This provider implements internal caching with 10-minute expiry and handles + * its own credential lifecycle independently + */ +export class DomainExecRoleCredentialsProvider implements CredentialsProvider { + private readonly logger = getLogger() + private credentialCache?: { + credentials: AWS.Credentials + expiresAt: Date + } + + constructor( + private readonly domainUrl: string, + private readonly domainId: string, + private readonly ssoRegion: string, + private readonly getAccessToken: () => Promise // Function to get SSO access token for the Connection + ) {} + + /** + * Gets the domain ID + * @returns Domain ID + */ + public getDomainId(): string { + return this.domainId + } + + /** + * Gets the domain URL + * @returns Domain URL + */ + public getDomainUrl(): string { + return this.domainUrl + } + + /** + * Gets the credentials ID + * @returns Credentials ID + */ + public getCredentialsId(): CredentialsId { + return { + credentialSource: 'sso', + credentialTypeId: this.domainId, + } + } + + /** + * Gets the provider type + * @returns Provider type + */ + public getProviderType(): CredentialsProviderType { + return 'sso' + } + + /** + * Gets the telemetry type + * @returns Telemetry type + */ + public getTelemetryType(): CredentialType { + return 'ssoProfile' + } + + /** + * Gets the default region + * @returns Default region + */ + public getDefaultRegion(): string | undefined { + return this.ssoRegion + } + + /** + * Gets the hash code + * @returns Hash code + */ + public getHashCode(): string { + const hashCode = `smus-der:${this.domainId}:${this.ssoRegion}` + return hashCode + } + + /** + * Determines if the provider can auto-connect + * @returns Promise resolving to boolean + */ + public async canAutoConnect(): Promise { + return false // SMUS requires manual authentication + } + + /** + * Determines if the provider is available + * @returns Promise resolving to boolean + */ + public async isAvailable(): Promise { + try { + // Check if we can get an access token + await this.getAccessToken() + return true + } catch { + return false + } + } + + /** + * Gets Domain Execution Role (DER) credentials with internal caching + * @returns Promise resolving to credentials + */ + public async getCredentials(): Promise { + this.logger.debug(`SMUS DER: Getting DER credentials for domain ${this.domainId}`) + + // Check cache first (10-minute expiry with 5-minute buffer for proactive refresh) + if (this.credentialCache && this.credentialCache.expiresAt > new Date()) { + this.logger.debug(`SMUS DER: Using cached DER credentials for domain ${this.domainId}`) + return this.credentialCache.credentials + } + + this.logger.debug(`SMUS DER: Fetching credentials from API for domain ${this.domainId}`) + + try { + // Get current SSO access token + const accessToken = await this.getAccessToken() + if (!accessToken) { + throw new ToolkitError('No access token available for DER credential refresh', { + code: 'NoTokenAvailable', + }) + } + + this.logger.debug(`SMUS DER: Got access token for refresh for domain ${this.domainId}`) + + // Call SMUS redeem token API to get DER credentials + const redeemUrl = new URL('/sso/redeem-token', this.domainUrl) + this.logger.debug(`SMUS DER: Calling redeem token endpoint: ${redeemUrl.toString()}`) + + const requestBody = { + domainId: this.domainId, + accessToken, + } + + const requestHeaders = { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'User-Agent': 'aws-toolkit-vscode', + } + + let response + try { + response = await fetch(redeemUrl.toString(), { + method: 'POST', + headers: requestHeaders, + body: JSON.stringify(requestBody), + timeout: SmusTimeouts.apiCallTimeoutMs, + }) + } catch (fetchError) { + // Handle timeout errors specifically + if ( + fetchError instanceof Error && + (fetchError.name === 'AbortError' || fetchError.message.includes('timeout')) + ) { + throw new ToolkitError( + `Redeem token request timed out after ${SmusTimeouts.apiCallTimeoutMs / 1000} seconds`, + { + code: SmusErrorCodes.ApiTimeout, + cause: fetchError, + } + ) + } + // Re-throw other fetch errors + throw fetchError + } + + this.logger.debug(`SMUS DER: Redeem token response status: ${response.status} for domain ${this.domainId}`) + + if (!response.ok) { + // Try to get response body for more details + let responseBody = '' + try { + responseBody = await response.text() + this.logger.debug(`SMUS DER: Error response body for domain ${this.domainId}: ${responseBody}`) + } catch (bodyErr) { + this.logger.debug( + `SMUS DER: Could not read error response body for domain ${this.domainId}: ${bodyErr}` + ) + } + + throw new ToolkitError( + `Failed to redeem access token: ${response.status} ${response.statusText}${responseBody ? ` - ${responseBody}` : ''}`, + { code: SmusErrorCodes.RedeemAccessTokenFailed } + ) + } + + const responseText = await response.text() + + const data = JSON.parse(responseText) as { + credentials: { + accessKeyId: string + secretAccessKey: string + sessionToken: string + expiration: string + } + } + this.logger.debug(`SMUS DER: Successfully received credentials from API for domain ${this.domainId}`) + + // Validate the response data structure + if (!data.credentials) { + throw new ToolkitError('Missing credentials object in API response', { + code: 'InvalidCredentialResponse', + }) + } + + const credentials = data.credentials + + // Validate the credential fields + validateCredentialFields(credentials, 'InvalidCredentialResponse', 'API response') + + // Create credentials with expiration + let credentialExpiresAt: Date + if (credentials.expiration) { + // Handle both epoch timestamps and ISO date strings + let parsedExpiration: Date + + // Check if expiration is a numeric string (epoch timestamp) + const expirationNum = Number(credentials.expiration) + if (!isNaN(expirationNum) && expirationNum > 0) { + // Treat as epoch timestamp in seconds and convert to milliseconds + const timestampMs = expirationNum * 1000 + parsedExpiration = new Date(timestampMs) + this.logger.debug( + `SMUS DER: Parsed epoch timestamp ${credentials.expiration} (seconds) as ${parsedExpiration.toISOString()}` + ) + } else { + // Treat as ISO date string + parsedExpiration = new Date(credentials.expiration) + if (!isNaN(parsedExpiration.getTime())) { + this.logger.debug( + `SMUS DER: Parsed ISO date string ${credentials.expiration} as ${parsedExpiration.toISOString()}` + ) + } else { + this.logger.debug( + `SMUS DER: Failed to parse ISO date string ${credentials.expiration} - invalid date format` + ) + } + } + + // Check if the parsed date is valid + if (isNaN(parsedExpiration.getTime())) { + this.logger.warn( + `SMUS DER: Invalid expiration value: ${credentials.expiration}, using default expiration` + ) + credentialExpiresAt = new Date(Date.now() + SmusCredentialExpiry.derExpiryMs) + } else { + credentialExpiresAt = parsedExpiration + } + if (!isNaN(credentialExpiresAt.getTime())) { + this.logger.debug(`SMUS DER: Credential expires at ${credentialExpiresAt.toISOString()}`) + } else { + this.logger.debug(`SMUS DER: Invalid credential expiration date, using default`) + } + } else { + this.logger.debug(`SMUS DER: No expiration provided, using default`) + credentialExpiresAt = new Date(Date.now() + SmusCredentialExpiry.derExpiryMs) + } + + const awsCredentials: AWS.Credentials = { + accessKeyId: credentials.accessKeyId as string, + secretAccessKey: credentials.secretAccessKey as string, + sessionToken: credentials.sessionToken as string, + expiration: credentialExpiresAt, + } + + // Cache DER credentials with 10-minute expiry (5-minute buffer for proactive refresh) + const cacheExpiresAt = new globals.clock.Date(Date.now() + SmusCredentialExpiry.derExpiryMs) + this.credentialCache = { + credentials: awsCredentials, + expiresAt: cacheExpiresAt, + } + + this.logger.debug( + 'SMUS DER: Successfully cached DER credentials for domain %s, cache expires in %s minutes', + this.domainId, + Math.round((cacheExpiresAt.getTime() - Date.now()) / 60000) + ) + + return awsCredentials + } catch (err) { + this.logger.error('SMUS DER: Failed to fetch credentials for domain %s: %s', this.domainId, err) + throw new ToolkitError(`Failed to fetch DER credentials for domain ${this.domainId}: ${err}`, { + code: 'DerCredentialsFetchFailed', + cause: err instanceof Error ? err : undefined, + }) + } + } + + /** + * Invalidates cached DER credentials + * Clears the internal cache without fetching new credentials + */ + public invalidate(): void { + this.logger.debug(`SMUS DER: Invalidating cached DER credentials for domain ${this.domainId}`) + // Clear cache to force fresh fetch on next getCredentials() call + this.credentialCache = undefined + this.logger.debug(`SMUS DER: Successfully invalidated DER credentials cache for domain ${this.domainId}`) + } + /** + * Disposes of the provider and cleans up resources + */ + public dispose(): void { + this.logger.debug(`SMUS DER: Disposing DER credentials provider for domain ${this.domainId}`) + this.invalidate() + this.logger.debug(`SMUS DER: Successfully disposed DER credentials provider for domain ${this.domainId}`) + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/auth/providers/projectRoleCredentialsProvider.ts b/packages/core/src/sagemakerunifiedstudio/auth/providers/projectRoleCredentialsProvider.ts new file mode 100644 index 00000000000..5eb42e1fd5f --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/auth/providers/projectRoleCredentialsProvider.ts @@ -0,0 +1,363 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getLogger } from '../../../shared/logger/logger' +import { ToolkitError } from '../../../shared/errors' +import * as AWS from '@aws-sdk/types' +import { CredentialsId, CredentialsProvider, CredentialsProviderType } from '../../../auth/providers/credentials' + +import { DataZoneClient } from '../../shared/client/datazoneClient' +import { SmusAuthenticationProvider } from './smusAuthenticationProvider' +import { CredentialType } from '../../../shared/telemetry/telemetry' +import { SmusCredentialExpiry, validateCredentialFields } from '../../shared/smusUtils' +import { loadMappings, saveMappings } from '../../../awsService/sagemaker/credentialMapping' + +/** + * Credentials provider for SageMaker Unified Studio Project Role credentials + * Uses Domain Execution Role (DER) credentials to get project-scoped credentials + * via the DataZone GetEnvironmentCredentials API + * + * This provider implements independent caching with 10-minute expiry and can be used + * with any AWS SDK client (S3Client, LambdaClient, etc.) + */ +export class ProjectRoleCredentialsProvider implements CredentialsProvider { + private readonly logger = getLogger() + private credentialCache?: { + credentials: AWS.Credentials + expiresAt: Date + } + private refreshTimer?: NodeJS.Timeout + private readonly refreshInterval = 10 * 60 * 1000 // 10 minutes + private readonly checkInterval = 10 * 1000 // 10 seconds - check frequently, refresh based on actual time + private sshRefreshActive = false + private lastRefreshTime?: Date + + constructor( + private readonly smusAuthProvider: SmusAuthenticationProvider, + private readonly projectId: string + ) {} + + /** + * Gets the project ID + * @returns Project ID + */ + public getProjectId(): string { + return this.projectId + } + + /** + * Gets the credentials ID + * @returns Credentials ID + */ + public getCredentialsId(): CredentialsId { + return { + credentialSource: 'temp', + credentialTypeId: `${this.smusAuthProvider.getDomainId()}:${this.projectId}`, + } + } + + /** + * Gets the provider type + * @returns Provider type + */ + public getProviderType(): CredentialsProviderType { + return 'temp' + } + + /** + * Gets the telemetry type + * @returns Telemetry type + */ + public getTelemetryType(): CredentialType { + return 'other' + } + + /** + * Gets the default region + * @returns Default region + */ + public getDefaultRegion(): string | undefined { + return this.smusAuthProvider.getDomainRegion() + } + + /** + * Gets the hash code + * @returns Hash code + */ + public getHashCode(): string { + const hashCode = `smus-project:${this.smusAuthProvider.getDomainId()}:${this.projectId}` + return hashCode + } + + /** + * Determines if the provider can auto-connect + * @returns Promise resolving to boolean + */ + public async canAutoConnect(): Promise { + return false // SMUS requires manual authentication + } + + /** + * Determines if the provider is available + * @returns Promise resolving to boolean + */ + public async isAvailable(): Promise { + return this.smusAuthProvider.isConnected() + } + + /** + * Gets Project Role credentials with independent caching + * @returns Promise resolving to credentials + */ + public async getCredentials(): Promise { + this.logger.debug(`SMUS Project: Getting credentials for project ${this.projectId}`) + + // Check cache first (10-minute expiry) + if (this.credentialCache && this.credentialCache.expiresAt > new Date()) { + this.logger.debug(`SMUS Project: Using cached project credentials for project ${this.projectId}`) + return this.credentialCache.credentials + } + + this.logger.debug(`SMUS Project: Fetching project credentials from API for project ${this.projectId}`) + + try { + const dataZoneClient = await DataZoneClient.getInstance(this.smusAuthProvider) + const response = await dataZoneClient.getProjectDefaultEnvironmentCreds(this.projectId) + + this.logger.debug( + `SMUS Project: Successfully received response from GetEnvironmentCredentials API for project ${this.projectId}` + ) + + // Validate credential fields - credentials are returned directly in the response + validateCredentialFields(response, 'InvalidProjectCredentialResponse', 'project credential response') + + // Create AWS credentials with expiration + // Use the expiration from the response if available, otherwise default to 10 minutes + let expiresAt: Date + if (response.expiration) { + // The API returns expiration as a string, parse it to Date + expiresAt = new Date(response.expiration) + } else { + expiresAt = new Date(Date.now() + SmusCredentialExpiry.projectExpiryMs) + } + + const awsCredentials: AWS.Credentials = { + accessKeyId: response.accessKeyId as string, + secretAccessKey: response.secretAccessKey as string, + sessionToken: response.sessionToken as string, + expiration: expiresAt, + } + + // Cache project credentials + this.credentialCache = { + credentials: awsCredentials, + expiresAt: expiresAt, + } + + this.logger.debug( + 'SMUS Project: Successfully cached project credentials for project %s, expires in %s minutes', + this.projectId, + Math.round((expiresAt.getTime() - Date.now()) / 60000) + ) + + // Write project credentials to mapping file to be used by Sagemaker local server for remote connections + await this.writeCredentialsToMapping(awsCredentials) + + return awsCredentials + } catch (err) { + this.logger.error('SMUS Project: Failed to get project credentials for project %s: %s', this.projectId, err) + + // Handle InvalidGrantException specially - indicates need for reauthentication + if (err instanceof Error && err.name === 'InvalidGrantException') { + // Invalidate cache when authentication fails + this.invalidate() + throw new ToolkitError( + `Failed to get project credentials for project ${this.projectId}: ${err.message}. Reauthentication required.`, + { + code: 'InvalidRefreshToken', + cause: err, + } + ) + } + + throw new ToolkitError(`Failed to get project credentials for project ${this.projectId}: ${err}`, { + code: 'ProjectCredentialsFetchFailed', + cause: err instanceof Error ? err : undefined, + }) + } + } + + /** + * Writes project credentials to mapping file for local server usage + */ + private async writeCredentialsToMapping(awsCredentials: AWS.Credentials): Promise { + try { + const mapping = await loadMappings() + mapping.smusProjects ??= {} + mapping.smusProjects[this.projectId] = { + accessKey: awsCredentials.accessKeyId, + secret: awsCredentials.secretAccessKey, + token: awsCredentials.sessionToken || '', + } + await saveMappings(mapping) + } catch (err) { + this.logger.warn('SMUS Project: Failed to write project credentials to mapping file: %s', err) + } + } + + /** + * Starts proactive credential refresh for SSH connections + * + * Uses an expiry-based approach with safety buffer: + * - Checks every 10 seconds using setTimeout + * - Refreshes when credentials expire within 5 minutes (safety buffer) + * - Falls back to 10-minute time-based refresh if no expiry information available + * - Handles sleep/resume because it uses wall-clock time for expiry checks + * + * This means credentials are refreshed just before they expire, reducing + * unnecessary API calls while ensuring credentials remain valid. + */ + public startProactiveCredentialRefresh(): void { + if (this.sshRefreshActive) { + this.logger.debug(`SMUS Project: SSH refresh already active for project ${this.projectId}`) + return + } + + this.logger.info(`SMUS Project: Starting SSH credential refresh for project ${this.projectId}`) + this.sshRefreshActive = true + this.lastRefreshTime = new Date() // Initialize refresh time + + // Start the check timer (checks every 10 seconds, refreshes every 10 minutes based on actual time) + this.scheduleNextCheck() + } + + /** + * Stops proactive credential refresh + * Called when SSH connection ends or SMUS disconnects + */ + public stopProactiveCredentialRefresh(): void { + if (!this.sshRefreshActive) { + return + } + + this.logger.info(`SMUS Project: Stopping SSH credential refresh for project ${this.projectId}`) + this.sshRefreshActive = false + this.lastRefreshTime = undefined + + // Clean up timer + if (this.refreshTimer) { + clearTimeout(this.refreshTimer) + this.refreshTimer = undefined + } + } + + /** + * Schedules the next credential check (every 10 seconds) + * Refreshes credentials when they expire within 5 minutes (safety buffer) + * Falls back to 10-minute time-based refresh if no expiry information available + * This handles sleep/resume scenarios correctly + */ + private scheduleNextCheck(): void { + if (!this.sshRefreshActive) { + return + } + // Check every 10 seconds, but only refresh every 10 minutes based on actual time elapsed + this.refreshTimer = setTimeout(async () => { + try { + const now = new Date() + // Check if we need to refresh based on actual time elapsed + if (this.shouldPerformRefresh(now)) { + await this.refresh() + } + // Schedule next check if still active + if (this.sshRefreshActive) { + this.scheduleNextCheck() + } + } catch (error) { + this.logger.error( + `SMUS Project: Failed to refresh credentials for project ${this.projectId}: %O`, + error + ) + // Continue trying even if refresh fails. Dispose will handle stopping the refresh. + if (this.sshRefreshActive) { + this.scheduleNextCheck() + } + } + }, this.checkInterval) + } + + /** + * Determines if a credential refresh should be performed based on credential expiration + * This handles sleep/resume scenarios properly and is more efficient than time-based refresh + */ + private shouldPerformRefresh(now: Date): boolean { + if (!this.lastRefreshTime || !this.credentialCache) { + // First refresh or no cached credentials + this.logger.debug(`SMUS Project: First refresh - no previous credentials for ${this.projectId}`) + return true + } + + // Check if credentials expire soon (with 5-minute safety buffer) + const safetyBufferMs = 5 * 60 * 1000 // 5 minutes before expiry + const expiryTime = this.credentialCache.credentials.expiration?.getTime() + + if (!expiryTime) { + // No expiry info - fall back to time-based refresh as safety net + const timeSinceLastRefresh = now.getTime() - this.lastRefreshTime.getTime() + const shouldRefresh = timeSinceLastRefresh >= this.refreshInterval + return shouldRefresh + } + + const timeUntilExpiry = expiryTime - now.getTime() + const shouldRefresh = timeUntilExpiry < safetyBufferMs + return shouldRefresh + } + + /** + * Performs credential refresh by invalidating cache and fetching fresh credentials + */ + private async refresh(): Promise { + const now = new Date() + const expiryTime = this.credentialCache?.credentials.expiration?.getTime() + + if (expiryTime) { + const minutesUntilExpiry = Math.round((expiryTime - now.getTime()) / 60000) + this.logger.debug( + `SMUS Project: Refreshing credentials for project ${this.projectId} - expires in ${minutesUntilExpiry} minutes` + ) + } else { + const minutesSinceLastRefresh = this.lastRefreshTime + ? Math.round((now.getTime() - this.lastRefreshTime.getTime()) / 60000) + : 0 + this.logger.debug( + `SMUS Project: Refreshing credentials for project ${this.projectId} - time-based refresh after ${minutesSinceLastRefresh} minutes` + ) + } + + await this.getCredentials() + this.lastRefreshTime = new Date() + } + + /** + * Invalidates cached project credentials + * Clears the internal cache without fetching new credentials + */ + public invalidate(): void { + this.logger.debug(`SMUS Project: Invalidating cached credentials for project ${this.projectId}`) + // Clear cache to force fresh fetch on next getCredentials() call + this.credentialCache = undefined + this.logger.debug( + `SMUS Project: Successfully invalidated project credentials cache for project ${this.projectId}` + ) + } + + /** + * Disposes of the provider and cleans up resources + */ + public dispose(): void { + this.stopProactiveCredentialRefresh() + this.invalidate() + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/auth/providers/smusAuthenticationProvider.ts b/packages/core/src/sagemakerunifiedstudio/auth/providers/smusAuthenticationProvider.ts new file mode 100644 index 00000000000..6c0f204cbd3 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/auth/providers/smusAuthenticationProvider.ts @@ -0,0 +1,742 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { Auth } from '../../../auth/auth' +import { getSecondaryAuth } from '../../../auth/secondaryAuth' +import { ToolkitError } from '../../../shared/errors' +import { withTelemetryContext } from '../../../shared/telemetry/util' +import { SsoConnection } from '../../../auth/connection' +import { showReauthenticateMessage } from '../../../shared/utilities/messages' +import * as localizedText from '../../../shared/localizedText' +import { ToolkitPromptSettings } from '../../../shared/settings' +import { setContext, getContext } from '../../../shared/vscode/setContext' +import { getLogger } from '../../../shared/logger/logger' +import { SmusUtils, SmusErrorCodes, extractAccountIdFromResourceMetadata } from '../../shared/smusUtils' +import { createSmusProfile, isValidSmusConnection, SmusConnection } from '../model' +import { DomainExecRoleCredentialsProvider } from './domainExecRoleCredentialsProvider' +import { ProjectRoleCredentialsProvider } from './projectRoleCredentialsProvider' +import { ConnectionCredentialsProvider } from './connectionCredentialsProvider' +import { ConnectionClientStore } from '../../shared/client/connectionClientStore' +import { getResourceMetadata } from '../../shared/utils/resourceMetadataUtils' +import { fromIni } from '@aws-sdk/credential-providers' +import { randomUUID } from '../../../shared/crypto' +import { DefaultStsClient } from '../../../shared/clients/stsClient' +import { DataZoneClient } from '../../shared/client/datazoneClient' + +/** + * Sets the context variable for SageMaker Unified Studio connection state + * @param isConnected Whether SMUS is connected + */ +export function setSmusConnectedContext(isConnected: boolean): Promise { + return setContext('aws.smus.connected', isConnected) +} + +/** + * Sets the context variable for SMUS space environment state + * @param inSmusSpace Whether we're in SMUS space environment + */ +export function setSmusSpaceEnvironmentContext(inSmusSpace: boolean): Promise { + return setContext('aws.smus.inSmusSpaceEnvironment', inSmusSpace) +} +const authClassName = 'SmusAuthenticationProvider' + +/** + * Authentication provider for SageMaker Unified Studio + * Manages authentication state and credentials for SMUS + */ +export class SmusAuthenticationProvider { + private readonly logger = getLogger() + public readonly onDidChangeActiveConnection = this.secondaryAuth.onDidChangeActiveConnection + private readonly onDidChangeEmitter = new vscode.EventEmitter() + public readonly onDidChange = this.onDidChangeEmitter.event + private credentialsProviderCache = new Map() + private projectCredentialProvidersCache = new Map() + private connectionCredentialProvidersCache = new Map() + private cachedDomainAccountId: string | undefined + private cachedProjectAccountIds = new Map() + + public constructor( + public readonly auth = Auth.instance, + public readonly secondaryAuth = getSecondaryAuth( + auth, + 'smus', + 'SageMaker Unified Studio', + isValidSmusConnection + ) + ) { + this.onDidChangeActiveConnection(async () => { + // Stop SSH credential refresh for all projects when connection changes + this.stopAllSshCredentialRefresh() + + // Invalidate any cached credentials for the previous connection + await this.invalidateAllCredentialsInCache() + // Clear credentials provider cache when connection changes + this.credentialsProviderCache.clear() + // Clear project provider cache when connection changes + this.projectCredentialProvidersCache.clear() + // Clear connection provider cache when connection changes + this.connectionCredentialProvidersCache.clear() + // Clear cached domain account ID when connection changes + this.cachedDomainAccountId = undefined + // Clear cached project account IDs when connection changes + this.cachedProjectAccountIds.clear() + // Clear all clients in client store when connection changes + ConnectionClientStore.getInstance().clearAll() + await setSmusConnectedContext(this.isConnected()) + await setSmusSpaceEnvironmentContext(SmusUtils.isInSmusSpaceEnvironment()) + this.onDidChangeEmitter.fire() + }) + + // Set initial context in case event does not trigger + void setSmusConnectedContext(this.isConnectionValid()) + void setSmusSpaceEnvironmentContext(SmusUtils.isInSmusSpaceEnvironment()) + } + + /** + * Stops SSH credential refresh for all projects + * Called when SMUS connection changes or extension deactivates + */ + public stopAllSshCredentialRefresh(): void { + this.logger.debug('SMUS Auth: Stopping SSH credential refresh for all projects') + for (const provider of this.projectCredentialProvidersCache.values()) { + provider.stopProactiveCredentialRefresh() + } + } + + /** + * Gets the active connection + */ + public get activeConnection() { + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + const resourceMetadata = getResourceMetadata()! + if (resourceMetadata.AdditionalMetadata!.DataZoneDomainRegion) { + return { + domainId: resourceMetadata.AdditionalMetadata!.DataZoneDomainId!, + ssoRegion: resourceMetadata.AdditionalMetadata!.DataZoneDomainRegion!, + // The following fields won't be needed in SMUS space environment + // Craft the domain url with known information + // Use randome id as placeholder + domainUrl: `https://${resourceMetadata.AdditionalMetadata!.DataZoneDomainId!}.sagemaker.${resourceMetadata.AdditionalMetadata!.DataZoneDomainRegion!}.on.aws/`, + id: randomUUID(), + } + } else { + throw new ToolkitError('Domain region not found in metadata file.') + } + } + return this.secondaryAuth.activeConnection + } + + /** + * Checks if using a saved connection + */ + public get isUsingSavedConnection() { + return this.secondaryAuth.hasSavedConnection + } + + /** + * Checks if the connection is valid + */ + public isConnectionValid(): boolean { + // When in SMUS space, the extension is already running in projet context and sign in is not needed + // Set isConnectionValid to always true + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + return true + } + return this.activeConnection !== undefined && !this.secondaryAuth.isConnectionExpired + } + + /** + * Checks if connected to SMUS + */ + public isConnected(): boolean { + // When in SMUS space, the extension is already running in projet context and sign in is not needed + // Set isConnected to always true + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + return true + } + return this.activeConnection !== undefined + } + + /** + * Restores the previous connection + * Uses a promise to prevent multiple simultaneous restore calls + */ + public async restore() { + await this.secondaryAuth.restoreConnection() + } + + /** + * Authenticates with SageMaker Unified Studio using a domain URL + * @param domainUrl The SageMaker Unified Studio domain URL + * @returns Promise resolving to the connection + */ + @withTelemetryContext({ name: 'connectToSmus', class: authClassName }) + public async connectToSmus(domainUrl: string): Promise { + const logger = getLogger() + + try { + // Extract domain info using SmusUtils + const { domainId, region } = SmusUtils.extractDomainInfoFromUrl(domainUrl) + + // Validate domain ID + if (!domainId) { + throw new ToolkitError('Invalid domain URL format', { code: 'InvalidDomainUrl' }) + } + + logger.info(`SMUS: Connecting to domain ${domainId} in region ${region}`) + + // Check if we already have a connection for this domain + const existingConn = (await this.auth.listConnections()).find( + (c): c is SmusConnection => + isValidSmusConnection(c) && (c as any).domainUrl?.toLowerCase() === domainUrl.toLowerCase() + ) + + if (existingConn) { + const connectionState = this.auth.getConnectionState(existingConn) + logger.info(`SMUS: Found existing connection ${existingConn.id} with state: ${connectionState}`) + + // If connection is valid, use it directly without triggering new auth flow + if (connectionState === 'valid') { + logger.info('SMUS: Using existing valid connection') + + // Use the existing connection + const result = await this.secondaryAuth.useNewConnection(existingConn) + + // Auto-invoke project selection after successful sign-in (but not in SMUS space environment) + if (!SmusUtils.isInSmusSpaceEnvironment()) { + void vscode.commands.executeCommand('aws.smus.switchProject') + } + + return result + } + + // If connection is invalid or expired, reauthenticate + if (connectionState === 'invalid') { + logger.info('SMUS: Existing connection is invalid, reauthenticating') + const reauthenticatedConn = await this.reauthenticate(existingConn) + + // Create the SMUS connection wrapper + const smusConn: SmusConnection = { + ...reauthenticatedConn, + domainUrl, + domainId, + } + + const result = await this.secondaryAuth.useNewConnection(smusConn) + logger.debug(`SMUS: Reauthenticated connection successfully, id=${result.id}`) + + // Auto-invoke project selection after successful reauthentication (but not in SMUS space environment) + if (!SmusUtils.isInSmusSpaceEnvironment()) { + void vscode.commands.executeCommand('aws.smus.switchProject') + } + + return result + } + } + + // No existing connection found, create a new one + logger.info('SMUS: No existing connection found, creating new connection') + + // Get SSO instance info from DataZone + const ssoInstanceInfo = await SmusUtils.getSsoInstanceInfo(domainUrl) + + // Create a new connection with appropriate scope based on domain URL + const profile = createSmusProfile(domainUrl, domainId, ssoInstanceInfo.issuerUrl, ssoInstanceInfo.region) + const newConn = await this.auth.createConnection(profile) + logger.debug(`SMUS: Created new connection ${newConn.id}`) + + const smusConn: SmusConnection = { + ...newConn, + domainUrl, + domainId, + } + + const result = await this.secondaryAuth.useNewConnection(smusConn) + + // Auto-invoke project selection after successful sign-in (but not in SMUS space environment) + if (!SmusUtils.isInSmusSpaceEnvironment()) { + void vscode.commands.executeCommand('aws.smus.switchProject') + } + + return result + } catch (e) { + throw ToolkitError.chain(e, 'Failed to connect to SageMaker Unified Studio', { + code: 'FailedToConnect', + }) + } + } + + /** + * Reauthenticates an existing connection + * @param conn Connection to reauthenticate + * @returns Promise resolving to the reauthenticated connection + */ + @withTelemetryContext({ name: 'reauthenticate', class: authClassName }) + public async reauthenticate(conn: SsoConnection) { + try { + return await this.auth.reauthenticate(conn) + } catch (err) { + throw ToolkitError.chain(err, 'Unable to reauthenticate SageMaker Unified Studio connection.') + } + } + + /** + * Shows a reauthentication prompt to the user + * @param conn Connection to reauthenticate + */ + public async showReauthenticationPrompt(conn: SsoConnection): Promise { + await showReauthenticateMessage({ + message: localizedText.connectionExpired('SageMaker Unified Studio'), + connect: localizedText.reauthenticate, + suppressId: 'smusConnectionExpired', + settings: ToolkitPromptSettings.instance, + source: 'SageMaker Unified Studio', + reauthFunc: async () => { + await this.reauthenticate(conn) + }, + }) + } + + /** + * Gets the current SSO access token for the active connection + * @returns Promise resolving to the access token string + * @throws ToolkitError if unable to retrieve access token + */ + public async getAccessToken(): Promise { + const logger = getLogger() + + if (!this.activeConnection) { + throw new ToolkitError('No active SMUS connection available', { code: SmusErrorCodes.NoActiveConnection }) + } + + try { + const accessToken = await this.auth.getSsoAccessToken(this.activeConnection) + logger.debug(`SMUS: Successfully retrieved SSO access token for connection ${this.activeConnection.id}`) + + return accessToken + } catch (err) { + logger.error( + `SMUS: Failed to retrieve SSO access token for connection ${this.activeConnection.id}: %s`, + err + ) + + // Check if this is a reauth error that should be handled by showing SMUS-specific prompt + if (err instanceof ToolkitError && err.code === 'InvalidConnection') { + // Re-throw the error to maintain the error flow + logger.debug( + `SMUS: Auth connection has been marked invalid - Likely due to expiry. Reauthentication flow will be triggered, ignoring error` + ) + } + + throw new ToolkitError(`Failed to retrieve SSO access token for connection ${this.activeConnection.id}`, { + code: SmusErrorCodes.RedeemAccessTokenFailed, + cause: err instanceof Error ? err : undefined, + }) + } + } + + /** + * Gets or creates a project credentials provider for the specified project + * @param projectId The project ID to get credentials for + * @returns Promise resolving to the project credentials provider + */ + public async getProjectCredentialProvider(projectId: string): Promise { + const logger = getLogger() + + if (!this.activeConnection) { + throw new ToolkitError('No active SMUS connection available', { code: SmusErrorCodes.NoActiveConnection }) + } + + logger.debug(`SMUS: Getting project provider for project ${projectId}`) + + // Check if we already have a cached provider for this project + if (this.projectCredentialProvidersCache.has(projectId)) { + logger.debug('SMUS: Using cached project provider') + return this.projectCredentialProvidersCache.get(projectId)! + } + + logger.debug('SMUS: Creating new project provider') + // Create a new project provider and cache it + const projectProvider = new ProjectRoleCredentialsProvider(this, projectId) + this.projectCredentialProvidersCache.set(projectId, projectProvider) + + logger.debug('SMUS: Cached new project provider') + + return projectProvider + } + + /** + * Gets or creates a connection credentials provider for the specified connection + * @param connectionId The connection ID to get credentials for + * @param projectId The project ID that owns the connection + * @param region The region for the connection + * @returns Promise resolving to the connection credentials provider + */ + public async getConnectionCredentialsProvider( + connectionId: string, + projectId: string, + region: string + ): Promise { + const logger = getLogger() + + if (!this.activeConnection) { + throw new ToolkitError('No active SMUS connection available', { code: SmusErrorCodes.NoActiveConnection }) + } + + const cacheKey = `${this.activeConnection.domainId}:${projectId}:${connectionId}` + logger.debug(`SMUS: Getting connection provider for connection ${connectionId}`) + + // Check if we already have a cached provider for this connection + if (this.connectionCredentialProvidersCache.has(cacheKey)) { + logger.debug('SMUS: Using cached connection provider') + return this.connectionCredentialProvidersCache.get(cacheKey)! + } + + logger.debug('SMUS: Creating new connection provider') + // Create a new connection provider and cache it + const connectionProvider = new ConnectionCredentialsProvider(this, connectionId) + this.connectionCredentialProvidersCache.set(cacheKey, connectionProvider) + + logger.debug('SMUS: Cached new connection provider') + + return connectionProvider + } + + /** + * Gets the domain ID from the active connection + * @returns Domain ID + */ + public getDomainId(): string { + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + return getResourceMetadata()!.AdditionalMetadata!.DataZoneDomainId! + } + + if (!this.activeConnection) { + throw new ToolkitError('No active SMUS connection available', { code: SmusErrorCodes.NoActiveConnection }) + } + return this.activeConnection.domainId + } + + /** + * Gets the domain URL from the active connection + * @returns Domain URL + */ + public getDomainUrl(): string { + if (!this.activeConnection) { + throw new ToolkitError('No active SMUS connection available', { code: SmusErrorCodes.NoActiveConnection }) + } + return this.activeConnection.domainUrl + } + + /** + * Gets the AWS account ID for the active domain connection + * In SMUS space environment, extracts from ResourceArn in metadata + * Otherwise, makes an STS GetCallerIdentity call using DER credentials and caches the result + * @returns Promise resolving to the domain's AWS account ID + * @throws ToolkitError if unable to retrieve account ID + */ + public async getDomainAccountId(): Promise { + const logger = getLogger() + + // Return cached value if available + if (this.cachedDomainAccountId) { + logger.debug('SMUS: Using cached domain account ID') + return this.cachedDomainAccountId + } + + // If in SMUS space environment, extract account ID from resource-metadata file + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + const accountId = await extractAccountIdFromResourceMetadata() + + // Cache the account ID + this.cachedDomainAccountId = accountId + logger.debug(`Successfully cached domain account ID: ${accountId}`) + + return accountId + } + + if (!this.activeConnection) { + throw new ToolkitError('No active SMUS connection available', { code: SmusErrorCodes.NoActiveConnection }) + } + + // Use existing STS GetCallerIdentity implementation for non-SMUS space environments + try { + logger.debug('Fetching domain account ID via STS GetCallerIdentity') + + // Get DER credentials provider + const derCredProvider = await this.getDerCredentialsProvider() + + // Get the region for STS client + const region = this.getDomainRegion() + + // Create STS client with DER credentials + const stsClient = new DefaultStsClient(region, await derCredProvider.getCredentials()) + + // Make GetCallerIdentity call + const callerIdentity = await stsClient.getCallerIdentity() + + if (!callerIdentity.Account) { + throw new ToolkitError('Account ID not found in STS GetCallerIdentity response', { + code: SmusErrorCodes.AccountIdNotFound, + }) + } + + // Cache the account ID + this.cachedDomainAccountId = callerIdentity.Account + + logger.debug(`Successfully retrieved and cached domain account ID: ${callerIdentity.Account}`) + + return callerIdentity.Account + } catch (err) { + logger.error(`Failed to retrieve domain account ID: %s`, err) + + throw new ToolkitError('Failed to retrieve AWS account ID for active domain connection', { + code: SmusErrorCodes.GetDomainAccountIdFailed, + cause: err instanceof Error ? err : undefined, + }) + } + } + + /** + * Gets the AWS account ID for a specific project using project credentials + * In SMUS space environment, extracts from ResourceArn in metadata (same as domain account) + * Otherwise, makes an STS GetCallerIdentity call using project credentials + * @param projectId The DataZone project ID + * @returns Promise resolving to the project's AWS account ID + */ + public async getProjectAccountId(projectId: string): Promise { + const logger = getLogger() + + // Return cached value if available + if (this.cachedProjectAccountIds.has(projectId)) { + logger.debug(`SMUS: Using cached project account ID for project ${projectId}`) + return this.cachedProjectAccountIds.get(projectId)! + } + + // If in SMUS space environment, extract account ID from resource-metadata file + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + const accountId = await extractAccountIdFromResourceMetadata() + + // Cache the account ID + this.cachedProjectAccountIds.set(projectId, accountId) + logger.debug(`Successfully cached project account ID for project ${projectId}: ${accountId}`) + + return accountId + } + + if (!this.activeConnection) { + throw new ToolkitError('No active SMUS connection available', { code: SmusErrorCodes.NoActiveConnection }) + } + + // For non-SMUS space environments, use project credentials with STS + try { + logger.debug('Fetching project account ID via STS GetCallerIdentity with project credentials') + + // Get project credentials + const projectCredProvider = await this.getProjectCredentialProvider(projectId) + const projectCreds = await projectCredProvider.getCredentials() + + // Get project region from tooling environment + const dzClient = await DataZoneClient.getInstance(this) + const toolingEnv = await dzClient.getToolingEnvironment(projectId) + const projectRegion = toolingEnv.awsAccountRegion + + if (!projectRegion) { + throw new ToolkitError('No AWS account region found in tooling environment', { + code: SmusErrorCodes.RegionNotFound, + }) + } + + // Use STS to get account ID from project credentials + const stsClient = new DefaultStsClient(projectRegion, projectCreds) + const callerIdentity = await stsClient.getCallerIdentity() + + if (!callerIdentity.Account) { + throw new ToolkitError('Account ID not found in STS GetCallerIdentity response', { + code: SmusErrorCodes.AccountIdNotFound, + }) + } + + // Cache the account ID + this.cachedProjectAccountIds.set(projectId, callerIdentity.Account) + logger.debug( + `Successfully retrieved and cached project account ID for project ${projectId}: ${callerIdentity.Account}` + ) + + return callerIdentity.Account + } catch (err) { + logger.error('Failed to get project account ID: %s', err as Error) + throw new ToolkitError(`Failed to get project account ID: ${(err as Error).message}`, { + code: SmusErrorCodes.GetProjectAccountIdFailed, + }) + } + } + + public getDomainRegion(): string { + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + const resourceMetadata = getResourceMetadata()! + if (resourceMetadata.AdditionalMetadata!.DataZoneDomainRegion) { + return resourceMetadata.AdditionalMetadata!.DataZoneDomainRegion + } else { + throw new ToolkitError('Domain region not found in metadata file.') + } + } + + if (!this.activeConnection) { + throw new ToolkitError('No active SMUS connection available', { code: SmusErrorCodes.NoActiveConnection }) + } + return this.activeConnection.ssoRegion + } + + /** + * Gets or creates a cached credentials provider for the active connection + * @returns Promise resolving to the credentials provider + */ + public async getDerCredentialsProvider(): Promise { + const logger = getLogger() + + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + // When in SMUS space, DomainExecutionRoleCreds can be found in config file + // Read the credentials from credential profile DomainExecutionRoleCreds + const credentials = fromIni({ profile: 'DomainExecutionRoleCreds' }) + return { + getCredentials: async () => await credentials(), + } + } + + if (!this.activeConnection) { + throw new ToolkitError('No active SMUS connection available', { code: SmusErrorCodes.NoActiveConnection }) + } + + // Create a cache key based on the connection details + const cacheKey = `${this.activeConnection.ssoRegion}:${this.activeConnection.domainId}` + + logger.debug(`SMUS: Getting credentials provider for cache key: ${cacheKey}`) + + // Check if we already have a cached provider + if (this.credentialsProviderCache.has(cacheKey)) { + logger.debug('SMUS: Using cached credentials provider') + return this.credentialsProviderCache.get(cacheKey) + } + + logger.debug('SMUS: Creating new credentials provider') + + // Create a new provider and cache it + const provider = new DomainExecRoleCredentialsProvider( + this.activeConnection.domainUrl, + this.activeConnection.domainId, + this.activeConnection.ssoRegion, + async () => await this.getAccessToken() + ) + + this.credentialsProviderCache.set(cacheKey, provider) + logger.debug('SMUS: Cached new credentials provider') + + return provider + } + + /** + * Invalidates all cached credentials (for all connections) + * Used during connection changes or logout + */ + private async invalidateAllCredentialsInCache(): Promise { + const logger = getLogger() + logger.debug('SMUS: Invalidating all cached credentials') + + // Clear all cached DER providers and their internal credentials + for (const [cacheKey, provider] of this.credentialsProviderCache.entries()) { + try { + provider.invalidate() // This will clear the provider's internal cache + logger.debug(`SMUS: Invalidated credentials for cache key: ${cacheKey}`) + } catch (err) { + logger.warn(`SMUS: Failed to invalidate credentials for cache key ${cacheKey}: %s`, err) + } + } + + // Clear all cached project providers and their internal credentials + + await this.invalidateAllProjectCredentialsInCache() + // Clear all cached connection providers and their internal credentials + for (const [cacheKey, connectionProvider] of this.connectionCredentialProvidersCache.entries()) { + try { + connectionProvider.invalidate() // This will clear the connection provider's internal cache + logger.debug(`SMUS: Invalidated connection credentials for cache key: ${cacheKey}`) + } catch (err) { + logger.warn(`SMUS: Failed to invalidate connection credentials for cache key ${cacheKey}: %s`, err) + } + } + + // Clear cached domain account ID + this.cachedDomainAccountId = undefined + logger.debug('SMUS: Cleared cached domain account ID') + + // Clear cached project account IDs + this.cachedProjectAccountIds.clear() + logger.debug('SMUS: Cleared cached project account IDs') + } + + /** + * Invalidates all project cached credentials + */ + public async invalidateAllProjectCredentialsInCache(): Promise { + const logger = getLogger() + logger.debug('SMUS: Invalidating all cached project credentials') + + for (const [projectId, projectProvider] of this.projectCredentialProvidersCache.entries()) { + try { + projectProvider.invalidate() // This will clear the project provider's internal cache + logger.debug(`SMUS: Invalidated project credentials for project: ${projectId}`) + } catch (err) { + logger.warn(`SMUS: Failed to invalidate project credentials for project ${projectId}: %s`, err) + } + } + } + + /** + * Stops SSH credential refresh and cleans up resources + */ + public dispose(): void { + this.logger.debug('SMUS Auth: Disposing authentication provider and all cached providers') + + // Dispose all project providers + for (const provider of this.projectCredentialProvidersCache.values()) { + provider.dispose() + } + this.projectCredentialProvidersCache.clear() + + // Dispose all connection providers + for (const provider of this.connectionCredentialProvidersCache.values()) { + provider.dispose() + } + this.connectionCredentialProvidersCache.clear() + + // Dispose all DER providers in the general cache + for (const provider of this.credentialsProviderCache.values()) { + if (provider && typeof provider.dispose === 'function') { + provider.dispose() + } + } + this.credentialsProviderCache.clear() + + // Clear cached domain account ID + this.cachedDomainAccountId = undefined + + // Clear cached project account IDs + this.cachedProjectAccountIds.clear() + + this.logger.debug('SMUS Auth: Successfully disposed authentication provider') + } + + static #instance: SmusAuthenticationProvider | undefined + + public static get instance(): SmusAuthenticationProvider | undefined { + return SmusAuthenticationProvider.#instance + } + + public static fromContext() { + return (this.#instance ??= new this()) + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/activation.ts b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/activation.ts new file mode 100644 index 00000000000..97ffadacc69 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/activation.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 { Constants } from './models/constants' +import { + getStatusBarProviders, + showConnectionQuickPick, + showProjectQuickPick, + parseNotebookCells, +} from './commands/commands' + +/** + * Activates the SageMaker Unified Studio Connection Magics Selector feature. + * + * @param extensionContext The extension context + */ +export async function activate(extensionContext: vscode.ExtensionContext): Promise { + extensionContext.subscriptions.push( + vscode.commands.registerCommand(Constants.CONNECTION_COMMAND, () => showConnectionQuickPick()), + vscode.commands.registerCommand(Constants.PROJECT_COMMAND, () => showProjectQuickPick()) + ) + + if ('NotebookEdit' in vscode) { + const { connectionProvider, projectProvider, separatorProvider } = getStatusBarProviders() + + extensionContext.subscriptions.push( + vscode.notebooks.registerNotebookCellStatusBarItemProvider('jupyter-notebook', connectionProvider), + vscode.notebooks.registerNotebookCellStatusBarItemProvider('jupyter-notebook', projectProvider), + vscode.notebooks.registerNotebookCellStatusBarItemProvider('jupyter-notebook', separatorProvider) + ) + + extensionContext.subscriptions.push( + vscode.window.onDidChangeActiveNotebookEditor(async () => { + await parseNotebookCells() + }) + ) + + extensionContext.subscriptions.push(vscode.workspace.onDidChangeTextDocument(handleTextDocumentChange)) + + void parseNotebookCells() + } +} + +/** + * Handles text document changes to update status bar when cells are manually edited + */ +function handleTextDocumentChange(event: vscode.TextDocumentChangeEvent): void { + if (event.document.uri.scheme !== 'vscode-notebook-cell') { + return + } + + const editor = vscode.window.activeNotebookEditor + if (!editor) { + return + } + + let changedCell: vscode.NotebookCell | undefined + for (let i = 0; i < editor.notebook.cellCount; i++) { + const cell = editor.notebook.cellAt(i) + if (cell.document.uri.toString() === event.document.uri.toString()) { + changedCell = cell + break + } + } + + if (changedCell && changedCell.kind === vscode.NotebookCellKind.Code) { + const { notebookStateManager } = require('./services/notebookStateManager') + + notebookStateManager.parseCellMagic(changedCell) + + setTimeout(() => { + const { connectionProvider, projectProvider } = getStatusBarProviders() + connectionProvider.refreshCellStatusBar() + projectProvider.refreshCellStatusBar() + }, 100) + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/client/connectedSpaceDataZoneClient.ts b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/client/connectedSpaceDataZoneClient.ts new file mode 100644 index 00000000000..8f0998e295f --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/client/connectedSpaceDataZoneClient.ts @@ -0,0 +1,109 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataZone, ListConnectionsCommandOutput } from '@aws-sdk/client-datazone' +import { getLogger } from '../../../shared/logger/logger' + +/** + * Represents a DataZone connection + */ +export interface DataZoneConnection { + connectionId: string + name: string + type: string + props?: Record +} + +/** + * DataZone client for use in a SageMaker Unified Studio connected space + * Uses the user's current AWS credentials (project role credentials) + */ +export class ConnectedSpaceDataZoneClient { + private datazoneClient: DataZone | undefined + private readonly logger = getLogger() + + constructor( + private readonly region: string, + private readonly customEndpoint?: string + ) {} + + /** + * Gets the DataZone client, initializing it if necessary + * Uses default AWS credentials from the environment (project role) + * Supports custom endpoints for non-production environments + */ + private getDataZoneClient(): DataZone { + if (!this.datazoneClient) { + try { + const clientConfig: any = { + region: this.region, + } + + // Use custom endpoint if provided (for non-prod environments) + if (this.customEndpoint) { + clientConfig.endpoint = this.customEndpoint + this.logger.debug( + `ConnectedSpaceDataZoneClient: Using custom DataZone endpoint: ${this.customEndpoint}` + ) + } else { + this.logger.debug( + `ConnectedSpaceDataZoneClient: Using default AWS DataZone endpoint for region: ${this.region}` + ) + } + + this.logger.debug('ConnectedSpaceDataZoneClient: Creating DataZone client with default credentials') + this.datazoneClient = new DataZone(clientConfig) + this.logger.debug('ConnectedSpaceDataZoneClient: Successfully created DataZone client') + } catch (err) { + this.logger.error('ConnectedSpaceDataZoneClient: Failed to create DataZone client: %s', err as Error) + throw err + } + } + return this.datazoneClient + } + + /** + * Lists the connections in a DataZone domain and project + * @param domainId The DataZone domain identifier + * @param projectId The DataZone project identifier + * @returns List of connections + */ + public async listConnections(domainId: string, projectId: string): Promise { + try { + this.logger.info( + `ConnectedSpaceDataZoneClient: Listing connections for domain ${domainId}, project ${projectId}` + ) + + const datazoneClient = this.getDataZoneClient() + + const response: ListConnectionsCommandOutput = await datazoneClient.listConnections({ + domainIdentifier: domainId, + projectIdentifier: projectId, + }) + + if (!response.items || response.items.length === 0) { + this.logger.info( + `ConnectedSpaceDataZoneClient: No connections found for domain ${domainId}, project ${projectId}` + ) + return [] + } + + const connections: DataZoneConnection[] = response.items.map((connection) => ({ + connectionId: connection.connectionId || '', + name: connection.name || '', + type: connection.type || '', + props: connection.props || {}, + })) + + this.logger.info( + `ConnectedSpaceDataZoneClient: Found ${connections.length} connections for domain ${domainId}, project ${projectId}` + ) + return connections + } catch (err) { + this.logger.error('ConnectedSpaceDataZoneClient: Failed to list connections: %s', err as Error) + throw err + } + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/commands/commands.ts b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/commands/commands.ts new file mode 100644 index 00000000000..01e269004c7 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/commands/commands.ts @@ -0,0 +1,195 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { connectionOptionsService } from '../services/connectionOptionsService' +import { notebookStateManager } from '../services/notebookStateManager' +import { + ConnectionStatusBarProvider, + ProjectStatusBarProvider, + SeparatorStatusBarProvider, +} from '../providers/notebookStatusBarProviders' +import { Constants } from '../models/constants' + +let connectionProvider: ConnectionStatusBarProvider | undefined +let projectProvider: ProjectStatusBarProvider | undefined +let separatorProvider: SeparatorStatusBarProvider | undefined + +/** + * Gets the status bar providers for registration, auto-initializing if needed + */ +export function getStatusBarProviders(): { + connectionProvider: ConnectionStatusBarProvider + projectProvider: ProjectStatusBarProvider + separatorProvider: SeparatorStatusBarProvider +} { + if (!connectionProvider) { + connectionProvider = new ConnectionStatusBarProvider(3, Constants.CONNECTION_COMMAND) + } + if (!projectProvider) { + projectProvider = new ProjectStatusBarProvider(2, Constants.PROJECT_COMMAND) + } + if (!separatorProvider) { + separatorProvider = new SeparatorStatusBarProvider(1) + } + + return { + connectionProvider, + projectProvider, + separatorProvider, + } +} + +/** + * Sets the selected connection for a cell and updates the magic command + */ +export async function setSelectedConnection(cell: vscode.NotebookCell, connectionLabel: string): Promise { + notebookStateManager.setSelectedConnection(cell, connectionLabel, true) + await notebookStateManager.updateCellWithMagic(cell) +} + +/** + * Sets the selected project for a cell and updates the magic command + */ +export async function setSelectedProject(cell: vscode.NotebookCell, projectLabel: string): Promise { + notebookStateManager.setSelectedProject(cell, projectLabel) + await notebookStateManager.updateCellWithMagic(cell) +} + +/** + * Shows a quick pick menu for selecting a connection type and sets the connection for the active cell + */ +export async function showConnectionQuickPick(): Promise { + const editor = vscode.window.activeNotebookEditor + if (!editor) { + return + } + + const cell = editor.selection.start !== undefined ? editor.notebook.cellAt(editor.selection.start) : undefined + if (!cell) { + return + } + + await connectionOptionsService.updateConnectionAndProjectOptions() + + const connectionOptions = connectionOptionsService.getConnectionOptionsSync() + + // Sort connections based on preferred connection order + const sortedOptions = connectionOptions.sort((a, b) => { + // Comparison logic + const aIndex = Constants.CONNECTION_QUICK_PICK_ORDER.indexOf(a.label as any) + const bIndex = Constants.CONNECTION_QUICK_PICK_ORDER.indexOf(b.label as any) + + // If both are in the priority list, sort by their position + if (aIndex !== -1 && bIndex !== -1) { + return aIndex - bIndex + } + // If only 'a' is in the priority list, it comes first + if (aIndex !== -1) { + return -1 + } + // If only 'b' is in the priority list, it comes first + if (bIndex !== -1) { + return 1 + } + // If neither is in the priority list, maintain original order + return 0 + }) + + const quickPickItems: vscode.QuickPickItem[] = sortedOptions.map((option) => { + return { + label: option.label, + description: `(${option.magic})`, + iconPath: new vscode.ThemeIcon('plug'), + } + }) + + const selected = await vscode.window.showQuickPick(quickPickItems, { + placeHolder: Constants.CONNECTION_QUICK_PICK_LABEL_PLACEHOLDER, + }) + + if (selected) { + const connectionLabel = selected.detail || selected.label + await setSelectedConnection(cell, connectionLabel) + } +} + +/** + * Shows a quick pick menu for selecting a project type and sets the project for the active cell + */ +export async function showProjectQuickPick(): Promise { + const editor = vscode.window.activeNotebookEditor + if (!editor) { + return + } + + const cell = editor.selection.start !== undefined ? editor.notebook.cellAt(editor.selection.start) : undefined + if (!cell) { + return + } + + const connection = notebookStateManager.getSelectedConnection(cell) + if (!connection) { + return + } + + await connectionOptionsService.updateConnectionAndProjectOptions() + + const options = notebookStateManager.getProjectOptionsForConnection(cell) + if (options.length === 0) { + return + } + + const projectQuickPickItems: vscode.QuickPickItem[] = options.map((option) => { + return { + label: option.project, + description: `(${option.connection})`, + iconPath: new vscode.ThemeIcon('server'), + } + }) + + const selected = await vscode.window.showQuickPick(projectQuickPickItems, { + placeHolder: Constants.PROJECT_QUICK_PICK_LABEL_PLACEHOLDER, + }) + + if (selected) { + if (!selected.label) { + return + } + + await setSelectedProject(cell, selected.label) + } +} + +/** + * Refreshes the status bar items + */ +export function refreshStatusBarItems(): void { + connectionProvider?.refreshCellStatusBar() + projectProvider?.refreshCellStatusBar() + separatorProvider?.refreshCellStatusBar() +} + +/** + * Parses all notebook cells to current cell magics + */ +export async function parseNotebookCells(): Promise { + await connectionOptionsService.updateConnectionAndProjectOptions() + + const editor = vscode.window.activeNotebookEditor + if (!editor) { + return + } + + for (let i = 0; i < editor.notebook.cellCount; i++) { + const cell = editor.notebook.cellAt(i) + + if (cell.kind === vscode.NotebookCellKind.Code && cell.document.languageId !== 'markdown') { + notebookStateManager.parseCellMagic(cell) + } + } + + refreshStatusBarItems() +} diff --git a/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/index.ts b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/index.ts new file mode 100644 index 00000000000..0f7f429b5e6 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/index.ts @@ -0,0 +1,11 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export { activate } from './activation' + +export * from './models/constants' +export * from './models/types' +export * from './services/connectionOptionsService' +export * from './services/notebookStateManager' diff --git a/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/models/constants.ts b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/models/constants.ts new file mode 100644 index 00000000000..d94d4c9f3f7 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/models/constants.ts @@ -0,0 +1,153 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ConnectionTypeProperties } from './types' + +export const Constants = { + // Connection types + CONNECTION_TYPE_EMR_EC2: 'SPARK_EMR_EC2', + CONNECTION_TYPE_EMR_SERVERLESS: 'SPARK_EMR_SERVERLESS', + CONNECTION_TYPE_GLUE: 'SPARK_GLUE', + CONNECTION_TYPE_SPARK: 'SPARK', + CONNECTION_TYPE_REDSHIFT: 'REDSHIFT', + CONNECTION_TYPE_ATHENA: 'ATHENA', + CONNECTION_TYPE_IAM: 'IAM', + + // UI labels and placeholders + CONNECTION_QUICK_PICK_LABEL_PLACEHOLDER: 'Select Connection', + CONNECTION_STATUS_BAR_ITEM_LABEL: 'Select Connection', + CONNECTION_STATUS_BAR_ITEM_ICON: '$(plug)', + DEFAULT_CONNECTION_STATUS_BAR_ITEM_LABEL: 'Connection', + PROJECT_QUICK_PICK_LABEL_PLACEHOLDER: 'Select Compute', + PROJECT_STATUS_BAR_ITEM_LABEL: 'Select Compute', + PROJECT_STATUS_BAR_ITEM_ICON: '$(server)', + DEFAULT_PROJECT_STATUS_BAR_ITEM_LABEL: 'Compute', + CONNECTION_QUICK_PICK_ORDER: ['Local Python', 'PySpark', 'ScalaSpark', 'SQL'] as const, + + // Command IDs + CONNECTION_COMMAND: 'aws.smus.connectionmagics.selectConnection', + PROJECT_COMMAND: 'aws.smus.connectionmagics.selectProject', + + // Magic string literals + LOCAL_PYTHON: 'Local Python', + PYSPARK: 'PySpark', + SCALA_SPARK: 'ScalaSpark', + SQL: 'SQL', + MAGIC_PREFIX: '%%', + LOCAL_MAGIC: '%%local', + NAME_FLAG_LONG: '--name', + NAME_FLAG_SHORT: '-n', + SAGEMAKER_CONNECTION_METADATA_KEY: 'sagemakerConnection', + MARKDOWN_LANGUAGE: 'markdown', + PROJECT_PYTHON: 'project.python', + PROJECT_SPARK_COMPATIBILITY: 'project.spark.compatibility', +} as const + +/** + * Maps connection types to their display properties + */ +export const connectionTypePropertiesMap: Record = { + [Constants.CONNECTION_TYPE_GLUE]: { + labels: ['PySpark', 'SQL'], // Glue supports both PySpark and SQL + magic: '%%pyspark', + language: 'python', + category: 'spark', + }, + [Constants.CONNECTION_TYPE_EMR_EC2]: { + labels: ['PySpark', 'SQL'], // EMR supports both PySpark and SQL + magic: '%%pyspark', + language: 'python', + category: 'spark', + }, + [Constants.CONNECTION_TYPE_EMR_SERVERLESS]: { + labels: ['PySpark', 'SQL'], // EMR supports both PySpark and SQL + magic: '%%pyspark', + language: 'python', + category: 'spark', + }, + [Constants.CONNECTION_TYPE_REDSHIFT]: { + labels: ['SQL'], // Redshift only supports SQL + magic: '%%sql', + language: 'sql', + category: 'sql', + }, + [Constants.CONNECTION_TYPE_ATHENA]: { + labels: ['SQL'], // Athena only supports SQL + magic: '%%sql', + language: 'sql', + category: 'sql', + }, +} + +/** + * Maps connection labels to their display properties + */ +export const connectionLabelPropertiesMap: Record< + string, + { description: string; magic: string; language: string; category: string } +> = { + PySpark: { + description: 'Python with Spark', + magic: '%%pyspark', + language: 'python', + category: 'spark', + }, + SQL: { + description: 'SQL Query', + magic: '%%sql', + language: 'sql', + category: 'sql', + }, + ScalaSpark: { + description: 'Scala with Spark', + magic: '%%scalaspark', + language: 'python', // Scala is not a supported language mode, defaulting to Python + category: 'spark', + }, + 'Local Python': { + description: 'Python', + magic: '%%local', + language: 'python', + category: 'python', + }, + IAM: { + description: 'IAM Connection', + magic: '%%iam', + language: 'python', + category: 'iam', + }, +} + +/** + * Maps connection types to their platform display names for grouping + */ +export const connectionTypeToComputeNameMap: Record = { + [Constants.CONNECTION_TYPE_GLUE]: 'Glue', + [Constants.CONNECTION_TYPE_REDSHIFT]: 'Redshift', + [Constants.CONNECTION_TYPE_ATHENA]: 'Athena', + [Constants.CONNECTION_TYPE_EMR_EC2]: 'EMR EC2', + [Constants.CONNECTION_TYPE_EMR_SERVERLESS]: 'EMR Serverless', +} + +/** + * Maps magic commands to their corresponding connection types + */ +export const magicCommandToConnectionMap: Record = { + '%%spark': 'PySpark', + '%%pyspark': 'PySpark', + '%%scalaspark': 'ScalaSpark', + '%%local': 'Local Python', + '%%sql': 'SQL', +} as const + +/** + * Default project names for each connection type + */ +export const defaultProjectsByConnection: Record = { + 'Local Python': ['project.python'], + PySpark: ['project.spark.compatibility'], + ScalaSpark: ['project.spark.compatibility'], + SQL: ['project.spark.compatibility'], +} as const diff --git a/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/models/types.ts b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/models/types.ts new file mode 100644 index 00000000000..b14daab1ce8 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/models/types.ts @@ -0,0 +1,68 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * SageMaker Connection Summary interface + */ +export interface SageMakerConnectionSummary { + name: string + type: string +} + +/** + * Connection option type definition + */ +export interface ConnectionOption { + label: string + description: string + magic: string + language: string + category: string +} + +/** + * Project option group type definition + */ +export interface ProjectOptionGroup { + connection: string + projects: string[] +} + +/** + * Project option type definition + */ +export interface ProjectOption { + connection: string + project: string +} + +/** + * Connection to project mapping type definition + */ +export interface ConnectionProjectMapping { + connection: string + projectOptions: ProjectOptionGroup[] +} + +/** + * Represents the state of a notebook cell's connection settings + */ +export interface CellState { + connection?: string + project?: string + isUserSelection?: boolean + originalMagicCommand?: string + lastParsedContent?: string +} + +/** + * Maps connection types to their display properties + */ +export interface ConnectionTypeProperties { + labels: string[] + magic: string + language: string + category: string +} diff --git a/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/providers/notebookStatusBarProviders.ts b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/providers/notebookStatusBarProviders.ts new file mode 100644 index 00000000000..8551f615110 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/providers/notebookStatusBarProviders.ts @@ -0,0 +1,143 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { notebookStateManager } from '../services/notebookStateManager' +import { Constants } from '../models/constants' + +/** + * Abstract base class for notebook status bar providers. + */ +export abstract class BaseNotebookStatusBarProvider implements vscode.NotebookCellStatusBarItemProvider { + protected item: vscode.NotebookCellStatusBarItem + protected onDidChangeCellStatusBarItemsEmitter = new vscode.EventEmitter() + protected priority: number + protected icon?: string + protected command?: string + protected tooltip?: string + + public constructor(priority: number, icon?: string, command?: string, tooltip?: string) { + this.priority = priority + this.icon = icon + this.command = command + this.tooltip = tooltip + this.item = new vscode.NotebookCellStatusBarItem('', vscode.NotebookCellStatusBarAlignment.Right) + this.item.priority = priority + } + + /** + * Abstract method that each provider must implement to provide their specific status bar item. + */ + public abstract provideCellStatusBarItems( + cell: vscode.NotebookCell, + token: vscode.CancellationToken + ): vscode.ProviderResult + + /** + * Creates a status bar item with the provided text and applies common settings. + */ + protected createStatusBarItem(text: string, isClickable: boolean = true): vscode.NotebookCellStatusBarItem { + const displayText = this.icon ? `${this.icon} ${text}` : text + const item = new vscode.NotebookCellStatusBarItem(displayText, vscode.NotebookCellStatusBarAlignment.Right) + item.priority = this.priority + + if (isClickable && this.command) { + item.command = this.command + item.tooltip = this.tooltip + } + + return item + } + + /** + * Refreshes the cell status bar items. + */ + public refreshCellStatusBar(): void { + this.onDidChangeCellStatusBarItemsEmitter.fire() + } + + /** + * Event that fires when the cell status bar items have changed. + */ + public get onDidChangeCellStatusBarItems(): vscode.Event { + return this.onDidChangeCellStatusBarItemsEmitter.event + } +} + +/** + * Status bar provider for connection selection in notebook cells. + */ +export class ConnectionStatusBarProvider extends BaseNotebookStatusBarProvider { + public constructor(priority: number, command: string) { + super(priority, Constants.CONNECTION_STATUS_BAR_ITEM_ICON, command, Constants.CONNECTION_STATUS_BAR_ITEM_LABEL) + } + + public provideCellStatusBarItems( + cell: vscode.NotebookCell, + token: vscode.CancellationToken + ): vscode.ProviderResult { + // Don't show on non-code or markdown code cells + if (cell.kind !== vscode.NotebookCellKind.Code || cell.document.languageId === 'markdown') { + return undefined + } + + const connection = notebookStateManager.getSelectedConnection(cell) + + const displayText = connection || Constants.DEFAULT_CONNECTION_STATUS_BAR_ITEM_LABEL + const item = this.createStatusBarItem(displayText) + + return item + } +} + +/** + * Status bar provider for project selection in notebook cells. + */ +export class ProjectStatusBarProvider extends BaseNotebookStatusBarProvider { + public constructor(priority: number, command: string) { + super(priority, Constants.PROJECT_STATUS_BAR_ITEM_ICON, command, Constants.PROJECT_STATUS_BAR_ITEM_LABEL) + } + + public provideCellStatusBarItems( + cell: vscode.NotebookCell, + token: vscode.CancellationToken + ): vscode.ProviderResult { + // Don't show on non-code or markdown code cells + if (cell.kind !== vscode.NotebookCellKind.Code || cell.document.languageId === 'markdown') { + return undefined + } + + const project = notebookStateManager.getSelectedProject(cell) + + const displayText = project || Constants.DEFAULT_PROJECT_STATUS_BAR_ITEM_LABEL + const item = this.createStatusBarItem(displayText) + + return item + } +} + +/** + * Status bar provider for displaying a separator between items in notebook cells. + */ +export class SeparatorStatusBarProvider extends BaseNotebookStatusBarProvider { + public constructor(priority: number, separatorText: string = '|') { + super(priority) + + this.item = new vscode.NotebookCellStatusBarItem(separatorText, vscode.NotebookCellStatusBarAlignment.Right) + this.item.priority = priority + } + + public provideCellStatusBarItems( + cell: vscode.NotebookCell, + token: vscode.CancellationToken + ): vscode.ProviderResult { + // Don't show on non-code or markdown code cells + if (cell.kind !== vscode.NotebookCellKind.Code || cell.document.languageId === 'markdown') { + return undefined + } + + return this.item + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/services/connectionOptionsService.ts b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/services/connectionOptionsService.ts new file mode 100644 index 00000000000..901c2e5a60f --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/services/connectionOptionsService.ts @@ -0,0 +1,293 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getLogger } from '../../../shared/logger/logger' +import { + Constants, + connectionTypePropertiesMap, + connectionLabelPropertiesMap, + connectionTypeToComputeNameMap, +} from '../models/constants' +import { + ConnectionOption, + ProjectOptionGroup, + ConnectionProjectMapping, + SageMakerConnectionSummary, +} from '../models/types' +import { ConnectedSpaceDataZoneClient } from '../client/connectedSpaceDataZoneClient' +import { getResourceMetadata } from '../../shared/utils/resourceMetadataUtils' + +let datazoneClient: ConnectedSpaceDataZoneClient | undefined + +/** + * Gets or creates the module-scoped DataZone client instance + */ +function getDataZoneClient(): ConnectedSpaceDataZoneClient { + if (!datazoneClient) { + const resourceMetadata = getResourceMetadata() + + if (!resourceMetadata?.AdditionalMetadata?.DataZoneDomainRegion) { + throw new Error('DataZone domain region not found in resource metadata') + } + + const region = resourceMetadata.AdditionalMetadata.DataZoneDomainRegion + const customEndpoint = resourceMetadata.AdditionalMetadata?.DataZoneEndpoint + + datazoneClient = new ConnectedSpaceDataZoneClient(region, customEndpoint) + } + return datazoneClient +} + +/** + * Service for managing connection options and project mappings + */ +class ConnectionOptionsService { + private connectionOptions: ConnectionOption[] = [] + private projectOptions: ConnectionProjectMapping[] = [] + private cachedConnections: SageMakerConnectionSummary[] = [] + + constructor() {} + + /** + * Gets the appropriate connection option for a given label + */ + private getConnectionOptionForLabel(label: string): ConnectionOption | undefined { + const labelProps = connectionLabelPropertiesMap[label] + if (!labelProps) { + return undefined + } + + return { + label, + description: labelProps.description, + magic: labelProps.magic, + language: labelProps.language, + category: labelProps.category, + } + } + + /** + * Gets filtered connections from DataZone, excluding IAM connections and processing SPARK connections + */ + private async getFilteredConnections(forceRefresh: boolean = false): Promise { + if (this.cachedConnections.length > 0 && !forceRefresh) { + return this.cachedConnections + } + + try { + const resourceMetadata = getResourceMetadata() + + if (!resourceMetadata?.AdditionalMetadata?.DataZoneDomainId) { + throw new Error('DataZone domain ID not found in resource metadata') + } + + if (!resourceMetadata?.AdditionalMetadata?.DataZoneProjectId) { + throw new Error('DataZone project ID not found in resource metadata') + } + + const connections = await getDataZoneClient().listConnections( + resourceMetadata.AdditionalMetadata.DataZoneDomainId, + resourceMetadata.AdditionalMetadata.DataZoneProjectId + ) + + const processedConnections: SageMakerConnectionSummary[] = [] + + for (const connection of connections) { + if ( + connection.type === Constants.CONNECTION_TYPE_REDSHIFT || + connection.type === Constants.CONNECTION_TYPE_ATHENA + ) { + processedConnections.push({ + name: connection.name || '', + type: connection.type || '', + }) + } else if (connection.type === Constants.CONNECTION_TYPE_SPARK) { + if ('sparkGlueProperties' in (connection.props || {})) { + processedConnections.push({ + name: connection.name || '', + type: Constants.CONNECTION_TYPE_GLUE, + }) + } else if ( + 'sparkEmrProperties' in (connection.props || {}) && + 'computeArn' in (connection.props?.sparkEmrProperties || {}) + ) { + const computeArn = connection.props?.sparkEmrProperties?.computeArn || '' + + if (computeArn.includes('cluster')) { + processedConnections.push({ + name: connection.name || '', + type: Constants.CONNECTION_TYPE_EMR_EC2, + }) + } else if (computeArn.includes('applications')) { + processedConnections.push({ + name: connection.name || '', + type: Constants.CONNECTION_TYPE_EMR_SERVERLESS, + }) + } + } + } + } + + this.cachedConnections = processedConnections + return processedConnections + } catch (error) { + getLogger().error('Failed to list DataZone connections: %s', error as Error) + return [] + } + } + + /** + * Adds custom Local Python option to the options list + */ + private addLocalPythonOption(options: ConnectionOption[], addedLabels: Set): void { + const localPythonOption = this.getConnectionOptionForLabel('Local Python') + if (localPythonOption) { + options.push(localPythonOption) + addedLabels.add('Local Python') + } + } + + /** + * Gets the available connection options, either from DataZone connections or defaults + * @returns Array of connection options + */ + public async getConnectionOptions(): Promise { + try { + const connections = await this.getFilteredConnections() + + if (connections.length === 0) { + return [] + } + + const options: ConnectionOption[] = [] + const addedLabels = new Set() + + this.addLocalPythonOption(options, addedLabels) + + for (const connection of connections) { + const typeProps = connectionTypePropertiesMap[connection.type] + if (typeProps) { + for (const label of typeProps.labels) { + if (!addedLabels.has(label)) { + const connectionOption = this.getConnectionOptionForLabel(label) + if (connectionOption) { + options.push(connectionOption) + addedLabels.add(label) + } + } + } + } + } + + if (addedLabels.has(Constants.PYSPARK) && !addedLabels.has(Constants.SCALA_SPARK)) { + const scalaSparkOption = this.getConnectionOptionForLabel(Constants.SCALA_SPARK) + if (scalaSparkOption) { + options.push(scalaSparkOption) + } + } + + return options + } catch (error) { + getLogger().error('Failed to get connection options: %s', error as Error) + return [] + } + } + + /** + * Gets the project options for a specific connection type + * @param connectionType The connection type + * @returns Project options for the connection type + */ + public async getProjectOptionsForConnectionType(connectionType: string): Promise { + try { + const connections = await this.getFilteredConnections() + + if (connections.length === 0) { + return [] + } + + const effectiveConnectionType = connectionType === 'ScalaSpark' ? 'PySpark' : connectionType + const filteredConnections: Record = {} + + for (const connection of connections) { + const typeProps = connectionTypePropertiesMap[connection.type] + + if (typeProps && typeProps.labels.includes(effectiveConnectionType)) { + const compute = connectionTypeToComputeNameMap[connection.type] || 'Unknown' + + if (!filteredConnections[compute]) { + filteredConnections[compute] = [] + } + filteredConnections[compute].push(connection.name) + } + } + + const projectOptions: ProjectOptionGroup[] = [] + for (const [compute, projects] of Object.entries(filteredConnections)) { + projectOptions.push({ connection: compute, projects }) + } + + return projectOptions + } catch (error) { + getLogger().error('Failed to get project options: %s', error as Error) + return [] + } + } + + /** + * Updates the connection and project options from DataZone + */ + public async updateConnectionAndProjectOptions(): Promise { + try { + this.connectionOptions = await this.getConnectionOptions() + + if (this.connectionOptions.length === 0) { + this.projectOptions = [] + return + } + + const newProjectOptions: ConnectionProjectMapping[] = [] + + newProjectOptions.push({ + connection: 'Local Python', + projectOptions: [{ connection: 'Local', projects: ['project.python'] }], + }) + + for (const option of this.connectionOptions) { + if (option.label !== 'Local Python') { + const projectOpts = await this.getProjectOptionsForConnectionType(option.label) + if (projectOpts.length > 0) { + newProjectOptions.push({ + connection: option.label, + projectOptions: projectOpts, + }) + } + } + } + + this.projectOptions = newProjectOptions + } catch (error) { + getLogger().error('Failed to update connection and project options: %s', error as Error) + this.connectionOptions = [] + this.projectOptions = [] + } + } + + /** + * Gets the current cached connection options + */ + public getConnectionOptionsSync(): ConnectionOption[] { + return this.connectionOptions + } + + /** + * Gets the current cached project options + */ + public getProjectOptionsSync(): ConnectionProjectMapping[] { + return this.projectOptions + } +} + +export const connectionOptionsService = new ConnectionOptionsService() diff --git a/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/services/notebookStateManager.ts b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/services/notebookStateManager.ts new file mode 100644 index 00000000000..80654b64ac0 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/connectionMagicsSelector/services/notebookStateManager.ts @@ -0,0 +1,420 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { CellState, ProjectOption } from '../models/types' +import { connectionOptionsService } from './connectionOptionsService' +import { getLogger } from '../../../shared/logger/logger' +import { magicCommandToConnectionMap, defaultProjectsByConnection, Constants } from '../models/constants' + +/** + * State manager for tracking notebook cell states and selections + */ +class NotebookStateManager { + private cellStates: Map = new Map() + + constructor() {} + + /** + * Gets the cell state for a specific cell + */ + private getCellState(cell: vscode.NotebookCell): CellState { + const cellId = cell.document.uri.toString() + if (!this.cellStates.has(cellId)) { + this.cellStates.set(cellId, {}) + } + return this.cellStates.get(cellId)! + } + + /** + * Sets metadata on a cell + */ + private async setCellMetadata(cell: vscode.NotebookCell, key: string, value: any): Promise { + try { + const edit = new vscode.WorkspaceEdit() + const notebookEdit = vscode.NotebookEdit.updateCellMetadata(cell.index, { + ...cell.metadata, + [key]: value, + }) + edit.set(cell.notebook.uri, [notebookEdit]) + await vscode.workspace.applyEdit(edit) + } catch (error) { + getLogger().warn('setCellMetadata: Failed to set metadata, falling back to in-memory storage') + } + } + + /** + * Gets the selected connection for a cell + */ + public getSelectedConnection(cell: vscode.NotebookCell): string | undefined { + const connection = cell.metadata?.[Constants.SAGEMAKER_CONNECTION_METADATA_KEY] as string + if (connection) { + return connection + } + + const state = this.getCellState(cell) + const currentCellContent = cell.document.getText() + + if (!state.connection || (!state.isUserSelection && state.lastParsedContent !== currentCellContent)) { + this.parseCellMagic(cell) + const updatedState = this.getCellState(cell) + updatedState.lastParsedContent = currentCellContent + + return updatedState.connection + } + + return state.connection + } + + /** + * Sets the selected connection for a cell + */ + public setSelectedConnection( + cell: vscode.NotebookCell, + value: string | undefined, + isUserSelection: boolean = false + ): void { + const state = this.getCellState(cell) + const previousConnection = state.connection + state.connection = value + + if (isUserSelection) { + state.isUserSelection = true + + if (value) { + void this.setCellMetadata(cell, Constants.SAGEMAKER_CONNECTION_METADATA_KEY, value) + } + } + + if (value === Constants.LOCAL_PYTHON || value === undefined) { + if (value === Constants.LOCAL_PYTHON && previousConnection !== value) { + state.project = undefined + this.setDefaultProjectForConnection(cell, Constants.LOCAL_PYTHON) + } else if (value === Constants.LOCAL_PYTHON && previousConnection === value) { + if (!state.project) { + this.setDefaultProjectForConnection(cell, Constants.LOCAL_PYTHON) + } + } else { + state.project = undefined + } + } else if (previousConnection !== value) { + state.project = undefined + this.setDefaultProjectForConnection(cell, value) + } + } + + /** + * Gets the selected project for a cell + */ + public getSelectedProject(cell: vscode.NotebookCell): string | undefined { + return this.getCellState(cell).project + } + + /** + * Sets the selected project for a cell + */ + public setSelectedProject(cell: vscode.NotebookCell, value: string | undefined): void { + const state = this.getCellState(cell) + state.project = value + } + + /** + * Gets the magic command for a cell using simplified format for UI operations + */ + public getMagicCommand(cell: vscode.NotebookCell): string | undefined { + const connection = this.getSelectedConnection(cell) + if (!connection) { + return + } + + if (connection === Constants.LOCAL_PYTHON) { + const state = this.getCellState(cell) + const hasLocalMagic = state.originalMagicCommand?.startsWith(Constants.LOCAL_MAGIC) + + if (!hasLocalMagic) { + return undefined + } + } + + const connectionOptions = connectionOptionsService.getConnectionOptionsSync() + + const connectionOption = connectionOptions.find((option) => option.label === connection) + if (!connectionOption) { + return undefined + } + + const project = this.getSelectedProject(cell) + + if (!project) { + return connectionOption.magic + } + + return `${connectionOption.magic} ${project}` + } + + /** + * Parses a cell's content to detect magic commands and updates the state manager + * @param cell The notebook cell to parse + */ + public parseCellMagic(cell: vscode.NotebookCell): void { + if ( + !cell || + cell.kind !== vscode.NotebookCellKind.Code || + cell.document.languageId === Constants.MARKDOWN_LANGUAGE + ) { + return + } + + const state = this.getCellState(cell) + if (state.isUserSelection) { + return + } + + const cellText = cell.document.getText() + const lines = cellText.split('\n') + + const firstLine = lines[0].trim() + if (!firstLine.startsWith(Constants.MAGIC_PREFIX)) { + this.setSelectedConnection(cell, Constants.LOCAL_PYTHON) + return + } + + const parsed = this.parseMagicCommandLine(firstLine) + if (!parsed) { + return + } + + const connectionType = magicCommandToConnectionMap[parsed.magic] + if (!connectionType) { + this.setSelectedConnection(cell, Constants.LOCAL_PYTHON) + this.setDefaultProjectForConnection(cell, Constants.LOCAL_PYTHON) + return + } + + const cellState = this.getCellState(cell) + cellState.originalMagicCommand = firstLine + + this.setSelectedConnection(cell, connectionType) + + if (parsed.project) { + this.setSelectedProject(cell, parsed.project) + } else { + this.setDefaultProjectForConnection(cell, connectionType) + } + } + + /** + * Parses a magic command line to extract magic and project parameters + * Supports formats: %%magic, %%magic project, %%magic --name project, %%magic -n project + */ + private parseMagicCommandLine(line: string): { magic: string; project?: string } | undefined { + const tokens = line.split(/\s+/) + if (tokens.length === 0 || !tokens[0].startsWith(Constants.MAGIC_PREFIX)) { + return undefined + } + + const magic = tokens[0] + let project: string | undefined + + if (tokens.length === 2) { + // Format: %%magic project + project = tokens[1] + } else if (tokens.length >= 3) { + // Format: %%magic --name project or %%magic -n project + const flagIndex = tokens.findIndex( + (token) => token === Constants.NAME_FLAG_LONG || token === Constants.NAME_FLAG_SHORT + ) + if (flagIndex !== -1 && flagIndex + 1 < tokens.length) { + project = tokens[flagIndex + 1] + } + } + + return { magic, project } + } + + /** + * Sets default project for a connection when no explicit project is specified + */ + private setDefaultProjectForConnection(cell: vscode.NotebookCell, connectionType: string): void { + const projectOptions = connectionOptionsService.getProjectOptionsSync() + + const mapping = projectOptions.find((option) => option.connection === connectionType) + if (!mapping || mapping.projectOptions.length === 0) { + return + } + + const defaultProjects = defaultProjectsByConnection[connectionType] || [] + + for (const defaultProject of defaultProjects) { + for (const projectOption of mapping.projectOptions) { + if (projectOption.projects.includes(defaultProject)) { + this.setSelectedProject(cell, defaultProject) + return + } + } + } + + const firstProjectOption = mapping.projectOptions[0] + if (firstProjectOption.projects.length > 0) { + this.setSelectedProject(cell, firstProjectOption.projects[0]) + } + } + + /** + * Updates the current cell with the magic command and sets the cell language + * @param cell The notebook cell to update + */ + public async updateCellWithMagic(cell: vscode.NotebookCell): Promise { + const connection = this.getSelectedConnection(cell) + if (!connection) { + return + } + + const connectionOptions = connectionOptionsService.getConnectionOptionsSync() + const connectionOption = connectionOptions.find((option) => option.label === connection) + if (!connectionOption) { + return + } + + try { + await vscode.languages.setTextDocumentLanguage(cell.document, connectionOption.language) + + const cellText = cell.document.getText() + const lines = cellText.split('\n') + const firstLine = lines[0] || '' + const isMagicCommand = firstLine.trim().startsWith(Constants.MAGIC_PREFIX) + + let newCellContent = cellText + + if (connection === Constants.LOCAL_PYTHON) { + const state = this.getCellState(cell) + const hasLocalMagic = state.originalMagicCommand?.startsWith(Constants.LOCAL_MAGIC) + + if (hasLocalMagic) { + const magicCommand = this.getMagicCommand(cell) + if (magicCommand) { + if (isMagicCommand) { + newCellContent = magicCommand + '\n' + lines.slice(1).join('\n') + } else { + newCellContent = magicCommand + '\n' + cellText + } + } + } else { + if (isMagicCommand) { + newCellContent = lines.slice(1).join('\n') + } + } + } else { + const magicCommand = this.getMagicCommand(cell) + + if (magicCommand) { + if (!magicCommand.startsWith(Constants.MAGIC_PREFIX)) { + return + } + + if (isMagicCommand) { + newCellContent = magicCommand + '\n' + lines.slice(1).join('\n') + } else { + newCellContent = magicCommand + '\n' + cellText + } + } + } + + if (newCellContent !== cellText) { + await this.updateCellContent(cell, newCellContent) + } + } catch (error) { + getLogger().error(`Error updating cell with magic command: ${error}`) + } + } + + /** + * Updates the content of a notebook cell using the most appropriate API for the environment + * @param cell The notebook cell to update + * @param newContent The new content for the cell + */ + private async updateCellContent(cell: vscode.NotebookCell, newContent: string): Promise { + try { + if (vscode.workspace.applyEdit && (vscode as any).NotebookEdit) { + const edit = new vscode.WorkspaceEdit() + const notebookUri = cell.notebook.uri + const cellIndex = cell.index + + const newCellData = new vscode.NotebookCellData(cell.kind, newContent, cell.document.languageId) + + const notebookEdit = (vscode as any).NotebookEdit.replaceCells( + new vscode.NotebookRange(cellIndex, cellIndex + 1), + [newCellData] + ) + edit.set(notebookUri, [notebookEdit]) + + const success = await vscode.workspace.applyEdit(edit) + if (success) { + return + } + } + } catch (error) { + getLogger().error(`NotebookEdit failed, attempting to update cell content with WorkspaceEdit: ${error}`) + } + + try { + const edit = new vscode.WorkspaceEdit() + + const fullRange = new vscode.Range( + new vscode.Position(0, 0), + new vscode.Position(cell.document.lineCount, 0) + ) + + edit.replace(cell.document.uri, fullRange, newContent) + + const success = await vscode.workspace.applyEdit(edit) + if (!success) { + getLogger().error('WorkspaceEdit failed to apply') + } + } catch (error) { + getLogger().error(`Failed to update cell content with WorkspaceEdit: ${error}`) + + try { + const document = cell.document + if (document && 'getText' in document && 'uri' in document) { + const edit = new vscode.WorkspaceEdit() + const fullText = document.getText() + const fullRange = new vscode.Range(document.positionAt(0), document.positionAt(fullText.length)) + edit.replace(document.uri, fullRange, newContent) + await vscode.workspace.applyEdit(edit) + } + } catch (finalError) { + getLogger().error(`All cell update methods failed: ${finalError}`) + } + } + } + + /** + * Gets the project options for the selected connection in a cell + */ + public getProjectOptionsForConnection(cell: vscode.NotebookCell): ProjectOption[] { + const connection = this.getSelectedConnection(cell) + if (!connection) { + return [] + } + + const projectOptions = connectionOptionsService.getProjectOptionsSync() + const mapping = projectOptions.find((option) => option.connection === connection) + if (!mapping) { + return [] + } + + const options: ProjectOption[] = [] + for (const projectOption of mapping.projectOptions) { + for (const project of projectOption.projects) { + options.push({ connection: projectOption.connection, project: project }) + } + } + + return options + } +} + +export const notebookStateManager = new NotebookStateManager() diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/activation.ts b/packages/core/src/sagemakerunifiedstudio/explorer/activation.ts new file mode 100644 index 00000000000..8a686b48654 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/activation.ts @@ -0,0 +1,142 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { ResourceTreeDataProvider } from '../../shared/treeview/resourceTreeDataProvider' +import { + smusLoginCommand, + smusLearnMoreCommand, + smusSignOutCommand, + SageMakerUnifiedStudioRootNode, + selectSMUSProject, +} from './nodes/sageMakerUnifiedStudioRootNode' +import { DataZoneClient } from '../shared/client/datazoneClient' +import { openRemoteConnect, stopSpace } from '../../awsService/sagemaker/commands' +import { SagemakerUnifiedStudioSpaceNode } from './nodes/sageMakerUnifiedStudioSpaceNode' +import { SageMakerUnifiedStudioProjectNode } from './nodes/sageMakerUnifiedStudioProjectNode' +import { getLogger } from '../../shared/logger/logger' +import { setSmusConnectedContext, SmusAuthenticationProvider } from '../auth/providers/smusAuthenticationProvider' +import { setupUserActivityMonitoring } from '../../awsService/sagemaker/sagemakerSpace' +import { telemetry } from '../../shared/telemetry/telemetry' +import { isSageMaker } from '../../shared/extensionUtilities' +import { recordSpaceTelemetry } from '../shared/telemetry' + +export async function activate(extensionContext: vscode.ExtensionContext): Promise { + // Initialize the SMUS authentication provider + const logger = getLogger() + logger.debug('SMUS: Initializing authentication provider') + // Create the auth provider instance (this will trigger restore() in the constructor) + const smusAuthProvider = SmusAuthenticationProvider.fromContext() + await smusAuthProvider.restore() + // Set initial auth context after restore + void setSmusConnectedContext(smusAuthProvider.isConnected()) + logger.debug('SMUS: Authentication provider initialized') + + // Create the SMUS projects tree view + const smusRootNode = new SageMakerUnifiedStudioRootNode(smusAuthProvider, extensionContext) + const treeDataProvider = new ResourceTreeDataProvider({ getChildren: () => smusRootNode.getChildren() }) + + // Register the tree view + const treeView = vscode.window.createTreeView('aws.smus.rootView', { treeDataProvider }) + treeDataProvider.refresh() + + // Register the commands + extensionContext.subscriptions.push( + smusLoginCommand.register(), + smusLearnMoreCommand.register(), + smusSignOutCommand.register(), + treeView, + vscode.commands.registerCommand('aws.smus.rootView.refresh', () => { + treeDataProvider.refresh() + }), + + vscode.commands.registerCommand( + 'aws.smus.projectView', + async (projectNode?: SageMakerUnifiedStudioProjectNode) => { + return await selectSMUSProject(projectNode) + } + ), + + vscode.commands.registerCommand('aws.smus.refreshProject', async () => { + const projectNode = smusRootNode.getProjectSelectNode() + await projectNode.refreshNode() + }), + + vscode.commands.registerCommand('aws.smus.switchProject', async () => { + // Get the project node from the root node to ensure we're using the same instance + const projectNode = smusRootNode.getProjectSelectNode() + return await selectSMUSProject(projectNode) + }), + + vscode.commands.registerCommand('aws.smus.stopSpace', async (node: SagemakerUnifiedStudioSpaceNode) => { + if (!validateNode(node)) { + return + } + await telemetry.smus_stopSpace.run(async (span) => { + await recordSpaceTelemetry(span, node) + await stopSpace(node.resource, extensionContext, node.resource.sageMakerClient) + }) + }), + + vscode.commands.registerCommand( + 'aws.smus.openRemoteConnection', + async (node: SagemakerUnifiedStudioSpaceNode) => { + if (!validateNode(node)) { + return + } + await telemetry.smus_openRemoteConnection.run(async (span) => { + await recordSpaceTelemetry(span, node) + await openRemoteConnect(node.resource, extensionContext, node.resource.sageMakerClient) + }) + } + ), + + vscode.commands.registerCommand('aws.smus.reauthenticate', async (connection?: any) => { + if (connection) { + try { + await smusAuthProvider.reauthenticate(connection) + // Refresh the tree view after successful reauthentication + treeDataProvider.refresh() + // Show success message + void vscode.window.showInformationMessage( + 'Successfully reauthenticated with SageMaker Unified Studio' + ) + } catch (error) { + // Show error message if reauthentication fails + void vscode.window.showErrorMessage(`Failed to reauthenticate: ${error}`) + logger.error('SMUS: Reauthentication failed: %O', error) + } + } + }), + // Dispose DataZoneClient when extension is deactivated + { dispose: () => DataZoneClient.dispose() }, + // Dispose SMUS auth provider when extension is deactivated + { dispose: () => smusAuthProvider.dispose() } + ) + + // Track user activity for autoshutdown feature when in SageMaker Unified Studio environment + if (isSageMaker('SMUS-SPACE-REMOTE-ACCESS')) { + logger.info('SageMaker Unified Studio environment detected, setting up user activity monitoring') + try { + await setupUserActivityMonitoring(extensionContext) + } catch (error) { + logger.error(`Error in UserActivityMonitoring: ${error}`) + throw error + } + } else { + logger.info('Not in SageMaker Unified Studio remote environment, skipping user activity monitoring') + } +} + +/** + * Checks if a node is undefined and shows a warning message if so. + */ +function validateNode(node: SagemakerUnifiedStudioSpaceNode): boolean { + if (!node) { + void vscode.window.showWarningMessage('Space information is being refreshed. Please try again shortly.') + return false + } + return true +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/lakehouseStrategy.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/lakehouseStrategy.ts new file mode 100644 index 00000000000..546a73135c6 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/lakehouseStrategy.ts @@ -0,0 +1,587 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { getLogger } from '../../../shared/logger/logger' +import { DataZoneConnection } from '../../shared/client/datazoneClient' +import { GlueCatalog, GlueCatalogClient } from '../../shared/client/glueCatalogClient' +import { GlueClient } from '../../shared/client/glueClient' +import { ConnectionClientStore } from '../../shared/client/connectionClientStore' +import { + NODE_ID_DELIMITER, + NodeType, + NodeData, + DATA_DEFAULT_LAKEHOUSE_CONNECTION_NAME_REGEXP, + DATA_DEFAULT_ATHENA_CONNECTION_NAME_REGEXP, + DATA_DEFAULT_IAM_CONNECTION_NAME_REGEXP, + AWS_DATA_CATALOG, + DatabaseObjects, + NO_DATA_FOUND_MESSAGE, +} from './types' +import { + getLabel, + isLeafNode, + getIconForNodeType, + getTooltip, + createColumnTreeItem, + getColumnType, + createErrorItem, +} from './utils' +import { createPlaceholderItem } from '../../../shared/treeview/utils' +import { Column, Database, Table } from '@aws-sdk/client-glue' +import { ConnectionCredentialsProvider } from '../../auth/providers/connectionCredentialsProvider' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { recordDataConnectionTelemetry } from '../../shared/telemetry' + +/** + * Lakehouse data node for SageMaker Unified Studio + */ +export class LakehouseNode implements TreeNode { + private childrenNodes: TreeNode[] | undefined + private isLoading = false + private readonly logger = getLogger() + + constructor( + public readonly data: NodeData, + private readonly childrenProvider?: (node: LakehouseNode) => Promise + ) {} + + public get id(): string { + return this.data.id + } + + public get resource(): any { + return this.data.value || {} + } + + public async getChildren(): Promise { + // Return cached children if available + if (this.childrenNodes && !this.isLoading) { + return this.childrenNodes + } + + // Return empty array for leaf nodes + if (isLeafNode(this.data)) { + return [] + } + + // If we have a children provider, use it + if (this.childrenProvider) { + try { + this.isLoading = true + const childrenNodes = await this.childrenProvider(this) + this.childrenNodes = childrenNodes + this.isLoading = false + return this.childrenNodes + } catch (err) { + this.isLoading = false + this.logger.error(`Failed to get children for node ${this.data.id}: ${(err as Error).message}`) + + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'getChildren', this.id) as LakehouseNode] + } + } + + return [] + } + + public async getTreeItem(): Promise { + const label = getLabel(this.data) + const isLeaf = isLeafNode(this.data) + + // For column nodes, show type as secondary text + if (this.data.nodeType === NodeType.REDSHIFT_COLUMN && this.data.value?.type) { + return createColumnTreeItem(label, this.data.value.type, this.data.nodeType) + } + + const collapsibleState = isLeaf + ? vscode.TreeItemCollapsibleState.None + : vscode.TreeItemCollapsibleState.Collapsed + + const item = new vscode.TreeItem(label, collapsibleState) + + // Set icon based on node type + item.iconPath = getIconForNodeType(this.data.nodeType, this.data.isContainer) + + // Set context value for command enablement + item.contextValue = this.data.nodeType + + // Set tooltip + item.tooltip = getTooltip(this.data) + + return item + } + + public getParent(): TreeNode | undefined { + return this.data.parent + } +} + +/** + * Creates a Lakehouse connection node + */ +export function createLakehouseConnectionNode( + connection: DataZoneConnection, + connectionCredentialsProvider: ConnectionCredentialsProvider, + region: string +): LakehouseNode { + const logger = getLogger() + + // Create Glue clients + const clientStore = ConnectionClientStore.getInstance() + const glueCatalogClient = clientStore.getGlueCatalogClient( + connection.connectionId, + region, + connectionCredentialsProvider + ) + const glueClient = clientStore.getGlueClient(connection.connectionId, region, connectionCredentialsProvider) + + // Create the connection node + return new LakehouseNode( + { + id: connection.connectionId, + nodeType: NodeType.CONNECTION, + value: { connection }, + path: { + connection: connection.name, + }, + }, + async (node) => { + return telemetry.smus_renderLakehouseNode.run(async (span) => { + await recordDataConnectionTelemetry(span, connection, connectionCredentialsProvider) + try { + logger.info(`Loading Lakehouse catalogs for connection ${connection.name}`) + + // Check if this is a default connection + const isDefaultConnection = + DATA_DEFAULT_IAM_CONNECTION_NAME_REGEXP.test(connection.name) || + DATA_DEFAULT_LAKEHOUSE_CONNECTION_NAME_REGEXP.test(connection.name) || + DATA_DEFAULT_ATHENA_CONNECTION_NAME_REGEXP.test(connection.name) + + // Follow the reference pattern with Promise.allSettled + const [awsDataCatalogResult, catalogsResult] = await Promise.allSettled([ + // AWS Data Catalog node (only for default connections) + isDefaultConnection + ? Promise.resolve([createAwsDataCatalogNode(node, glueClient)]) + : Promise.resolve([]), + // Get catalogs by calling Glue API + getCatalogs(glueCatalogClient, glueClient, node), + ]) + + const awsDataCatalog = awsDataCatalogResult.status === 'fulfilled' ? awsDataCatalogResult.value : [] + const apiCatalogs = catalogsResult.status === 'fulfilled' ? catalogsResult.value : [] + const errors: LakehouseNode[] = [] + + if (awsDataCatalogResult.status === 'rejected') { + const errorMessage = (awsDataCatalogResult.reason as Error).message + void vscode.window.showErrorMessage(errorMessage) + errors.push(createErrorItem(errorMessage, 'aws-data-catalog', node.id) as LakehouseNode) + } + + if (catalogsResult.status === 'rejected') { + const errorMessage = (catalogsResult.reason as Error).message + void vscode.window.showErrorMessage(errorMessage) + errors.push(createErrorItem(errorMessage, 'catalogs', node.id) as LakehouseNode) + } + + const allNodes = [...awsDataCatalog, ...apiCatalogs, ...errors] + return allNodes.length > 0 + ? allNodes + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as LakehouseNode] + } catch (err) { + logger.error(`Failed to get Lakehouse catalogs: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'lakehouse-catalogs', node.id) as LakehouseNode] + } + }) + } + ) +} + +/** + * Creates AWS Data Catalog node for default connections + */ +function createAwsDataCatalogNode(parent: LakehouseNode, glueClient: GlueClient): LakehouseNode { + return new LakehouseNode( + { + id: `${parent.id}${NODE_ID_DELIMITER}${AWS_DATA_CATALOG}`, + nodeType: NodeType.GLUE_CATALOG, + value: { + catalog: { name: AWS_DATA_CATALOG, type: 'AWS' }, + catalogName: AWS_DATA_CATALOG, + }, + path: { + ...parent.data.path, + catalog: AWS_DATA_CATALOG, + }, + parent, + }, + async (node) => { + const allDatabases = [] + let nextToken: string | undefined + + do { + const { databases, nextToken: token } = await glueClient.getDatabases( + undefined, + 'ALL', + ['NAME'], + nextToken + ) + allDatabases.push(...databases) + nextToken = token + } while (nextToken) + + return allDatabases.length > 0 + ? allDatabases.map((database) => createDatabaseNode(database.Name || '', database, glueClient, node)) + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as LakehouseNode] + } + ) +} + +export interface CatalogTree { + parent: GlueCatalog + children?: GlueCatalog[] +} + +/** + * Builds catalog tree from flat catalog list + * + * AWS Glue catalogs can have parent-child relationships, but the API returns them as a flat list. + * This function reconstructs the hierarchical tree structure needed for proper UI display. + * + * Two-pass algorithm is required because: + * 1. First pass: Create a lookup map of all catalogs by name for O(1) access during relationship building + * 2. Second pass: Build parent-child relationships by linking catalogs that reference ParentCatalogNames + * + * Without the first pass, we'd need O(n²) time to find parent catalogs for each child catalog. + */ +function buildCatalogTree(catalogs: GlueCatalog[]): CatalogTree[] { + const catalogMap: Record = {} + const rootCatalogs: CatalogTree[] = [] + + // First pass: create a map of all catalogs with their metadata + // This allows us to quickly look up any catalog by name when building parent-child relationships in the second pass + for (const catalog of catalogs) { + if (catalog.Name) { + catalogMap[catalog.Name] = { parent: catalog, children: [] } + } + } + + // Second pass: build the hierarchical tree structure by linking children to their parents + // Catalogs with ParentCatalogNames become children, others become root-level catalogs + for (const catalog of catalogs) { + if (catalog.Name) { + if (catalog.ParentCatalogNames && catalog.ParentCatalogNames.length > 0) { + const parentName = catalog.ParentCatalogNames[0] + const parent = catalogMap[parentName] + if (parent) { + if (!parent.children) { + parent.children = [] + } + parent.children.push(catalog) + } + } else { + rootCatalogs.push(catalogMap[catalog.Name]) + } + } + } + rootCatalogs.sort((a, b) => { + const timeA = new Date(a.parent.CreateTime ?? 0).getTime() + const timeB = new Date(b.parent.CreateTime ?? 0).getTime() + return timeA - timeB // For oldest first + }) + + return rootCatalogs +} + +/** + * Gets catalogs from the GlueCatalogClient + */ +async function getCatalogs( + glueCatalogClient: GlueCatalogClient, + glueClient: GlueClient, + parent: LakehouseNode +): Promise { + const allCatalogs = [] + let nextToken: string | undefined + + do { + const { catalogs, nextToken: token } = await glueCatalogClient.getCatalogs(nextToken) + allCatalogs.push(...catalogs) + nextToken = token + } while (nextToken) + + const catalogs = allCatalogs + const tree = buildCatalogTree(catalogs) + + return tree.map((catalog) => { + const parentCatalog = catalog.parent + + // If parent catalog has children, create node that shows child catalogs + if (catalog.children && catalog.children.length > 0) { + return new LakehouseNode( + { + id: parentCatalog.Name || parentCatalog.CatalogId || '', + nodeType: NodeType.GLUE_CATALOG, + value: { + catalog: parentCatalog, + catalogName: parentCatalog.Name || '', + }, + path: { + ...parent.data.path, + catalog: parentCatalog.CatalogId || '', + }, + parent, + }, + async (node: LakehouseNode) => { + // Parent catalogs only show child catalogs + const childCatalogs = + catalog.children?.map((childCatalog) => + createCatalogNode(childCatalog.CatalogId || '', childCatalog, glueClient, node, false) + ) || [] + return childCatalogs + } + ) + } + + // For catalogs without children, create regular catalog node + return createCatalogNode(parentCatalog.CatalogId || '', parentCatalog, glueClient, parent, false) + }) +} + +/** + * Creates a catalog node + */ +function createCatalogNode( + catalogId: string, + catalog: GlueCatalog, + glueClient: GlueClient, + parent: LakehouseNode, + isParent: boolean = false +): LakehouseNode { + const logger = getLogger() + + return new LakehouseNode( + { + id: catalog.Name || catalogId, + nodeType: NodeType.GLUE_CATALOG, + value: { + catalog, + catalogName: catalog.Name || catalogId, + }, + path: { + ...parent.data.path, + catalog: catalogId, + }, + parent, + }, + // Child catalogs load databases, parent catalogs will have their children provider overridden + isParent + ? async () => [] // Placeholder, will be overridden for parent catalogs with children + : async (node) => { + try { + logger.info(`Loading databases for catalog ${catalogId}`) + + const allDatabases = [] + let nextToken: string | undefined + + do { + const { databases, nextToken: token } = await glueClient.getDatabases( + catalogId, + undefined, + ['NAME'], + nextToken + ) + allDatabases.push(...databases) + nextToken = token + } while (nextToken) + + return allDatabases.length > 0 + ? allDatabases.map((database) => + createDatabaseNode(database.Name || '', database, glueClient, node) + ) + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as LakehouseNode] + } catch (err) { + logger.error(`Failed to get databases for catalog ${catalogId}: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'catalog-databases', node.id) as LakehouseNode] + } + } + ) +} + +/** + * Creates a database node + */ +function createDatabaseNode( + databaseName: string, + database: Database, + glueClient: GlueClient, + parent: LakehouseNode +): LakehouseNode { + const logger = getLogger() + + return new LakehouseNode( + { + id: databaseName, + nodeType: NodeType.GLUE_DATABASE, + value: { + database, + databaseName, + }, + path: { + ...parent.data.path, + database: databaseName, + }, + parent, + }, + async (node) => { + try { + logger.info(`Loading tables for database ${databaseName}`) + + const allTables = [] + let nextToken: string | undefined + const catalogId = parent.data.path?.catalog === AWS_DATA_CATALOG ? undefined : parent.data.path?.catalog + + do { + const { tables, nextToken: token } = await glueClient.getTables( + databaseName, + catalogId, + ['NAME', 'TABLE_TYPE'], + nextToken + ) + allTables.push(...tables) + nextToken = token + } while (nextToken) + + // Group tables and views separately + const tables = allTables.filter((table) => table.TableType !== DatabaseObjects.VIRTUAL_VIEW) + const views = allTables.filter((table) => table.TableType === DatabaseObjects.VIRTUAL_VIEW) + + const containerNodes: LakehouseNode[] = [] + + // Create tables container if there are tables + if (tables.length > 0) { + containerNodes.push(createContainerNode(NodeType.GLUE_TABLE, tables, glueClient, node)) + } + + // Create views container if there are views + if (views.length > 0) { + containerNodes.push(createContainerNode(NodeType.GLUE_VIEW, views, glueClient, node)) + } + + return containerNodes.length > 0 + ? containerNodes + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as LakehouseNode] + } catch (err) { + logger.error(`Failed to get tables for database ${databaseName}: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'database-tables', node.id) as LakehouseNode] + } + } + ) +} + +/** + * Creates a table node + */ +function createTableNode( + tableName: string, + table: Table, + glueClient: GlueClient, + parent: LakehouseNode +): LakehouseNode { + const logger = getLogger() + + return new LakehouseNode( + { + id: tableName, + nodeType: NodeType.GLUE_TABLE, + value: { + table, + tableName, + }, + path: { + ...parent.data.path, + table: tableName, + }, + parent, + }, + async (node) => { + try { + logger.info(`Loading columns for table ${tableName}`) + + const databaseName = node.data.path?.database || '' + const catalogId = node.data.path?.catalog === AWS_DATA_CATALOG ? undefined : node.data.path?.catalog + const tableDetails = await glueClient.getTable(databaseName, tableName, catalogId) + const columns = tableDetails?.StorageDescriptor?.Columns || [] + const partitions = tableDetails?.PartitionKeys || [] + + const allColumns = [...columns, ...partitions] + return allColumns.length > 0 + ? allColumns.map((column) => createColumnNode(column.Name || '', column, node)) + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as LakehouseNode] + } catch (err) { + logger.error(`Failed to get columns for table ${tableName}: ${(err as Error).message}`) + return [] + } + } + ) +} + +/** + * Creates a column node + */ +function createColumnNode(columnName: string, column: Column, parent: LakehouseNode): LakehouseNode { + const columnType = getColumnType(column?.Type) + + return new LakehouseNode({ + id: `${parent.id}${NODE_ID_DELIMITER}${columnName}`, + nodeType: NodeType.REDSHIFT_COLUMN, + value: { + name: columnName, + type: columnType, + }, + path: { + ...parent.data.path, + column: columnName, + }, + parent, + }) +} + +/** + * Creates a container node for grouping objects by type + */ +function createContainerNode( + nodeType: NodeType, + items: Table[], + glueClient: GlueClient, + parent: LakehouseNode +): LakehouseNode { + return new LakehouseNode( + { + id: `${parent.id}${NODE_ID_DELIMITER}${nodeType}-container`, + nodeType: nodeType, + value: { + items, + }, + path: parent.data.path, + parent, + isContainer: true, + }, + async (node) => { + // Map items to nodes + return items.length > 0 + ? items.map((item) => createTableNode(item.Name || '', item, glueClient, node)) + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as LakehouseNode] + } + ) +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/redshiftStrategy.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/redshiftStrategy.ts new file mode 100644 index 00000000000..af0d7cfbbac --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/redshiftStrategy.ts @@ -0,0 +1,1038 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { getLogger } from '../../../shared/logger/logger' +import { DataZoneConnection } from '../../shared/client/datazoneClient' +import { ConnectionConfig, createRedshiftConnectionConfig } from '../../shared/client/sqlWorkbenchClient' +import { ConnectionClientStore } from '../../shared/client/connectionClientStore' +import { NODE_ID_DELIMITER, NodeType, ResourceType, NodeData, NO_DATA_FOUND_MESSAGE } from './types' +import { + getLabel, + isLeafNode, + getIconForNodeType, + createColumnTreeItem, + isRedLakeDatabase, + getTooltip, + getColumnType, + createErrorItem, +} from './utils' +import { createPlaceholderItem } from '../../../shared/treeview/utils' +import { ConnectionCredentialsProvider } from '../../auth/providers/connectionCredentialsProvider' +import { GlueCatalog } from '../../shared/client/glueCatalogClient' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { recordDataConnectionTelemetry } from '../../shared/telemetry' + +/** + * Redshift data node for SageMaker Unified Studio + */ +export class RedshiftNode implements TreeNode { + private childrenNodes: TreeNode[] | undefined + private isLoading = false + private readonly logger = getLogger() + + constructor( + public readonly data: NodeData, + private readonly childrenProvider?: (node: RedshiftNode) => Promise + ) {} + + public get id(): string { + return this.data.id + } + + public get resource(): any { + return this.data.value || {} + } + + public async getChildren(): Promise { + // Return cached children if available + if (this.childrenNodes && !this.isLoading) { + return this.childrenNodes + } + + // Return empty array for leaf nodes + if (isLeafNode(this.data)) { + return [] + } + + // If we have a children provider, use it + if (this.childrenProvider) { + try { + this.isLoading = true + const childrenNodes = await this.childrenProvider(this) + this.childrenNodes = childrenNodes + this.isLoading = false + return this.childrenNodes + } catch (err) { + this.isLoading = false + this.logger.error(`Failed to get children for node ${this.data.id}: ${(err as Error).message}`) + + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'getChildren', this.id) as RedshiftNode] + } + } + + return [] + } + + public async getTreeItem(): Promise { + const label = getLabel(this.data) + const isLeaf = isLeafNode(this.data) + + // For column nodes, create a TreeItem with label and description (column type) + if (this.data.nodeType === NodeType.REDSHIFT_COLUMN && this.data.value?.type) { + return createColumnTreeItem(label, this.data.value.type, this.data.nodeType) + } + + // For other nodes, use standard TreeItem + const collapsibleState = isLeaf + ? vscode.TreeItemCollapsibleState.None + : vscode.TreeItemCollapsibleState.Collapsed + + const item = new vscode.TreeItem(label, collapsibleState) + + // Set icon based on node type + item.iconPath = getIconForNodeType(this.data.nodeType, this.data.isContainer) + + // Set context value for command enablement + item.contextValue = this.data.nodeType + + // Set tooltip + item.tooltip = getTooltip(this.data) + + return item + } + + public getParent(): TreeNode | undefined { + return this.data.parent + } +} + +/** + * Creates a Redshift connection node + */ +export function createRedshiftConnectionNode( + connection: DataZoneConnection, + connectionCredentialsProvider: ConnectionCredentialsProvider +): RedshiftNode { + const logger = getLogger() + return new RedshiftNode( + { + id: connection.connectionId, + nodeType: NodeType.CONNECTION, + value: { connection, connectionCredentialsProvider }, + path: { + connection: connection.name, + }, + }, + async (node) => { + return telemetry.smus_renderRedshiftNode.run(async (span) => { + logger.info(`Loading Redshift resources for connection ${connection.name}`) + await recordDataConnectionTelemetry(span, connection, connectionCredentialsProvider) + + const connectionParams = extractConnectionParams(connection) + if (!connectionParams) { + return [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } + + const isGlueCatalogDatabase = isRedLakeDatabase(connectionParams.database) + + // Create connection config with all available information + const connectionConfig = await createRedshiftConnectionConfig( + connectionParams.host, + connectionParams.database, + connectionParams.accountId, + connectionParams.region, + connectionParams.secretArn, + isGlueCatalogDatabase + ) + + // Wake up the database with a simple query + await wakeUpDatabase( + connectionConfig, + connectionParams.region, + connectionCredentialsProvider, + connection + ) + + const clientStore = ConnectionClientStore.getInstance() + const sqlClient = clientStore.getSQLWorkbenchClient( + connection.connectionId, + connectionParams.region, + connectionCredentialsProvider + ) + + // Fetch Glue catalogs for filtering purposes only + // This will help determine which catalogs are accessible within the project + let glueCatalogs: GlueCatalog[] = [] + try { + glueCatalogs = await listGlueCatalogs( + connection.connectionId, + connectionParams.region, + connectionCredentialsProvider + ) + } catch (err) { + logger.warn(`Failed to fetch Glue catalogs for filtering: ${(err as Error).message}`) + } + + // Fetch databases and catalogs using getResources + const [databasesResult, catalogsResult] = await Promise.allSettled([ + fetchResources(sqlClient, connectionConfig, ResourceType.DATABASE), + fetchResources(sqlClient, connectionConfig, ResourceType.CATALOG), + ]) + + const databases = databasesResult.status === 'fulfilled' ? databasesResult.value : [] + const catalogs = catalogsResult.status === 'fulfilled' ? catalogsResult.value : [] + const allNodes: RedshiftNode[] = [] + + // Filter databases + const filteredDatabases = databases.filter( + (r: any) => + r.type === ResourceType.DATABASE || + r.type === ResourceType.EXTERNAL_DATABASE || + r.type === ResourceType.SHARED_DATABASE + ) + + // Filter catalogs using listGlueCatalogs results + const filteredCatalogs = catalogs.filter((catalog: any) => { + if (catalog.displayName?.toLowerCase() === 'awsdatacatalog') { + return true // Always include AWS Data Catalog + } + // Filter using Glue catalogs list + return glueCatalogs.some((glueCatalog) => catalog.displayName?.endsWith(glueCatalog.Name ?? '')) + }) + + // Add database nodes + if (filteredDatabases.length === 0) { + if (databasesResult.status === 'rejected') { + const errorMessage = `Failed to fetch databases - ${databasesResult.reason?.message || databasesResult.reason}.` + void vscode.window.showErrorMessage(errorMessage) + allNodes.push(createErrorItem(errorMessage, 'databases', node.id) as RedshiftNode) + } else { + allNodes.push(createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode) + } + } else { + allNodes.push( + ...filteredDatabases.map((db: any) => + createDatabaseNode(db.displayName, connectionConfig, node) + ) + ) + } + + // Add catalog nodes + if (filteredCatalogs.length === 0) { + if (catalogsResult.status === 'rejected') { + const errorMessage = `Failed to fetch catalogs - ${catalogsResult.reason?.message || catalogsResult.reason}` + void vscode.window.showErrorMessage(errorMessage) + allNodes.push(createErrorItem(errorMessage, 'catalogs', node.id) as RedshiftNode) + } else { + allNodes.push(createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode) + } + } else { + allNodes.push( + ...filteredCatalogs.map((catalog: any) => + createCatalogNode( + catalog.displayName || catalog.identifier || '', + catalog, + connectionConfig, + node + ) + ) + ) + } + + return allNodes + }) + } + ) +} + +/** + * Extracts connection parameters from DataZone connection + */ +function extractConnectionParams(connection: DataZoneConnection) { + const redshiftProps = connection.props?.redshiftProperties || {} + const jdbcConnection = connection.props?.jdbcConnection || {} + + let host = jdbcConnection.host + if (!host && jdbcConnection.jdbcUrl) { + // Example: jdbc:redshift://test-cluster.123456789012.us-east-1.redshift.amazonaws.com:5439/dev + // match[0] = entire URL, match[1] = host, match[2] = port, match[3] = database + const match = jdbcConnection.jdbcUrl.match(/jdbc:redshift:\/\/([^:]+):(\d+)\/(.+)/) + if (match) { + host = match[1] + } + } + + const database = jdbcConnection.dbname || redshiftProps.databaseName + const secretArn = jdbcConnection.secretId || redshiftProps.credentials?.secretArn + const accountId = connection.location?.awsAccountId + const region = connection.location?.awsRegion + + if (!host || !database || !accountId || !region) { + return undefined + } + + return { host, database, secretArn, accountId, region } +} + +/** + * Wake up the database with a simple query + */ +async function wakeUpDatabase( + connectionConfig: ConnectionConfig, + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider, + connection: DataZoneConnection +) { + const logger = getLogger() + const clientStore = ConnectionClientStore.getInstance() + const sqlClient = clientStore.getSQLWorkbenchClient(connection.connectionId, region, connectionCredentialsProvider) + try { + await sqlClient.executeQuery(connectionConfig, 'select 1 from sys_query_history limit 1;') + } catch (e) { + logger.debug(`Wake-up query failed: ${(e as Error).message}`) + } +} + +/** + * Creates a database node + */ +function createDatabaseNode( + databaseName: string, + connectionConfig: ConnectionConfig, + parent: RedshiftNode +): RedshiftNode { + const logger = getLogger() + + return new RedshiftNode( + { + id: databaseName, + nodeType: NodeType.REDSHIFT_DATABASE, + value: { + database: databaseName, + connectionConfig, + identifier: databaseName, + type: ResourceType.DATABASE, + childObjectTypes: [ResourceType.SCHEMA, ResourceType.EXTERNAL_SCHEMA, ResourceType.SHARED_SCHEMA], + }, + path: { + ...parent.data.path, + database: databaseName, + }, + parent, + }, + async (node) => { + try { + // Get the original credentials from the root connection node + const rootCredentials = getRootCredentials(parent) + + // Create SQL client with the original credentials + const clientStore = ConnectionClientStore.getInstance() + const sqlClient = clientStore.getSQLWorkbenchClient( + connectionConfig.id, + connectionConfig.id.split(':')[3], // region + rootCredentials + ) + + // Update connection config with the database + const dbConnectionConfig = { + ...connectionConfig, + database: databaseName, + } + + // Get schemas + const allResources = [] + let nextToken: string | undefined + + do { + const response = await sqlClient.getResources({ + connection: dbConnectionConfig, + resourceType: ResourceType.SCHEMA, + includeChildren: true, + maxItems: 100, + parents: [ + { + parentId: databaseName, + parentType: ResourceType.DATABASE, + }, + ], + forceRefresh: true, + pageToken: nextToken, + }) + allResources.push(...(response.resources || [])) + nextToken = response.nextToken + } while (nextToken) + + const schemas = allResources.filter( + (r: any) => + r.type === ResourceType.SCHEMA || + r.type === ResourceType.EXTERNAL_SCHEMA || + r.type === ResourceType.SHARED_SCHEMA + ) + + if (schemas.length === 0) { + return [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } + + // Map schemas to nodes + return schemas.map((schema: any) => createSchemaNode(schema.displayName, dbConnectionConfig, node)) + } catch (err) { + logger.error(`Failed to get schemas: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'schemas', node.id) as RedshiftNode] + } + } + ) +} + +/** + * Creates a schema node + */ +function createSchemaNode(schemaName: string, connectionConfig: ConnectionConfig, parent: RedshiftNode): RedshiftNode { + const logger = getLogger() + + return new RedshiftNode( + { + id: schemaName, + nodeType: NodeType.REDSHIFT_SCHEMA, + value: { + schema: schemaName, + connectionConfig, + identifier: schemaName, + type: ResourceType.SCHEMA, + childObjectTypes: [ + ResourceType.TABLE, + ResourceType.VIEW, + ResourceType.FUNCTION, + ResourceType.STORED_PROCEDURE, + ResourceType.EXTERNAL_TABLE, + ResourceType.CATALOG_TABLE, + ResourceType.DATA_CATALOG_TABLE, + ], + }, + path: { + ...parent.data.path, + schema: schemaName, + }, + parent, + }, + async (node) => { + try { + // Get the original credentials from the root connection node + const rootCredentials = getRootCredentials(parent) + + // Create SQL client with the original credentials + const clientStore = ConnectionClientStore.getInstance() + const rootConnection = getRootConnection(parent) + const sqlClient = clientStore.getSQLWorkbenchClient( + rootConnection.connectionId, + connectionConfig.id.split(':')[3], // region + rootCredentials + ) + + // Get schema objects + // Make sure we're using the correct database in the connection config + const schemaConnectionConfig = { + ...connectionConfig, + database: parent.data.path?.database || connectionConfig.database, + } + + // Create request params object for logging + const requestParams = { + connection: schemaConnectionConfig, + resourceType: ResourceType.TABLE, + includeChildren: true, + maxItems: 100, + parents: [ + { + parentId: schemaName, + parentType: ResourceType.SCHEMA, + }, + { + parentId: schemaConnectionConfig.database, + parentType: ResourceType.DATABASE, + }, + ], + forceRefresh: true, + } + + const allResources = [] + let nextToken: string | undefined + + do { + const response = await sqlClient.getResources({ + ...requestParams, + pageToken: nextToken, + }) + allResources.push(...(response.resources || [])) + nextToken = response.nextToken + } while (nextToken) + + // Group resources by type + const tables = allResources.filter( + (r: any) => + r.type === ResourceType.TABLE || + r.type === ResourceType.EXTERNAL_TABLE || + r.type === ResourceType.CATALOG_TABLE || + r.type === ResourceType.DATA_CATALOG_TABLE + ) + const views = allResources.filter((r: any) => r.type === ResourceType.VIEW) + const functions = allResources.filter((r: any) => r.type === ResourceType.FUNCTION) + const procedures = allResources.filter((r: any) => r.type === ResourceType.STORED_PROCEDURE) + + // Create container nodes for each type + const containerNodes: RedshiftNode[] = [] + + // Tables container + if (tables.length > 0) { + containerNodes.push(createContainerNode(NodeType.REDSHIFT_TABLE, tables, connectionConfig, node)) + } + + // Views container + if (views.length > 0) { + containerNodes.push(createContainerNode(NodeType.REDSHIFT_VIEW, views, connectionConfig, node)) + } + + // Functions container + if (functions.length > 0) { + containerNodes.push( + createContainerNode(NodeType.REDSHIFT_FUNCTION, functions, connectionConfig, node) + ) + } + + // Stored procedures container + if (procedures.length > 0) { + containerNodes.push( + createContainerNode(NodeType.REDSHIFT_STORED_PROCEDURE, procedures, connectionConfig, node) + ) + } + + if (containerNodes.length === 0) { + return [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } + + return containerNodes + } catch (err) { + logger.error(`Failed to get schema contents: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'schema-contents', node.id) as RedshiftNode] + } + } + ) +} + +/** + * Creates a container node for grouping objects by type + */ +function createContainerNode( + nodeType: NodeType, + resources: any[], + connectionConfig: ConnectionConfig, + parent: RedshiftNode +): RedshiftNode { + return new RedshiftNode( + { + id: `${parent.id}${NODE_ID_DELIMITER}${nodeType}-container`, + nodeType: nodeType, + value: { + connectionConfig, + resources, + }, + path: parent.data.path, + parent, + isContainer: true, + }, + async (node) => { + // Map resources to nodes + if (nodeType === NodeType.REDSHIFT_TABLE && parent.data.value?.type === ResourceType.CATALOG_DATABASE) { + // For catalog tables, use catalog table node + return resources.length > 0 + ? resources.map((resource: any) => + createCatalogTableNode(resource.displayName, resource, connectionConfig, node) + ) + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } + return resources.length > 0 + ? resources.map((resource: any) => + createObjectNode(resource.displayName, nodeType, resource, connectionConfig, node) + ) + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } + ) +} + +/** + * Creates an object node (table, view, function, etc.) + */ +function createObjectNode( + name: string, + nodeType: NodeType, + resource: any, + connectionConfig: ConnectionConfig, + parent: RedshiftNode +): RedshiftNode { + const logger = getLogger() + + return new RedshiftNode( + { + id: `${parent.id}${NODE_ID_DELIMITER}${name}`, + nodeType: nodeType, + value: { + ...resource, + connectionConfig, + }, + path: { + ...parent.data.path, + [nodeType]: name, + }, + parent, + }, + async (node) => { + // Only tables have columns + if (nodeType !== NodeType.REDSHIFT_TABLE) { + return [] + } + + try { + // Get the original credentials from the root connection node + const rootCredentials = getRootCredentials(parent) + + // Create SQL client with the original credentials + const clientStore = ConnectionClientStore.getInstance() + const rootConnection = getRootConnection(parent) + const sqlClient = clientStore.getSQLWorkbenchClient( + rootConnection.connectionId, + connectionConfig.id.split(':')[3], // region + rootCredentials + ) + + // Get schema and database from path + const schemaName = node.data.path?.schema + const databaseName = node.data.path?.database + const tableName = node.data.path?.table + + if (!schemaName || !databaseName || !tableName) { + logger.error('Missing schema, database, or table name in path') + return [] + } + + // Create request params for getResources to get columns + const requestParams = { + connection: connectionConfig, + resourceType: ResourceType.COLUMNS, + includeChildren: true, + maxItems: 100, + parents: [ + { + parentId: tableName, + parentType: ResourceType.TABLE, + }, + { + parentId: schemaName, + parentType: ResourceType.SCHEMA, + }, + { + parentId: databaseName, + parentType: ResourceType.DATABASE, + }, + ], + forceRefresh: true, + } + + // Call getResources to get columns + const allColumns = [] + let nextToken: string | undefined + + do { + const response = await sqlClient.getResources({ + ...requestParams, + pageToken: nextToken, + }) + allColumns.push(...(response.resources || [])) + nextToken = response.nextToken + } while (nextToken) + + // Create column nodes from API response + return allColumns.length > 0 + ? allColumns.map((column: any) => { + // Extract column type from resourceMetadata + let columnType = 'UNKNOWN' + if (column.resourceMetadata && Array.isArray(column.resourceMetadata)) { + const typeMetadata = column.resourceMetadata.find( + (meta: any) => meta.key === 'COLUMN_TYPE' + ) + if (typeMetadata) { + columnType = typeMetadata.value + } + } + + columnType = getColumnType(columnType) + + return createColumnNode( + column.displayName, + { + name: column.displayName, + type: columnType, + }, + connectionConfig, + node + ) + }) + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } catch (err) { + logger.error(`Failed to get columns: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'columns', node.id) as RedshiftNode] + } + } + ) +} + +/** + * Creates a column node + */ +function createColumnNode( + name: string, + columnInfo: { name: string; type: string }, + connectionConfig: ConnectionConfig, + parent: RedshiftNode +): RedshiftNode { + return new RedshiftNode({ + id: `${parent.id}${NODE_ID_DELIMITER}${name}`, + nodeType: NodeType.REDSHIFT_COLUMN, + value: { + name, + type: columnInfo.type, + connectionConfig, + }, + path: { + ...parent.data.path, + column: name, + }, + parent, + }) +} + +/** + * Gets the root connection from a node + */ +function getRootConnection(node: RedshiftNode): DataZoneConnection { + // Start with the current node + let currentNode = node + + // Traverse up to the root connection node + while (currentNode.data.parent) { + currentNode = currentNode.data.parent + } + + // Get connection from the root node + return currentNode.data.value?.connection +} + +/** + * Gets the original credentials from the root connection node + */ +function getRootCredentials(node: RedshiftNode): ConnectionCredentialsProvider { + // Start with the current node + let currentNode = node + + // Traverse up to the root connection node + while (currentNode.data.parent) { + currentNode = currentNode.data.parent + } + + // Get credentials from the root node + const credentials = currentNode.data.value?.connectionCredentialsProvider + + // Return credentials or fallback to dummy credentials + return ( + credentials || { + accessKeyId: 'dummy', + secretAccessKey: 'dummy', + } + ) +} + +/** + * Fetch glue catalogs, this will help determine which catalogs are accessible within the project + */ +async function listGlueCatalogs( + connectionId: string, + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider +): Promise { + const clientStore = ConnectionClientStore.getInstance() + const glueCatalogClient = clientStore.getGlueCatalogClient(connectionId, region, connectionCredentialsProvider) + + const allCatalogs = [] + let nextToken: string | undefined + + do { + const { catalogs, nextToken: token } = await glueCatalogClient.getCatalogs(nextToken) + allCatalogs.push(...catalogs) + nextToken = token + } while (nextToken) + + return allCatalogs +} + +/** + * Main logic to fetch catalog and database resources using getResources + */ +async function fetchResources( + sqlClient: any, + connectionConfig: ConnectionConfig, + resourceType: ResourceType, + parents: any[] = [] +): Promise { + const allResources = [] + let nextToken: string | undefined + + do { + const requestParams = { + connection: connectionConfig, + resourceType, + includeChildren: true, + maxItems: 100, + parents, + forceRefresh: true, + pageToken: nextToken, + } + const response = await sqlClient.getResources(requestParams) + allResources.push(...(response.resources || [])) + nextToken = response.nextToken + } while (nextToken) + + return allResources +} + +/** + * Creates a catalog database node + */ +function createCatalogDatabaseNode( + databaseName: string, + database: any, + connectionConfig: ConnectionConfig, + parent: RedshiftNode +): RedshiftNode { + return new RedshiftNode( + { + id: `${parent.id}${NODE_ID_DELIMITER}${databaseName}`, + nodeType: NodeType.REDSHIFT_CATALOG_DATABASE, + value: { + ...database, + connectionConfig, + identifier: databaseName, + type: ResourceType.CATALOG_DATABASE, + }, + path: { + ...parent.data.path, + database: databaseName, + }, + parent, + }, + async (node) => { + try { + const rootCredentials = getRootCredentials(parent) + const clientStore = ConnectionClientStore.getInstance() + const rootConnection = getRootConnection(parent) + const sqlClient = clientStore.getSQLWorkbenchClient( + rootConnection.connectionId, + connectionConfig.id.split(':')[3], + rootCredentials + ) + + // Use getResources to fetch tables within this catalog database + const tables = await fetchResources(sqlClient, connectionConfig, ResourceType.CATALOG_TABLE, [ + { + parentId: database.identifier, + parentType: ResourceType.CATALOG_DATABASE, + }, + { + parentId: parent.data.value?.catalog?.identifier || parent.data.path?.catalog, + parentType: ResourceType.CATALOG, + }, + ]) + + if (tables.length === 0) { + return [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } + + // Create container node for tables + return [createContainerNode(NodeType.REDSHIFT_TABLE, tables, connectionConfig, node)] + } catch (err) { + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'catalog-tables', node.id) as RedshiftNode] + } + } + ) +} + +/** + * Creates a catalog table node + */ +function createCatalogTableNode( + tableName: string, + table: any, + connectionConfig: ConnectionConfig, + parent: RedshiftNode +): RedshiftNode { + return new RedshiftNode( + { + id: `${parent.id}${NODE_ID_DELIMITER}${tableName}`, + nodeType: NodeType.REDSHIFT_TABLE, + value: { + ...table, + connectionConfig, + }, + path: { + ...parent.data.path, + table: tableName, + }, + parent, + }, + async (node) => { + try { + const rootCredentials = getRootCredentials(parent) + const clientStore = ConnectionClientStore.getInstance() + const rootConnection = getRootConnection(parent) + const sqlClient = clientStore.getSQLWorkbenchClient( + rootConnection.connectionId, + connectionConfig.id.split(':')[3], + rootCredentials + ) + + // Use getResources to fetch columns within this catalog table + // Need to traverse up to find the actual database and catalog nodes + let databaseNode = parent + while (databaseNode && databaseNode.data.nodeType !== NodeType.REDSHIFT_CATALOG_DATABASE) { + databaseNode = databaseNode.data.parent + } + + let catalogNode = databaseNode?.data.parent + while (catalogNode && catalogNode.data.nodeType !== NodeType.REDSHIFT_CATALOG) { + catalogNode = catalogNode.data.parent + } + + const parents = [ + { + parentId: table.identifier, + parentType: ResourceType.CATALOG_TABLE, + }, + { + parentId: databaseNode?.data.value?.identifier, + parentType: ResourceType.CATALOG_DATABASE, + }, + { + parentId: catalogNode?.data.value?.catalog?.identifier || catalogNode?.data.value?.identifier, + parentType: ResourceType.CATALOG, + }, + ] + + const columns = await fetchResources(sqlClient, connectionConfig, ResourceType.CATALOG_COLUMN, parents) + + return columns.length > 0 + ? columns.map((column: any) => { + let columnType = 'UNKNOWN' + if (column.resourceMetadata && Array.isArray(column.resourceMetadata)) { + const typeMetadata = column.resourceMetadata.find( + (meta: any) => meta.key === 'COLUMN_TYPE' + ) + if (typeMetadata) { + columnType = typeMetadata.value + } + } + + columnType = getColumnType(columnType) + + return createColumnNode( + column.displayName, + { + name: column.displayName, + type: columnType, + }, + connectionConfig, + node + ) + }) + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } catch (err) { + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'catalog-columns', node.id) as RedshiftNode] + } + } + ) +} + +/** + * Creates a catalog node + */ +function createCatalogNode( + catalogName: string, + catalog: any, + connectionConfig: ConnectionConfig, + parent: RedshiftNode +): RedshiftNode { + return new RedshiftNode( + { + id: `${parent.id}${NODE_ID_DELIMITER}${catalogName}`, + nodeType: NodeType.REDSHIFT_CATALOG, + value: { + catalog, + catalogName, + connectionConfig, + identifier: catalogName, + type: ResourceType.CATALOG, + }, + path: { + ...parent.data.path, + catalog: catalogName, + }, + parent, + }, + async (node) => { + try { + const rootCredentials = getRootCredentials(parent) + const clientStore = ConnectionClientStore.getInstance() + const rootConnection = getRootConnection(parent) + const sqlClient = clientStore.getSQLWorkbenchClient( + rootConnection.connectionId, + connectionConfig.id.split(':')[3], + rootCredentials + ) + + // Use getResources to fetch databases within this catalog + const databases = await fetchResources(sqlClient, connectionConfig, ResourceType.CATALOG_DATABASE, [ + { + parentId: catalog.identifier, + parentType: ResourceType.CATALOG, + }, + ]) + + if (databases.length === 0) { + return [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } + + return databases.length > 0 + ? databases.map((database: any) => + createCatalogDatabaseNode(database.displayName, database, connectionConfig, node) + ) + : [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as RedshiftNode] + } catch (err) { + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'catalog-databases', node.id) as RedshiftNode] + } + } + ) +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/s3Strategy.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/s3Strategy.ts new file mode 100644 index 00000000000..4106a0b4889 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/s3Strategy.ts @@ -0,0 +1,599 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { getLogger } from '../../../shared/logger/logger' +import { DataZoneConnection } from '../../shared/client/datazoneClient' +import { S3Client } from '../../shared/client/s3Client' +import { ConnectionClientStore } from '../../shared/client/connectionClientStore' +import { NODE_ID_DELIMITER, NodeType, ConnectionType, NodeData, NO_DATA_FOUND_MESSAGE } from './types' +import { getLabel, isLeafNode, getIconForNodeType, getTooltip, createErrorItem } from './utils' +import { createPlaceholderItem } from '../../../shared/treeview/utils' +import { + ListCallerAccessGrantsCommand, + GetDataAccessCommand, + ListCallerAccessGrantsEntry, +} from '@aws-sdk/client-s3-control' +import { S3, ListObjectsV2Command } from '@aws-sdk/client-s3' +import { ConnectionCredentialsProvider } from '../../auth/providers/connectionCredentialsProvider' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { recordDataConnectionTelemetry } from '../../shared/telemetry' + +// Regex to match default S3 connection names +// eslint-disable-next-line @typescript-eslint/naming-convention +export const DATA_DEFAULT_S3_CONNECTION_NAME_REGEXP = /^(project\.s3_default_folder)|(default\.s3)$/ + +/** + * S3 data node for SageMaker Unified Studio + */ +export class S3Node implements TreeNode { + private readonly logger = getLogger() + private childrenNodes: TreeNode[] | undefined + private isLoading = false + + constructor( + public readonly data: NodeData, + private readonly childrenProvider?: (node: S3Node) => Promise + ) {} + + public get id(): string { + return this.data.id + } + + public get resource(): any { + return this.data.value || {} + } + + public async getChildren(): Promise { + // Return cached children if available + if (this.childrenNodes && !this.isLoading) { + return this.childrenNodes + } + + // Return empty array for leaf nodes + if (isLeafNode(this.data)) { + return [] + } + + // If we have a children provider, use it + if (this.childrenProvider) { + try { + this.isLoading = true + const childrenNodes = await this.childrenProvider(this) + this.childrenNodes = childrenNodes + this.isLoading = false + return this.childrenNodes + } catch (err) { + this.isLoading = false + this.logger.error(`Failed to get children for node ${this.data.id}: ${(err as Error).message}`) + + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'getChildren', this.id) as S3Node] + } + } + + return [] + } + + public async getTreeItem(): Promise { + const collapsibleState = isLeafNode(this.data) + ? vscode.TreeItemCollapsibleState.None + : vscode.TreeItemCollapsibleState.Collapsed + + const label = getLabel(this.data) + const item = new vscode.TreeItem(label, collapsibleState) + + // Set icon based on node type + item.iconPath = getIconForNodeType(this.data.nodeType, this.data.isContainer) + + // Set context value for command enablement + item.contextValue = this.data.nodeType + + // Set tooltip + item.tooltip = getTooltip(this.data) + + return item + } + + public getParent(): TreeNode | undefined { + return this.data.parent + } +} + +/** + * Creates an S3 connection node + */ +export function createS3ConnectionNode( + connection: DataZoneConnection, + connectionCredentialsProvider: ConnectionCredentialsProvider, + region: string +): S3Node { + const logger = getLogger() + + // Parse S3 URI from connection + const s3Info = parseS3Uri(connection) + if (!s3Info) { + logger.warn(`No S3 URI found in connection properties for connection ${connection.name}`) + const errorMessage = 'No S3 URI configured' + void vscode.window.showErrorMessage(errorMessage) + return createErrorItem(errorMessage, 'connection', connection.connectionId) as S3Node + } + + // Get S3 client from store + const clientStore = ConnectionClientStore.getInstance() + const s3Client = clientStore.getS3Client(connection.connectionId, region, connectionCredentialsProvider) + + // Check if this is a default S3 connection + const isDefaultConnection = DATA_DEFAULT_S3_CONNECTION_NAME_REGEXP.test(connection.name) + + // Create the connection node + return new S3Node( + { + id: connection.connectionId, + nodeType: NodeType.CONNECTION, + connectionType: ConnectionType.S3, + value: { connection }, + path: { + connection: connection.name, + bucket: s3Info.bucket, + }, + }, + async (node) => { + return telemetry.smus_renderS3Node.run(async (span) => { + await recordDataConnectionTelemetry(span, connection, connectionCredentialsProvider) + try { + if (isDefaultConnection && s3Info.prefix) { + // For default connections, show the full path as the first node + const fullPath = `${s3Info.bucket}/${s3Info.prefix}` + return [ + new S3Node( + { + id: fullPath, + nodeType: NodeType.S3_BUCKET, + connectionType: ConnectionType.S3, + value: { bucket: s3Info.bucket, prefix: s3Info.prefix }, + path: { + connection: connection.name, + bucket: s3Info.bucket, + key: s3Info.prefix, + label: fullPath, + }, + parent: node, + }, + async (bucketNode) => { + try { + // List objects starting from the prefix + const allPaths = [] + let nextToken: string | undefined + + do { + const result = await s3Client.listPaths( + s3Info.bucket, + s3Info.prefix, + nextToken + ) + allPaths.push(...result.paths) + nextToken = result.nextToken + } while (nextToken) + + if (allPaths.length === 0) { + return [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as S3Node] + } + + // Convert paths to nodes + return allPaths.map((path) => { + const nodeId = `${path.bucket}-${path.prefix || 'root'}` + + return new S3Node( + { + id: nodeId, + nodeType: path.isFolder ? NodeType.S3_FOLDER : NodeType.S3_FILE, + connectionType: ConnectionType.S3, + value: path, + path: { + connection: connection.name, + bucket: path.bucket, + key: path.prefix, + label: path.displayName, + }, + parent: bucketNode, + }, + path.isFolder ? createFolderChildrenProvider(s3Client, path) : undefined + ) + }) + } catch (err) { + logger.error(`Failed to list bucket contents: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [ + createErrorItem( + errorMessage, + 'bucket-contents-default', + bucketNode.id + ) as S3Node, + ] + } + } + ), + ] + } else { + // For non-default connections, show bucket as the first node + return [ + new S3Node( + { + id: s3Info.bucket, + nodeType: NodeType.S3_BUCKET, + connectionType: ConnectionType.S3, + value: { bucket: s3Info.bucket }, + path: { + connection: connection.name, + bucket: s3Info.bucket, + }, + parent: node, + }, + async (bucketNode) => { + try { + // List objects in the bucket + const allPaths = [] + let nextToken: string | undefined + + do { + const result = await s3Client.listPaths( + s3Info.bucket, + s3Info.prefix, + nextToken + ) + allPaths.push(...result.paths) + nextToken = result.nextToken + } while (nextToken) + + if (allPaths.length === 0) { + return [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as S3Node] + } + + // Convert paths to nodes + return allPaths.map((path) => { + const nodeId = `${path.bucket}-${path.prefix || 'root'}` + + return new S3Node( + { + id: nodeId, + nodeType: path.isFolder ? NodeType.S3_FOLDER : NodeType.S3_FILE, + connectionType: ConnectionType.S3, + value: path, + path: { + connection: connection.name, + bucket: path.bucket, + key: path.prefix, + label: path.displayName, + }, + parent: bucketNode, + }, + path.isFolder ? createFolderChildrenProvider(s3Client, path) : undefined + ) + }) + } catch (err) { + logger.error(`Failed to list bucket contents: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [ + createErrorItem( + errorMessage, + 'bucket-contents-regular', + bucketNode.id + ) as S3Node, + ] + } + } + ), + ] + } + } catch (err) { + logger.error(`Failed to create bucket node: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'bucket-node', node.id) as S3Node] + } + }) + } + ) +} + +/** + * Creates S3 access grant nodes for project.s3_default_folder connections + */ +export async function createS3AccessGrantNodes( + connection: DataZoneConnection, + connectionCredentialsProvider: ConnectionCredentialsProvider, + region: string, + accountId: string | undefined +): Promise { + if (connection.name !== 'project.s3_default_folder' || !accountId) { + return [] + } + + return await listCallerAccessGrants(connectionCredentialsProvider, region, accountId, connection.connectionId) +} + +/** + * Creates a children provider function for a folder node + */ +function createFolderChildrenProvider(s3Client: S3Client, folderPath: any): (node: S3Node) => Promise { + const logger = getLogger() + + return async (node: S3Node) => { + try { + // List objects in the folder + const allPaths = [] + let nextToken: string | undefined + + do { + const result = await s3Client.listPaths(folderPath.bucket, folderPath.prefix, nextToken) + allPaths.push(...result.paths) + nextToken = result.nextToken + } while (nextToken) + + if (allPaths.length === 0) { + return [createPlaceholderItem(NO_DATA_FOUND_MESSAGE) as S3Node] + } + + // Convert paths to nodes + return allPaths.map((path) => { + const nodeId = `${path.bucket}-${path.prefix || 'root'}` + + return new S3Node( + { + id: nodeId, + nodeType: path.isFolder ? NodeType.S3_FOLDER : NodeType.S3_FILE, + connectionType: ConnectionType.S3, + value: path, + path: { + connection: node.data.path?.connection, + bucket: path.bucket, + key: path.prefix, + label: path.displayName, + }, + parent: node, + }, + path.isFolder ? createFolderChildrenProvider(s3Client, path) : undefined + ) + }) + } catch (err) { + logger.error(`Failed to list folder contents: ${(err as Error).message}`) + const errorMessage = (err as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'folder-contents', node.id) as S3Node] + } + } +} + +/** + * Parse S3 URI from connection + */ +function parseS3Uri(connection: DataZoneConnection): { bucket: string; prefix?: string } | undefined { + const s3Properties = connection.props?.s3Properties + const s3Uri = s3Properties?.s3Uri + + if (!s3Uri) { + return undefined + } + + // Parse S3 URI: s3://bucket-name/prefix/path/ + const uriWithoutPrefix = s3Uri.replace('s3://', '') + // Since the URI ends with a slash, the last item will be an empty string, so ignore it in the parts. + const parts = uriWithoutPrefix.split('/').slice(0, -1) + const bucket = parts[0] + + // If parts only contains 1 item, then only a bucket was provided, and the key is empty. + const prefix = parts.length > 1 ? parts.slice(1).join('/') + '/' : undefined + + return { bucket, prefix } +} + +async function listCallerAccessGrants( + connectionCredentialsProvider: ConnectionCredentialsProvider, + region: string, + accountId: string, + connectionId: string +): Promise { + const logger = getLogger() + try { + const clientStore = ConnectionClientStore.getInstance() + const s3ControlClient = clientStore.getS3ControlClient(connectionId, region, connectionCredentialsProvider) + + const allGrants: ListCallerAccessGrantsEntry[] = [] + let nextToken: string | undefined + + do { + const command = new ListCallerAccessGrantsCommand({ + AccountId: accountId, + NextToken: nextToken, + }) + + const response = await s3ControlClient.send(command) + const grants = response.CallerAccessGrantsList?.filter((entry) => !!entry) ?? [] + allGrants.push(...grants) + nextToken = response.NextToken + } while (nextToken) + + logger.info(`Listed ${allGrants.length} caller access grants`) + + const accessGrantNodes = allGrants.map((grant) => + getRootNodeFromS3AccessGrant(grant, accountId, region, connectionCredentialsProvider, connectionId) + ) + return accessGrantNodes + } catch (error) { + logger.error(`Failed to list caller access grants: ${(error as Error).message}`) + return [] + } +} + +function parseS3UriForAccessGrant(s3Uri: string): { bucket: string; key: string } { + const uriWithoutPrefix = s3Uri.replace('s3://', '') + const parts = uriWithoutPrefix.split('/').slice(0, -1) + const bucket = parts[0] + const key = parts.length > 1 ? parts.slice(1).join('/') + '/' : '' + return { bucket, key } +} + +function getRootNodeFromS3AccessGrant( + s3AccessGrant: ListCallerAccessGrantsEntry, + accountId: string, + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider, + connectionId: string +): S3Node { + const s3Uri = s3AccessGrant.GrantScope + let bucket: string | undefined + let key: string | undefined + let nodeId = '' + let label: string + + if (s3Uri) { + const { bucket: parsedBucket, key: parsedKey } = parseS3UriForAccessGrant(s3Uri) + bucket = parsedBucket + key = parsedKey + label = s3Uri.replace('s3://', '').replace('*', '') + nodeId = label + } else { + label = s3AccessGrant.GrantScope ?? '' + } + + return new S3Node( + { + id: nodeId, + nodeType: NodeType.S3_ACCESS_GRANT, + connectionType: ConnectionType.S3, + value: s3AccessGrant, + path: { accountId, bucket, key, label }, + }, + async (node) => { + return await fetchAccessGrantChildren(node, accountId, region, connectionCredentialsProvider, connectionId) + } + ) +} + +async function fetchAccessGrantChildren( + node: S3Node, + accountId: string, + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider, + connectionId: string +): Promise { + const logger = getLogger() + const path = node.data.path + + try { + const clientStore = ConnectionClientStore.getInstance() + const s3ControlClient = clientStore.getS3ControlClient(connectionId, region, connectionCredentialsProvider) + + const target = `s3://${path?.bucket ?? ''}/${path?.key ?? ''}*` + + const getDataAccessCommand = new GetDataAccessCommand({ + AccountId: accountId, + Target: target, + Permission: 'READ', + }) + + const grantCredentialsProvider = async () => { + const response = await s3ControlClient.send(getDataAccessCommand) + if ( + !response.Credentials?.AccessKeyId || + !response.Credentials?.SecretAccessKey || + !response.Credentials?.SessionToken + ) { + throw new Error('Missing required credentials from access grant response') + } + return { + accessKeyId: response.Credentials.AccessKeyId, + secretAccessKey: response.Credentials.SecretAccessKey, + sessionToken: response.Credentials.SessionToken, + expiration: response.Credentials.Expiration, + } + } + + const s3ClientWithGrant = new S3({ + credentials: grantCredentialsProvider, + region, + }) + + const response = await s3ClientWithGrant.send( + new ListObjectsV2Command({ + Bucket: path?.bucket ?? '', + Prefix: path?.key ?? '', + Delimiter: '/', + MaxKeys: 100, + }) + ) + + const children: S3Node[] = [] + + // Add folders + if (response.CommonPrefixes) { + for (const prefix of response.CommonPrefixes) { + const folderName = + prefix.Prefix?.split('/') + .filter((name) => !!name) + .at(-1) + '/' + children.push( + new S3Node( + { + id: `${node.id}${NODE_ID_DELIMITER}${folderName}`, + nodeType: NodeType.S3_FOLDER, + connectionType: ConnectionType.S3, + value: prefix, + path: { + accountId, + bucket: path?.bucket, + key: prefix.Prefix, + label: folderName, + }, + parent: node, + }, + async (folderNode) => { + return await fetchAccessGrantChildren( + folderNode, + accountId, + region, + connectionCredentialsProvider, + connectionId + ) + } + ) + ) + } + } + + // Add files + if (response.Contents) { + for (const content of response.Contents.filter((content) => content.Key !== response.Prefix)) { + const fileName = content.Key?.split('/').at(-1) ?? '' + children.push( + new S3Node({ + id: `${node.id}${NODE_ID_DELIMITER}${fileName}`, + nodeType: NodeType.S3_FILE, + connectionType: ConnectionType.S3, + value: content, + path: { + bucket: path?.bucket, + key: content.Key, + label: fileName, + }, + parent: node, + }) + ) + } + } + + return children + } catch (error) { + logger.error(`Failed to fetch access grant children: ${(error as Error).message}`) + const errorMessage = (error as Error).message + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'access-grant-children', node.id) as S3Node] + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioAuthInfoNode.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioAuthInfoNode.ts new file mode 100644 index 00000000000..ff25f64cf74 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioAuthInfoNode.ts @@ -0,0 +1,90 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { SageMakerUnifiedStudioRootNode } from './sageMakerUnifiedStudioRootNode' +import { SmusAuthenticationProvider } from '../../auth/providers/smusAuthenticationProvider' + +/** + * Node representing the SageMaker Unified Studio authentication information + */ +export class SageMakerUnifiedStudioAuthInfoNode implements TreeNode { + public readonly id = 'smusAuthInfoNode' + public readonly resource = this + private readonly authProvider: SmusAuthenticationProvider + + private readonly onDidChangeEmitter = new vscode.EventEmitter() + public readonly onDidChangeTreeItem = this.onDidChangeEmitter.event + + constructor(private readonly parent?: SageMakerUnifiedStudioRootNode) { + this.authProvider = SmusAuthenticationProvider.fromContext() + + // Subscribe to auth provider connection changes to refresh the node + this.authProvider.onDidChange(() => { + this.onDidChangeEmitter.fire() + }) + } + + public getTreeItem(): vscode.TreeItem { + // Use the cached authentication provider to check connection status + const isConnected = this.authProvider.isConnected() + const isValid = this.authProvider.isConnectionValid() + + // Get the domain ID and region from auth provider + let domainId = 'Unknown' + let region = 'Unknown' + + if (isConnected && this.authProvider.activeConnection) { + const conn = this.authProvider.activeConnection + domainId = conn.domainId || 'Unknown' + region = conn.ssoRegion || 'Unknown' + } + + // Create display based on connection status + let label: string + let iconPath: vscode.ThemeIcon + let tooltip: string + + if (isConnected && isValid) { + label = `Domain: ${domainId}` + iconPath = new vscode.ThemeIcon('key', new vscode.ThemeColor('charts.green')) + tooltip = `Connected to SageMaker Unified Studio\nDomain ID: ${domainId}\nRegion: ${region}\nStatus: Connected` + } else if (isConnected && !isValid) { + label = `Domain: ${domainId} (Expired) - Click to reauthenticate` + iconPath = new vscode.ThemeIcon('warning', new vscode.ThemeColor('charts.yellow')) + tooltip = `Connection to SageMaker Unified Studio has expired\nDomain ID: ${domainId}\nRegion: ${region}\nStatus: Expired - Click to reauthenticate` + } else { + label = 'Not Connected' + iconPath = new vscode.ThemeIcon('circle-slash', new vscode.ThemeColor('charts.red')) + tooltip = 'Not connected to SageMaker Unified Studio\nPlease sign in to access your projects' + } + + const item = new vscode.TreeItem(label, vscode.TreeItemCollapsibleState.None) + + // Add region as description (appears to the right) if connected + if (isConnected) { + item.description = region + } + + // Add command for reauthentication when connection is expired + if (isConnected && !isValid) { + item.command = { + command: 'aws.smus.reauthenticate', + title: 'Reauthenticate', + arguments: [this.authProvider.activeConnection], + } + } + + item.tooltip = tooltip + item.contextValue = 'smusAuthInfo' + item.iconPath = iconPath + return item + } + + public getParent(): TreeNode | undefined { + return this.parent + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioComputeNode.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioComputeNode.ts new file mode 100644 index 00000000000..01293e7e523 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioComputeNode.ts @@ -0,0 +1,66 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { getIcon } from '../../../shared/icons' +import { SageMakerUnifiedStudioSpacesParentNode } from './sageMakerUnifiedStudioSpacesParentNode' +import { SageMakerUnifiedStudioProjectNode } from './sageMakerUnifiedStudioProjectNode' +import { SagemakerClient } from '../../../shared/clients/sagemaker' +import { SmusAuthenticationProvider } from '../../auth/providers/smusAuthenticationProvider' +import { SageMakerUnifiedStudioConnectionParentNode } from './sageMakerUnifiedStudioConnectionParentNode' +import { ConnectionType } from '@aws-sdk/client-datazone' + +export class SageMakerUnifiedStudioComputeNode implements TreeNode { + public readonly id = 'smusComputeNode' + public readonly resource = this + private spacesNode: SageMakerUnifiedStudioSpacesParentNode | undefined + + constructor( + public readonly parent: SageMakerUnifiedStudioProjectNode, + private readonly extensionContext: vscode.ExtensionContext, + public readonly authProvider: SmusAuthenticationProvider, + private readonly sagemakerClient: SagemakerClient + ) {} + + public async getTreeItem(): Promise { + const item = new vscode.TreeItem('Compute', vscode.TreeItemCollapsibleState.Expanded) + item.iconPath = getIcon('vscode-chip') + item.contextValue = this.getContext() + return item + } + + public async getChildren(): Promise { + const childrenNodes: TreeNode[] = [] + const projectId = this.parent.getProject()?.id + + if (projectId) { + childrenNodes.push( + new SageMakerUnifiedStudioConnectionParentNode(this, ConnectionType.REDSHIFT, 'Data warehouse') + ) + childrenNodes.push( + new SageMakerUnifiedStudioConnectionParentNode(this, ConnectionType.SPARK, 'Data processing') + ) + this.spacesNode = new SageMakerUnifiedStudioSpacesParentNode( + this, + projectId, + this.extensionContext, + this.authProvider, + this.sagemakerClient + ) + childrenNodes.push(this.spacesNode) + } + + return childrenNodes + } + + public getParent(): TreeNode | undefined { + return this.parent + } + + private getContext(): string { + return 'smusComputeNode' + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioConnectionNode.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioConnectionNode.ts new file mode 100644 index 00000000000..969efa9823d --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioConnectionNode.ts @@ -0,0 +1,63 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger } from '../../../shared/logger/logger' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { SageMakerUnifiedStudioConnectionParentNode } from './sageMakerUnifiedStudioConnectionParentNode' +import { ConnectionSummary, ConnectionType } from '@aws-sdk/client-datazone' + +export class SageMakerUnifiedStudioConnectionNode implements TreeNode { + public resource: SageMakerUnifiedStudioConnectionNode + contextValue: string + private readonly logger = getLogger() + id: string + public constructor( + private readonly parent: SageMakerUnifiedStudioConnectionParentNode, + private readonly connection: ConnectionSummary + ) { + this.id = connection.name ?? '' + this.resource = this + this.contextValue = this.getContext() + this.logger.debug(`SageMaker Space Node created: ${this.id}`) + } + + public async getTreeItem(): Promise { + const item = new vscode.TreeItem(this.id, vscode.TreeItemCollapsibleState.None) + item.contextValue = this.getContext() + item.tooltip = new vscode.MarkdownString(this.buildTooltip()) + return item + } + private buildTooltip(): string { + if (this.connection.type === ConnectionType.REDSHIFT) { + const tooltip = ''.concat( + '### Compute Details\n\n', + `**Type** \n${this.connection.type}\n\n`, + `**Environment ID** \n${this.connection.environmentId}\n\n`, + `**JDBC URL** \n${this.connection.props?.redshiftProperties?.jdbcUrl}` + ) + return tooltip + } else if (this.connection.type === ConnectionType.SPARK) { + const tooltip = ''.concat( + '### Compute Details\n\n', + `**Type** \n${this.connection.type}\n\n`, + `**Glue version** \n${this.connection.props?.sparkGlueProperties?.glueVersion}\n\n`, + `**Worker type** \n${this.connection.props?.sparkGlueProperties?.workerType}\n\n`, + `**Number of workers** \n${this.connection.props?.sparkGlueProperties?.numberOfWorkers}\n\n`, + `**Idle timeout (minutes)** \n${this.connection.props?.sparkGlueProperties?.idleTimeout}\n\n` + ) + return tooltip + } else { + return '' + } + } + private getContext(): string { + return 'SageMakerUnifiedStudioConnectionNode' + } + + public getParent(): TreeNode | undefined { + return this.parent + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioConnectionParentNode.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioConnectionParentNode.ts new file mode 100644 index 00000000000..a04377f0133 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioConnectionParentNode.ts @@ -0,0 +1,65 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { SageMakerUnifiedStudioComputeNode } from './sageMakerUnifiedStudioComputeNode' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { ListConnectionsCommandOutput, ConnectionType } from '@aws-sdk/client-datazone' +import { SageMakerUnifiedStudioConnectionNode } from './sageMakerUnifiedStudioConnectionNode' +import { DataZoneClient } from '../../shared/client/datazoneClient' + +// eslint-disable-next-line id-length +export class SageMakerUnifiedStudioConnectionParentNode implements TreeNode { + public resource: SageMakerUnifiedStudioConnectionParentNode + contextValue: string + public connections: ListConnectionsCommandOutput | undefined + public constructor( + private readonly parent: SageMakerUnifiedStudioComputeNode, + private readonly connectionType: ConnectionType, + public id: string + ) { + this.resource = this + this.contextValue = this.getContext() + } + + public async getTreeItem(): Promise { + const item = new vscode.TreeItem(this.id, vscode.TreeItemCollapsibleState.Collapsed) + item.contextValue = this.getContext() + return item + } + + public async getChildren(): Promise { + const client = await DataZoneClient.getInstance(this.parent.authProvider) + this.connections = await client.fetchConnections( + this.parent.parent.project?.domainId, + this.parent.parent.project?.id, + this.connectionType + ) + const childrenNodes = [] + if (!this.connections?.items || this.connections.items.length === 0) { + return [ + { + id: 'smusNoConnections', + resource: {}, + getTreeItem: () => + new vscode.TreeItem('[No connections found]', vscode.TreeItemCollapsibleState.None), + getParent: () => this, + }, + ] + } + for (const connection of this.connections.items) { + childrenNodes.push(new SageMakerUnifiedStudioConnectionNode(this, connection)) + } + return childrenNodes + } + + private getContext(): string { + return 'SageMakerUnifiedStudioConnectionParentNode' + } + + public getParent(): TreeNode | undefined { + return this.parent + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioDataNode.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioDataNode.ts new file mode 100644 index 00000000000..4294a3e42f4 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioDataNode.ts @@ -0,0 +1,250 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { getIcon } from '../../../shared/icons' + +import { getLogger } from '../../../shared/logger/logger' +import { DataZoneClient, DataZoneConnection, DataZoneProject } from '../../shared/client/datazoneClient' +import { createS3ConnectionNode, createS3AccessGrantNodes } from './s3Strategy' +import { createRedshiftConnectionNode } from './redshiftStrategy' +import { createLakehouseConnectionNode } from './lakehouseStrategy' +import { SageMakerUnifiedStudioProjectNode } from './sageMakerUnifiedStudioProjectNode' +import { isFederatedConnection, createErrorItem } from './utils' +import { createPlaceholderItem } from '../../../shared/treeview/utils' +import { ConnectionType, NO_DATA_FOUND_MESSAGE } from './types' +import { SmusAuthenticationProvider } from '../../auth/providers/smusAuthenticationProvider' + +/** + * Tree node representing a Data folder that contains S3 and Redshift connections + */ +export class SageMakerUnifiedStudioDataNode implements TreeNode { + public readonly id = 'smusDataExplorer' + public readonly resource = {} + private readonly logger = getLogger() + private childrenNodes: TreeNode[] | undefined + private readonly authProvider: SmusAuthenticationProvider + + constructor( + private readonly parent: SageMakerUnifiedStudioProjectNode, + initialChildren: TreeNode[] = [] + ) { + this.childrenNodes = initialChildren.length > 0 ? initialChildren : undefined + this.authProvider = SmusAuthenticationProvider.fromContext() + } + + public getTreeItem(): vscode.TreeItem { + const item = new vscode.TreeItem('Data', vscode.TreeItemCollapsibleState.Collapsed) + item.iconPath = getIcon('vscode-library') + item.contextValue = 'dataFolder' + return item + } + + public async getChildren(): Promise { + if (this.childrenNodes !== undefined) { + return this.childrenNodes + } + + try { + const project = this.parent.getProject() + if (!project) { + const errorMessage = 'No project information available' + this.logger.error(errorMessage) + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'project', this.id)] + } + + const datazoneClient = await DataZoneClient.getInstance(this.authProvider) + const connections = await datazoneClient.listConnections(project.domainId, undefined, project.id) + this.logger.info(`Found ${connections.length} connections for project ${project.id}`) + + if (connections.length === 0) { + this.childrenNodes = [createPlaceholderItem(NO_DATA_FOUND_MESSAGE)] + return this.childrenNodes + } + + const dataNodes = await this.createConnectionNodes(project, connections) + this.childrenNodes = dataNodes + return dataNodes + } catch (err) { + const project = this.parent.getProject() + const projectInfo = project ? `project: ${project.id}, domain: ${project.domainId}` : 'unknown project' + const errorMessage = 'Failed to get connections' + this.logger.error(`Failed to get connections for ${projectInfo}: ${(err as Error).message}`) + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, 'connections', this.id)] + } + } + + public getParent(): TreeNode | undefined { + return this.parent + } + + private async createConnectionNodes( + project: DataZoneProject, + connections: DataZoneConnection[] + ): Promise { + const region = this.authProvider.getDomainRegion() + const dataNodes: TreeNode[] = [] + + const s3Connections = connections.filter((conn) => (conn.type as ConnectionType) === ConnectionType.S3) + const redshiftConnections = connections.filter( + (conn) => (conn.type as ConnectionType) === ConnectionType.REDSHIFT + ) + const lakehouseConnections = connections.filter( + (conn) => (conn.type as ConnectionType) === ConnectionType.LAKEHOUSE + ) + + // Add Lakehouse nodes first + for (const connection of lakehouseConnections) { + const node = await this.createLakehouseNode(project, connection, region) + dataNodes.push(node) + } + + // Add Redshift nodes second + for (const connection of redshiftConnections) { + if (connection.name.startsWith('project.lakehouse')) { + continue + } + if (isFederatedConnection(connection)) { + continue + } + const node = await this.createRedshiftNode(project, connection, region) + dataNodes.push(node) + } + + // Add S3 Bucket parent node last + if (s3Connections.length > 0) { + const bucketNode = this.createBucketParentNode(project, s3Connections, region) + dataNodes.push(bucketNode) + } + + this.logger.info(`Created ${dataNodes.length} total connection nodes`) + return dataNodes + } + + private async createS3Node( + project: DataZoneProject, + connection: DataZoneConnection, + region: string + ): Promise { + try { + const datazoneClient = await DataZoneClient.getInstance(this.authProvider) + const getConnectionResponse = await datazoneClient.getConnection({ + domainIdentifier: project.domainId, + identifier: connection.connectionId, + withSecret: true, + }) + + const connectionCredentialsProvider = await this.authProvider.getConnectionCredentialsProvider( + connection.connectionId, + project.id, + getConnectionResponse.location?.awsRegion || region + ) + + const s3ConnectionNode = createS3ConnectionNode( + connection, + connectionCredentialsProvider, + getConnectionResponse.location?.awsRegion || region + ) + + const accessGrantNodes = await createS3AccessGrantNodes( + connection, + connectionCredentialsProvider, + getConnectionResponse.location?.awsRegion || region, + getConnectionResponse.location?.awsAccountId + ) + + return [s3ConnectionNode, ...accessGrantNodes] + } catch (connErr) { + const errorMessage = `Failed to get S3 connection - ${(connErr as Error).message}` + this.logger.error(`Failed to get S3 connection details: ${(connErr as Error).message}`) + void vscode.window.showErrorMessage(errorMessage) + return [createErrorItem(errorMessage, `s3-${connection.connectionId}`, this.id)] + } + } + + private async createRedshiftNode( + project: DataZoneProject, + connection: DataZoneConnection, + region: string + ): Promise { + try { + const datazoneClient = await DataZoneClient.getInstance(this.authProvider) + const getConnectionResponse = await datazoneClient.getConnection({ + domainIdentifier: project.domainId, + identifier: connection.connectionId, + withSecret: true, + }) + + const connectionCredentialsProvider = await this.authProvider.getConnectionCredentialsProvider( + connection.connectionId, + project.id, + getConnectionResponse.location?.awsRegion || region + ) + + return createRedshiftConnectionNode(connection, connectionCredentialsProvider) + } catch (connErr) { + const errorMessage = `Failed to get Redshift connection - ${(connErr as Error).message}` + this.logger.error(`Failed to get Redshift connection details: ${(connErr as Error).message}`) + void vscode.window.showErrorMessage(errorMessage) + return createErrorItem(errorMessage, `redshift-${connection.connectionId}`, this.id) + } + } + + private async createLakehouseNode( + project: DataZoneProject, + connection: DataZoneConnection, + region: string + ): Promise { + try { + const datazoneClient = await DataZoneClient.getInstance(this.authProvider) + const getConnectionResponse = await datazoneClient.getConnection({ + domainIdentifier: project.domainId, + identifier: connection.connectionId, + withSecret: true, + }) + + const connectionCredentialsProvider = await this.authProvider.getConnectionCredentialsProvider( + connection.connectionId, + project.id, + getConnectionResponse.location?.awsRegion || region + ) + + return createLakehouseConnectionNode(connection, connectionCredentialsProvider, region) + } catch (connErr) { + const errorMessage = `Failed to get Lakehouse connection - ${(connErr as Error).message}` + this.logger.error(`Failed to get Lakehouse connection details: ${(connErr as Error).message}`) + void vscode.window.showErrorMessage(errorMessage) + return createErrorItem(errorMessage, `lakehouse-${connection.connectionId}`, this.id) + } + } + + private createBucketParentNode( + project: DataZoneProject, + s3Connections: DataZoneConnection[], + region: string + ): TreeNode { + return { + id: 'bucket-parent', + resource: {}, + getTreeItem: () => { + const item = new vscode.TreeItem('Buckets', vscode.TreeItemCollapsibleState.Collapsed) + item.contextValue = 'bucketFolder' + return item + }, + getChildren: async () => { + const s3Nodes: TreeNode[] = [] + for (const connection of s3Connections) { + const nodes = await this.createS3Node(project, connection, region) + s3Nodes.push(...nodes) + } + return s3Nodes + }, + getParent: () => this, + } + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioProjectNode.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioProjectNode.ts new file mode 100644 index 00000000000..8097ceed9e7 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioProjectNode.ts @@ -0,0 +1,242 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { getLogger } from '../../../shared/logger/logger' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { AwsCredentialIdentity } from '@aws-sdk/types' +import { SageMakerUnifiedStudioDataNode } from './sageMakerUnifiedStudioDataNode' +import { DataZoneClient, DataZoneProject } from '../../shared/client/datazoneClient' +import { SageMakerUnifiedStudioRootNode } from './sageMakerUnifiedStudioRootNode' +import { SagemakerClient } from '../../../shared/clients/sagemaker' +import { SmusAuthenticationProvider } from '../../auth/providers/smusAuthenticationProvider' +import { SageMakerUnifiedStudioComputeNode } from './sageMakerUnifiedStudioComputeNode' +import { getIcon } from '../../../shared/icons' +import { getResourceMetadata } from '../../shared/utils/resourceMetadataUtils' +import { getContext } from '../../../shared/vscode/setContext' + +/** + * Tree node representing a SageMaker Unified Studio project + */ +export class SageMakerUnifiedStudioProjectNode implements TreeNode { + public readonly id = 'smusProjectNode' + public readonly resource = this + private readonly onDidChangeEmitter = new vscode.EventEmitter() + public readonly onDidChangeTreeItem = this.onDidChangeEmitter.event + public readonly onDidChangeChildren = this.onDidChangeEmitter.event + public project?: DataZoneProject + private logger = getLogger() + private sagemakerClient?: SagemakerClient + private hasShownFirstTimeMessage = false + private isFirstTimeSelection = false + + constructor( + private readonly parent: SageMakerUnifiedStudioRootNode, + private readonly authProvider: SmusAuthenticationProvider, + private readonly extensionContext: vscode.ExtensionContext + ) { + // If we're in SMUS space environment, set project from resource metadata + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + const resourceMetadata = getResourceMetadata()! + if (resourceMetadata.AdditionalMetadata!.DataZoneProjectId) { + this.project = { + id: resourceMetadata!.AdditionalMetadata!.DataZoneProjectId!, + name: 'Current Project', + domainId: resourceMetadata!.AdditionalMetadata!.DataZoneDomainId!, + } + // Fetch the actual project name asynchronously + void this.fetchProjectName() + } + } + } + + public async getTreeItem(): Promise { + if (this.project) { + const item = new vscode.TreeItem('Project: ' + this.project.name, vscode.TreeItemCollapsibleState.Expanded) + item.contextValue = 'smusSelectedProject' + item.tooltip = `Project: ${this.project.name}\nID: ${this.project.id}` + item.iconPath = getIcon('vscode-folder-opened') + return item + } + + const item = new vscode.TreeItem('Select a project', vscode.TreeItemCollapsibleState.Expanded) + item.contextValue = 'smusProjectSelectPicker' + item.command = { + command: 'aws.smus.projectView', + title: 'Select Project', + arguments: [this], + } + item.iconPath = getIcon('vscode-folder-opened') + + return item + } + + public async getChildren(): Promise { + if (!this.project) { + return [] + } + + return telemetry.smus_renderProjectChildrenNode.run(async (span) => { + try { + const isInSmusSpace = getContext('aws.smus.inSmusSpaceEnvironment') + const accountId = await this.authProvider.getDomainAccountId() + span.record({ + smusToolkitEnv: isInSmusSpace ? 'smus_space' : 'local', + smusDomainId: this.project?.domainId, + smusDomainAccountId: accountId, + smusProjectId: this.project?.id, + smusDomainRegion: this.authProvider.getDomainRegion(), + }) + + // Skip access check if we're in SMUS space environment (already in project space) + if (!getContext('aws.smus.inSmusSpaceEnvironment')) { + const hasAccess = await this.checkProjectCredsAccess(this.project!.id) + if (!hasAccess) { + return [ + { + id: 'smusProjectAccessDenied', + resource: {}, + getTreeItem: () => { + const item = new vscode.TreeItem( + 'You do not have access to this project. Contact your administrator.', + vscode.TreeItemCollapsibleState.None + ) + return item + }, + getParent: () => this, + }, + ] + } + } + + const dataNode = new SageMakerUnifiedStudioDataNode(this) + + // If we're in SMUS space environment, only show data node + if (getContext('aws.smus.inSmusSpaceEnvironment')) { + return [dataNode] + } + + const dzClient = await DataZoneClient.getInstance(this.authProvider) + if (!this.project?.id) { + throw new Error('Project ID is required') + } + const toolingEnv = await dzClient.getToolingEnvironment(this.project.id) + const spaceAwsAccountRegion = toolingEnv.awsAccountRegion + + if (!spaceAwsAccountRegion) { + throw new Error('No AWS account region found in tooling environment') + } + if (this.isFirstTimeSelection && !this.hasShownFirstTimeMessage) { + this.hasShownFirstTimeMessage = true + void vscode.window.showInformationMessage( + 'Find your space in the Explorer panel under SageMaker Unified Studio. Hover over any space and click the connection icon to connect remotely.' + ) + } + this.sagemakerClient = await this.initializeSagemakerClient(spaceAwsAccountRegion) + const computeNode = new SageMakerUnifiedStudioComputeNode( + this, + this.extensionContext, + this.authProvider, + this.sagemakerClient + ) + return [dataNode, computeNode] + } catch (err) { + this.logger.error('Failed to select project: %s', (err as Error).message) + throw err + } + }) + } + + public getParent(): TreeNode | undefined { + return this.parent + } + + public async refreshNode(): Promise { + this.onDidChangeEmitter.fire() + } + + public async setProject(project: any): Promise { + await this.cleanupProjectResources() + this.isFirstTimeSelection = !this.project + this.project = project + } + + public getProject(): DataZoneProject | undefined { + return this.project + } + + public async clearProject(): Promise { + await this.cleanupProjectResources() + // Don't clear project if we're in SMUS space environment + if (!getContext('aws.smus.inSmusSpaceEnvironment')) { + this.project = undefined + } + await this.refreshNode() + } + + private async cleanupProjectResources(): Promise { + await this.authProvider.invalidateAllProjectCredentialsInCache() + if (this.sagemakerClient) { + this.sagemakerClient.dispose() + this.sagemakerClient = undefined + } + } + + private async checkProjectCredsAccess(projectId: string): Promise { + // TODO: Ideally we should be checking user project access by calling fetchAllProjectMemberships + // and checking if user is part of that, or get user groups and check if any of the groupIds + // exists in the project memberships for more comprehensive access validation. + try { + const projectProvider = await this.authProvider.getProjectCredentialProvider(projectId) + this.logger.info(`Successfully obtained project credentials provider for project ${projectId}`) + await projectProvider.getCredentials() + return true + } catch (err) { + // If err.name is 'AccessDeniedException', it means user doesn't have access to the project + // We can safely return false in that case without logging the error + if ((err as any).name === 'AccessDeniedException') { + this.logger.debug( + 'Access denied when obtaining project credentials, user likely lacks project access or role permissions' + ) + } + return false + } + } + + private async fetchProjectName(): Promise { + if (!this.project || !getContext('aws.smus.inSmusSpaceEnvironment')) { + return + } + + try { + const dzClient = await DataZoneClient.getInstance(this.authProvider) + const projectDetails = await dzClient.getProject(this.project.id) + + if (projectDetails && projectDetails.name) { + this.project.name = projectDetails.name + // Refresh the tree item to show the updated name + this.onDidChangeEmitter.fire() + } + } catch (err) { + // No need to show error, this is just to dynamically show project name + // If we fail to fetch project name, we will just show the default name + this.logger.debug(`Failed to fetch project name: ${(err as Error).message}`) + } + } + + private async initializeSagemakerClient(regionCode: string): Promise { + if (!this.project) { + throw new Error('No project selected for initializing SageMaker client') + } + const projectProvider = await this.authProvider.getProjectCredentialProvider(this.project.id) + this.logger.info(`Successfully obtained project credentials provider for project ${this.project.id}`) + const awsCredentialProvider = async (): Promise => { + return await projectProvider.getCredentials() + } + const sagemakerClient = new SagemakerClient(regionCode, awsCredentialProvider) + return sagemakerClient + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioRootNode.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioRootNode.ts new file mode 100644 index 00000000000..db3f6959969 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioRootNode.ts @@ -0,0 +1,463 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { getIcon } from '../../../shared/icons' +import { getLogger } from '../../../shared/logger/logger' +import { DataZoneClient, DataZoneProject } from '../../shared/client/datazoneClient' +import { Commands } from '../../../shared/vscode/commands2' +import { telemetry } from '../../../shared/telemetry/telemetry' +import { createQuickPick } from '../../../shared/ui/pickerPrompter' +import { SageMakerUnifiedStudioProjectNode } from './sageMakerUnifiedStudioProjectNode' +import { SageMakerUnifiedStudioAuthInfoNode } from './sageMakerUnifiedStudioAuthInfoNode' +import { SmusErrorCodes, SmusUtils } from '../../shared/smusUtils' +import { SmusAuthenticationProvider } from '../../auth/providers/smusAuthenticationProvider' +import { ToolkitError } from '../../../../src/shared/errors' +import { recordAuthTelemetry } from '../../shared/telemetry' + +const contextValueSmusRoot = 'sageMakerUnifiedStudioRoot' +const contextValueSmusLogin = 'sageMakerUnifiedStudioLogin' +const contextValueSmusLearnMore = 'sageMakerUnifiedStudioLearnMore' +const projectPickerTitle = 'Select a SageMaker Unified Studio project you want to open' +const projectPickerPlaceholder = 'Select project' + +export class SageMakerUnifiedStudioRootNode implements TreeNode { + public readonly id = 'smusRootNode' + public readonly resource = this + private readonly logger = getLogger() + private readonly projectNode: SageMakerUnifiedStudioProjectNode + private readonly authInfoNode: SageMakerUnifiedStudioAuthInfoNode + private readonly onDidChangeEmitter = new vscode.EventEmitter() + public readonly onDidChangeTreeItem = this.onDidChangeEmitter.event + public readonly onDidChangeChildren = this.onDidChangeEmitter.event + + public constructor( + private readonly authProvider: SmusAuthenticationProvider, + private readonly extensionContext: vscode.ExtensionContext + ) { + this.authInfoNode = new SageMakerUnifiedStudioAuthInfoNode(this) + this.projectNode = new SageMakerUnifiedStudioProjectNode(this, this.authProvider, this.extensionContext) + + // Subscribe to auth provider connection changes to refresh the node + this.authProvider.onDidChange(async () => { + // Clear the project when connection changes + await this.projectNode.clearProject() + this.onDidChangeEmitter.fire() + // Immediately refresh the tree view to show authenticated state + try { + await vscode.commands.executeCommand('aws.smus.rootView.refresh') + } catch (refreshErr) { + this.logger.debug( + `Failed to refresh views after connection state change: ${(refreshErr as Error).message}` + ) + } + }) + } + + public getTreeItem(): vscode.TreeItem { + const item = new vscode.TreeItem('SageMaker Unified Studio', vscode.TreeItemCollapsibleState.Expanded) + item.contextValue = contextValueSmusRoot + item.iconPath = getIcon('vscode-database') + + // Set description based on authentication state + if (!this.isAuthenticated()) { + item.description = 'Not authenticated' + } else { + item.description = 'Connected' + } + + return item + } + + public async getChildren(): Promise { + const isAuthenticated = this.isAuthenticated() + const hasExpiredConnection = this.hasExpiredConnection() + + this.logger.debug( + `SMUS Root Node getChildren: isAuthenticated=${isAuthenticated}, hasExpiredConnection=${hasExpiredConnection}` + ) + + // Check for expired connection first + if (hasExpiredConnection) { + // Show auth info node with expired indication + return [this.authInfoNode] // This will show expired connection info + } + + // Check authentication state + if (!isAuthenticated) { + // Show login option and learn more link when not authenticated + return [ + { + id: 'smusLogin', + resource: {}, + getTreeItem: () => { + const item = new vscode.TreeItem('Sign in to get started', vscode.TreeItemCollapsibleState.None) + item.contextValue = contextValueSmusLogin + item.iconPath = getIcon('vscode-account') + + // Set up the login command + item.command = { + command: 'aws.smus.login', + title: 'Sign in to SageMaker Unified Studio', + } + + return item + }, + getParent: () => this, + }, + { + id: 'smusLearnMore', + resource: {}, + getTreeItem: () => { + const item = new vscode.TreeItem( + 'Learn more about SageMaker Unified Studio', + vscode.TreeItemCollapsibleState.None + ) + item.contextValue = contextValueSmusLearnMore + item.iconPath = getIcon('vscode-question') + + // Set up the learn more command + item.command = { + command: 'aws.smus.learnMore', + title: 'Learn more about SageMaker Unified Studio', + } + + return item + }, + getParent: () => this, + }, + ] + } + + // When authenticated, show auth info and projects + return [this.authInfoNode, this.projectNode] + } + + public getProjectSelectNode(): SageMakerUnifiedStudioProjectNode { + return this.projectNode + } + + public getAuthInfoNode(): SageMakerUnifiedStudioAuthInfoNode { + return this.authInfoNode + } + + public refresh(): void { + this.onDidChangeEmitter.fire() + } + + /** + * Checks if the user has authenticated to SageMaker Unified Studio + * This is validated by checking existing Connections for SMUS or resource metadata. + */ + private isAuthenticated(): boolean { + try { + // Check if the connection is valid using the authentication provider + const result = this.authProvider.isConnectionValid() + this.logger.debug(`SMUS Root Node: Authentication check result: ${result}`) + return result + } catch (err) { + this.logger.debug('Authentication check failed: %s', (err as Error).message) + return false + } + } + + private hasExpiredConnection(): boolean { + try { + const activeConnection = this.authProvider.activeConnection + const isConnectionValid = this.authProvider.isConnectionValid() + + this.logger.debug( + `SMUS Root Node: activeConnection=${!!activeConnection}, isConnectionValid=${isConnectionValid}` + ) + + // Check if there's an active connection but it's expired/invalid + const hasExpiredConnection = activeConnection && !isConnectionValid + + if (hasExpiredConnection) { + this.logger.debug('SMUS Root Node: Connection is expired, showing reauthentication prompt') + // Show reauthentication prompt to user + void this.authProvider.showReauthenticationPrompt(activeConnection as any) + return true + } + return false + } catch (err) { + this.logger.debug('Failed to check expired connection: %s', (err as Error).message) + return false + } + } +} + +/** + * Command to open the SageMaker Unified Studio documentation + */ +export const smusLearnMoreCommand = Commands.declare('aws.smus.learnMore', () => async () => { + const logger = getLogger() + try { + // Open the SageMaker Unified Studio documentation + await vscode.env.openExternal(vscode.Uri.parse('https://aws.amazon.com/sagemaker/unified-studio/')) + + // Log telemetry + telemetry.record({ + name: 'smus_learnMoreClicked', + result: 'Succeeded', + passive: false, + }) + } catch (err) { + logger.error('Failed to open SageMaker Unified Studio documentation: %s', (err as Error).message) + + // Log failure telemetry + telemetry.record({ + name: 'smus_learnMoreClicked', + result: 'Failed', + passive: false, + }) + } +}) + +/** + * Command to login to SageMaker Unified Studio + */ +export const smusLoginCommand = Commands.declare('aws.smus.login', () => async () => { + const logger = getLogger() + return telemetry.smus_login.run(async (span) => { + try { + // Get DataZoneClient instance for URL validation + + // Show domain URL input dialog + const domainUrl = await vscode.window.showInputBox({ + title: 'SageMaker Unified Studio Authentication', + prompt: 'Enter your SageMaker Unified Studio Domain URL', + placeHolder: 'https://.sagemaker..on.aws', + validateInput: (value) => SmusUtils.validateDomainUrl(value), + }) + + if (!domainUrl) { + // User cancelled + logger.debug('User cancelled domain URL input') + throw new ToolkitError('User cancelled domain URL input', { + cancelled: true, + code: SmusErrorCodes.UserCancelled, + }) + } + + // Show a simple status bar message instead of progress dialog + vscode.window.setStatusBarMessage('Connecting to SageMaker Unified Studio...', 10000) + + try { + // Get the authentication provider instance + const authProvider = SmusAuthenticationProvider.fromContext() + + // Connect to SMUS using the authentication provider + const connection = await authProvider.connectToSmus(domainUrl) + + if (!connection) { + throw new ToolkitError('Failed to establish connection', { + code: SmusErrorCodes.FailedAuthConnecton, + }) + } + + // Extract domain account ID, domain ID, and region for logging + const domainId = connection.domainId + const region = connection.ssoRegion + + logger.info(`Connected to SageMaker Unified Studio domain: ${domainId} in region ${region}`) + await recordAuthTelemetry(span, authProvider, domainId, region) + + // Show success message + void vscode.window.showInformationMessage( + `Successfully connected to SageMaker Unified Studio domain: ${domainId}` + ) + + // Clear the status bar message + vscode.window.setStatusBarMessage('Connected to SageMaker Unified Studio', 3000) + + // Immediately refresh the tree view to show authenticated state + try { + await vscode.commands.executeCommand('aws.smus.rootView.refresh') + } catch (refreshErr) { + logger.debug(`Failed to refresh views after login: ${(refreshErr as Error).message}`) + } + } catch (connectionErr) { + // Clear the status bar message + vscode.window.setStatusBarMessage('Connection to SageMaker Unified Studio Failed') + + // Log the error and re-throw to be handled by the outer catch block + logger.error('Connection failed: %s', (connectionErr as Error).message) + throw new ToolkitError('Connection failed.', { + cause: connectionErr as Error, + code: (connectionErr as Error).name, + }) + } + } catch (err) { + const isUserCancelled = err instanceof ToolkitError && err.code === SmusErrorCodes.UserCancelled + if (!isUserCancelled) { + void vscode.window.showErrorMessage( + `SageMaker Unified Studio: Failed to initiate login: ${(err as Error).message}` + ) + } + logger.error('Failed to initiate login: %s', (err as Error).message) + throw new ToolkitError('Failed to initiate login.', { + cause: err as Error, + code: (err as Error).name, + }) + } + }) +}) + +/** + * Command to sign out from SageMaker Unified Studio + */ +export const smusSignOutCommand = Commands.declare('aws.smus.signOut', () => async () => { + const logger = getLogger() + return telemetry.smus_signOut.run(async (span) => { + try { + // Get the authentication provider instance + const authProvider = SmusAuthenticationProvider.fromContext() + + // Check if there's an active connection to sign out from + if (!authProvider.isConnected()) { + void vscode.window.showInformationMessage( + 'No active SageMaker Unified Studio connection to sign out from.' + ) + return + } + + // Get connection details for logging + const activeConnection = authProvider.activeConnection + const domainId = activeConnection?.domainId + const region = activeConnection?.ssoRegion + + // Show status message + vscode.window.setStatusBarMessage('Signing out from SageMaker Unified Studio...', 5000) + await recordAuthTelemetry(span, authProvider, domainId, region) + + // Delete the connection (this will also invalidate tokens and clear cache) + if (activeConnection) { + await authProvider.secondaryAuth.deleteConnection() + logger.info(`Signed out from SageMaker Unified Studio${domainId}`) + } + + // Show success message + void vscode.window.showInformationMessage('Successfully signed out from SageMaker Unified Studio.') + + // Clear the status bar message + vscode.window.setStatusBarMessage('Signed out from SageMaker Unified Studio', 3000) + + // Refresh the tree view to show the sign-in state + try { + await vscode.commands.executeCommand('aws.smus.rootView.refresh') + } catch (refreshErr) { + logger.debug(`Failed to refresh views after sign out: ${(refreshErr as Error).message}`) + throw new ToolkitError('Failed to refresh views after sign out.', { + cause: refreshErr as Error, + code: (refreshErr as Error).name, + }) + } + } catch (err) { + void vscode.window.showErrorMessage( + `SageMaker Unified Studio: Failed to sign out: ${(err as Error).message}` + ) + logger.error('Failed to sign out: %s', (err as Error).message) + + // Log failure telemetry + throw new ToolkitError('Failed to sign out.', { + cause: err as Error, + code: (err as Error).name, + }) + } + }) +}) + +function isAccessDenied(error: Error): boolean { + return error.name.includes('AccessDenied') +} + +function createProjectQuickPickItems(projects: DataZoneProject[]) { + return projects + .sort( + (a, b) => + (b.updatedAt ? new Date(b.updatedAt).getTime() : 0) - + (a.updatedAt ? new Date(a.updatedAt).getTime() : 0) + ) + .filter((project) => project.name !== 'GenerativeAIModelGovernanceProject') + .map((project) => ({ + label: project.name, + detail: 'ID: ' + project.id, + description: project.description, + data: project, + })) +} + +async function showQuickPick(items: any[]) { + const quickPick = createQuickPick(items, { + title: projectPickerTitle, + placeholder: projectPickerPlaceholder, + }) + return await quickPick.prompt() +} + +export async function selectSMUSProject(projectNode?: SageMakerUnifiedStudioProjectNode) { + const logger = getLogger() + + return telemetry.smus_accessProject.run(async (span) => { + try { + const authProvider = SmusAuthenticationProvider.fromContext() + if (!authProvider.activeConnection) { + logger.error('No active connection to display project view') + return + } + + const client = await DataZoneClient.getInstance(authProvider) + logger.debug('DataZone client instance obtained successfully') + + const allProjects = await client.fetchAllProjects() + const items = createProjectQuickPickItems(allProjects) + + if (items.length === 0) { + logger.info('No projects found in the domain') + void vscode.window.showInformationMessage('No projects found in the domain') + await showQuickPick([{ label: 'No projects found', detail: '', description: '', data: {} }]) + return + } + + const selectedProject = await showQuickPick(items) + const accountId = await authProvider.getDomainAccountId() + span.record({ + smusDomainId: authProvider.getDomainId(), + smusProjectId: (selectedProject as DataZoneProject).id as string | undefined, + smusDomainRegion: authProvider.getDomainRegion(), + smusDomainAccountId: accountId, + }) + if ( + selectedProject && + typeof selectedProject === 'object' && + selectedProject !== null && + !('type' in selectedProject) && + projectNode + ) { + await projectNode.setProject(selectedProject) + await vscode.commands.executeCommand('aws.smus.rootView.refresh') + } + + return selectedProject + } catch (err) { + const error = err as Error + + if (isAccessDenied(error)) { + await showQuickPick([ + { + label: '$(error)', + description: "You don't have permissions to view projects. Please contact your administrator", + }, + ]) + return + } + + logger.error('Failed to select project: %s', error.message) + void vscode.window.showErrorMessage(`Failed to select project: ${error.message}`) + } + }) +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpaceNode.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpaceNode.ts new file mode 100644 index 00000000000..53ae501d967 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpaceNode.ts @@ -0,0 +1,108 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { SagemakerClient, SagemakerSpaceApp } from '../../../shared/clients/sagemaker' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { SageMakerUnifiedStudioSpacesParentNode } from './sageMakerUnifiedStudioSpacesParentNode' +import { SagemakerSpace } from '../../../awsService/sagemaker/sagemakerSpace' + +export class SagemakerUnifiedStudioSpaceNode implements TreeNode { + private smSpace: SagemakerSpace + private readonly onDidChangeEmitter = new vscode.EventEmitter() + public readonly onDidChangeTreeItem = this.onDidChangeEmitter.event + public readonly onDidChangeChildren = this.onDidChangeEmitter.event + + public constructor( + private readonly parent: SageMakerUnifiedStudioSpacesParentNode, + public readonly sageMakerClient: SagemakerClient, + public readonly regionCode: string, + public readonly spaceApp: SagemakerSpaceApp, + isSMUSSpace: boolean + ) { + this.smSpace = new SagemakerSpace(this.sageMakerClient, this.regionCode, this.spaceApp, isSMUSSpace) + } + + public getTreeItem(): vscode.TreeItem { + return { + label: this.smSpace.label, + description: this.smSpace.description, + tooltip: this.smSpace.tooltip, + iconPath: this.smSpace.iconPath, + contextValue: this.smSpace.contextValue, + collapsibleState: vscode.TreeItemCollapsibleState.None, + } + } + + public getChildren(): TreeNode[] { + return [] + } + + public getParent(): TreeNode | undefined { + return this.parent + } + + public async refreshNode(): Promise { + this.onDidChangeEmitter.fire() + } + + public get id(): string { + return 'smusSpaceNode' + this.name + } + + public get resource() { + return this + } + + // Delegate all core functionality to SageMakerSpace instance + public updateSpace(spaceApp: SagemakerSpaceApp) { + this.smSpace.updateSpace(spaceApp) + if (this.isPending()) { + this.parent.trackPendingNode(this.DomainSpaceKey) + } + } + + public setSpaceStatus(spaceStatus: string, appStatus: string) { + this.smSpace.setSpaceStatus(spaceStatus, appStatus) + } + public isPending(): boolean { + return this.smSpace.isPending() + } + public getStatus(): string { + return this.smSpace.getStatus() + } + public async getAppStatus() { + return this.smSpace.getAppStatus() + } + public get name(): string { + return this.smSpace.name + } + public get arn(): string { + return this.smSpace.arn + } + public async getAppArn() { + return this.smSpace.getAppArn() + } + public async getSpaceArn() { + return this.smSpace.getSpaceArn() + } + public async updateSpaceAppStatus() { + await this.smSpace.updateSpaceAppStatus() + + if (this.isPending()) { + this.parent.trackPendingNode(this.DomainSpaceKey) + } + return + } + public buildTooltip() { + return this.smSpace.buildTooltip() + } + public getAppIcon() { + return this.smSpace.getAppIcon() + } + public get DomainSpaceKey(): string { + return this.smSpace.DomainSpaceKey + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpacesParentNode.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpacesParentNode.ts new file mode 100644 index 00000000000..a5421716d8f --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/sageMakerUnifiedStudioSpacesParentNode.ts @@ -0,0 +1,235 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { SageMakerUnifiedStudioComputeNode } from './sageMakerUnifiedStudioComputeNode' +import { updateInPlace } from '../../../shared/utilities/collectionUtils' +import { DataZoneClient } from '../../shared/client/datazoneClient' +import { DescribeDomainResponse } from '@amzn/sagemaker-client' +import { getDomainUserProfileKey } from '../../../awsService/sagemaker/utils' +import { getLogger } from '../../../shared/logger/logger' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { SagemakerClient, SagemakerSpaceApp } from '../../../shared/clients/sagemaker' +import { UserProfileMetadata } from '../../../awsService/sagemaker/explorer/sagemakerParentNode' +import { SagemakerUnifiedStudioSpaceNode } from './sageMakerUnifiedStudioSpaceNode' +import { PollingSet } from '../../../shared/utilities/pollingSet' +import { SmusAuthenticationProvider } from '../../auth/providers/smusAuthenticationProvider' +import { SmusUtils } from '../../shared/smusUtils' +import { getIcon } from '../../../shared/icons' +import { PENDING_NODE_POLLING_INTERVAL_MS } from './utils' + +export class SageMakerUnifiedStudioSpacesParentNode implements TreeNode { + public readonly id = 'smusSpacesParentNode' + public readonly resource = this + private readonly sagemakerSpaceNodes: Map = new Map() + private spaceApps: Map = new Map() + private domainUserProfiles: Map = new Map() + private readonly logger = getLogger() + private readonly onDidChangeEmitter = new vscode.EventEmitter() + public readonly onDidChangeTreeItem = this.onDidChangeEmitter.event + public readonly onDidChangeChildren = this.onDidChangeEmitter.event + public readonly pollingSet: PollingSet = new PollingSet( + PENDING_NODE_POLLING_INTERVAL_MS, + this.updatePendingNodes.bind(this) + ) + private spaceAwsAccountRegion: string | undefined + + public constructor( + private readonly parent: SageMakerUnifiedStudioComputeNode, + private readonly projectId: string, + private readonly extensionContext: vscode.ExtensionContext, + private readonly authProvider: SmusAuthenticationProvider, + private readonly sagemakerClient: SagemakerClient + ) {} + + public async getTreeItem(): Promise { + const item = new vscode.TreeItem('Spaces', vscode.TreeItemCollapsibleState.Expanded) + item.iconPath = { + light: vscode.Uri.joinPath( + this.extensionContext.extensionUri, + 'resources/icons/aws/sagemakerunifiedstudio/spaces-dark.svg' + ), + dark: vscode.Uri.joinPath( + this.extensionContext.extensionUri, + 'resources/icons/aws/sagemakerunifiedstudio/spaces.svg' + ), + } + item.contextValue = 'smusSpacesNode' + item.description = 'Hover over any space and click the connection icon to connect remotely' + item.tooltip = item.description + return item + } + + public async getChildren(): Promise { + try { + await this.updateChildren() + } catch (err) { + const error = err as Error + if (error.name === 'AccessDeniedException') { + return this.getAccessDeniedChildren() + } + return this.getNoSpacesFoundChildren() + } + const nodes = [...this.sagemakerSpaceNodes.values()] + if (nodes.length === 0) { + return this.getNoSpacesFoundChildren() + } + return nodes + } + + private getNoSpacesFoundChildren(): TreeNode[] { + return [ + { + id: 'smusNoSpaces', + resource: {}, + getTreeItem: () => new vscode.TreeItem('[No Spaces found]', vscode.TreeItemCollapsibleState.None), + getParent: () => this, + }, + ] + } + + private getAccessDeniedChildren(): TreeNode[] { + return [ + { + id: 'smusAccessDenied', + resource: {}, + getTreeItem: () => { + const item = new vscode.TreeItem( + "You don't have permission to view spaces. Please contact your administrator.", + vscode.TreeItemCollapsibleState.None + ) + item.iconPath = getIcon('vscode-error') + return item + }, + getParent: () => this, + }, + ] + } + + public getParent(): TreeNode | undefined { + return this.parent + } + + public getProjectId(): string { + return this.projectId + } + + public getAuthProvider(): SmusAuthenticationProvider { + return this.authProvider + } + + public async refreshNode(): Promise { + this.onDidChangeEmitter.fire() + } + + public trackPendingNode(domainSpaceKey: string) { + this.pollingSet.add(domainSpaceKey) + } + + public getSpaceNodes(spaceKey: string): SagemakerUnifiedStudioSpaceNode { + 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 getSageMakerDomainId(): Promise { + const activeConnection = this.authProvider.activeConnection + if (!activeConnection) { + this.logger.error('There is no active connection to get SageMaker domain ID') + throw new Error('No active connection found to get SageMaker domain ID') + } + + this.logger.debug('SMUS: Getting DataZone client instance') + const datazoneClient = await DataZoneClient.getInstance(this.authProvider) + if (!datazoneClient) { + throw new Error('DataZone client is not initialized') + } + + const toolingEnv = await datazoneClient.getToolingEnvironment(this.projectId) + this.spaceAwsAccountRegion = toolingEnv.awsAccountRegion + if (toolingEnv.provisionedResources) { + for (const resource of toolingEnv.provisionedResources) { + if (resource.name === 'sageMakerDomainId') { + if (!resource.value) { + throw new Error('SageMaker domain ID not found in tooling environment') + } + getLogger().debug(`Found SageMaker domain ID: ${resource.value}`) + return resource.value + } + } + } + throw new Error('No SageMaker domain found in the tooling environment') + } + + private async updatePendingNodes() { + for (const spaceKey of this.pollingSet.values()) { + const childNode = this.getSpaceNodes(spaceKey) + await this.updatePendingSpaceNode(childNode) + } + } + + private async updatePendingSpaceNode(node: SagemakerUnifiedStudioSpaceNode) { + await node.updateSpaceAppStatus() + if (!node.isPending()) { + this.pollingSet.delete(node.DomainSpaceKey) + await node.refreshNode() + } + } + + private async updateChildren(): Promise { + const datazoneClient = await DataZoneClient.getInstance(this.authProvider) + // Will be of format: 'ABCA4NU3S7PEOLDQPLXYZ:user-12345678-d061-70a4-0bf2-eeee67a6ab12' + const userId = await datazoneClient.getUserId() + const ssoUserProfileId = SmusUtils.extractSSOIdFromUserId(userId || '') + const sagemakerDomainId = await this.getSageMakerDomainId() + const [spaceApps, domains] = await this.sagemakerClient.fetchSpaceAppsAndDomains( + sagemakerDomainId, + false /* filterSmusDomains */ + ) + // Filter spaceApps to only show spaces owned by current user + const filteredSpaceApps = new Map() + for (const [key, app] of spaceApps.entries()) { + const userProfile = app.OwnershipSettingsSummary?.OwnerUserProfileName + if (ssoUserProfileId === userProfile) { + filteredSpaceApps.set(key, app) + } + } + this.spaceApps = filteredSpaceApps + this.domainUserProfiles.clear() + + for (const app of this.spaceApps.values()) { + const domainId = app.DomainId + const userProfile = app.OwnershipSettingsSummary?.OwnerUserProfileName + if (!domainId || !userProfile) { + continue + } + + const domainUserProfileKey = getDomainUserProfileKey(domainId, userProfile) + this.domainUserProfiles.set(domainUserProfileKey, { + domain: domains.get(domainId) as DescribeDomainResponse, + }) + } + + updateInPlace( + this.sagemakerSpaceNodes, + this.spaceApps.keys(), + (key) => this.sagemakerSpaceNodes.get(key)!.updateSpace(this.spaceApps.get(key)!), + (key) => + new SagemakerUnifiedStudioSpaceNode( + this as any, + this.sagemakerClient, + this.spaceAwsAccountRegion || + (() => { + throw new Error('No AWS account region found in tooling environment') + })(), + this.spaceApps.get(key)!, + true /* isSMUSSpace */ + ) + ) + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/types.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/types.ts new file mode 100644 index 00000000000..a94d25fccc4 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/types.ts @@ -0,0 +1,207 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Node delimiter for creating unique IDs +// eslint-disable-next-line @typescript-eslint/naming-convention +export const NODE_ID_DELIMITER = '/' + +// eslint-disable-next-line @typescript-eslint/naming-convention +export const AWS_DATA_CATALOG = 'AwsDataCatalog' +// eslint-disable-next-line @typescript-eslint/naming-convention +export const DATA_DEFAULT_IAM_CONNECTION_NAME_REGEXP = /^(project\.iam)|(default\.iam)$/ +// eslint-disable-next-line @typescript-eslint/naming-convention, id-length +export const DATA_DEFAULT_LAKEHOUSE_CONNECTION_NAME_REGEXP = /^(project\.default_lakehouse)|(default\.catalog)$/ +// eslint-disable-next-line @typescript-eslint/naming-convention, id-length +export const DATA_DEFAULT_ATHENA_CONNECTION_NAME_REGEXP = /^(project\.athena)|(default\.sql)$/ +// eslint-disable-next-line @typescript-eslint/naming-convention +export const DATA_DEFAULT_S3_CONNECTION_NAME_REGEXP = /^(project\.s3_default_folder)|(default\.s3)$/ + +// Database object types +export enum DatabaseObjects { + EXTERNAL_TABLE = 'EXTERNAL_TABLE', + VIRTUAL_VIEW = 'VIRTUAL_VIEW', +} + +// Ref: https://docs.aws.amazon.com/athena/latest/ug/data-types.html +export const lakeHouseColumnTypes = { + NUMERIC: ['TINYINT', 'SMALLINT', 'INT', 'INTEGER', 'BIGINT', 'FLOAT', 'REAL', 'DOUBLE', 'DECIMAL'], + STRING: ['CHAR', 'STRING', 'VARCHAR', 'UUID'], + TIME: ['DATE', 'TIMESTAMP', 'INTERVAL'], + BOOLEAN: ['BOOLEAN'], + BINARY: ['BINARY', 'VARBINARY'], + COMPLEX: ['ARRAY', 'MAP', 'STRUCT', 'ROW', 'JSON'], +} + +// Ref: https://docs.aws.amazon.com/redshift/latest/dg/c_Supported_data_types.html +export const redshiftColumnTypes = { + NUMERIC: ['SMALLINT', 'INT2', 'INTEGER', 'INT', 'BIGINT', 'DECIMAL', 'NUMERIC', 'REAL', 'FLOAT', 'DOUBLE'], + STRING: ['CHAR', 'CHARACTER', 'NCHAR', 'BPCHAR', 'VARCHAR', 'VARCHAR', 'VARYING', 'NVARCHAR', 'TEXT'], + TIME: ['TIME', 'TIMETZ', 'TIMESTAMP', 'TIMESTAMPTZ', 'INTERVAL'], + BOOLEAN: ['BOOLEAN', 'BOOL'], + BINARY: ['VARBYTE', 'VARBINARY', 'BINARY', 'VARYING'], + COMPLEX: ['HLLSKETCH', 'SUPER', 'GEOMETRY', 'GEOGRAPHY'], +} + +/** + * Node types for different resources + */ +export enum NodeType { + // Common types + CONNECTION = 'connection', + ERROR = 'error', + LOADING = 'loading', + EMPTY = 'empty', + + // S3 types + S3_BUCKET = 's3-bucket', + S3_FOLDER = 'folder', + S3_FILE = 'file', + S3_ACCESS_GRANT = 's3-access-grant', + + // Redshift types + REDSHIFT_CLUSTER = 'redshift-cluster', + REDSHIFT_DATABASE = 'database', + REDSHIFT_SCHEMA = 'schema', + REDSHIFT_TABLE = 'table', + REDSHIFT_VIEW = 'view', + REDSHIFT_FUNCTION = 'function', + REDSHIFT_STORED_PROCEDURE = 'storedProcedure', + REDSHIFT_COLUMN = 'column', + REDSHIFT_CONTAINER = 'container', + + // Glue types + GLUE_CATALOG = 'catalog', + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values + GLUE_DATABASE = 'database', + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values + GLUE_TABLE = 'table', + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values + GLUE_VIEW = 'view', + + // Redshift-specific catalog types + REDSHIFT_CATALOG = 'redshift-catalog', + REDSHIFT_CATALOG_DATABASE = 'redshift-catalog-database', +} + +/** + * Connection types + */ +export enum ConnectionType { + S3 = 'S3', + REDSHIFT = 'REDSHIFT', + ATHENA = 'ATHENA', + GLUE = 'GLUE', + LAKEHOUSE = 'LAKEHOUSE', +} + +/** + * Resource types for Redshift + */ +export enum ResourceType { + DATABASE = 'DATABASE', + CATALOG_DATABASE = 'CATALOG_DATABASE', + SCHEMA = 'SCHEMA', + TABLE = 'TABLE', + VIEW = 'VIEW', + FUNCTION = 'FUNCTION', + STORED_PROCEDURE = 'STORED_PROCEDURE', + COLUMNS = 'COLUMNS', + CATALOG = 'CATALOG', + EXTERNAL_DATABASE = 'EXTERNAL_DATABASE', + SHARED_DATABASE = 'SHARED_DATABASE', + EXTERNAL_SCHEMA = 'EXTERNAL_SCHEMA', + SHARED_SCHEMA = 'SHARED_SCHEMA', + EXTERNAL_TABLE = 'EXTERNAL_TABLE', + CATALOG_TABLE = 'CATALOG_TABLE', + DATA_CATALOG_TABLE = 'DATA_CATALOG_TABLE', + CATALOG_COLUMN = 'CATALOG_COLUMN', +} + +/** + * Node path information + */ +export interface NodePath { + connection?: string + bucket?: string + key?: string + catalog?: string + database?: string + schema?: string + table?: string + column?: string + cluster?: string + label?: string + [key: string]: any +} + +/** + * Node data interface for tree nodes + */ +export interface NodeData { + id: string + nodeType: NodeType + connectionType?: ConnectionType + value?: any + path?: NodePath + parent?: any + isContainer?: boolean + children?: any[] +} + +/** + * Redshift deployment types + */ +export enum RedshiftType { + Serverless = 'SERVERLESS', + ServerlessDev = 'SERVERLESS_DEV', + ServerlessQA = 'SERVERLESS_QA', + Cluster = 'CLUSTER', + ClusterDev = 'CLUSTER_DEV', + ClusterQA = 'CLUSTER_QA', +} + +/** + * Authentication types for database integration connections + */ +export enum DatabaseIntegrationConnectionAuthenticationTypes { + FEDERATED = '4', + TEMPORARY_CREDENTIALS_WITH_IAM = '5', + SECRET = '6', + IDC_ENHANCED_IAM_CREDENTIALS = '8', +} + +/** + * Redshift service model URLs + */ +export const RedshiftServiceModelUrl = { + REDSHIFT_SERVERLESS_URL: 'redshift-serverless.amazonaws.com', + REDSHIFT_CLUSTER_URL: 'redshift.amazonaws.com', +} + +/** + * Client types for ClientStore + */ +export enum ClientType { + S3Client = 'S3Client', + S3ControlClient = 'S3ControlClient', + SQLWorkbenchClient = 'SQLWorkbenchClient', + GlueClient = 'GlueClient', + GlueCatalogClient = 'GlueCatalogClient', +} + +/** + * Node types that are always leaf nodes + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export const LEAF_NODE_TYPES = [ + NodeType.S3_FILE, + NodeType.REDSHIFT_COLUMN, + NodeType.ERROR, + NodeType.LOADING, + NodeType.EMPTY, +] + +// eslint-disable-next-line @typescript-eslint/naming-convention +export const NO_DATA_FOUND_MESSAGE = '[No data found]' diff --git a/packages/core/src/sagemakerunifiedstudio/explorer/nodes/utils.ts b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/utils.ts new file mode 100644 index 00000000000..32924ad3d9f --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/explorer/nodes/utils.ts @@ -0,0 +1,391 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getIcon, IconPath, addColor } from '../../../shared/icons' +import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' +import { + NODE_ID_DELIMITER, + NodeType, + RedshiftServiceModelUrl, + RedshiftType, + ConnectionType, + NodeData, + LEAF_NODE_TYPES, + DATA_DEFAULT_LAKEHOUSE_CONNECTION_NAME_REGEXP, + redshiftColumnTypes, + lakeHouseColumnTypes, +} from './types' +import { DataZoneConnection } from '../../shared/client/datazoneClient' + +/** + * Polling interval in milliseconds for checking space status updates + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export const PENDING_NODE_POLLING_INTERVAL_MS = 5000 + +/** + * Gets the label for a node based on its data + */ +export function getLabel(data: { + id: string + nodeType: NodeType + isContainer?: boolean + path?: { key?: string; label?: string } + value?: any +}): string { + // For S3 access grant nodes, use S3 (label) format + if (data.nodeType === NodeType.S3_ACCESS_GRANT && data.path?.label) { + return `S3 (${data.path.label})` + } + + // For connection nodes, use the connection name + if (data.nodeType === NodeType.CONNECTION && data.value?.connection?.name) { + if ( + data.value?.connection?.type === ConnectionType.LAKEHOUSE && + DATA_DEFAULT_LAKEHOUSE_CONNECTION_NAME_REGEXP.test(data.value?.connection?.name) + ) { + return 'Lakehouse' + } + const formattedType = data.value?.connection?.type?.replace(/([A-Z]+(?:_[A-Z]+)*)/g, (match: string) => { + const words = match.split('_') + return words.map((word: string) => word.charAt(0) + word.slice(1).toLowerCase()).join(' ') + }) + return `${formattedType} (${data.value.connection.name})` + } + + // For container nodes, use the node type + if (data.isContainer) { + switch (data.nodeType) { + case NodeType.REDSHIFT_TABLE: + return 'Tables' + case NodeType.REDSHIFT_VIEW: + return 'Views' + case NodeType.REDSHIFT_FUNCTION: + return 'Functions' + case NodeType.REDSHIFT_STORED_PROCEDURE: + return 'Stored Procedures' + default: + return data.nodeType + } + } + + // For path-based nodes, use the last part of the path + if (data.path?.label) { + return data.path.label + } + + // For S3 folders, add a trailing slash + if (data.nodeType === NodeType.S3_FOLDER) { + const key = data.path?.key || '' + const parts = key.split('/') + return parts[parts.length - 2] + '/' + } + + // For S3 files, use the filename + if (data.nodeType === NodeType.S3_FILE) { + const key = data.path?.key || '' + const parts = key.split('/') + return parts[parts.length - 1] + } + + // For other nodes, use the last part of the ID + const parts = data.id.split(NODE_ID_DELIMITER) + return parts[parts.length - 1] +} + +/** + * Determines if a node is a leaf node + */ +export function isLeafNode(data: { nodeType: NodeType; isContainer?: boolean }): boolean { + // Container nodes are never leaf nodes + if (data.isContainer) { + return false + } + + return LEAF_NODE_TYPES.includes(data.nodeType) +} + +/** + * Gets the icon for a node type + */ +export function getIconForNodeType(nodeType: NodeType, isContainer?: boolean): vscode.ThemeIcon | IconPath | undefined { + switch (nodeType) { + case NodeType.CONNECTION: + case NodeType.S3_ACCESS_GRANT: + return undefined + case NodeType.S3_BUCKET: + return getIcon('aws-s3-bucket') + case NodeType.S3_FOLDER: + return getIcon('vscode-folder') + case NodeType.S3_FILE: + return getIcon('vscode-file') + case NodeType.REDSHIFT_CLUSTER: + return getIcon('aws-redshift-cluster') + case NodeType.REDSHIFT_DATABASE: + case NodeType.GLUE_DATABASE: + return new vscode.ThemeIcon('database') + case NodeType.REDSHIFT_SCHEMA: + return getIcon('aws-redshift-schema') + case NodeType.REDSHIFT_TABLE: + case NodeType.GLUE_TABLE: + return isContainer ? new vscode.ThemeIcon('table') : getIcon('aws-redshift-table') + case NodeType.REDSHIFT_VIEW: + return isContainer ? new vscode.ThemeIcon('list-tree') : new vscode.ThemeIcon('eye') + case NodeType.REDSHIFT_FUNCTION: + case NodeType.REDSHIFT_STORED_PROCEDURE: + return isContainer ? new vscode.ThemeIcon('list-tree') : new vscode.ThemeIcon('symbol-method') + case NodeType.GLUE_CATALOG: + return getIcon('aws-sagemakerunifiedstudio-catalog') + case NodeType.REDSHIFT_CATALOG: + return new vscode.ThemeIcon('database') + case NodeType.REDSHIFT_CATALOG_DATABASE: + return getIcon('aws-redshift-schema') + case NodeType.ERROR: + return new vscode.ThemeIcon('error') + case NodeType.LOADING: + return new vscode.ThemeIcon('loading~spin') + case NodeType.EMPTY: + return new vscode.ThemeIcon('info') + default: + return getIcon('vscode-circle-outline') + } +} + +/** + * Creates a standard tree item for a node + */ +export function createTreeItem( + label: string, + nodeType: NodeType, + isLeaf: boolean, + isContainer?: boolean, + tooltip?: string +): vscode.TreeItem { + const collapsibleState = isLeaf ? vscode.TreeItemCollapsibleState.None : vscode.TreeItemCollapsibleState.Collapsed + + const item = new vscode.TreeItem(label, collapsibleState) + + // Set icon based on node type + item.iconPath = getIconForNodeType(nodeType, isContainer) + + // Set context value for command enablement + item.contextValue = nodeType + + // Set tooltip if provided + if (tooltip) { + item.tooltip = tooltip + } + + return item +} + +/** + * Gets the column type category from a raw column type string + */ +export function getColumnType(columnTypeString?: string): string { + if (!columnTypeString) { + return 'UNKNOWN' + } + + const lowerType = columnTypeString.toLowerCase() + + // Search in both redshift and lakehouse column types + const allTypes = [...Object.values(redshiftColumnTypes).flat(), ...Object.values(lakeHouseColumnTypes).flat()].map( + (type) => type.toLowerCase() + ) + + return allTypes.find((key) => lowerType.startsWith(key)) || 'UNKNOWN' +} + +/** + * Gets the icon for a column based on its type + */ +function getColumnIcon(columnType: string): vscode.ThemeIcon | IconPath { + const upperType = columnType.toUpperCase() + + // Check if it's a numeric type + if ( + lakeHouseColumnTypes.NUMERIC.some((type) => upperType.includes(type)) || + redshiftColumnTypes.NUMERIC.some((type) => upperType.includes(type)) + ) { + return getIcon('aws-sagemakerunifiedstudio-symbol-int') + } + + // Check if it's a string type + if ( + lakeHouseColumnTypes.STRING.some((type) => upperType.includes(type)) || + redshiftColumnTypes.STRING.some((type) => upperType.includes(type)) + ) { + return getIcon('vscode-symbol-key') + } + + // Check if it's a time type + if ( + lakeHouseColumnTypes.TIME.some((type) => upperType.includes(type)) || + redshiftColumnTypes.TIME.some((type) => upperType.includes(type)) + ) { + return getIcon('vscode-calendar') + } + + // Default icon for unknown types + return new vscode.ThemeIcon('symbol-field') +} + +/** + * Creates a tree item for a column node with type information + */ +export function createColumnTreeItem(label: string, columnType: string, nodeType: NodeType): vscode.TreeItem { + const item = new vscode.TreeItem(label, vscode.TreeItemCollapsibleState.None) + + // Add column type as description (secondary text) + item.description = columnType + + // Set icon based on column type + item.iconPath = getColumnIcon(columnType) + + // Set context value for command enablement + item.contextValue = nodeType + + // Set tooltip + item.tooltip = `${label}: ${columnType}` + + return item +} + +/** + * Creates an error node + */ +export function createErrorTreeItem(message: string): vscode.TreeItem { + const item = new vscode.TreeItem(message, vscode.TreeItemCollapsibleState.None) + item.iconPath = new vscode.ThemeIcon('error') + return item +} + +/** + * Creates an error item with unique ID and proper styling + */ +export function createErrorItem(message: string, context: string, parentId: string): TreeNode { + return { + id: `${parentId}-error-${context}-${Date.now()}`, + resource: message, + getTreeItem: () => { + const item = new vscode.TreeItem(message, vscode.TreeItemCollapsibleState.None) + item.iconPath = addColor(getIcon('vscode-error'), 'testing.iconErrored') + return item + }, + } +} + +export const isRedLakeDatabase = (databaseName?: string) => { + if (!databaseName) { + return false + } + const regex = /[\w\d\-_]+@[\w\d\-_]+/gs + return regex.test(databaseName) +} + +/** + * Gets the tooltip for a node + * @param data The node data + * @returns The tooltip text + */ +export function getTooltip(data: NodeData): string { + const label = getLabel(data) + + switch (data.nodeType) { + // Common node types + case NodeType.CONNECTION: + return data.connectionType === ConnectionType.REDSHIFT + ? `Redshift Connection: ${label}` + : `Connection: ${label}\nType: ${data.connectionType}` + + // S3 node types + case NodeType.S3_BUCKET: + return `S3 Bucket: ${data.path?.bucket}` + case NodeType.S3_FOLDER: + return `Folder: ${label}\nBucket: ${data.path?.bucket}` + case NodeType.S3_FILE: + return `File: ${label}\nBucket: ${data.path?.bucket}` + + // Redshift node types + case NodeType.REDSHIFT_CLUSTER: + return `Redshift Cluster: ${label}` + case NodeType.REDSHIFT_DATABASE: + return `Database: ${label}` + case NodeType.REDSHIFT_SCHEMA: + return `Schema: ${label}` + case NodeType.REDSHIFT_TABLE: + return data.isContainer ? `Tables in ${data.path?.schema}` : `Table: ${data.path?.schema}.${label}` + case NodeType.REDSHIFT_VIEW: + return data.isContainer ? `Views in ${data.path?.schema}` : `View: ${data.path?.schema}.${label}` + case NodeType.REDSHIFT_FUNCTION: + return data.isContainer ? `Functions in ${data.path?.schema}` : `Function: ${data.path?.schema}.${label}` + case NodeType.REDSHIFT_STORED_PROCEDURE: + return data.isContainer + ? `Stored Procedures in ${data.path?.schema}` + : `Stored Procedure: ${data.path?.schema}.${label}` + + // Glue node types + case NodeType.GLUE_CATALOG: + return `Glue Catalog: ${label}` + case NodeType.GLUE_DATABASE: + return `Glue Database: ${label}` + case NodeType.GLUE_TABLE: + return `Glue Table: ${label}` + + // Default + default: + return label + } +} + +/** + * Gets the Redshift type from a host + * @param host Redshift host + * @returns Redshift type or null if not recognized + */ +export function getRedshiftTypeFromHost(host?: string): RedshiftType | undefined { + /* + 'default-workgroup.{accountID}.us-west-2.redshift-serverless.amazonaws.com' - SERVERLESS + 'default-rs-cluster.{id}.us-west-2.redshift.amazonaws.com' - CLUSTER + 'default-rs-cluster.{id}.us-west-2.redshift.amazonaws.com:5439/dev' - CLUSTER + */ + if (!host) { + return undefined + } + + const cleanHost = host.split(':')[0] + const parts = cleanHost.split('.') + if (parts.length < 3) { + return undefined + } + + const domain = parts.slice(parts.length - 3).join('.') + + if (domain === RedshiftServiceModelUrl.REDSHIFT_SERVERLESS_URL) { + return RedshiftType.Serverless + } else if (domain === RedshiftServiceModelUrl.REDSHIFT_CLUSTER_URL) { + return RedshiftType.Cluster + } else { + return undefined + } +} + +/** + * Determines if a connection is a federated connection by checking its type. + * A connection is considered federated if it's either: + * 1. A Redshift connection with Glue properties, or + * 2. A connection type that exists in GlueConnectionType + * + * @param connection + * @returns - boolean + */ +export function isFederatedConnection(connection?: DataZoneConnection): boolean { + if (connection?.type === ConnectionType.REDSHIFT) { + return !!connection?.props?.glueProperties + } + return false +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/README.md b/packages/core/src/sagemakerunifiedstudio/shared/client/README.md new file mode 100644 index 00000000000..17cc4767beb --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/README.md @@ -0,0 +1 @@ +# Common business logic and APIs for SageMaker Unified Studio features diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/connectionClientStore.ts b/packages/core/src/sagemakerunifiedstudio/shared/client/connectionClientStore.ts new file mode 100644 index 00000000000..edf317f6479 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/connectionClientStore.ts @@ -0,0 +1,138 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { S3Client } from './s3Client' +import { SQLWorkbenchClient } from './sqlWorkbenchClient' +import { GlueClient } from './glueClient' +import { GlueCatalogClient } from './glueCatalogClient' +import { ConnectionCredentialsProvider } from '../../auth/providers/connectionCredentialsProvider' +import { ClientType } from '../../explorer/nodes/types' +import { S3ControlClient } from '@aws-sdk/client-s3-control' +import { getLogger } from '../../../shared/logger/logger' + +/** + * Client store for managing service clients per connection + */ +export class ConnectionClientStore { + private static instance: ConnectionClientStore + private clientCache: Record> = {} + + private constructor() {} + + public static getInstance(): ConnectionClientStore { + if (!ConnectionClientStore.instance) { + ConnectionClientStore.instance = new ConnectionClientStore() + } + return ConnectionClientStore.instance + } + + /** + * Gets or creates a client for a specific connection + */ + public getClient(connectionId: string, clientType: string, factory: () => T): T { + if (!this.clientCache[connectionId]) { + this.clientCache[connectionId] = {} + } + + if (!this.clientCache[connectionId][clientType]) { + this.clientCache[connectionId][clientType] = factory() + } + + return this.clientCache[connectionId][clientType] + } + + /** + * Gets or creates an S3Client for a connection + */ + public getS3Client( + connectionId: string, + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider + ): S3Client { + return this.getClient( + connectionId, + ClientType.S3Client, + () => new S3Client(region, connectionCredentialsProvider) + ) + } + + /** + * Gets or creates a SQLWorkbenchClient for a connection + */ + public getSQLWorkbenchClient( + connectionId: string, + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider + ): SQLWorkbenchClient { + return this.getClient(connectionId, ClientType.SQLWorkbenchClient, () => + SQLWorkbenchClient.createWithCredentials(region, connectionCredentialsProvider) + ) + } + + /** + * Gets or creates a GlueClient for a connection + */ + public getGlueClient( + connectionId: string, + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider + ): GlueClient { + return this.getClient( + connectionId, + ClientType.GlueClient, + () => new GlueClient(region, connectionCredentialsProvider) + ) + } + + /** + * Gets or creates a GlueCatalogClient for a connection + */ + public getGlueCatalogClient( + connectionId: string, + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider + ): GlueCatalogClient { + return this.getClient(connectionId, ClientType.GlueCatalogClient, () => + GlueCatalogClient.createWithCredentials(region, connectionCredentialsProvider) + ) + } + + /** + * Gets or creates an S3ControlClient for a connection + */ + public getS3ControlClient( + connectionId: string, + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider + ): S3ControlClient { + return this.getClient(connectionId, ClientType.S3ControlClient, () => { + const credentialsProvider = async () => { + const credentials = await connectionCredentialsProvider.getCredentials() + return { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + sessionToken: credentials.sessionToken, + expiration: credentials.expiration, + } + } + return new S3ControlClient({ region, credentials: credentialsProvider }) + }) + } + + /** + * Clears all cached clients for a connection + */ + public clearConnection(connectionId: string): void { + delete this.clientCache[connectionId] + } + + /** + * Clears all cached clients + */ + public clearAll(): void { + getLogger().info('SMUS Connection: Clearing all cached clients') + this.clientCache = {} + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/credentialsAdapter.ts b/packages/core/src/sagemakerunifiedstudio/shared/client/credentialsAdapter.ts new file mode 100644 index 00000000000..88d08c93b86 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/credentialsAdapter.ts @@ -0,0 +1,60 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as AWS from 'aws-sdk' +import { ConnectionCredentialsProvider } from '../../auth/providers/connectionCredentialsProvider' +import { getLogger } from '../../../shared/logger/logger' + +/** + * Adapts a ConnectionCredentialsProvider (SDK v3) to work with SDK v2's CredentialProviderChain + */ +export function adaptConnectionCredentialsProvider( + connectionCredentialsProvider: ConnectionCredentialsProvider +): AWS.CredentialProviderChain { + const provider = () => { + // Create SDK v2 Credentials that will resolve the provider when needed + const credentials = new AWS.Credentials({ + accessKeyId: '', + secretAccessKey: '', + sessionToken: '', + }) + + // Override the get method to use the connection credentials provider + credentials.get = (callback) => { + getLogger().debug('Attempting to get credentials from ConnectionCredentialsProvider') + + connectionCredentialsProvider + .getCredentials() + .then((creds) => { + getLogger().debug('Successfully got credentials') + + credentials.accessKeyId = creds.accessKeyId as string + credentials.secretAccessKey = creds.secretAccessKey as string + credentials.sessionToken = creds.sessionToken as string + credentials.expireTime = creds.expiration as Date + callback() + }) + .catch((err) => { + getLogger().debug(`Failed to get credentials: ${err}`) + + callback(err) + }) + } + + // Override needsRefresh to delegate to the connection credentials provider + credentials.needsRefresh = () => { + return true // Always call refresh, this is okay because there is caching existing in credential provider + } + + // Override refresh to use the connection credentials provider + credentials.refresh = (callback) => { + credentials.get(callback) + } + + return credentials + } + + return new AWS.CredentialProviderChain([provider]) +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/datazoneClient.ts b/packages/core/src/sagemakerunifiedstudio/shared/client/datazoneClient.ts new file mode 100644 index 00000000000..ffa0e7bfbf3 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/datazoneClient.ts @@ -0,0 +1,792 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + ConnectionCredentials, + ConnectionSummary, + DataZone, + GetConnectionCommandOutput, + GetEnvironmentCredentialsCommandOutput, + ListConnectionsCommandOutput, + PhysicalEndpoint, + RedshiftPropertiesOutput, + S3PropertiesOutput, + ConnectionType, + GluePropertiesOutput, + GetEnvironmentCommandOutput, +} from '@aws-sdk/client-datazone' +import { getLogger } from '../../../shared/logger/logger' +import type { SmusAuthenticationProvider } from '../../auth/providers/smusAuthenticationProvider' +import { DefaultStsClient } from '../../../shared/clients/stsClient' + +/** + * Represents a DataZone project + */ +export interface DataZoneProject { + id: string + name: string + description?: string + domainId: string + createdAt?: Date + updatedAt?: Date +} + +/** + * Represents JDBC connection properties + */ +export interface JdbcConnection { + jdbcIamUrl?: string + jdbcUrl?: string + username?: string + password?: string + secretId?: string + isProvisionedSecret?: boolean + redshiftTempDir?: string + host?: string + engine?: string + port?: number + dbname?: string + [key: string]: any +} + +/** + * Represents a DataZone connection + */ +export interface DataZoneConnection { + connectionId: string + name: string + description?: string + type: string + domainId: string + environmentId?: string + projectId: string + props?: { + s3Properties?: S3PropertiesOutput + redshiftProperties?: RedshiftPropertiesOutput + glueProperties?: GluePropertiesOutput + jdbcConnection?: JdbcConnection + [key: string]: any + } + /** + * Connection credentials when retrieved with withSecret=true + */ + connectionCredentials?: ConnectionCredentials + /** + * Location information parsed from physical endpoints + */ + location?: { + accessRole?: string + awsRegion?: string + awsAccountId?: string + iamConnectionId?: string + } +} + +// Constants for DataZone environment configuration +const toolingBlueprintName = 'Tooling' +const sageMakerProviderName = 'Amazon SageMaker' + +/** + * Client for interacting with AWS DataZone API with DER credential support + * + * This client integrates with SmusAuthenticationProvider to provide authenticated + * DataZone operations using Domain Execution Role (DER) credentials. + * + * One instance per connection/domainId is maintained to avoid duplication. + */ +export class DataZoneClient { + /** + * Parse a Redshift connection info object from JDBC URL + * @param jdbcURL Example JDBC URL: jdbc:redshift://redshift-serverless-workgroup-3zzw0fjmccdixz.123456789012.us-east-1.redshift-serverless.amazonaws.com:5439/dev + * @returns A object contains info of host, engine, port, dbName + */ + private getRedshiftConnectionInfoFromJdbcURL(jdbcURL: string) { + if (!jdbcURL) { + return + } + + const [, engine, hostWithLeadingSlashes, portAndDBName] = jdbcURL.split(':') + const [port, dbName] = portAndDBName.split('/') + return { + host: hostWithLeadingSlashes.split('/')[2], + engine, + port, + dbName, + } + } + + /** + * Builds a JDBC connection object from Redshift properties + * @param redshiftProps The Redshift properties + * @returns A JDBC connection object + */ + private buildJdbcConnectionFromRedshiftProps(redshiftProps: RedshiftPropertiesOutput): JdbcConnection { + const redshiftConnectionInfo = this.getRedshiftConnectionInfoFromJdbcURL(redshiftProps.jdbcUrl ?? '') + + return { + jdbcIamUrl: redshiftProps.jdbcIamUrl, + jdbcUrl: redshiftProps.jdbcUrl, + username: redshiftProps.credentials?.usernamePassword?.username, + password: redshiftProps.credentials?.usernamePassword?.password, + secretId: redshiftProps.credentials?.secretArn, + isProvisionedSecret: redshiftProps.isProvisionedSecret, + redshiftTempDir: redshiftProps.redshiftTempDir, + host: redshiftConnectionInfo?.host, + engine: redshiftConnectionInfo?.engine, + port: Number(redshiftConnectionInfo?.port), + dbname: redshiftConnectionInfo?.dbName, + } + } + + private datazoneClient: DataZone | undefined + private static instances = new Map() + private readonly logger = getLogger() + + private constructor( + private readonly authProvider: SmusAuthenticationProvider, + private readonly domainId: string, + private readonly region: string + ) {} + + /** + * Gets an authenticated DataZoneClient instance using DER credentials + * One instance per connection/domainId is maintained + * @param authProvider The SMUS authentication provider + * @returns Promise resolving to authenticated DataZoneClient instance + */ + public static async getInstance(authProvider: SmusAuthenticationProvider): Promise { + const logger = getLogger() + + if (!authProvider.isConnected()) { + throw new Error('SMUS authentication provider is not connected') + } + + const activeConnection = authProvider.activeConnection! + const instanceKey = `${activeConnection.domainId}:${activeConnection.ssoRegion}` + + logger.debug(`DataZoneClient: Getting instance for domain: ${instanceKey}`) + + // Check if we already have an instance for this domain/region + if (DataZoneClient.instances.has(instanceKey)) { + const existingInstance = DataZoneClient.instances.get(instanceKey)! + logger.debug('DataZoneClient: Using existing instance') + return existingInstance + } + + // Create new instance + logger.debug('DataZoneClient: Creating new instance') + const instance = new DataZoneClient(authProvider, activeConnection.domainId, activeConnection.ssoRegion) + DataZoneClient.instances.set(instanceKey, instance) + + // Set up cleanup when connection changes + const disposable = authProvider.onDidChangeActiveConnection(() => { + logger.debug(`DataZoneClient: Connection changed, cleaning up instance for: ${instanceKey}`) + DataZoneClient.instances.delete(instanceKey) + instance.datazoneClient = undefined + disposable.dispose() + }) + + logger.info(`DataZoneClient: Created instance for domain ${activeConnection.domainId}`) + return instance + } + + /** + * Disposes all instances and cleans up resources + */ + public static dispose(): void { + const logger = getLogger() + logger.debug('DataZoneClient: Disposing all instances') + + for (const [key, instance] of DataZoneClient.instances.entries()) { + instance.datazoneClient = undefined + logger.debug(`DataZoneClient: Disposed instance for: ${key}`) + } + + DataZoneClient.instances.clear() + } + + /** + * Gets the DataZone domain ID + * @returns DataZone domain ID + */ + public getDomainId(): string { + return this.domainId + } + + /** + * Gets the AWS region + * @returns AWS region + */ + public getRegion(): string { + return this.region + } + + /** + * Gets the default tooling environment credentials for a DataZone project + * @param projectId The DataZone project identifier + * @returns Promise resolving to environment credentials + * @throws Error if tooling blueprint or environment is not found + */ + public async getProjectDefaultEnvironmentCreds(projectId: string): Promise { + try { + this.logger.debug( + `Getting project default environment credentials for domain ${this.domainId}, project ${projectId}` + ) + const datazoneClient = await this.getDataZoneClient() + + this.logger.debug('Listing environment blueprints') + const domainBlueprints = await datazoneClient.listEnvironmentBlueprints({ + domainIdentifier: this.domainId, + managed: true, + name: toolingBlueprintName, + }) + + const toolingBlueprint = domainBlueprints.items?.[0] + if (!toolingBlueprint) { + this.logger.error('Failed to get tooling blueprint') + throw new Error('Failed to get tooling blueprint') + } + this.logger.debug(`Found tooling blueprint with ID: ${toolingBlueprint.id}, listing environments`) + + const listEnvs = await datazoneClient.listEnvironments({ + domainIdentifier: this.domainId, + projectIdentifier: projectId, + environmentBlueprintIdentifier: toolingBlueprint.id, + provider: sageMakerProviderName, + }) + + const defaultEnv = listEnvs.items?.find((env) => env.name === toolingBlueprintName) + if (!defaultEnv) { + this.logger.error('Failed to find default Tooling environment') + throw new Error('Failed to find default Tooling environment') + } + this.logger.debug(`Found default environment with ID: ${defaultEnv.id}, getting environment credentials`) + + const defaultEnvCreds = await datazoneClient.getEnvironmentCredentials({ + domainIdentifier: this.domainId, + environmentIdentifier: defaultEnv.id, + }) + + return defaultEnvCreds + } catch (err) { + this.logger.error('Failed to get project default environment credentials: %s', err as Error) + throw err + } + } + + /** + * Gets the DataZone client, initializing it if necessary + */ + private async getDataZoneClient(): Promise { + if (!this.datazoneClient) { + try { + this.logger.debug('DataZoneClient: Creating authenticated DataZone client with DER credentials') + + const credentialsProvider = async () => { + const credentials = await (await this.authProvider.getDerCredentialsProvider()).getCredentials() + return { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + sessionToken: credentials.sessionToken, + expiration: credentials.expiration, + } + } + + this.datazoneClient = new DataZone({ + region: this.region, + credentials: credentialsProvider, + }) + this.logger.debug('DataZoneClient: Successfully created authenticated DataZone client') + } catch (err) { + this.logger.error('DataZoneClient: Failed to create DataZone client: %s', err as Error) + throw err + } + } + return this.datazoneClient + } + + /** + * Lists project memberships in a DataZone project with pagination support + * @param options Options for listing project memberships + * @returns Paginated list of DataZone project permissions with nextToken + */ + public async listProjectMemberships(options: { + projectIdentifier: string + maxResults?: number + nextToken?: string + }): Promise<{ memberships: any[]; nextToken?: string }> { + try { + this.logger.info( + `DataZoneClient: Listing project memberships for project ${options.projectIdentifier} in domain ${this.domainId}` + ) + + const datazoneClient = await this.getDataZoneClient() + + const response = await datazoneClient.listProjectMemberships({ + domainIdentifier: this.domainId, + projectIdentifier: options.projectIdentifier, + maxResults: options.maxResults, + nextToken: options.nextToken, + }) + + if (!response.members || response.members.length === 0) { + this.logger.info( + `DataZoneClient: No project memberships found for project ${options.projectIdentifier}` + ) + return { memberships: [] } + } + + this.logger.debug( + `DataZoneClient: Found ${response.members.length} project memberships for project ${options.projectIdentifier}` + ) + return { memberships: response.members, nextToken: response.nextToken } + } catch (err) { + this.logger.error('DataZoneClient: Failed to list project memberships: %s', (err as Error).message) + throw err + } + } + + /** + * Fetches all project memberships in a DataZone project by handling pagination automatically + * @param projectIdentifier The DataZone project identifier + * @returns Promise resolving to an array of all project memberships + */ + public async fetchAllProjectMemberships(projectIdentifier: string): Promise { + try { + let allMemberships: any[] = [] + let nextToken: string | undefined + do { + const maxResultsPerPage = 50 + const response = await this.listProjectMemberships({ + projectIdentifier, + nextToken, + maxResults: maxResultsPerPage, + }) + allMemberships = [...allMemberships, ...response.memberships] + nextToken = response.nextToken + } while (nextToken) + + this.logger.debug(`DataZoneClient: Fetched a total of ${allMemberships.length} project memberships`) + return allMemberships + } catch (err) { + this.logger.error('DataZoneClient: Failed to fetch all project memberships: %s', (err as Error).message) + throw err + } + } + + /** + * Lists projects in a DataZone domain with pagination support + * @param options Options for listing projects + * @returns Paginated list of DataZone projects with nextToken + */ + public async listProjects(options?: { + maxResults?: number + userIdentifier?: string + groupIdentifier?: string + name?: string + nextToken?: string + }): Promise<{ projects: DataZoneProject[]; nextToken?: string }> { + try { + this.logger.info(`DataZoneClient: Listing projects for domain ${this.domainId} in region ${this.region}`) + + const datazoneClient = await this.getDataZoneClient() + + // Call the DataZone API to list projects with pagination + const response = await datazoneClient.listProjects({ + domainIdentifier: this.domainId, + maxResults: options?.maxResults, + userIdentifier: options?.userIdentifier, + groupIdentifier: options?.groupIdentifier, + name: options?.name, + nextToken: options?.nextToken, + }) + + if (!response.items || response.items.length === 0) { + this.logger.info(`DataZoneClient: No projects found for domain ${this.domainId}`) + return { projects: [] } + } + + // Map the response to our DataZoneProject interface + const projects: DataZoneProject[] = response.items.map((project) => ({ + id: project.id || '', + name: project.name || '', + description: project.description, + domainId: this.domainId, + createdAt: project.createdAt ? new Date(project.createdAt) : undefined, + updatedAt: project.updatedAt ? new Date(project.updatedAt) : undefined, + })) + + this.logger.debug(`DataZoneClient: Found ${projects.length} projects for domain ${this.domainId}`) + return { projects, nextToken: response.nextToken } + } catch (err) { + this.logger.error('DataZoneClient: Failed to list projects: %s', (err as Error).message) + throw err + } + } + + /** + * Fetches all projects in a DataZone domain by handling pagination automatically + * @param options Options for listing projects (excluding nextToken which is handled internally) + * @returns Promise resolving to an array of all DataZone projects + */ + public async fetchAllProjects(options?: { + userIdentifier?: string + groupIdentifier?: string + name?: string + }): Promise { + try { + let allProjects: DataZoneProject[] = [] + let nextToken: string | undefined + do { + const maxResultsPerPage = 50 + const response = await this.listProjects({ + ...options, + nextToken, + maxResults: maxResultsPerPage, + }) + allProjects = [...allProjects, ...response.projects] + nextToken = response.nextToken + } while (nextToken) + + this.logger.debug(`DataZoneClient: Fetched a total of ${allProjects.length} projects`) + return allProjects + } catch (err) { + this.logger.error('DataZoneClient: Failed to fetch all projects: %s', (err as Error).message) + throw err + } + } + + /** + * Gets a specific project by ID + * @param projectId The project identifier + * @returns Promise resolving to the project details + */ + public async getProject(projectId: string): Promise { + try { + this.logger.info(`DataZoneClient: Getting project ${projectId} in domain ${this.domainId}`) + + const datazoneClient = await this.getDataZoneClient() + + const response = await datazoneClient.getProject({ + domainIdentifier: this.domainId, + identifier: projectId, + }) + + const project: DataZoneProject = { + id: response.id || '', + name: response.name || '', + description: response.description, + domainId: this.domainId, + createdAt: response.createdAt ? new Date(response.createdAt) : undefined, + updatedAt: response.lastUpdatedAt ? new Date(response.lastUpdatedAt) : undefined, + } + + this.logger.debug(`DataZoneClient: Retrieved project ${projectId} with name: ${project.name}`) + return project + } catch (err) { + this.logger.error('DataZoneClient: Failed to get project: %s', err as Error) + throw err + } + } + + /* + * Processes a connection response to add jdbcConnection if it's a Redshift connection + * @param connection The connection object to process + * @param connectionType The connection type + */ + private processRedshiftConnection(connection: ConnectionSummary): void { + if ( + connection && + connection.props && + 'redshiftProperties' in connection.props && + connection.props.redshiftProperties && + connection.type?.toLowerCase().includes('redshift') + ) { + const redshiftProps = connection.props.redshiftProperties as RedshiftPropertiesOutput + const props = connection.props as Record + + if (!props.jdbcConnection) { + props.jdbcConnection = this.buildJdbcConnectionFromRedshiftProps(redshiftProps) + } + } + } + + /** + * Parses location from physical endpoints + * @param physicalEndpoints Array of physical endpoints + * @returns Location object or undefined + */ + private parseLocationFromPhysicalEndpoints(physicalEndpoints?: PhysicalEndpoint[]): DataZoneConnection['location'] { + if (physicalEndpoints && physicalEndpoints.length > 0) { + const physicalEndpoint = physicalEndpoints[0] + return { + accessRole: physicalEndpoint.awsLocation?.accessRole, + awsRegion: physicalEndpoint.awsLocation?.awsRegion, + awsAccountId: physicalEndpoint.awsLocation?.awsAccountId, + iamConnectionId: physicalEndpoint.awsLocation?.iamConnectionId, + } + } + return undefined + } + + /** + * Gets a specific connection by ID + * @param params Parameters for getting a connection + * @returns The connection details + */ + public async getConnection(params: { + domainIdentifier: string + identifier: string + withSecret?: boolean + }): Promise { + try { + this.logger.info( + `DataZoneClient: Getting connection ${params.identifier} in domain ${params.domainIdentifier}` + ) + + const datazoneClient = await this.getDataZoneClient() + + // Call the DataZone API to get connection + const response: GetConnectionCommandOutput = await datazoneClient.getConnection({ + domainIdentifier: params.domainIdentifier, + identifier: params.identifier, + withSecret: params.withSecret !== undefined ? params.withSecret : true, + }) + + // Process the connection to add jdbcConnection if it's a Redshift connection + this.processRedshiftConnection(response) + + // Parse location from physical endpoints + const location = this.parseLocationFromPhysicalEndpoints(response.physicalEndpoints) + + // Return as DataZoneConnection, currently only required fields are added + // Can always include new fields in DataZoneConnection when needed + const connection: DataZoneConnection = { + connectionId: response.connectionId || '', + name: response.name || '', + description: response.description, + type: response.type || '', + domainId: params.domainIdentifier, + projectId: response.projectId || '', + props: response.props || {}, + connectionCredentials: response.connectionCredentials, + location, + } + + return connection + } catch (err) { + this.logger.error('DataZoneClient: Failed to get connection: %s', err as Error) + throw err + } + } + + public async fetchConnections( + domain: string | undefined, + project: string | undefined, + ConnectionType: ConnectionType + ): Promise { + const datazoneClient = await this.getDataZoneClient() + return datazoneClient.listConnections({ + domainIdentifier: domain, + projectIdentifier: project, + type: ConnectionType, + }) + } + /** + * Lists connections in a DataZone environment + * @param domainId The DataZone domain identifier + * @param environmentId The DataZone environment identifier + * @param projectId The DataZone project identifier + * @returns List of DataZone connections + */ + public async listConnections( + domainId: string, + environmentId: string | undefined, + projectId: string + ): Promise { + try { + this.logger.info( + `DataZoneClient: Listing connections for environment ${environmentId} in domain ${domainId}` + ) + + const datazoneClient = await this.getDataZoneClient() + let allConnections: DataZoneConnection[] = [] + let nextToken: string | undefined + + do { + // Call the DataZone API to list connections with pagination + const response: ListConnectionsCommandOutput = await datazoneClient.listConnections({ + domainIdentifier: domainId, + projectIdentifier: projectId, + environmentIdentifier: environmentId, + nextToken, + maxResults: 50, + }) + + if (response.items && response.items.length > 0) { + // Map the response to our DataZoneConnection interface + const connections: DataZoneConnection[] = response.items.map((connection) => { + // Process the connection to add jdbcConnection if it's a Redshift connection + this.processRedshiftConnection(connection) + + // Parse location from physical endpoints + const location = this.parseLocationFromPhysicalEndpoints(connection.physicalEndpoints) + + return { + connectionId: connection.connectionId || '', + name: connection.name || '', + description: '', + type: connection.type || '', + domainId, + environmentId, + projectId, + props: connection.props || {}, + location, + } + }) + allConnections = [...allConnections, ...connections] + } + + nextToken = response.nextToken + } while (nextToken) + + this.logger.info(`DataZoneClient: Fetched a total of ${allConnections.length} connections`) + return allConnections + } catch (err) { + this.logger.error('DataZoneClient: Failed to list connections: %s', err as Error) + throw err + } + } + + /** + * Gets the tooling environment ID for a project + * @param domainId The DataZone domain identifier + * @param projectId The DataZone project identifier + * @returns Promise resolving to the tooling environment ID + */ + public async getToolingEnvironmentId(domainId: string, projectId: string): Promise { + this.logger.debug(`Getting tooling environment ID for domain ${domainId}, project ${projectId}`) + const datazoneClient = await this.getDataZoneClient() + + let domainBlueprints + try { + // Get the tooling blueprint + domainBlueprints = await datazoneClient.listEnvironmentBlueprints({ + domainIdentifier: domainId, + managed: true, + name: toolingBlueprintName, + }) + } catch (err) { + this.logger.error( + 'Failed to list environment blueprints for domain %s, %s', + domainId, + (err as Error).message + ) + throw err + } + + const toolingBlueprint = domainBlueprints.items?.[0] + if (!toolingBlueprint) { + this.logger.error('No tooling blueprint found for domain %s', domainId) + throw new Error('No tooling blueprint found') + } + + // List environments for the project + let listEnvs + try { + this.logger.debug(`Listing environments for project ${projectId} with blueprint ${toolingBlueprint.id}`) + listEnvs = await datazoneClient.listEnvironments({ + domainIdentifier: domainId, + projectIdentifier: projectId, + environmentBlueprintIdentifier: toolingBlueprint.id, + provider: sageMakerProviderName, + }) + } catch (err) { + this.logger.error( + 'Failed to list environments for domainId: %s, projectId: %s, %s', + domainId, + projectId, + (err as Error).message + ) + throw err + } + + const defaultEnv = listEnvs.items?.find((env) => env.name === toolingBlueprintName) + if (!defaultEnv || !defaultEnv.id) { + this.logger.error( + 'No default Tooling environment found for domainId: %s, projectId: %s', + domainId, + projectId + ) + throw new Error('No default Tooling environment found for project') + } + this.logger.debug(`Found tooling environment with ID: ${defaultEnv.id}`) + return defaultEnv.id + } + + /** + * Gets environment details + * @param domainId The DataZone domain identifier + * @param environmentId The environment identifier + * @returns Promise resolving to environment details + */ + public async getEnvironmentDetails( + environmentId: string + ): Promise { + try { + this.logger.debug( + `Getting environment details for domain ${this.getDomainId()}, environment ${environmentId}` + ) + const datazoneClient = await this.getDataZoneClient() + + const environment = await datazoneClient.getEnvironment({ + domainIdentifier: this.getDomainId(), + identifier: environmentId, + }) + + this.logger.debug(`Retrieved environment details for ${environmentId}`) + return environment + } catch (err) { + this.logger.error('Failed to get environment details: %s', err as Error) + throw err + } + } + + /** + * Gets the tooling environment details for a project + * @param projectId The project ID + * @returns The tooling environment details + */ + public async getToolingEnvironment(projectId: string): Promise { + const logger = getLogger() + + const datazoneClient = await DataZoneClient.getInstance(this.authProvider) + if (!datazoneClient) { + throw new Error('DataZone client is not initialized') + } + + const toolingEnvId = await datazoneClient + .getToolingEnvironmentId(datazoneClient.getDomainId(), projectId) + .catch((err) => { + logger.error('Failed to get tooling environment ID for project %s', projectId) + throw new Error(`Failed to get tooling environment ID: ${err.message}`) + }) + + if (!toolingEnvId) { + throw new Error('No default environment found for project') + } + + return await datazoneClient.getEnvironmentDetails(toolingEnvId) + } + + public async getUserId(): Promise { + const derCredProvider = await this.authProvider.getDerCredentialsProvider() + this.logger.debug(`Calling STS GetCallerIdentity using DER credentials of ${this.getDomainId()}`) + const stsClient = new DefaultStsClient(this.getRegion(), await derCredProvider.getCredentials()) + const callerIdentity = await stsClient.getCallerIdentity() + this.logger.debug(`Retrieved caller identity, UserId: ${callerIdentity.UserId}`) + return callerIdentity.UserId + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/glueCatalogClient.ts b/packages/core/src/sagemakerunifiedstudio/shared/client/glueCatalogClient.ts new file mode 100644 index 00000000000..bbd3c440478 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/glueCatalogClient.ts @@ -0,0 +1,136 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Service } from 'aws-sdk' +import globals from '../../../shared/extensionGlobals' +import { getLogger } from '../../../shared/logger/logger' +import * as GlueCatalogApi from './gluecatalogapi' +import apiConfig = require('./gluecatalogapi.json') +import { ServiceConfigurationOptions } from 'aws-sdk/lib/service' +import { ConnectionCredentialsProvider } from '../../auth/providers/connectionCredentialsProvider' +import { adaptConnectionCredentialsProvider } from './credentialsAdapter' + +/** + * Represents a Glue catalog + */ +export type GlueCatalog = GlueCatalogApi.Types.Catalog + +/** + * Client for interacting with Glue Catalog API + */ +export class GlueCatalogClient { + private glueClient: GlueCatalogApi | undefined + private static instance: GlueCatalogClient | undefined + private readonly logger = getLogger() + + private constructor( + private readonly region: string, + private readonly connectionCredentialsProvider?: ConnectionCredentialsProvider + ) {} + + /** + * Gets a singleton instance of the GlueCatalogClient + * @returns GlueCatalogClient instance + */ + public static getInstance(region: string): GlueCatalogClient { + if (!GlueCatalogClient.instance) { + GlueCatalogClient.instance = new GlueCatalogClient(region) + } + return GlueCatalogClient.instance + } + + /** + * Creates a new GlueCatalogClient instance with specific credentials + * @param region AWS region + * @param credentials AWS credentials + * @returns GlueCatalogClient instance with credentials + */ + public static createWithCredentials( + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider + ): GlueCatalogClient { + return new GlueCatalogClient(region, connectionCredentialsProvider) + } + + /** + * Gets the AWS region + * @returns AWS region + */ + public getRegion(): string { + return this.region + } + + /** + * Lists Glue catalogs with pagination support + * @param nextToken Optional pagination token + * @returns Object containing catalogs and nextToken + */ + public async getCatalogs(nextToken?: string): Promise<{ catalogs: GlueCatalog[]; nextToken?: string }> { + try { + this.logger.info(`GlueCatalogClient: Getting catalogs in region ${this.region}`) + + const glueClient = await this.getGlueCatalogClient() + + // Call the GetCatalogs API with pagination + const response = await glueClient + .getCatalogs({ + Recursive: true, + NextToken: nextToken, + }) + .promise() + + const catalogs: GlueCatalog[] = response.CatalogList || [] + + this.logger.info(`GlueCatalogClient: Found ${catalogs.length} catalogs in this page`) + return { + catalogs, + nextToken: response.NextToken, + } + } catch (err) { + this.logger.error('GlueCatalogClient: Failed to get catalogs: %s', err as Error) + throw err + } + } + + /** + * Gets the Glue client, initializing it if necessary + */ + private async getGlueCatalogClient(): Promise { + if (!this.glueClient) { + try { + if (this.connectionCredentialsProvider) { + // Create client with provided credentials + this.glueClient = (await globals.sdkClientBuilder.createAwsService( + Service, + { + apiConfig: apiConfig, + region: this.region, + credentialProvider: adaptConnectionCredentialsProvider(this.connectionCredentialsProvider), + } as ServiceConfigurationOptions, + undefined, + false + )) as GlueCatalogApi + } else { + // Use the SDK client builder for default credentials + this.glueClient = (await globals.sdkClientBuilder.createAwsService( + Service, + { + apiConfig: apiConfig, + region: this.region, + } as ServiceConfigurationOptions, + undefined, + false + )) as GlueCatalogApi + } + + this.logger.debug('GlueCatalogClient: Successfully created Glue client') + } catch (err) { + this.logger.error('GlueCatalogClient: Failed to create Glue client: %s', err as Error) + throw err + } + } + return this.glueClient + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/glueClient.ts b/packages/core/src/sagemakerunifiedstudio/shared/client/glueClient.ts new file mode 100644 index 00000000000..15034a488cf --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/glueClient.ts @@ -0,0 +1,166 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + Glue, + GetDatabasesCommand, + GetTablesCommand, + GetTableCommand, + Table, + ResourceShareType, + DatabaseAttributes, + TableAttributes, + Database, +} from '@aws-sdk/client-glue' +import { getLogger } from '../../../shared/logger/logger' +import { ConnectionCredentialsProvider } from '../../auth/providers/connectionCredentialsProvider' + +/** + * Client for interacting with AWS Glue API using public SDK + */ +export class GlueClient { + private glueClient: Glue | undefined + private readonly logger = getLogger() + + constructor( + private readonly region: string, + private readonly connectionCredentialsProvider: ConnectionCredentialsProvider + ) {} + + /** + * Gets databases from a catalog + * @param catalogId Optional catalog ID (uses default if not provided) + * @param nextToken Optional pagination token + * @returns List of databases + */ + public async getDatabases( + catalogId?: string, + resourceShareType?: ResourceShareType, + attributesToGet?: DatabaseAttributes[], + nextToken?: string + ): Promise<{ databases: Database[]; nextToken?: string }> { + try { + this.logger.info(`GlueClient: Getting databases for catalog ${catalogId || 'default'}`) + + const glueClient = await this.getGlueClient() + const response = await glueClient.send( + new GetDatabasesCommand({ + CatalogId: catalogId, + ResourceShareType: resourceShareType, + AttributesToGet: attributesToGet, + NextToken: nextToken, + MaxResults: 100, + }) + ) + + const databases = response.DatabaseList || [] + this.logger.info(`GlueClient: Found ${databases.length} databases`) + + return { + databases, + nextToken: response.NextToken, + } + } catch (err) { + this.logger.error('GlueClient: Failed to get databases: %s', err as Error) + throw err + } + } + + /** + * Gets tables from a database + * @param databaseName Database name + * @param catalogId Optional catalog ID + * @param nextToken Optional pagination token + * @returns List of tables + */ + public async getTables( + databaseName: string, + catalogId?: string, + attributesToGet?: TableAttributes[], + nextToken?: string + ): Promise<{ tables: Table[]; nextToken?: string }> { + try { + this.logger.info(`GlueClient: Getting tables for database ${databaseName}`) + + const glueClient = await this.getGlueClient() + const response = await glueClient.send( + new GetTablesCommand({ + DatabaseName: databaseName, + CatalogId: catalogId, + AttributesToGet: attributesToGet, + NextToken: nextToken, + MaxResults: 100, + }) + ) + + const tables = response.TableList || [] + this.logger.info(`GlueClient: Found ${tables.length} tables`) + + return { + tables, + nextToken: response.NextToken, + } + } catch (err) { + this.logger.error('GlueClient: Failed to get tables: %s', err as Error) + throw err + } + } + + /** + * Gets table details including columns + * @param databaseName Database name + * @param tableName Table name + * @param catalogId Optional catalog ID + * @returns Table details with columns + */ + public async getTable(databaseName: string, tableName: string, catalogId?: string): Promise { + try { + this.logger.info(`GlueClient: Getting table ${tableName} from database ${databaseName}`) + + const glueClient = await this.getGlueClient() + const response = await glueClient.send( + new GetTableCommand({ + DatabaseName: databaseName, + Name: tableName, + CatalogId: catalogId, + }) + ) + + return response.Table + } catch (err) { + this.logger.error('GlueClient: Failed to get table: %s', err as Error) + throw err + } + } + + /** + * Gets the Glue client, initializing it if necessary + */ + private async getGlueClient(): Promise { + if (!this.glueClient) { + try { + const credentialsProvider = async () => { + const credentials = await this.connectionCredentialsProvider.getCredentials() + return { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + sessionToken: credentials.sessionToken, + expiration: credentials.expiration, + } + } + + this.glueClient = new Glue({ + region: this.region, + credentials: credentialsProvider, + }) + this.logger.debug('GlueClient: Successfully created Glue client') + } catch (err) { + this.logger.error('GlueClient: Failed to create Glue client: %s', err as Error) + throw err + } + } + return this.glueClient + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/gluecatalogapi.json b/packages/core/src/sagemakerunifiedstudio/shared/client/gluecatalogapi.json new file mode 100644 index 00000000000..ecd3705c096 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/gluecatalogapi.json @@ -0,0 +1,2695 @@ +{ + "version": "2.0", + "metadata": { + "apiVersion": "2022-07-26", + "auth": ["aws.auth#sigv4"], + "endpointPrefix": "glue", + "jsonVersion": "1.1", + "protocol": "json", + "protocols": ["json"], + "serviceFullName": "Glue Private Service", + "serviceId": "GlueCatalogAPI", + "signatureVersion": "v4", + "signingName": "glue", + "targetPrefix": "AWSGlue", + "uid": "gluecatalogapi-2022-07-26" + }, + "operations": { + "DescribeConnectionType": { + "name": "DescribeConnectionType", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "DescribeConnectionTypeRequest" + }, + "output": { + "shape": "DescribeConnectionTypeResponse" + }, + "errors": [ + { + "shape": "InternalServiceException" + }, + { + "shape": "InvalidInputException" + }, + { + "shape": "AccessDeniedException" + }, + { + "shape": "ValidationException" + } + ] + }, + "GetCatalog": { + "name": "GetCatalog", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GetCatalogRequest" + }, + "output": { + "shape": "GetCatalogResponse" + }, + "errors": [ + { + "shape": "InternalServiceException" + }, + { + "shape": "FederationSourceException" + }, + { + "shape": "InvalidInputException" + }, + { + "shape": "GlueEncryptionException" + }, + { + "shape": "EntityNotFoundException" + }, + { + "shape": "OperationTimeoutException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "GetCatalogs": { + "name": "GetCatalogs", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GetCatalogsRequest" + }, + "output": { + "shape": "GetCatalogsResponse" + }, + "errors": [ + { + "shape": "InternalServiceException" + }, + { + "shape": "InvalidInputException" + }, + { + "shape": "GlueEncryptionException" + }, + { + "shape": "FederationSourceException" + }, + { + "shape": "EntityNotFoundException" + }, + { + "shape": "OperationTimeoutException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "GetCompletion": { + "name": "GetCompletion", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GetCompletionRequest" + }, + "output": { + "shape": "GetCompletionResponse" + }, + "errors": [ + { + "shape": "AlreadyExistsException" + }, + { + "shape": "InternalServiceException" + }, + { + "shape": "InvalidInputException" + }, + { + "shape": "EntityNotFoundException" + }, + { + "shape": "OperationTimeoutException" + }, + { + "shape": "AccessDeniedException" + }, + { + "shape": "ValidationException" + } + ] + }, + "GetEntityRecords": { + "name": "GetEntityRecords", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GetEntityRecordsRequest" + }, + "output": { + "shape": "GetEntityRecordsResponse" + }, + "errors": [ + { + "shape": "InvalidInputException" + }, + { + "shape": "GlueEncryptionException" + }, + { + "shape": "FederationSourceException" + }, + { + "shape": "EntityNotFoundException" + }, + { + "shape": "OperationTimeoutException" + }, + { + "shape": "AccessDeniedException" + }, + { + "shape": "ValidationException" + } + ] + }, + "GetJobRun": { + "name": "GetJobRun", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GetJobRunRequest" + }, + "output": { + "shape": "GetJobRunResponse" + }, + "errors": [ + { + "shape": "InternalServiceException" + }, + { + "shape": "InvalidInputException" + }, + { + "shape": "EntityNotFoundException" + }, + { + "shape": "OperationTimeoutException" + } + ] + }, + "GetJobRuns": { + "name": "GetJobRuns", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GetJobRunsRequest" + }, + "output": { + "shape": "GetJobRunsResponse" + }, + "errors": [ + { + "shape": "InternalServiceException" + }, + { + "shape": "InvalidInputException" + }, + { + "shape": "EntityNotFoundException" + }, + { + "shape": "OperationTimeoutException" + } + ] + }, + "GetTable": { + "name": "GetTable", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GetTableRequest" + }, + "output": { + "shape": "GetTableResponse" + }, + "errors": [ + { + "shape": "ResourceNotReadyException" + }, + { + "shape": "FederationSourceRetryableException" + }, + { + "shape": "InternalServiceException" + }, + { + "shape": "InvalidInputException" + }, + { + "shape": "GlueEncryptionException" + }, + { + "shape": "FederationSourceException" + }, + { + "shape": "EntityNotFoundException" + }, + { + "shape": "OperationTimeoutException" + } + ] + }, + "ListConnectionTypes": { + "name": "ListConnectionTypes", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "ListConnectionTypesRequest" + }, + "output": { + "shape": "ListConnectionTypesResponse" + }, + "errors": [ + { + "shape": "InternalServiceException" + }, + { + "shape": "AccessDeniedException" + } + ] + }, + "StartCompletion": { + "name": "StartCompletion", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "StartCompletionRequest" + }, + "output": { + "shape": "StartCompletionResponse" + }, + "errors": [ + { + "shape": "AlreadyExistsException" + }, + { + "shape": "InternalServiceException" + }, + { + "shape": "InvalidInputException" + }, + { + "shape": "EntityNotFoundException" + }, + { + "shape": "OperationTimeoutException" + }, + { + "shape": "AccessDeniedException" + }, + { + "shape": "ValidationException" + } + ] + } + }, + "shapes": { + "AccessDeniedException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + } + }, + "documentation": "

This exception is thrown when the client doesn't have permission for the operation they requested.

", + "exception": true + }, + "AllowedValue": { + "type": "structure", + "required": ["DisplayName", "Description", "Value"], + "members": { + "DisplayName": { + "shape": "AllowedValueDisplayNameString" + }, + "Description": { + "shape": "AllowedValueDescriptionString" + }, + "Value": { + "shape": "AllowedValueValueString" + } + } + }, + "AllowedValueDescriptionString": { + "type": "string", + "max": 1024, + "min": 0 + }, + "AllowedValueDisplayNameString": { + "type": "string", + "max": 128, + "min": 1 + }, + "AllowedValueValueString": { + "type": "string", + "max": 128, + "min": 1 + }, + "AllowedValues": { + "type": "list", + "member": { + "shape": "AllowedValue" + } + }, + "AlreadyExistsException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + } + }, + "documentation": "

This exception occurs when a user submits for an already existing script

", + "exception": true + }, + "ApiVersion": { + "type": "string", + "max": 256, + "min": 1, + "pattern": "[a-zA-Z0-9.-]*" + }, + "ArnString": { + "type": "string", + "max": 2048, + "min": 20 + }, + "AttemptCount": { + "type": "integer", + "box": true + }, + "AttributeCondition": { + "type": "structure", + "members": { + "Expression": { + "shape": "ExpressionString" + }, + "Scope": { + "shape": "ScopeString" + } + } + }, + "AuthConfiguration": { + "type": "structure", + "required": ["AuthenticationType", "SecretArn"], + "members": { + "AuthenticationType": { + "shape": "Property" + }, + "SecretArn": { + "shape": "Property" + }, + "OAuth2Properties": { + "shape": "PropertiesMap" + }, + "BasicAuthenticationProperties": { + "shape": "PropertiesMap" + }, + "CustomAuthenticationProperties": { + "shape": "PropertiesMap" + } + } + }, + "AuthenticationType": { + "type": "string", + "enum": ["BASIC", "OAUTH2", "CUSTOM"] + }, + "AuthenticationTypes": { + "type": "list", + "member": { + "shape": "AuthenticationType" + } + }, + "BlobParametersMap": { + "type": "map", + "key": { + "shape": "KeyString" + }, + "value": { + "shape": "BlobParametersMapValue" + } + }, + "BlobParametersMapValue": { + "type": "blob" + }, + "Bool": { + "type": "boolean", + "box": true + }, + "Boolean": { + "type": "boolean", + "box": true + }, + "BooleanValue": { + "type": "boolean", + "box": true + }, + "Capabilities": { + "type": "structure", + "required": ["SupportedAuthenticationTypes", "SupportedDataOperations", "SupportedComputeEnvironments"], + "members": { + "SupportedAuthenticationTypes": { + "shape": "AuthenticationTypes" + }, + "SupportedDataOperations": { + "shape": "DataOperations" + }, + "SupportedComputeEnvironments": { + "shape": "ComputeEnvironments" + } + } + }, + "Catalog": { + "type": "structure", + "members": { + "CatalogId": { + "shape": "CatalogIdString" + }, + "Name": { + "shape": "CatalogNameString" + }, + "Description": { + "shape": "GlueCommonDescriptionString" + }, + "ResourceArn": { + "shape": "ResourceArnString" + }, + "Parameters": { + "shape": "ParametersMap" + }, + "DataParameters": { + "shape": "BlobParametersMap" + }, + "CatalogType": { + "shape": "CatalogType" + }, + "CreateTime": { + "shape": "Timestamp" + }, + "UpdateTime": { + "shape": "Timestamp" + }, + "TargetCatalog": { + "shape": "TargetCatalog" + }, + "FederatedCatalog": { + "shape": "FederatedCatalog" + }, + "CatalogProperties": { + "shape": "CatalogPropertiesOutput" + }, + "CatalogIdentifier": { + "shape": "CatalogIdentifier" + }, + "ParentCatalogIdentifiers": { + "shape": "CatalogIdentifierList" + }, + "ParentCatalogNames": { + "shape": "CatalogNameList" + }, + "CreateTableDefaultPermissions": { + "shape": "PrincipalPermissionsList" + }, + "CreateDatabaseDefaultPermissions": { + "shape": "PrincipalPermissionsList" + } + } + }, + "CatalogIdString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "CatalogIdentifier": { + "type": "string", + "max": 100, + "min": 0, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "CatalogIdentifierList": { + "type": "list", + "member": { + "shape": "CatalogIdentifier" + } + }, + "CatalogList": { + "type": "list", + "member": { + "shape": "Catalog" + } + }, + "CatalogNameList": { + "type": "list", + "member": { + "shape": "CatalogNameString" + } + }, + "CatalogNameString": { + "type": "string", + "max": 30, + "min": 1, + "pattern": "(?!(.*[.\\/\\\\]|aws:)).*" + }, + "CatalogPropertiesOutput": { + "type": "structure", + "members": { + "DataLakeAccessProperties": { + "shape": "DataLakeAccessPropertiesOutput" + }, + "IcebergOptimizationProperties": { + "shape": "IcebergOptimizationPropertiesOutput" + } + } + }, + "CatalogType": { + "type": "string", + "enum": [ + "REDSHIFT_CATALOG", + "FEDERATED", + "NATIVE", + "REDSHIFT", + "LINKCONTAINER", + "LINK_FEDERATED", + "LINK_NATIVE", + "LINK_REDSHIFT" + ] + }, + "Column": { + "type": "structure", + "required": ["Name"], + "members": { + "Name": { + "shape": "NameString" + }, + "Type": { + "shape": "TypeString" + }, + "Comment": { + "shape": "CommentString" + }, + "Parameters": { + "shape": "ParametersMap" + } + } + }, + "ColumnList": { + "type": "list", + "member": { + "shape": "Column" + } + }, + "ColumnValueStringList": { + "type": "list", + "member": { + "shape": "ColumnValuesString" + } + }, + "ColumnValuesString": { + "type": "string" + }, + "CommentString": { + "type": "string", + "max": 255, + "min": 0, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "CompletionIdString": { + "type": "string", + "max": 36, + "min": 36, + "pattern": ".*[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}.*" + }, + "CompletionStatus": { + "type": "string", + "enum": ["SUBMITTED", "SUCCEEDED", "FAILED", "RUNNING", "EXPIRED", "DELETED"] + }, + "CompletionString": { + "type": "string", + "max": 30720, + "min": 1 + }, + "ComputeEnvironment": { + "type": "string", + "enum": ["SPARK", "PYTHON", "ATHENA"] + }, + "ComputeEnvironmentConfiguration": { + "type": "structure", + "required": [ + "Name", + "Description", + "ComputeEnvironment", + "SupportedAuthenticationTypes", + "AdditionalConnectionProperties", + "AdditionalConnectionOptions", + "ConnectionPropertyNameOverrides", + "ConnectionOptionNameOverrides", + "ConnectionPropertyExclusions", + "ConnectionOptionExclusions", + "ConnectionPropertiesRequiredOverrides" + ], + "members": { + "Name": { + "shape": "ComputeEnvironmentName" + }, + "Description": { + "shape": "String" + }, + "ComputeEnvironment": { + "shape": "ComputeEnvironment" + }, + "SupportedAuthenticationTypes": { + "shape": "AuthenticationTypes" + }, + "AdditionalConnectionProperties": { + "shape": "PropertiesMap" + }, + "AdditionalConnectionOptions": { + "shape": "PropertiesMap" + }, + "ConnectionPropertyNameOverrides": { + "shape": "PropertyNameOverrides" + }, + "ConnectionOptionNameOverrides": { + "shape": "PropertyNameOverrides" + }, + "ConnectionPropertyExclusions": { + "shape": "ListOfString" + }, + "ConnectionOptionExclusions": { + "shape": "ListOfString" + }, + "ConnectionPropertiesRequiredOverrides": { + "shape": "ListOfString" + }, + "PhysicalConnectionPropertiesRequired": { + "shape": "Bool" + } + } + }, + "ComputeEnvironmentConfigurationMap": { + "type": "map", + "key": { + "shape": "ComputeEnvironmentName" + }, + "value": { + "shape": "ComputeEnvironmentConfiguration" + } + }, + "ComputeEnvironmentName": { + "type": "string", + "max": 128, + "min": 1 + }, + "ComputeEnvironments": { + "type": "list", + "member": { + "shape": "ComputeEnvironment" + } + }, + "ConditionStatement": { + "type": "map", + "key": { + "shape": "String" + }, + "value": { + "shape": "String" + } + }, + "ConditionStatements": { + "type": "list", + "member": { + "shape": "ConditionStatement" + } + }, + "ConnectionOptions": { + "type": "map", + "key": { + "shape": "OptionKey" + }, + "value": { + "shape": "OptionValue" + } + }, + "ConnectionType": { + "type": "string", + "enum": [ + "JDBC", + "SFTP", + "REDSHIFT", + "ATHENA", + "MONGODB", + "KAFKA", + "NETWORK", + "YARNRESOURCEMANAGER", + "MARKETPLACE", + "HIVE_METASTORE", + "CUSTOM", + "SALESFORCE", + "VIEW_VALIDATION_REDSHIFT", + "VIEW_VALIDATION_ATHENA" + ] + }, + "ConnectionTypeBrief": { + "type": "structure", + "members": { + "ConnectionType": { + "shape": "ConnectionType" + }, + "DisplayName": { + "shape": "DisplayName" + }, + "Vendor": { + "shape": "Vendor" + }, + "Description": { + "shape": "Description" + }, + "Categories": { + "shape": "ListOfString" + }, + "Capabilities": { + "shape": "Capabilities" + }, + "LogoUrl": { + "shape": "UrlString" + }, + "DocumentationUrl": { + "shape": "UrlString" + }, + "ConnectionTypeVariants": { + "shape": "ConnectionTypeVariantList" + } + } + }, + "ConnectionTypeList": { + "type": "list", + "member": { + "shape": "ConnectionTypeBrief" + } + }, + "ConnectionTypeVariant": { + "type": "structure", + "members": { + "ConnectionTypeVariantName": { + "shape": "DisplayName" + }, + "DisplayName": { + "shape": "DisplayName" + }, + "Description": { + "shape": "Description" + }, + "LogoUrl": { + "shape": "UrlString" + }, + "DocumentationUrl": { + "shape": "UrlString" + } + } + }, + "ConnectionTypeVariantList": { + "type": "list", + "member": { + "shape": "ConnectionTypeVariant" + } + }, + "DataAccessModeEnum": { + "type": "string", + "enum": ["LakeFormation", "Hybrid", "Other"] + }, + "DataLakeAccessPropertiesOutput": { + "type": "structure", + "members": { + "DataLakeAccess": { + "shape": "Boolean" + }, + "DataTransferRole": { + "shape": "GlueCommonIAMRoleArn" + }, + "KmsKey": { + "shape": "ResourceArnString" + }, + "ManagedWorkgroupName": { + "shape": "GlueCommonNameString" + }, + "ManagedWorkgroupStatus": { + "shape": "GlueCommonNameString" + }, + "NamespaceArn": { + "shape": "ResourceArnString" + }, + "RedshiftDatabaseName": { + "shape": "GlueCommonNameString" + }, + "StatusMessage": { + "shape": "GlueCommonNameString" + }, + "CatalogType": { + "shape": "GlueCommonNameString" + } + } + }, + "DataLakePrincipal": { + "type": "structure", + "members": { + "DataLakePrincipalIdentifier": { + "shape": "DataLakePrincipalString" + }, + "AttributeCondition": { + "shape": "AttributeCondition" + } + } + }, + "DataLakePrincipalString": { + "type": "string", + "max": 255, + "min": 1 + }, + "DataOperation": { + "type": "string", + "enum": ["READ", "WRITE"] + }, + "DataOperations": { + "type": "list", + "member": { + "shape": "DataOperation" + } + }, + "DataType": { + "type": "string", + "enum": ["STRING", "INTEGER", "BOOLEAN", "STRING_LIST"] + }, + "DatabaseIdString": { + "type": "string", + "max": 100, + "min": 0, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "DescribeConnectionTypeRequest": { + "type": "structure", + "members": { + "ConnectionType": { + "shape": "NameString" + } + } + }, + "DescribeConnectionTypeResponse": { + "type": "structure", + "members": { + "ConnectionType": { + "shape": "NameString" + }, + "DisplayName": { + "shape": "DisplayName" + }, + "Vendor": { + "shape": "Vendor" + }, + "Description": { + "shape": "Description" + }, + "LogoUrl": { + "shape": "UrlString" + }, + "DocumentationUrl": { + "shape": "UrlString" + }, + "Categories": { + "shape": "ListOfString" + }, + "Capabilities": { + "shape": "Capabilities" + }, + "ConnectionProperties": { + "shape": "PropertiesMap" + }, + "SparkConnectionProperties": { + "shape": "PropertiesMap" + }, + "AthenaConnectionProperties": { + "shape": "PropertiesMap" + }, + "ConnectionOptions": { + "shape": "PropertiesMap" + }, + "AuthenticationConfiguration": { + "shape": "AuthConfiguration" + }, + "ComputeEnvironmentConfigurations": { + "shape": "ComputeEnvironmentConfigurationMap" + }, + "PhysicalConnectionRequirements": { + "shape": "PropertiesMap" + } + } + }, + "Description": { + "type": "string", + "max": 1024, + "min": 0 + }, + "DescriptionErrorString": { + "type": "string", + "max": 400000, + "min": 0 + }, + "DescriptionString": { + "type": "string", + "max": 2048, + "min": 0, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\r\\n\\t]*.*" + }, + "DisplayName": { + "type": "string", + "max": 128, + "min": 1 + }, + "EntityFieldName": { + "type": "string" + }, + "EntityName": { + "type": "string" + }, + "EntityNotFoundException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + }, + "fromFederationSource": { + "shape": "NullableBoolean" + } + }, + "documentation": "

This exception is thrown when the requested entity is not found in the server side.

", + "exception": true + }, + "ErrorDetail": { + "type": "structure", + "members": { + "ErrorCode": { + "shape": "NameString" + }, + "ErrorMessage": { + "shape": "DescriptionString" + } + } + }, + "ExecutionClass": { + "type": "string", + "enum": ["FLEX", "STANDARD"] + }, + "ExecutionTime": { + "type": "integer", + "box": true + }, + "ExpressionString": { + "type": "string" + }, + "FederatedCatalog": { + "type": "structure", + "members": { + "Identifier": { + "shape": "GlueCommonFederationIdentifier" + }, + "ConnectionName": { + "shape": "GlueCommonNameString" + } + } + }, + "FederatedTable": { + "type": "structure", + "members": { + "Identifier": { + "shape": "FederationIdentifier" + }, + "DatabaseIdentifier": { + "shape": "FederationIdentifier" + }, + "ProfileName": { + "shape": "NameString" + }, + "ConnectionName": { + "shape": "NameString" + }, + "ConnectionType": { + "shape": "NameString" + } + } + }, + "FederationIdentifier": { + "type": "string", + "max": 512, + "min": 1, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "FederationSourceException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "FederationSourceRetryableException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "FilterPredicate": { + "type": "string", + "max": 2048, + "min": 1, + "pattern": "[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\r\\n\\t]*" + }, + "FormatString": { + "type": "string", + "max": 128, + "min": 0, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "GenericMap": { + "type": "map", + "key": { + "shape": "GenericString" + }, + "value": { + "shape": "GenericString" + } + }, + "GenericString": { + "type": "string" + }, + "GetCatalogRequest": { + "type": "structure", + "required": ["Name"], + "members": { + "Name": { + "shape": "CatalogNameString" + }, + "ParentCatalogId": { + "shape": "CatalogIdString" + }, + "CatalogId": { + "shape": "CatalogIdString" + }, + "CatalogIdentifier": { + "shape": "CatalogIdentifier" + }, + "ContextMap": { + "shape": "RequestContextMap" + }, + "FederateToSource": { + "shape": "Boolean" + } + } + }, + "GetCatalogResponse": { + "type": "structure", + "required": ["Catalog"], + "members": { + "Catalog": { + "shape": "Catalog" + }, + "DataParameters": { + "shape": "BlobParametersMap" + } + } + }, + "GetCatalogsRequest": { + "type": "structure", + "members": { + "ParentCatalogId": { + "shape": "CatalogIdString" + }, + "NextToken": { + "shape": "NextToken" + }, + "MaxResults": { + "shape": "PageSize" + }, + "Recursive": { + "shape": "NullableBoolean" + }, + "ContextMap": { + "shape": "RequestContextMap" + } + } + }, + "GetCatalogsResponse": { + "type": "structure", + "required": ["CatalogList"], + "members": { + "CatalogList": { + "shape": "CatalogList" + }, + "NextToken": { + "shape": "NextToken" + } + } + }, + "GetCompletionRequest": { + "type": "structure", + "required": ["CompletionId"], + "members": { + "CompletionId": { + "shape": "CompletionIdString" + } + } + }, + "GetCompletionResponse": { + "type": "structure", + "required": ["CompletionId", "LastModifiedOn", "Status"], + "members": { + "CompletionId": { + "shape": "CompletionIdString" + }, + "StartedOn": { + "shape": "startedOn" + }, + "LastModifiedOn": { + "shape": "lastModifiedOn" + }, + "ErrorMessage": { + "shape": "HashString" + }, + "CompletedOn": { + "shape": "completedOn" + }, + "Status": { + "shape": "CompletionStatus" + }, + "Completion": { + "shape": "CompletionString" + }, + "SourceURLs": { + "shape": "SourceUrlList" + }, + "Tags": { + "shape": "TagsMap" + } + } + }, + "GetEntityRecordsRequest": { + "type": "structure", + "required": ["EntityName", "Limit"], + "members": { + "EntityName": { + "shape": "EntityName" + }, + "Limit": { + "shape": "Limit" + }, + "ConnectionName": { + "shape": "NameString" + }, + "CatalogId": { + "shape": "CatalogIdString" + }, + "NextToken": { + "shape": "NextToken" + }, + "DataStoreApiVersion": { + "shape": "ApiVersion" + }, + "ConnectionOptions": { + "shape": "ConnectionOptions" + }, + "FilterPredicate": { + "shape": "FilterPredicate" + }, + "OrderBy": { + "shape": "String" + }, + "SelectedFields": { + "shape": "SelectedFields" + }, + "StagingConfiguration": { + "shape": "StagingConfiguration" + } + } + }, + "GetEntityRecordsResponse": { + "type": "structure", + "members": { + "Records": { + "shape": "Records" + }, + "NextToken": { + "shape": "NextToken" + } + } + }, + "GetJobRunRequest": { + "type": "structure", + "required": ["JobName", "RunId"], + "members": { + "JobName": { + "shape": "NameString" + }, + "RunId": { + "shape": "IdString" + }, + "PredecessorsIncluded": { + "shape": "BooleanValue" + } + } + }, + "GetJobRunResponse": { + "type": "structure", + "members": { + "JobRun": { + "shape": "JobRun" + } + } + }, + "GetJobRunsRequest": { + "type": "structure", + "required": ["JobName"], + "members": { + "JobName": { + "shape": "NameString" + }, + "NextToken": { + "shape": "OrchestrationToken" + }, + "MaxResults": { + "shape": "OrchestrationPageSize200" + } + } + }, + "GetJobRunsResponse": { + "type": "structure", + "members": { + "JobRuns": { + "shape": "JobRunList" + }, + "NextToken": { + "shape": "OrchestrationToken" + } + } + }, + "GetTableRequest": { + "type": "structure", + "required": ["DatabaseName", "Name"], + "members": { + "DatabaseName": { + "shape": "NameString" + }, + "Name": { + "shape": "NameString" + }, + "CatalogId": { + "shape": "CatalogIdString" + }, + "TransactionId": { + "shape": "TransactionIdString" + }, + "QueryAsOfTime": { + "shape": "Timestamp" + }, + "IncludeAccessMode": { + "shape": "NullableBoolean" + }, + "IncludeStatusDetails": { + "shape": "NullableBoolean" + }, + "AttributesToGet": { + "shape": "TableAttributesList" + }, + "CatalogIdentifier": { + "shape": "CatalogIdentifier" + }, + "DatabaseIdentifier": { + "shape": "DatabaseIdString" + }, + "TableIdentifier": { + "shape": "TableIdString" + }, + "ContextMap": { + "shape": "RequestContextMap" + } + } + }, + "GetTableResponse": { + "type": "structure", + "members": { + "Table": { + "shape": "Table" + }, + "UseAdvancedFiltering": { + "shape": "NullableBoolean" + } + } + }, + "GlueCommonDescriptionString": { + "type": "string", + "max": 2048, + "min": 1, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "GlueCommonFederationIdentifier": { + "type": "string", + "max": 512, + "min": 1, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "GlueCommonIAMRoleArn": { + "type": "string", + "pattern": "arn:aws(-(cn|us-gov|iso(-[bef])?))?:iam::[0-9]{12}:role/.+.*" + }, + "GlueCommonNameString": { + "type": "string", + "max": 155, + "min": 1, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "GlueEncryptionException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "GlueResourceArn": { + "type": "string", + "pattern": ".*arn:aws(-(cn|us-gov|iso(-[bef])?))?:glue:.*" + }, + "GlueVersionString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": "(\\w+\\.)+\\w+" + }, + "HashString": { + "type": "string", + "max": 255, + "min": 1 + }, + "IcebergOptimizationPropertiesOutput": { + "type": "structure", + "members": { + "RoleArn": { + "shape": "GlueCommonIAMRoleArn" + }, + "Compaction": { + "shape": "ParametersMap" + }, + "Retention": { + "shape": "ParametersMap" + }, + "OrphanFileDeletion": { + "shape": "ParametersMap" + }, + "LastUpdatedTime": { + "shape": "Timestamp" + } + } + }, + "IdString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "Integer": { + "type": "integer", + "box": true + }, + "IntegerFlag": { + "type": "integer", + "box": true, + "max": 1, + "min": 0 + }, + "IntegerValue": { + "type": "integer", + "box": true + }, + "InternalServiceException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + } + }, + "documentation": "

This exception is thrown when a call fails due to internal error.

", + "exception": true, + "fault": true + }, + "InvalidInputException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + }, + "fromFederationSource": { + "shape": "NullableBoolean" + } + }, + "documentation": "

This exception is thrown when the format of the input is incorrect.

", + "exception": true + }, + "JobMode": { + "type": "string", + "enum": ["SCRIPT", "VISUAL", "NOTEBOOK"] + }, + "JobRun": { + "type": "structure", + "members": { + "Id": { + "shape": "IdString" + }, + "Attempt": { + "shape": "AttemptCount" + }, + "PreviousRunId": { + "shape": "IdString" + }, + "TriggerName": { + "shape": "NameString" + }, + "JobName": { + "shape": "NameString" + }, + "JobMode": { + "shape": "JobMode" + }, + "JobRunQueuingEnabled": { + "shape": "NullableBoolean" + }, + "StartedOn": { + "shape": "TimestampValue" + }, + "LastModifiedOn": { + "shape": "TimestampValue" + }, + "CompletedOn": { + "shape": "TimestampValue" + }, + "JobRunState": { + "shape": "JobRunState" + }, + "Arguments": { + "shape": "GenericMap" + }, + "ErrorMessage": { + "shape": "DescriptionErrorString" + }, + "PredecessorRuns": { + "shape": "PredecessorList" + }, + "AllocatedCapacity": { + "shape": "IntegerValue" + }, + "ExecutionTime": { + "shape": "ExecutionTime" + }, + "Timeout": { + "shape": "Timeout" + }, + "MaxCapacity": { + "shape": "NullableDouble" + }, + "WorkerType": { + "shape": "WorkerType" + }, + "NumberOfWorkers": { + "shape": "NullableInteger" + }, + "SecurityConfiguration": { + "shape": "NameString" + }, + "LogGroupName": { + "shape": "LogGroupString" + }, + "NotificationProperty": { + "shape": "NotificationProperty" + }, + "GlueVersion": { + "shape": "GlueVersionString" + }, + "ExecutionClass": { + "shape": "ExecutionClass" + }, + "MinFlexWorkers": { + "shape": "NullableInteger" + }, + "DPUSeconds": { + "shape": "NullableDouble" + }, + "ExecutionArguments": { + "shape": "GenericMap" + }, + "ProfileName": { + "shape": "NameString" + }, + "StateDetail": { + "shape": "OrchestrationMessageString" + }, + "MaintenanceWindow": { + "shape": "MaintenanceWindow" + }, + "UpgradeAnalysisMetadata": { + "shape": "UpgradeAnalysisMetadata" + } + } + }, + "JobRunList": { + "type": "list", + "member": { + "shape": "JobRun" + } + }, + "JobRunState": { + "type": "string", + "enum": [ + "STARTING", + "RUNNING", + "STOPPING", + "STOPPED", + "SUCCEEDED", + "FAILED", + "TIMEOUT", + "ERROR", + "WAITING", + "EXPIRED" + ] + }, + "KeyString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "LakeFormationPermissionEnforcedEnum": { + "type": "string", + "enum": ["AllUsers", "SomeUsers", "NoUser"] + }, + "Limit": { + "type": "long", + "box": true, + "max": 1000, + "min": 1 + }, + "ListConnectionTypesRequest": { + "type": "structure", + "members": { + "MaxResults": { + "shape": "PageSize" + }, + "NextToken": { + "shape": "NextToken" + } + } + }, + "ListConnectionTypesResponse": { + "type": "structure", + "members": { + "ConnectionTypes": { + "shape": "ConnectionTypeList" + }, + "NextToken": { + "shape": "NextToken" + } + } + }, + "ListOfString": { + "type": "list", + "member": { + "shape": "String" + } + }, + "LocationMap": { + "type": "map", + "key": { + "shape": "ColumnValuesString" + }, + "value": { + "shape": "ColumnValuesString" + } + }, + "LocationString": { + "type": "string", + "max": 2056, + "min": 0, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\r\\n\\t]*.*" + }, + "LocationStringList": { + "type": "list", + "member": { + "shape": "LocationString" + } + }, + "LogGroupString": { + "type": "string", + "max": 400000, + "min": 0 + }, + "MaintenanceWindow": { + "type": "string", + "pattern": "(Sun|Mon|Tue|Wed|Thu|Fri|Sat):([01]?[0-9]|2[0-3])" + }, + "Maximum": { + "type": "integer", + "box": true + }, + "Minimum": { + "type": "integer", + "box": true + }, + "NameString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "NameStringList": { + "type": "list", + "member": { + "shape": "NameString" + } + }, + "NextToken": { + "type": "string" + }, + "NonNegativeInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "NotificationProperty": { + "type": "structure", + "members": { + "NotifyDelayAfter": { + "shape": "NotifyDelayAfter" + } + } + }, + "NotifyDelayAfter": { + "type": "integer", + "box": true, + "min": 1 + }, + "NullableBoolean": { + "type": "boolean", + "box": true + }, + "NullableDouble": { + "type": "double", + "box": true + }, + "NullableInteger": { + "type": "integer", + "box": true + }, + "OperationTimeoutException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + } + }, + "documentation": "

This exception occurs when the server throws a timeout

", + "exception": true + }, + "OptionKey": { + "type": "string", + "max": 256, + "min": 1, + "pattern": "[\\w]*" + }, + "OptionValue": { + "type": "string", + "max": 256, + "min": 1, + "pattern": "[\\S]*" + }, + "OrchestrationMessageString": { + "type": "string", + "max": 400000, + "min": 0 + }, + "OrchestrationPageSize200": { + "type": "integer", + "box": true, + "max": 200, + "min": 1 + }, + "OrchestrationToken": { + "type": "string", + "max": 400000, + "min": 0 + }, + "Order": { + "type": "structure", + "required": ["Column", "SortOrder"], + "members": { + "Column": { + "shape": "NameString" + }, + "SortOrder": { + "shape": "IntegerFlag" + } + } + }, + "OrderList": { + "type": "list", + "member": { + "shape": "Order" + } + }, + "OutputLocation": { + "type": "string" + }, + "PageSize": { + "type": "integer", + "box": true, + "max": 1000, + "min": 1 + }, + "ParametersMap": { + "type": "map", + "key": { + "shape": "KeyString" + }, + "value": { + "shape": "ParametersMapValue" + }, + "max": 50, + "min": 0 + }, + "ParametersMapValue": { + "type": "string", + "max": 512000, + "min": 0 + }, + "Permission": { + "type": "string", + "enum": [ + "ALL", + "SELECT", + "ALTER", + "DROP", + "DELETE", + "INSERT", + "DESCRIBE", + "CREATE_DATABASE", + "CREATE_TABLE", + "DATA_LOCATION_ACCESS", + "READ", + "WRITE", + "CREATE_LF_TAG", + "ASSOCIATE", + "UPDATE", + "GRANT_WITH_LF_TAG_EXPRESSION", + "CREATE_LF_TAG_EXPRESSION" + ] + }, + "PermissionList": { + "type": "list", + "member": { + "shape": "Permission" + } + }, + "Phase": { + "type": "string", + "enum": ["AUTHENTICATION", "CONNECTION_CREATION"] + }, + "Predecessor": { + "type": "structure", + "members": { + "JobName": { + "shape": "NameString" + }, + "RunId": { + "shape": "IdString" + } + } + }, + "PredecessorList": { + "type": "list", + "member": { + "shape": "Predecessor" + } + }, + "PrimitiveInteger": { + "type": "integer", + "box": true + }, + "PrincipalPermissions": { + "type": "structure", + "members": { + "Principal": { + "shape": "DataLakePrincipal" + }, + "Permissions": { + "shape": "PermissionList" + } + } + }, + "PrincipalPermissionsList": { + "type": "list", + "member": { + "shape": "PrincipalPermissions" + } + }, + "PromptString": { + "type": "string", + "max": 30720, + "min": 1 + }, + "PropertiesMap": { + "type": "map", + "key": { + "shape": "PropertyName" + }, + "value": { + "shape": "Property" + } + }, + "Property": { + "type": "structure", + "members": { + "Name": { + "shape": "PropertyName" + }, + "DisplayName": { + "shape": "PropertyName" + }, + "Description": { + "shape": "PropertyDescriptionString" + }, + "DataType": { + "shape": "DataType" + }, + "Required": { + "shape": "Bool" + }, + "ConditionallyRequired": { + "shape": "ConditionStatements" + }, + "DefaultValue": { + "shape": "String" + }, + "Phase": { + "shape": "Phase" + }, + "PropertyTypes": { + "shape": "PropertyTypes" + }, + "AllowedValues": { + "shape": "AllowedValues" + }, + "Validations": { + "shape": "Validations" + }, + "DataOperationScopes": { + "shape": "DataOperations" + }, + "Order": { + "shape": "PrimitiveInteger" + }, + "DocumentationUrl": { + "shape": "String" + }, + "Reference": { + "shape": "String" + }, + "Format": { + "shape": "String" + } + } + }, + "PropertyDescriptionString": { + "type": "string", + "max": 1024, + "min": 0 + }, + "PropertyName": { + "type": "string", + "max": 128, + "min": 1 + }, + "PropertyNameOverrides": { + "type": "map", + "key": { + "shape": "PropertyName" + }, + "value": { + "shape": "PropertyName" + } + }, + "PropertyType": { + "type": "string", + "enum": ["USER_INPUT", "SECRET", "READ_ONLY", "UNUSED"] + }, + "PropertyTypes": { + "type": "list", + "member": { + "shape": "PropertyType" + } + }, + "Record": { + "type": "structure", + "members": {}, + "document": true, + "sensitive": true + }, + "Records": { + "type": "list", + "member": { + "shape": "Record" + } + }, + "RequestContextKey": { + "type": "string", + "max": 1024, + "min": 1 + }, + "RequestContextMap": { + "type": "map", + "key": { + "shape": "RequestContextKey" + }, + "value": { + "shape": "RequestContextValue" + }, + "max": 50, + "min": 0 + }, + "RequestContextValue": { + "type": "string", + "max": 10240, + "min": 0 + }, + "ResourceAction": { + "type": "string", + "enum": ["CREATE", "UPDATE"] + }, + "ResourceArnString": { + "type": "string" + }, + "ResourceNotReadyException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "ResourceState": { + "type": "string", + "enum": ["QUEUED", "IN_PROGRESS", "SUCCESS", "STOPPED", "FAILED"] + }, + "SchemaId": { + "type": "structure", + "members": { + "SchemaArn": { + "shape": "GlueResourceArn" + }, + "SchemaName": { + "shape": "SchemaRegistryNameString" + }, + "RegistryName": { + "shape": "SchemaRegistryNameString" + } + } + }, + "SchemaReference": { + "type": "structure", + "members": { + "SchemaId": { + "shape": "SchemaId" + }, + "SchemaVersionId": { + "shape": "SchemaVersionIdString" + }, + "SchemaVersionNumber": { + "shape": "VersionLongNumber" + } + } + }, + "SchemaRegistryNameString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": ".*[a-zA-Z0-9-_$#.]+.*" + }, + "SchemaVersionIdString": { + "type": "string", + "max": 36, + "min": 36, + "pattern": ".*[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}.*" + }, + "ScopeString": { + "type": "string", + "max": 25, + "min": 25 + }, + "ScriptLocationString": { + "type": "string", + "max": 400000, + "min": 0 + }, + "SelectedFields": { + "type": "list", + "member": { + "shape": "EntityFieldName" + } + }, + "SerDeInfo": { + "type": "structure", + "members": { + "Name": { + "shape": "NameString" + }, + "SerializationLibrary": { + "shape": "NameString" + }, + "Parameters": { + "shape": "ParametersMap" + } + } + }, + "SkewedInfo": { + "type": "structure", + "members": { + "SkewedColumnNames": { + "shape": "NameStringList" + }, + "SkewedColumnValues": { + "shape": "ColumnValueStringList" + }, + "SkewedColumnValueLocationMaps": { + "shape": "LocationMap" + } + } + }, + "SourceUrlList": { + "type": "list", + "member": { + "shape": "HashString" + }, + "max": 3, + "min": 1 + }, + "StagingConfiguration": { + "type": "structure", + "members": { + "OutputLocation": { + "shape": "OutputLocation" + } + } + }, + "StartCompletionContext": { + "type": "list", + "member": { + "shape": "StartCompletionContextItem" + } + }, + "StartCompletionContextItem": { + "type": "map", + "key": { + "shape": "HashString" + }, + "value": { + "shape": "HashString" + } + }, + "StartCompletionRequest": { + "type": "structure", + "required": ["Prompt"], + "members": { + "Prompt": { + "shape": "PromptString" + }, + "Tags": { + "shape": "TagsMap" + }, + "Context": { + "shape": "StartCompletionContext" + } + } + }, + "StartCompletionResponse": { + "type": "structure", + "required": ["CompletionId", "ConversationId"], + "members": { + "CompletionId": { + "shape": "CompletionIdString" + }, + "ConversationId": { + "shape": "CompletionIdString" + } + } + }, + "StatusDetails": { + "type": "structure", + "members": { + "RequestedChange": { + "shape": "Table" + }, + "ViewValidations": { + "shape": "ViewValidationList" + } + } + }, + "StorageDescriptor": { + "type": "structure", + "members": { + "Columns": { + "shape": "ColumnList" + }, + "Location": { + "shape": "LocationString" + }, + "AdditionalLocations": { + "shape": "LocationStringList" + }, + "InputFormat": { + "shape": "FormatString" + }, + "OutputFormat": { + "shape": "FormatString" + }, + "Compressed": { + "shape": "Boolean" + }, + "NumberOfBuckets": { + "shape": "Integer" + }, + "SerDeInfo": { + "shape": "SerDeInfo" + }, + "BucketColumns": { + "shape": "NameStringList" + }, + "SortColumns": { + "shape": "OrderList" + }, + "Parameters": { + "shape": "ParametersMap" + }, + "SkewedInfo": { + "shape": "SkewedInfo" + }, + "StoredAsSubDirectories": { + "shape": "Boolean" + }, + "SchemaReference": { + "shape": "SchemaReference" + } + } + }, + "String": { + "type": "string" + }, + "Table": { + "type": "structure", + "required": ["Name"], + "members": { + "Name": { + "shape": "NameString" + }, + "DatabaseName": { + "shape": "NameString" + }, + "Description": { + "shape": "DescriptionString" + }, + "Owner": { + "shape": "NameString" + }, + "CreateTime": { + "shape": "Timestamp" + }, + "UpdateTime": { + "shape": "Timestamp" + }, + "LastAccessTime": { + "shape": "Timestamp" + }, + "LastAnalyzedTime": { + "shape": "Timestamp" + }, + "Retention": { + "shape": "NonNegativeInteger" + }, + "StorageDescriptor": { + "shape": "StorageDescriptor" + }, + "PartitionKeys": { + "shape": "ColumnList" + }, + "ViewOriginalText": { + "shape": "ViewTextString" + }, + "ViewExpandedText": { + "shape": "ViewTextString" + }, + "TableType": { + "shape": "TableTypeString" + }, + "Parameters": { + "shape": "ParametersMap" + }, + "DataParameters": { + "shape": "BlobParametersMap" + }, + "CreatedBy": { + "shape": "NameString" + }, + "IsRegisteredWithLakeFormation": { + "shape": "Boolean" + }, + "LakeFormationPermissionEnforced": { + "shape": "LakeFormationPermissionEnforcedEnum" + }, + "DataAccessMode": { + "shape": "DataAccessModeEnum" + }, + "TargetTable": { + "shape": "TableIdentifier" + }, + "FederatedTable": { + "shape": "FederatedTable" + }, + "CatalogId": { + "shape": "CatalogIdString" + }, + "IsRowFilteringEnabled": { + "shape": "Boolean" + }, + "VersionId": { + "shape": "VersionString" + }, + "CatalogIdentifier": { + "shape": "CatalogIdentifier" + }, + "TableId": { + "shape": "TableIdString" + }, + "DatabaseId": { + "shape": "DatabaseIdString" + }, + "ViewDefinition": { + "shape": "ViewDefinition" + }, + "DataProvider": { + "shape": "NameString" + }, + "IsMultiDialectView": { + "shape": "Boolean" + }, + "Status": { + "shape": "TableStatus" + } + } + }, + "TableAttributes": { + "type": "string", + "enum": ["NAME", "VERSION_ID", "DATA_ACCESS_MODE", "DEFAULT", "ALL", "TABLE_TYPE", "DESCRIPTION"] + }, + "TableAttributesList": { + "type": "list", + "member": { + "shape": "TableAttributes" + } + }, + "TableIdString": { + "type": "string", + "max": 100, + "min": 0, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "TableIdentifier": { + "type": "structure", + "members": { + "CatalogId": { + "shape": "CatalogIdString" + }, + "DatabaseName": { + "shape": "NameString" + }, + "Name": { + "shape": "NameString" + }, + "Region": { + "shape": "NameString" + }, + "DatabaseId": { + "shape": "DatabaseIdString" + } + } + }, + "TableStatus": { + "type": "structure", + "members": { + "RequestedBy": { + "shape": "NameString" + }, + "UpdatedBy": { + "shape": "NameString" + }, + "RequestTime": { + "shape": "Timestamp" + }, + "UpdateTime": { + "shape": "Timestamp" + }, + "Action": { + "shape": "ResourceAction" + }, + "State": { + "shape": "ResourceState" + }, + "Error": { + "shape": "ErrorDetail" + }, + "Details": { + "shape": "StatusDetails" + } + } + }, + "TableTypeString": { + "type": "string", + "max": 255, + "min": 0 + }, + "TagKey": { + "type": "string", + "max": 128, + "min": 1 + }, + "TagValue": { + "type": "string", + "max": 256, + "min": 0 + }, + "TagsMap": { + "type": "map", + "key": { + "shape": "TagKey" + }, + "value": { + "shape": "TagValue" + }, + "max": 50, + "min": 0 + }, + "TargetCatalog": { + "type": "structure", + "members": { + "CatalogArn": { + "shape": "ResourceArnString" + }, + "CatalogIdentifier": { + "shape": "CatalogIdentifier" + }, + "AutoDiscovery": { + "shape": "Boolean" + } + } + }, + "Timeout": { + "type": "integer", + "box": true + }, + "Timestamp": { + "type": "timestamp" + }, + "TimestampValue": { + "type": "timestamp" + }, + "TransactionIdString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": ".*[\\p{L}\\p{N}\\p{P}]*.*" + }, + "TypeString": { + "type": "string", + "max": 20000, + "min": 0, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "UpgradeAnalysisMetadata": { + "type": "structure", + "members": { + "ValidationJobRunId": { + "shape": "NameString" + }, + "GlueVersion": { + "shape": "NameString" + }, + "ScriptLocation": { + "shape": "ScriptLocationString" + }, + "AnalysisId": { + "shape": "IdString" + } + } + }, + "UrlString": { + "type": "string" + }, + "Validation": { + "type": "structure", + "members": { + "ValidationType": { + "shape": "ValidationType" + }, + "Patterns": { + "shape": "ListOfString" + }, + "Description": { + "shape": "ValidationDescriptionString" + }, + "MaxLength": { + "shape": "Maximum" + }, + "Maximum": { + "shape": "Maximum" + }, + "Minimum": { + "shape": "Minimum" + } + } + }, + "ValidationDescriptionString": { + "type": "string", + "max": 1024, + "min": 0 + }, + "ValidationDryRunOpts": { + "type": "structure", + "members": { + "SerializedMockEngineResult": { + "shape": "String" + }, + "ErrorMessage": { + "shape": "String" + }, + "MinimumReceiveCount": { + "shape": "Integer" + } + } + }, + "ValidationException": { + "type": "structure", + "members": { + "message": { + "shape": "String" + } + }, + "documentation": "

This exception occurs when the dag cannot be successfully validated

", + "exception": true + }, + "ValidationType": { + "type": "string", + "enum": ["REGEX", "RANGE"] + }, + "Validations": { + "type": "list", + "member": { + "shape": "Validation" + } + }, + "Vendor": { + "type": "string", + "max": 128, + "min": 1 + }, + "VersionLongNumber": { + "type": "long", + "box": true, + "max": 100000, + "min": 1 + }, + "VersionString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": ".*[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\t]*.*" + }, + "ViewDefinition": { + "type": "structure", + "members": { + "IsProtected": { + "shape": "Boolean" + }, + "Definer": { + "shape": "ArnString" + }, + "SubObjects": { + "shape": "ViewSubObjectsList" + }, + "Representations": { + "shape": "ViewRepresentationList" + } + } + }, + "ViewDialect": { + "type": "string", + "enum": ["REDSHIFT", "ATHENA", "SPARK"] + }, + "ViewDialectVersionString": { + "type": "string", + "max": 255, + "min": 1, + "pattern": ".*[a-zA-Z0-9_.-]+.*" + }, + "ViewRepresentation": { + "type": "structure", + "members": { + "Dialect": { + "shape": "ViewDialect" + }, + "DialectVersion": { + "shape": "ViewDialectVersionString" + }, + "ViewOriginalText": { + "shape": "ViewTextString" + }, + "ViewExpandedText": { + "shape": "ViewTextString" + }, + "ValidationConnection": { + "shape": "NameString" + }, + "IsStale": { + "shape": "Boolean" + }, + "ValidationDryRunOpts": { + "shape": "ValidationDryRunOpts" + } + } + }, + "ViewRepresentationList": { + "type": "list", + "member": { + "shape": "ViewRepresentation" + }, + "max": 1000, + "min": 1 + }, + "ViewSubObjectsList": { + "type": "list", + "member": { + "shape": "ArnString" + }, + "max": 10, + "min": 0 + }, + "ViewTextString": { + "type": "string", + "max": 409600, + "min": 0 + }, + "ViewValidation": { + "type": "structure", + "members": { + "Dialect": { + "shape": "ViewDialect" + }, + "DialectVersion": { + "shape": "ViewDialectVersionString" + }, + "ViewValidationText": { + "shape": "ViewTextString" + }, + "UpdateTime": { + "shape": "Timestamp" + }, + "State": { + "shape": "ResourceState" + }, + "Error": { + "shape": "ErrorDetail" + } + } + }, + "ViewValidationList": { + "type": "list", + "member": { + "shape": "ViewValidation" + } + }, + "WorkerType": { + "type": "string", + "enum": ["Standard", "G_1X", "G_2X", "G_4X", "G_8X", "G_025X", "Z_2X"] + }, + "completedOn": { + "type": "long", + "box": true + }, + "lastModifiedOn": { + "type": "long", + "box": true + }, + "startedOn": { + "type": "long", + "box": true + } + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/s3Client.ts b/packages/core/src/sagemakerunifiedstudio/shared/client/s3Client.ts new file mode 100644 index 00000000000..d86c3904a07 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/s3Client.ts @@ -0,0 +1,147 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { S3 } from '@aws-sdk/client-s3' +import { getLogger } from '../../../shared/logger/logger' +import { ConnectionCredentialsProvider } from '../../auth/providers/connectionCredentialsProvider' + +/** + * Represents an S3 path (bucket or prefix) + */ +export interface S3Path { + bucket: string + prefix?: string + displayName: string + isFolder: boolean + size?: number + lastModified?: Date +} + +/** + * Client for interacting with AWS S3 API using project credentials + */ +export class S3Client { + private s3Client: S3 | undefined + private readonly logger = getLogger() + + constructor( + private readonly region: string, + private readonly connectionCredentialsProvider: ConnectionCredentialsProvider + ) {} + + /** + * Lists S3 paths (folders and objects) using prefix-based navigation + * Uses S3's hierarchical folder-like structure by leveraging prefixes and delimiters + * @param bucket S3 bucket name to list objects from + * @param prefix Optional prefix to filter objects (acts like a folder path) + * @param continuationToken Optional continuation token for pagination + * @returns Object containing paths and nextToken for pagination + */ + public async listPaths( + bucket: string, + prefix?: string, + continuationToken?: string + ): Promise<{ paths: S3Path[]; nextToken?: string }> { + try { + this.logger.info(`S3Client: Listing paths in bucket ${bucket} with prefix ${prefix || 'root'}`) + + const s3Client = await this.getS3Client() + + // Call S3 ListObjectsV2 API with delimiter to simulate folder structure + // Delimiter '/' treats forward slashes as folder separators + // This returns both CommonPrefixes (folders) and Contents (files) + const response = await s3Client.listObjectsV2({ + Bucket: bucket, + Prefix: prefix, // Filter objects that start with this prefix + Delimiter: '/', // Treat '/' as folder separator for hierarchical listing + ContinuationToken: continuationToken, // For pagination + }) + + const paths: S3Path[] = [] + + // Process CommonPrefixes - these represent "folders" in S3 + // CommonPrefixes are object keys that share a common prefix up to the delimiter + if (response.CommonPrefixes) { + for (const commonPrefix of response.CommonPrefixes) { + if (commonPrefix.Prefix) { + // Extract folder name by removing the parent prefix and trailing slash + // Example: if prefix="folder1/" and commonPrefix="folder1/subfolder/" + // folderName becomes "subfolder" + const folderName = commonPrefix.Prefix.replace(prefix || '', '').replace('/', '') + paths.push({ + bucket, + prefix: commonPrefix.Prefix, // Full S3 prefix for this folder + displayName: folderName, // Human-readable folder name + isFolder: true, // Mark as folder for UI rendering + }) + } + } + } + + // Process Contents - these represent actual S3 objects (files) + if (response.Contents) { + for (const object of response.Contents) { + // Skip if no key or if key matches the prefix exactly (folder itself) + if (object.Key && object.Key !== prefix) { + // Extract file name by removing the parent prefix + // Example: if prefix="folder1/" and object.Key="folder1/file.txt" + // fileName becomes "file.txt" + const fileName = object.Key.replace(prefix || '', '') + + // Only include actual files (not folder markers ending with '/') + if (fileName && !fileName.endsWith('/')) { + paths.push({ + bucket, + prefix: object.Key, // Full S3 object key + displayName: fileName, // Human-readable file name + isFolder: false, // Mark as file for UI rendering + size: object.Size, // File size in bytes + lastModified: object.LastModified, // Last modification timestamp + }) + } + } + } + } + + this.logger.info(`S3Client: Found ${paths.length} paths in bucket ${bucket}`) + return { + paths, + nextToken: response.NextContinuationToken, + } + } catch (err) { + this.logger.error('S3Client: Failed to list paths: %s', err as Error) + throw err + } + } + + /** + * Gets the S3 client, initializing it if necessary + */ + private async getS3Client(): Promise { + if (!this.s3Client) { + try { + const credentialsProvider = async () => { + const credentials = await this.connectionCredentialsProvider.getCredentials() + return { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + sessionToken: credentials.sessionToken, + expiration: credentials.expiration, + } + } + + this.s3Client = new S3({ + region: this.region, + credentials: credentialsProvider, + }) + this.logger.debug('S3Client: Successfully created S3 client') + } catch (err) { + this.logger.error('S3Client: Failed to create S3 client: %s', err as Error) + throw err + } + } + return this.s3Client + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/sqlWorkbenchClient.ts b/packages/core/src/sagemakerunifiedstudio/shared/client/sqlWorkbenchClient.ts new file mode 100644 index 00000000000..5513f139d2b --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/sqlWorkbenchClient.ts @@ -0,0 +1,318 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Service } from 'aws-sdk' +import { ServiceConfigurationOptions } from 'aws-sdk/lib/service' +import globals from '../../../shared/extensionGlobals' +import { getLogger } from '../../../shared/logger/logger' +import * as SQLWorkbench from './sqlworkbench' +import apiConfig = require('./sqlworkbench.json') +import { v4 as uuidv4 } from 'uuid' +import { getRedshiftTypeFromHost } from '../../explorer/nodes/utils' +import { DatabaseIntegrationConnectionAuthenticationTypes, RedshiftType } from '../../explorer/nodes/types' +import { ConnectionCredentialsProvider } from '../../auth/providers/connectionCredentialsProvider' +import { adaptConnectionCredentialsProvider } from './credentialsAdapter' + +/** + * Connection configuration for SQL Workbench + */ +export interface ConnectionConfig { + id: string + type: string + databaseType: string + connectableResourceIdentifier: string + connectableResourceType: string + database: string + auth?: { + secretArn?: string + } +} + +/** + * Resource parent information + */ +export interface ParentResource { + parentId: string + parentType: string +} + +/** + * Gets a SQL Workbench ARN + * @param region AWS region + * @param accountId Optional AWS account ID (will be determined if not provided) + * @returns SQL Workbench ARN + */ +export async function generateSqlWorkbenchArn(region: string, accountId: string): Promise { + return `arn:aws:sqlworkbench:${region}:${accountId}:connection/${uuidv4()}` +} + +/** + * Creates a connection configuration for Redshift + */ +export async function createRedshiftConnectionConfig( + host: string, + database: string, + accountId: string, + region: string, + secretArn?: string, + isGlueCatalogDatabase?: boolean +): Promise { + // Get Redshift deployment type from host + const redshiftDeploymentType = getRedshiftTypeFromHost(host) + + // Extract resource identifier from host + const resourceIdentifier = host.split('.')[0] + + if (!resourceIdentifier) { + throw new Error('Resource identifier could not be determined from host') + } + + // Create connection ID using the proper ARN format + const connectionId = await generateSqlWorkbenchArn(region, accountId) + + // Determine if serverless or cluster based on deployment type + const isServerless = + redshiftDeploymentType === RedshiftType.Serverless || + redshiftDeploymentType === RedshiftType.ServerlessDev || + redshiftDeploymentType === RedshiftType.ServerlessQA + + const isCluster = + redshiftDeploymentType === RedshiftType.Cluster || + redshiftDeploymentType === RedshiftType.ClusterDev || + redshiftDeploymentType === RedshiftType.ClusterQA + + // Validate the Redshift type + if (!isServerless && !isCluster) { + throw new Error(`Unsupported Redshift type for host: ${host}`) + } + + // Determine auth type based on the provided parameters + let authType: string + + if (secretArn) { + authType = DatabaseIntegrationConnectionAuthenticationTypes.SECRET + } else if (isCluster) { + authType = DatabaseIntegrationConnectionAuthenticationTypes.TEMPORARY_CREDENTIALS_WITH_IAM + } else { + // For serverless + authType = DatabaseIntegrationConnectionAuthenticationTypes.FEDERATED + } + + // Enforce specific authentication type for S3Table/RedLake databases + if (isGlueCatalogDatabase) { + authType = isServerless + ? DatabaseIntegrationConnectionAuthenticationTypes.FEDERATED + : DatabaseIntegrationConnectionAuthenticationTypes.TEMPORARY_CREDENTIALS_WITH_IAM + } + + // Create the connection configuration + const connectionConfig: ConnectionConfig = { + id: connectionId, + type: authType, + databaseType: 'REDSHIFT', + connectableResourceIdentifier: resourceIdentifier, + connectableResourceType: isServerless ? 'WORKGROUP' : 'CLUSTER', + database: database, + } + + // Add auth object for SECRET authentication type + if ( + (authType as DatabaseIntegrationConnectionAuthenticationTypes) === + DatabaseIntegrationConnectionAuthenticationTypes.SECRET && + secretArn + ) { + connectionConfig.auth = { secretArn } + } + + return connectionConfig +} + +/** + * Client for interacting with SQL Workbench API + */ +export class SQLWorkbenchClient { + private sqlClient: SQLWorkbench | undefined + private static instance: SQLWorkbenchClient | undefined + private readonly logger = getLogger() + + private constructor( + private readonly region: string, + private readonly connectionCredentialsProvider?: ConnectionCredentialsProvider + ) {} + + /** + * Gets a singleton instance of the SQLWorkbenchClient + * @returns SQLWorkbenchClient instance + */ + public static getInstance(region: string): SQLWorkbenchClient { + if (!SQLWorkbenchClient.instance) { + SQLWorkbenchClient.instance = new SQLWorkbenchClient(region) + } + return SQLWorkbenchClient.instance + } + + /** + * Creates a new SQLWorkbenchClient instance with specific credentials + * @param region AWS region + * @param connectionCredentialsProvider ConnectionCredentialsProvider + * @returns SQLWorkbenchClient instance with credentials provider + */ + public static createWithCredentials( + region: string, + connectionCredentialsProvider: ConnectionCredentialsProvider + ): SQLWorkbenchClient { + return new SQLWorkbenchClient(region, connectionCredentialsProvider) + } + + /** + * Gets the AWS region + * @returns AWS region + */ + public getRegion(): string { + return this.region + } + + /** + * Gets resources from SQL Workbench + * @param params Request parameters + * @returns Raw response from getResources API + */ + public async getResources(params: { + connection: ConnectionConfig + resourceType: string + includeChildren?: boolean + maxItems?: number + parents?: ParentResource[] + pageToken?: string + forceRefresh?: boolean + }): Promise { + try { + this.logger.info(`SQLWorkbenchClient: Getting resources in region ${this.region}`) + + const sqlClient = await this.getSQLClient() + + const requestParams = { + connection: params.connection, + type: params.resourceType, + maxItems: params.maxItems || 100, + parents: params.parents || [], + pageToken: params.pageToken, + forceRefresh: params.forceRefresh || true, + accountSettings: {}, + } + + // Call the GetResources API + const response = await sqlClient.getResources(requestParams).promise() + + return { + resources: response.resources || [], + nextToken: response.nextToken, + } + } catch (err) { + this.logger.error('SQLWorkbenchClient: Failed to get resources: %s', err as Error) + throw err + } + } + + /** + * Execute a SQL query + * @param connectionConfig Connection configuration + * @param query SQL query to execute + * @returns Query execution ID + */ + public async executeQuery(connectionConfig: ConnectionConfig, query: string): Promise { + try { + this.logger.info(`SQLWorkbenchClient: Executing query in region ${this.region}`) + + const sqlClient = await this.getSQLClient() + + // Call the ExecuteQuery API + const response = await sqlClient + .executeQuery({ + connection: connectionConfig as any, + databaseType: 'REDSHIFT', + accountSettings: {}, + executionContext: [ + { + parentType: 'DATABASE', + parentId: connectionConfig.database || '', + }, + ], + query, + queryExecutionType: 'NO_SESSION', + queryResponseDeliveryType: 'ASYNC', + maxItems: 100, + ignoreHistory: true, + tabId: 'data_explorer', + }) + .promise() + + // Log the response + this.logger.info( + `SQLWorkbenchClient: Query execution started with ID: ${response.queryExecutions?.[0]?.queryExecutionId}` + ) + + return response.queryExecutions?.[0]?.queryExecutionId + } catch (err) { + this.logger.error('SQLWorkbenchClient: Failed to execute query: %s', err as Error) + throw err + } + } + + /** + * Gets the SQL client, initializing it if necessary + */ + /** + * Gets the SQL Workbench endpoint URL for the given region + * @param region AWS region + * @returns SQL Workbench endpoint URL + */ + private getSQLWorkbenchEndpoint(region: string): string { + return `https://api-v2.sqlworkbench.${region}.amazonaws.com` + } + + private async getSQLClient(): Promise { + if (!this.sqlClient) { + try { + // Get the endpoint URL for the region + const endpoint = this.getSQLWorkbenchEndpoint(this.region) + this.logger.info(`Using SQL Workbench endpoint: ${endpoint}`) + + if (this.connectionCredentialsProvider) { + // Create client with provided credentials + this.sqlClient = (await globals.sdkClientBuilder.createAwsService( + Service, + { + apiConfig: apiConfig, + region: this.region, + endpoint: endpoint, + credentialProvider: adaptConnectionCredentialsProvider(this.connectionCredentialsProvider), + } as ServiceConfigurationOptions, + undefined, + false + )) as SQLWorkbench + } else { + // Use the SDK client builder for default credentials + this.sqlClient = (await globals.sdkClientBuilder.createAwsService( + Service, + { + apiConfig: apiConfig, + region: this.region, + endpoint: endpoint, + } as ServiceConfigurationOptions, + undefined, + false + )) as SQLWorkbench + } + + this.logger.debug('SQLWorkbenchClient: Successfully created SQL client') + } catch (err) { + this.logger.error('SQLWorkbenchClient: Failed to create SQL client: %s', err as Error) + throw err + } + } + return this.sqlClient + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/client/sqlworkbench.json b/packages/core/src/sagemakerunifiedstudio/shared/client/sqlworkbench.json new file mode 100644 index 00000000000..e403ec34a88 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/client/sqlworkbench.json @@ -0,0 +1,2102 @@ +{ + "version": "2.0", + "metadata": { + "apiVersion": "2024-02-12", + "auth": ["aws.auth#sigv4"], + "endpointPrefix": "sqlworkbench", + "protocol": "rest-json", + "protocols": ["rest-json"], + "serviceFullName": "AmazonSQLWorkbench", + "serviceId": "SQLWorkbench", + "signatureVersion": "v4", + "signingName": "sqlworkbench", + "uid": "sqlworkbench-2024-02-12" + }, + "operations": { + "CancelQueries": { + "name": "CancelQueries", + "http": { + "method": "POST", + "requestUri": "/database/cancelQueries", + "responseCode": 200 + }, + "input": { "shape": "CancelQueriesRequest" }, + "output": { "shape": "CancelQueriesResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "CreateConnection": { + "name": "CreateConnection", + "http": { + "method": "PUT", + "requestUri": "/connections", + "responseCode": 200 + }, + "input": { "shape": "CreateConnectionRequest" }, + "output": { "shape": "CreateConnectionResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "DeleteConnection": { + "name": "DeleteConnection", + "http": { + "method": "DELETE", + "requestUri": "/connections/{connectionId}", + "responseCode": 200 + }, + "input": { "shape": "DeleteConnectionRequest" }, + "output": { "shape": "DeleteConnectionResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "ExecuteQuery": { + "name": "ExecuteQuery", + "http": { + "method": "POST", + "requestUri": "/database/executeQuery", + "responseCode": 200 + }, + "input": { "shape": "ExecuteQueryRequest" }, + "output": { "shape": "ExecuteQueryResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "ExportQueryResults": { + "name": "ExportQueryResults", + "http": { + "method": "POST", + "requestUri": "/database/exportResults", + "responseCode": 200 + }, + "input": { "shape": "ExportQueryResultsRequest" }, + "output": { "shape": "ExportQueryResultsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "GetConnectableResources": { + "name": "GetConnectableResources", + "http": { + "method": "POST", + "requestUri": "/database/getConnectableResources", + "responseCode": 200 + }, + "input": { "shape": "GetConnectableResourcesRequest" }, + "output": { "shape": "GetConnectableResourcesResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "GetConnection": { + "name": "GetConnection", + "http": { + "method": "GET", + "requestUri": "/connections/{connectionId}", + "responseCode": 200 + }, + "input": { "shape": "GetConnectionRequest" }, + "output": { "shape": "GetConnectionResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "GetDatabaseConfigurations": { + "name": "GetDatabaseConfigurations", + "http": { + "method": "POST", + "requestUri": "/database/configurations", + "responseCode": 200 + }, + "input": { "shape": "GetDatabaseConfigurationsRequest" }, + "output": { "shape": "GetDatabaseConfigurationsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "GetQueryExecutionHistory": { + "name": "GetQueryExecutionHistory", + "http": { + "method": "POST", + "requestUri": "/queryExecutionHistory/details", + "responseCode": 200 + }, + "input": { "shape": "GetQueryExecutionHistoryRequest" }, + "output": { "shape": "GetQueryExecutionHistoryResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "GetQueryResult": { + "name": "GetQueryResult", + "http": { + "method": "POST", + "requestUri": "/database/getQueryResults", + "responseCode": 200 + }, + "input": { "shape": "GetQueryResultRequest" }, + "output": { "shape": "GetQueryResultResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "GetResources": { + "name": "GetResources", + "http": { + "method": "POST", + "requestUri": "/database/getResources", + "responseCode": 200 + }, + "input": { "shape": "GetResourcesRequest" }, + "output": { "shape": "GetResourcesResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "GetTabStates": { + "name": "GetTabStates", + "http": { + "method": "POST", + "requestUri": "/tab/state", + "responseCode": 200 + }, + "input": { "shape": "GetTabStatesRequest" }, + "output": { "shape": "GetTabStatesResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "ListQueryExecutionHistory": { + "name": "ListQueryExecutionHistory", + "http": { + "method": "POST", + "requestUri": "/queryExecutionHistory/list", + "responseCode": 200 + }, + "input": { "shape": "ListQueryExecutionHistoryRequest" }, + "output": { "shape": "ListQueryExecutionHistoryResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "ListTagsForResource": { + "name": "ListTagsForResource", + "http": { + "method": "GET", + "requestUri": "/tags/{resourceArn}", + "responseCode": 200 + }, + "input": { "shape": "ListTagsForResourceRequest" }, + "output": { "shape": "ListTagsForResourceResponse" }, + "errors": [ + { "shape": "BadRequestError" }, + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "PollQueryExecutionEvents": { + "name": "PollQueryExecutionEvents", + "http": { + "method": "POST", + "requestUri": "/database/pollQueryExecutionEvents", + "responseCode": 200 + }, + "input": { "shape": "PollQueryExecutionEventsRequest" }, + "output": { "shape": "PollQueryExecutionEventsResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "BadRequestError" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "TagResource": { + "name": "TagResource", + "http": { + "method": "POST", + "requestUri": "/tags/{resourceArn}", + "responseCode": 204 + }, + "input": { "shape": "TagResourceRequest" }, + "output": { "shape": "TagResourceResponse" }, + "errors": [ + { "shape": "BadRequestError" }, + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "UntagResource": { + "name": "UntagResource", + "http": { + "method": "DELETE", + "requestUri": "/tags/{resourceArn}", + "responseCode": 204 + }, + "input": { "shape": "UntagResourceRequest" }, + "output": { "shape": "UntagResourceResponse" }, + "errors": [ + { "shape": "BadRequestError" }, + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ], + "idempotent": true + }, + "UpdateConnection": { + "name": "UpdateConnection", + "http": { + "method": "POST", + "requestUri": "/connections", + "responseCode": 200 + }, + "input": { "shape": "UpdateConnectionRequest" }, + "output": { "shape": "UpdateConnectionResponse" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "ConflictException" }, + { "shape": "InternalServerError" }, + { "shape": "ValidationException" } + ] + }, + "VerifyResourcesExistForTagris": { + "name": "VerifyResourcesExistForTagris", + "http": { + "method": "POST", + "requestUri": "/verifyResourcesExistForTagris", + "responseCode": 200 + }, + "input": { "shape": "TagrisVerifyResourcesExistInput" }, + "output": { "shape": "TagrisVerifyResourcesExistOutput" }, + "errors": [ + { "shape": "ThrottlingException" }, + { "shape": "InternalServerError" }, + { "shape": "TagrisInvalidParameterException" }, + { "shape": "TagrisAccessDeniedException" }, + { "shape": "TagrisInvalidArnException" }, + { "shape": "ResourceNotFoundException" }, + { "shape": "TagrisInternalServiceException" }, + { "shape": "ServiceQuotaExceededException" }, + { "shape": "AccessDeniedException" }, + { "shape": "TagrisPartialResourcesExistResultsException" }, + { "shape": "TagrisThrottledException" }, + { "shape": "ConflictException" }, + { "shape": "ValidationException" } + ] + } + }, + "shapes": { + "AccessDeniedException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "code": { "shape": "ErrorCode" } + }, + "error": { + "httpStatusCode": 403, + "senderFault": true + }, + "exception": true + }, + "AckIds": { + "type": "list", + "member": { "shape": "AckIdsMemberString" } + }, + "AckIdsMemberString": { + "type": "string", + "max": 100, + "min": 0 + }, + "Arn": { + "type": "string", + "max": 1011, + "min": 20 + }, + "AvailableConnectionConfigurationOptions": { + "type": "list", + "member": { "shape": "AvailableConnectionConfigurationOptionsMemberString" } + }, + "AvailableConnectionConfigurationOptionsMemberString": { + "type": "string", + "max": 50, + "min": 0 + }, + "BadRequestError": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "code": { "shape": "ErrorCode" } + }, + "error": { + "httpStatusCode": 400, + "senderFault": true + }, + "exception": true + }, + "Boolean": { + "type": "boolean", + "box": true + }, + "CancelQueriesRequest": { + "type": "structure", + "required": ["queryExecutionIds", "databaseType"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" }, + "queryExecutionIds": { "shape": "CancelQueriesRequestQueryExecutionIdsList" }, + "databaseType": { + "shape": "DatabaseType", + "location": "querystring", + "locationName": "databaseType" + } + } + }, + "CancelQueriesRequestQueryExecutionIdsList": { + "type": "list", + "member": { "shape": "CancelQueriesRequestQueryExecutionIdsListMemberString" }, + "max": 100, + "min": 1 + }, + "CancelQueriesRequestQueryExecutionIdsListMemberString": { + "type": "string", + "max": 100, + "min": 1 + }, + "CancelQueriesResponse": { + "type": "structure", + "required": ["cancelQueryResponses"], + "members": { + "cancelQueryResponses": { "shape": "CancelQueryResponses" } + } + }, + "CancelQueryResponse": { + "type": "structure", + "required": ["queryExecutionId"], + "members": { + "queryExecutionId": { "shape": "CancelQueryResponseQueryExecutionIdString" }, + "queryCancellationStatus": { "shape": "QueryCancellationStatus" } + } + }, + "CancelQueryResponseQueryExecutionIdString": { + "type": "string", + "max": 1000, + "min": 0 + }, + "CancelQueryResponses": { + "type": "list", + "member": { "shape": "CancelQueryResponse" } + }, + "ChildObjectTypes": { + "type": "list", + "member": { "shape": "ChildObjectTypesMemberString" } + }, + "ChildObjectTypesMemberString": { + "type": "string", + "max": 50, + "min": 0 + }, + "Columns": { + "type": "list", + "member": { "shape": "QueryResultCellValue" } + }, + "ConflictException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "code": { "shape": "ErrorCode" } + }, + "error": { + "httpStatusCode": 409, + "senderFault": true + }, + "exception": true + }, + "ConnectableResource": { + "type": "structure", + "required": ["displayName", "identifier", "childObjectTypes", "availableConnectionConfigurationOptions"], + "members": { + "displayName": { "shape": "ResourceDisplayName" }, + "identifier": { "shape": "ResourceIdentifier" }, + "type": { "shape": "ConnectableResourceTypeString" }, + "unavailable": { "shape": "Boolean" }, + "tooltipTranslationKey": { "shape": "ConnectableResourceTooltipTranslationKeyString" }, + "childObjectTypes": { "shape": "ChildObjectTypes" }, + "availableConnectionConfigurationOptions": { "shape": "AvailableConnectionConfigurationOptions" } + } + }, + "ConnectableResourceTooltipTranslationKeyString": { + "type": "string", + "max": 50, + "min": 0 + }, + "ConnectableResourceTypeString": { + "type": "string", + "max": 50, + "min": 0 + }, + "ConnectableResourceTypes": { + "type": "list", + "member": { "shape": "ConnectableResourceTypesMemberString" } + }, + "ConnectableResourceTypesMemberString": { + "type": "string", + "max": 50, + "min": 0 + }, + "ConnectableResources": { + "type": "list", + "member": { "shape": "ConnectableResource" } + }, + "Connection": { + "type": "structure", + "members": { + "id": { + "shape": "String", + "documentation": "

Id of the connection

" + }, + "name": { + "shape": "ConnectionName", + "documentation": "

Name of the connection

" + }, + "authenticationType": { + "shape": "ConnectionAuthenticationTypes", + "documentation": "

Number representing the type of authentication to use (2 = IAM, 3 = Username and Password). Today we only support the types 2 and 3

" + }, + "secretArn": { + "shape": "String", + "documentation": "

Secret that is linked to this connection

" + }, + "databaseName": { + "shape": "DatabaseName", + "documentation": "

Name of the database where the query is run

" + }, + "clusterId": { + "shape": "String", + "documentation": "

Id of the cluster of the connection

" + }, + "dbUser": { + "shape": "DbUser", + "documentation": "

User of the database

" + }, + "isServerless": { "shape": "Boolean" }, + "isProd": { "shape": "String" }, + "isEnabled": { "shape": "String" }, + "userSettings": { "shape": "UserSettings" }, + "recordDate": { "shape": "String" }, + "updatedDate": { "shape": "String" }, + "tags": { "shape": "Tags" }, + "databaseType": { "shape": "DatabaseType" }, + "connectableResourceType": { "shape": "String" }, + "connectableResourceIdentifier": { "shape": "ResourceIdentifier" } + } + }, + "ConnectionAuthenticationTypes": { + "type": "string", + "enum": ["2", "3", "4", "5", "6", "7", "8"], + "sensitive": true + }, + "ConnectionName": { + "type": "string", + "sensitive": true + }, + "ConnectionProperties": { + "type": "map", + "key": { "shape": "ConnectionPropertyKey" }, + "value": { "shape": "ConnectionPropertyValue" }, + "max": 50, + "min": 1 + }, + "ConnectionPropertyKey": { + "type": "string", + "max": 1000, + "min": 1 + }, + "ConnectionPropertyValue": { + "type": "string", + "max": 1000, + "min": 0 + }, + "CreateConnectionRequest": { + "type": "structure", + "required": ["name", "databaseName", "authenticationType"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "name": { + "shape": "CreateConnectionRequestNameString", + "documentation": "

Name of the connection

" + }, + "databaseName": { + "shape": "CreateConnectionRequestDatabaseNameString", + "documentation": "

Name of the database used for this connection

" + }, + "authenticationType": { + "shape": "CreateConnectionRequestAuthenticationTypeEnum", + "documentation": "

Number representing the type of authentication to use (2 = IAM, 3 = Username and Password, 4 = Federated connection)

" + }, + "isProd": { "shape": "CreateConnectionRequestIsProdString" }, + "userSettings": { "shape": "UserSettings" }, + "secretArn": { + "shape": "CreateConnectionRequestSecretArnString", + "documentation": "

secretArn for redshift cluster

" + }, + "clusterId": { + "shape": "CreateConnectionRequestClusterIdString", + "documentation": "

Id of the cluster used for this connection

" + }, + "isServerless": { + "shape": "Boolean", + "documentation": "

Is serverless connection

" + }, + "dbUser": { + "shape": "DbUser", + "documentation": "

User of the database used for this connection

" + }, + "isStoreNewSecret": { "shape": "CreateConnectionRequestIsStoreNewSecretString" }, + "username": { + "shape": "DbUser", + "documentation": "

Username used in the Username_Password connection type

" + }, + "password": { + "shape": "CreateConnectionRequestPasswordString", + "documentation": "

Password of the user used for this connection

" + }, + "tags": { "shape": "Tags" }, + "host": { + "shape": "CreateConnectionRequestHostString", + "documentation": "

Host address used for creating secret for Username_Password connection type

" + }, + "secretName": { "shape": "CreateConnectionRequestSecretNameString" }, + "description": { "shape": "CreateConnectionRequestDescriptionString" }, + "databaseType": { "shape": "DatabaseType" }, + "connectableResourceIdentifier": { + "shape": "CreateConnectionRequestConnectableResourceIdentifierString", + "documentation": "

Id of the connectable resource used for this connection

" + }, + "connectableResourceType": { + "shape": "CreateConnectionRequestConnectableResourceTypeString", + "documentation": "

Type of the connectable resource used for this connection

" + } + } + }, + "CreateConnectionRequestAuthenticationTypeEnum": { + "type": "string", + "enum": ["2", "3", "4", "5", "6", "7", "8"], + "max": 1, + "min": 1, + "sensitive": true + }, + "CreateConnectionRequestClusterIdString": { + "type": "string", + "max": 63, + "min": 1 + }, + "CreateConnectionRequestConnectableResourceIdentifierString": { + "type": "string", + "max": 63, + "min": 1, + "sensitive": true + }, + "CreateConnectionRequestConnectableResourceTypeString": { + "type": "string", + "max": 63, + "min": 1 + }, + "CreateConnectionRequestDatabaseNameString": { + "type": "string", + "max": 64, + "min": 1, + "sensitive": true + }, + "CreateConnectionRequestDescriptionString": { + "type": "string", + "max": 1000, + "min": 0 + }, + "CreateConnectionRequestHostString": { + "type": "string", + "max": 1000, + "min": 0 + }, + "CreateConnectionRequestIsProdString": { + "type": "string", + "max": 1000, + "min": 0 + }, + "CreateConnectionRequestIsStoreNewSecretString": { + "type": "string", + "max": 1000, + "min": 0 + }, + "CreateConnectionRequestNameString": { + "type": "string", + "max": 512, + "min": 1, + "sensitive": true + }, + "CreateConnectionRequestPasswordString": { + "type": "string", + "max": 64, + "min": 8, + "sensitive": true + }, + "CreateConnectionRequestSecretArnString": { + "type": "string", + "max": 1000, + "min": 1 + }, + "CreateConnectionRequestSecretNameString": { + "type": "string", + "max": 1000, + "min": 0 + }, + "CreateConnectionResponse": { + "type": "structure", + "members": { + "data": { "shape": "Connection" } + } + }, + "DatabaseAuthenticationMethod": { + "type": "string", + "enum": ["USERNAME_PASSWORD", "TEMPORARY_CREDENTIALS_WITH_IAM"] + }, + "DatabaseAuthenticationMethods": { + "type": "list", + "member": { "shape": "DatabaseAuthenticationMethod" } + }, + "DatabaseAuthenticationOption": { + "type": "structure", + "required": ["connectableResourceType", "authenticationMethods"], + "members": { + "connectableResourceType": { "shape": "String" }, + "authenticationMethods": { "shape": "DatabaseAuthenticationMethods" } + } + }, + "DatabaseAuthenticationOptions": { + "type": "list", + "member": { "shape": "DatabaseAuthenticationOption" } + }, + "DatabaseConfiguration": { + "type": "structure", + "required": [ + "databaseType", + "authenticationOptions", + "connectableResourceTypes", + "sessionSupported", + "eventAcknowledgementSupported", + "appendingLimitToQuerySupported", + "queryStatsSupported" + ], + "members": { + "databaseType": { "shape": "DatabaseType" }, + "authenticationOptions": { "shape": "DatabaseAuthenticationOptions" }, + "connectableResourceTypes": { "shape": "ConnectableResourceTypes" }, + "sessionSupported": { "shape": "Boolean" }, + "eventAcknowledgementSupported": { "shape": "Boolean" }, + "appendingLimitToQuerySupported": { "shape": "Boolean" }, + "queryStatsSupported": { "shape": "Boolean" } + } + }, + "DatabaseConfigurations": { + "type": "list", + "member": { "shape": "DatabaseConfiguration" } + }, + "DatabaseConnectionAccountSettings": { + "type": "structure", + "members": { + "masterKeyArn": { "shape": "KmsKeyArn" } + } + }, + "DatabaseConnectionConfiguration": { + "type": "structure", + "required": ["id", "type", "databaseType", "connectableResourceIdentifier", "connectableResourceType"], + "members": { + "id": { "shape": "DatabaseConnectionConfigurationIdString" }, + "type": { "shape": "DatabaseIntegrationConnectionAuthenticationTypes" }, + "auth": { "shape": "DatabaseConnectionConfigurationAuth" }, + "databaseType": { "shape": "DatabaseType" }, + "connectableResourceIdentifier": { "shape": "ResourceIdentifier" }, + "connectableResourceType": { "shape": "DatabaseConnectionConfigurationConnectableResourceTypeString" }, + "database": { "shape": "DatabaseName" } + } + }, + "DatabaseConnectionConfigurationAuth": { + "type": "structure", + "members": { + "secretArn": { "shape": "SecretKeyArn" }, + "username": { "shape": "DatabaseConnectionConfigurationAuthUsernameString" }, + "password": { "shape": "DatabaseConnectionConfigurationAuthPasswordString" } + } + }, + "DatabaseConnectionConfigurationAuthPasswordString": { + "type": "string", + "max": 1000, + "min": 0, + "sensitive": true + }, + "DatabaseConnectionConfigurationAuthUsernameString": { + "type": "string", + "max": 1000, + "min": 0, + "sensitive": true + }, + "DatabaseConnectionConfigurationConnectableResourceTypeString": { + "type": "string", + "max": 50, + "min": 0 + }, + "DatabaseConnectionConfigurationIdString": { + "type": "string", + "max": 2048, + "min": 32 + }, + "DatabaseIntegrationConnectionAuthenticationTypes": { + "type": "string", + "enum": ["4", "5", "6", "8"], + "sensitive": true + }, + "DatabaseName": { + "type": "string", + "max": 150, + "min": 0, + "sensitive": true + }, + "DatabaseType": { + "type": "string", + "enum": ["REDSHIFT", "ATHENA"] + }, + "DbUser": { + "type": "string", + "max": 127, + "min": 1, + "pattern": "[a-zA-Z0-9_][a-zA-Z_0-9+.@$-]*", + "sensitive": true + }, + "DeleteConnectionRequest": { + "type": "structure", + "required": ["connectionId"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "connectionId": { + "shape": "DeleteConnectionRequestConnectionIdString", + "documentation": "

Id of connection to delete

", + "location": "uri", + "locationName": "connectionId" + } + } + }, + "DeleteConnectionRequestConnectionIdString": { + "type": "string", + "max": 1000, + "min": 1 + }, + "DeleteConnectionResponse": { + "type": "structure", + "members": {} + }, + "ErrorCode": { + "type": "string", + "enum": ["QUERY_EXECUTION_NOT_FOUND", "QUERY_EXECUTION_ACCESS_DENIED"] + }, + "ExecuteQueryRequest": { + "type": "structure", + "required": ["query", "queryExecutionType", "queryResponseDeliveryType", "maxItems"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "connectionId": { "shape": "ExecuteQueryRequestConnectionIdString" }, + "databaseType": { + "shape": "DatabaseType", + "location": "querystring", + "locationName": "databaseType" + }, + "connection": { "shape": "DatabaseConnectionConfiguration" }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" }, + "tabId": { "shape": "ExecuteQueryRequestTabIdString" }, + "executionContext": { "shape": "ExecuteQueryRequestExecutionContextList" }, + "query": { "shape": "ExecuteQueryRequestQueryString" }, + "queryExecutionType": { "shape": "QueryExecutionType" }, + "sessionId": { "shape": "ExecuteQueryRequestSessionIdString" }, + "queryResponseDeliveryType": { "shape": "QueryResponseDeliveryType" }, + "maxItems": { "shape": "ExecuteQueryRequestMaxItemsInteger" }, + "limitQueryResults": { "shape": "ExecuteQueryRequestLimitQueryResultsInteger" }, + "isExplain": { "shape": "Boolean" }, + "ignoreHistory": { "shape": "Boolean" }, + "timeoutMillis": { "shape": "ExecuteQueryRequestTimeoutMillisInteger" } + } + }, + "ExecuteQueryRequestConnectionIdString": { + "type": "string", + "max": 2048, + "min": 32 + }, + "ExecuteQueryRequestExecutionContextList": { + "type": "list", + "member": { "shape": "ParentResource" }, + "max": 100, + "min": 0 + }, + "ExecuteQueryRequestLimitQueryResultsInteger": { + "type": "integer", + "box": true, + "max": 1000, + "min": 0 + }, + "ExecuteQueryRequestMaxItemsInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 20 + }, + "ExecuteQueryRequestQueryString": { + "type": "string", + "max": 1000000, + "min": 0, + "sensitive": true + }, + "ExecuteQueryRequestSessionIdString": { + "type": "string", + "max": 100, + "min": 0 + }, + "ExecuteQueryRequestTabIdString": { + "type": "string", + "max": 100, + "min": 1 + }, + "ExecuteQueryRequestTimeoutMillisInteger": { + "type": "integer", + "box": true, + "max": 120000, + "min": 0 + }, + "ExecuteQueryResponse": { + "type": "structure", + "required": ["queryExecutions"], + "members": { + "sessionId": { "shape": "ExecuteQueryResponseSessionIdString" }, + "queryExecutions": { "shape": "QueryExecutions" }, + "statusCode": { + "shape": "statusCode", + "location": "statusCode" + } + } + }, + "ExecuteQueryResponseSessionIdString": { + "type": "string", + "max": 100, + "min": 0 + }, + "ExportQueryResultsRequest": { + "type": "structure", + "required": ["queryExecutionId", "databaseType"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" }, + "queryExecutionId": { "shape": "ExportQueryResultsRequestQueryExecutionIdString" }, + "databaseType": { + "shape": "DatabaseType", + "location": "querystring", + "locationName": "databaseType" + }, + "fileType": { "shape": "FileType" } + } + }, + "ExportQueryResultsRequestQueryExecutionIdString": { + "type": "string", + "max": 100, + "min": 1 + }, + "ExportQueryResultsResponse": { + "type": "structure", + "required": ["queryResult", "contentType", "fileName"], + "members": { + "queryResult": { "shape": "StreamingBlob" }, + "contentType": { + "shape": "String", + "location": "header", + "locationName": "Content-Type" + }, + "fileName": { + "shape": "String", + "location": "header", + "locationName": "Content-Disposition" + } + }, + "payload": "queryResult" + }, + "FileType": { + "type": "string", + "enum": ["JSON", "CSV"] + }, + "FullQueryText": { + "type": "string", + "max": 1000000, + "min": 0, + "sensitive": true + }, + "GetConnectableResourcesRequest": { + "type": "structure", + "required": ["type", "maxItems", "databaseType"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" }, + "type": { "shape": "GetConnectableResourcesRequestTypeString" }, + "maxItems": { "shape": "GetConnectableResourcesRequestMaxItemsInteger" }, + "pageToken": { "shape": "PageToken" }, + "databaseType": { + "shape": "DatabaseType", + "location": "querystring", + "locationName": "databaseType" + } + } + }, + "GetConnectableResourcesRequestMaxItemsInteger": { + "type": "integer", + "box": true, + "max": 50, + "min": 20 + }, + "GetConnectableResourcesRequestTypeString": { + "type": "string", + "max": 150, + "min": 0 + }, + "GetConnectableResourcesResponse": { + "type": "structure", + "required": ["connectableResources"], + "members": { + "connectableResources": { "shape": "ConnectableResources" }, + "nextToken": { "shape": "String" } + } + }, + "GetConnectionRequest": { + "type": "structure", + "required": ["connectionId"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "connectionId": { + "shape": "GetConnectionRequestConnectionIdString", + "documentation": "

Id of connection to delete

", + "location": "uri", + "locationName": "connectionId" + } + } + }, + "GetConnectionRequestConnectionIdString": { + "type": "string", + "max": 1000, + "min": 1 + }, + "GetConnectionResponse": { + "type": "structure", + "members": { + "data": { "shape": "Connection" } + } + }, + "GetDatabaseConfigurationsRequest": { + "type": "structure", + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" } + } + }, + "GetDatabaseConfigurationsResponse": { + "type": "structure", + "members": { + "configurations": { "shape": "DatabaseConfigurations" } + } + }, + "GetQueryExecutionHistoryRequest": { + "type": "structure", + "required": ["queryExecutionId"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "queryExecutionId": { "shape": "GetQueryExecutionHistoryRequestQueryExecutionIdString" }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" } + } + }, + "GetQueryExecutionHistoryRequestQueryExecutionIdString": { + "type": "string", + "max": 100, + "min": 1 + }, + "GetQueryExecutionHistoryResponse": { + "type": "structure", + "members": { + "id": { "shape": "String" }, + "querySourceId": { "shape": "String" }, + "queryStartTime": { "shape": "Long" }, + "queryEndTime": { "shape": "Long" }, + "status": { "shape": "QueryExecutionStatus" }, + "queryText": { "shape": "FullQueryText" }, + "serializedMetadata": { "shape": "SerializedMetadata" }, + "serializedQueryStats": { "shape": "SerializedQueryStats" }, + "databaseType": { "shape": "DatabaseType" } + } + }, + "GetQueryResultRequest": { + "type": "structure", + "required": ["queryExecutionId", "databaseType"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "queryExecutionId": { "shape": "GetQueryResultRequestQueryExecutionIdString" }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" }, + "pageToken": { "shape": "PageToken" }, + "databaseType": { + "shape": "DatabaseType", + "location": "querystring", + "locationName": "databaseType" + }, + "pageSize": { "shape": "GetQueryResultRequestPageSizeInteger" } + } + }, + "GetQueryResultRequestPageSizeInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "GetQueryResultRequestQueryExecutionIdString": { + "type": "string", + "max": 100, + "min": 1 + }, + "GetQueryResultResponse": { + "type": "structure", + "members": { + "queryResult": { "shape": "QueryResult" }, + "nextToken": { "shape": "String" }, + "previousToken": { "shape": "String" } + } + }, + "GetResourcesRequest": { + "type": "structure", + "required": ["parents", "type", "maxItems"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "connectionId": { "shape": "GetResourcesRequestConnectionIdString" }, + "databaseType": { + "shape": "DatabaseType", + "location": "querystring", + "locationName": "databaseType" + }, + "connection": { "shape": "DatabaseConnectionConfiguration" }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" }, + "parents": { "shape": "ParentResources" }, + "type": { "shape": "GetResourcesRequestTypeString" }, + "maxItems": { "shape": "GetResourcesRequestMaxItemsInteger" }, + "pageToken": { "shape": "PageToken" }, + "forceRefresh": { "shape": "Boolean" }, + "forceRefreshRecursive": { "shape": "Boolean" } + } + }, + "GetResourcesRequestConnectionIdString": { + "type": "string", + "max": 2048, + "min": 32 + }, + "GetResourcesRequestMaxItemsInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 20 + }, + "GetResourcesRequestTypeString": { + "type": "string", + "max": 150, + "min": 0 + }, + "GetResourcesResponse": { + "type": "structure", + "members": { + "resources": { "shape": "Resources" }, + "nextToken": { "shape": "String" }, + "statusCode": { + "shape": "statusCode", + "location": "statusCode" + }, + "connectionProperties": { "shape": "ConnectionProperties" } + } + }, + "GetTabStatesRequest": { + "type": "structure", + "required": ["tabId"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" }, + "tabId": { "shape": "String" } + } + }, + "GetTabStatesResponse": { + "type": "structure", + "required": ["queryExecutionStates"], + "members": { + "queryExecutionStates": { "shape": "QueryExecutionStates" }, + "sessionId": { "shape": "String" } + } + }, + "Integer": { + "type": "integer", + "box": true + }, + "InternalServerError": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "code": { "shape": "ErrorCode" } + }, + "error": { "httpStatusCode": 500 }, + "exception": true, + "fault": true + }, + "KmsKeyArn": { + "type": "string", + "max": 1000, + "min": 0, + "pattern": "arn:.*" + }, + "ListQueryExecutionHistoryRequest": { + "type": "structure", + "required": ["maxItems"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "maxItems": { "shape": "ListQueryExecutionHistoryRequestMaxItemsInteger" }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" }, + "pageToken": { "shape": "ListQueryExecutionHistoryRequestPageTokenString" }, + "querySourceId": { "shape": "ListQueryExecutionHistoryRequestQuerySourceIdString" }, + "databaseType": { "shape": "DatabaseType" }, + "status": { "shape": "QueryExecutionStatus" }, + "startTime": { "shape": "QueryHistoryTimestamp" }, + "endTime": { "shape": "QueryHistoryTimestamp" }, + "containsText": { "shape": "ListQueryExecutionHistoryRequestContainsTextString" } + } + }, + "ListQueryExecutionHistoryRequestContainsTextString": { + "type": "string", + "max": 100, + "min": 0 + }, + "ListQueryExecutionHistoryRequestMaxItemsInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 1 + }, + "ListQueryExecutionHistoryRequestPageTokenString": { + "type": "string", + "max": 10000, + "min": 0 + }, + "ListQueryExecutionHistoryRequestQuerySourceIdString": { + "type": "string", + "max": 100, + "min": 0 + }, + "ListQueryExecutionHistoryResponse": { + "type": "structure", + "required": ["items"], + "members": { + "items": { "shape": "QueryExecutionHistoryPreviews" }, + "nextToken": { "shape": "ListQueryExecutionHistoryResponseNextTokenString" } + } + }, + "ListQueryExecutionHistoryResponseNextTokenString": { + "type": "string", + "max": 1000, + "min": 0 + }, + "ListTagsForResourceRequest": { + "type": "structure", + "required": ["resourceArn"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "resourceArn": { + "shape": "Arn", + "location": "uri", + "locationName": "resourceArn" + } + } + }, + "ListTagsForResourceResponse": { + "type": "structure", + "required": ["tags"], + "members": { + "tags": { "shape": "Tags" } + } + }, + "Long": { + "type": "long", + "box": true + }, + "PageToken": { + "type": "string", + "max": 1000, + "min": 0 + }, + "ParentResource": { + "type": "structure", + "required": ["parentId", "parentType"], + "members": { + "parentId": { "shape": "ParentResourceParentIdString" }, + "parentType": { "shape": "ParentResourceParentTypeString" } + } + }, + "ParentResourceParentIdString": { + "type": "string", + "max": 1000, + "min": 1, + "sensitive": true + }, + "ParentResourceParentTypeString": { + "type": "string", + "max": 100, + "min": 1 + }, + "ParentResources": { + "type": "list", + "member": { "shape": "ParentResource" } + }, + "PollQueryExecutionEventsRequest": { + "type": "structure", + "required": ["queryExecutionIds", "databaseType"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "queryExecutionIds": { "shape": "PollQueryExecutionEventsRequestQueryExecutionIdsList" }, + "accountSettings": { "shape": "DatabaseConnectionAccountSettings" }, + "databaseType": { + "shape": "DatabaseType", + "location": "querystring", + "locationName": "databaseType" + }, + "ackIds": { "shape": "AckIds" } + } + }, + "PollQueryExecutionEventsRequestQueryExecutionIdsList": { + "type": "list", + "member": { "shape": "PollQueryExecutionEventsRequestQueryExecutionIdsListMemberString" }, + "max": 100, + "min": 1 + }, + "PollQueryExecutionEventsRequestQueryExecutionIdsListMemberString": { + "type": "string", + "max": 100, + "min": 1 + }, + "PollQueryExecutionEventsResponse": { + "type": "structure", + "members": { + "events": { "shape": "QueryExecutionEvents" } + } + }, + "QueryCancellationStatus": { + "type": "string", + "enum": ["CANCELLED", "DOES_NOT_EXISTS", "ALREADY_FINISHED", "CANCELLATION_FAILED"] + }, + "QueryExecution": { + "type": "structure", + "required": ["queryExecutionId"], + "members": { + "queryExecutionStatus": { "shape": "QueryExecutionStatus" }, + "queryExecutionId": { "shape": "QueryExecutionQueryExecutionIdString" }, + "queryResult": { "shape": "QueryResult" }, + "queryText": { "shape": "QueryText" } + } + }, + "QueryExecutionEvent": { + "type": "structure", + "required": ["queryExecutionEventType", "queryExecutionId"], + "members": { + "queryExecutionEventType": { "shape": "QueryExecutionEventType" }, + "queryExecutionId": { "shape": "QueryExecutionEventQueryExecutionIdString" }, + "queryExecutionStatus": { "shape": "QueryExecutionStatus" }, + "queryResult": { "shape": "QueryResult" }, + "nextToken": { "shape": "String" }, + "ackId": { "shape": "String" } + } + }, + "QueryExecutionEventQueryExecutionIdString": { + "type": "string", + "max": 100, + "min": 0 + }, + "QueryExecutionEventType": { + "type": "string", + "enum": ["QUERY_EXECUTION_STATUS", "QUERY_EXECUTION_RESULT"] + }, + "QueryExecutionEvents": { + "type": "list", + "member": { "shape": "QueryExecutionEvent" } + }, + "QueryExecutionHistoryPreview": { + "type": "structure", + "members": { + "id": { "shape": "String" }, + "querySourceId": { "shape": "String" }, + "queryStartTime": { "shape": "Long" }, + "queryEndTime": { "shape": "Long" }, + "status": { "shape": "QueryExecutionStatus" }, + "queryTextPreview": { "shape": "QueryTextPreview" }, + "serializedMetadata": { "shape": "SerializedMetadata" }, + "databaseType": { "shape": "DatabaseType" } + } + }, + "QueryExecutionHistoryPreviews": { + "type": "list", + "member": { "shape": "QueryExecutionHistoryPreview" } + }, + "QueryExecutionQueryExecutionIdString": { + "type": "string", + "max": 100, + "min": 0 + }, + "QueryExecutionState": { + "type": "structure", + "required": ["queryExecutionId", "status", "databaseType"], + "members": { + "queryExecutionId": { "shape": "String" }, + "status": { "shape": "String" }, + "databaseType": { "shape": "DatabaseType" } + } + }, + "QueryExecutionStates": { + "type": "list", + "member": { "shape": "QueryExecutionState" } + }, + "QueryExecutionStatus": { + "type": "string", + "enum": ["SCHEDULED", "RUNNING", "FAILED", "CANCELLED", "FINISHED"] + }, + "QueryExecutionType": { + "type": "string", + "enum": ["PERSIST_SESSION", "NO_SESSION"] + }, + "QueryExecutionWarning": { + "type": "structure", + "members": { + "message": { "shape": "QueryExecutionWarningMessage" }, + "level": { "shape": "QueryExecutionWarningLevel" } + } + }, + "QueryExecutionWarningLevel": { + "type": "string", + "enum": ["INFO", "WARNING"] + }, + "QueryExecutionWarningMessage": { + "type": "string", + "max": 1000, + "min": 0, + "sensitive": true + }, + "QueryExecutionWarnings": { + "type": "list", + "member": { "shape": "QueryExecutionWarning" } + }, + "QueryExecutions": { + "type": "list", + "member": { "shape": "QueryExecution" } + }, + "QueryHistoryTimestamp": { + "type": "long", + "box": true + }, + "QueryResponseDeliveryType": { + "type": "string", + "enum": ["SYNC", "ASYNC"] + }, + "QueryResult": { + "type": "structure", + "members": { + "queryExecutionStatus": { "shape": "QueryExecutionStatus" }, + "headers": { "shape": "QueryResultHeaders" }, + "rows": { "shape": "Rows" }, + "affectedRows": { "shape": "Integer" }, + "totalRowCount": { "shape": "Integer" }, + "elapsedTime": { "shape": "Long" }, + "errorMessage": { "shape": "QueryResultErrorMessage" }, + "errorPosition": { "shape": "Integer" }, + "queryResultWarningCode": { "shape": "QueryResultQueryResultWarningCodeString" }, + "warnings": { "shape": "QueryExecutionWarnings" }, + "queryExecutionId": { "shape": "String" }, + "sessionId": { "shape": "String" }, + "queryText": { "shape": "QueryText" }, + "statementType": { "shape": "StatementType" }, + "serializedMetadata": { "shape": "SerializedMetadata" }, + "connectionProperties": { "shape": "ConnectionProperties" } + } + }, + "QueryResultCellType": { + "type": "string", + "enum": ["STRING", "BOOLEAN", "INTEGER", "BIG_INTEGER", "FLOAT", "BIG_DECIMAL", "DATE", "TIME", "DATETIME"] + }, + "QueryResultCellValue": { + "type": "string", + "sensitive": true + }, + "QueryResultErrorMessage": { + "type": "string", + "max": 1000, + "min": 0, + "sensitive": true + }, + "QueryResultHeader": { + "type": "structure", + "required": ["displayName", "type"], + "members": { + "displayName": { "shape": "QueryResultHeaderDisplayName" }, + "type": { "shape": "QueryResultCellType" } + } + }, + "QueryResultHeaderDisplayName": { + "type": "string", + "sensitive": true + }, + "QueryResultHeaders": { + "type": "list", + "member": { "shape": "QueryResultHeader" } + }, + "QueryResultQueryResultWarningCodeString": { + "type": "string", + "max": 100, + "min": 0 + }, + "QueryText": { + "type": "string", + "sensitive": true + }, + "QueryTextPreview": { + "type": "string", + "max": 150, + "min": 0, + "sensitive": true + }, + "Resource": { + "type": "structure", + "required": ["displayName", "identifier", "childObjectTypes"], + "members": { + "displayName": { "shape": "ResourceDisplayName" }, + "identifier": { "shape": "ResourceIdentifier" }, + "type": { "shape": "ResourceTypeString" }, + "unavailable": { "shape": "Boolean" }, + "tooltipTranslationKey": { "shape": "ResourceTooltipTranslationKeyString" }, + "childObjectTypes": { "shape": "ChildObjectTypes" }, + "allowedActions": { "shape": "ResourceActions" }, + "resourceMetadata": { "shape": "ResourceMetadataItems" } + } + }, + "ResourceAction": { + "type": "string", + "enum": ["Drop", "Truncate", "GenerateDefinition", "GenerateSelectQuery"] + }, + "ResourceActions": { + "type": "list", + "member": { "shape": "ResourceAction" } + }, + "ResourceDisplayName": { + "type": "string", + "max": 150, + "min": 0, + "sensitive": true + }, + "ResourceIdentifier": { + "type": "string", + "max": 150, + "min": 0, + "sensitive": true + }, + "ResourceMetadata": { + "type": "structure", + "members": { + "key": { "shape": "String" }, + "value": { "shape": "String" } + } + }, + "ResourceMetadataItems": { + "type": "list", + "member": { "shape": "ResourceMetadata" } + }, + "ResourceNotFoundException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "code": { "shape": "ErrorCode" } + }, + "error": { + "httpStatusCode": 404, + "senderFault": true + }, + "exception": true + }, + "ResourceTooltipTranslationKeyString": { + "type": "string", + "max": 50, + "min": 0 + }, + "ResourceTypeString": { + "type": "string", + "max": 50, + "min": 0 + }, + "Resources": { + "type": "list", + "member": { "shape": "Resource" } + }, + "Row": { + "type": "structure", + "members": { + "row": { "shape": "Columns" } + } + }, + "Rows": { + "type": "list", + "member": { "shape": "Row" } + }, + "SecretKeyArn": { + "type": "string", + "max": 1000, + "min": 0, + "pattern": "arn:.*" + }, + "SerializedMetadata": { + "type": "string", + "max": 1000000, + "min": 0, + "sensitive": true + }, + "SerializedQueryStats": { + "type": "string", + "max": 1000000, + "min": 0, + "sensitive": true + }, + "ServiceQuotaExceededException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "code": { "shape": "ErrorCode" } + }, + "error": { + "httpStatusCode": 402, + "senderFault": true + }, + "exception": true + }, + "SqlworkbenchSource": { + "type": "string", + "enum": ["SUS", "RQEV2"] + }, + "StatementType": { + "type": "string", + "enum": ["DQL", "DML", "DDL", "DCL", "Utility"] + }, + "StreamingBlob": { + "type": "blob", + "streaming": true + }, + "String": { "type": "string" }, + "TagKey": { + "type": "string", + "max": 128, + "min": 1, + "pattern": "([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)" + }, + "TagKeyList": { + "type": "list", + "member": { "shape": "TagKey" }, + "max": 6500, + "min": 1 + }, + "TagResourceRequest": { + "type": "structure", + "required": ["resourceArn", "tags"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "resourceArn": { + "shape": "Arn", + "location": "uri", + "locationName": "resourceArn" + }, + "tags": { "shape": "Tags" } + } + }, + "TagResourceResponse": { + "type": "structure", + "members": {} + }, + "TagValue": { + "type": "string", + "max": 256, + "min": 0, + "pattern": "([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)" + }, + "TagrisAccessDeniedException": { + "type": "structure", + "members": { + "message": { "shape": "TagrisExceptionMessage" } + }, + "exception": true + }, + "TagrisAccountId": { + "type": "string", + "max": 12, + "min": 12 + }, + "TagrisAmazonResourceName": { + "type": "string", + "max": 1011, + "min": 1 + }, + "TagrisExceptionMessage": { + "type": "string", + "max": 2048, + "min": 0 + }, + "TagrisInternalId": { + "type": "string", + "max": 64, + "min": 0 + }, + "TagrisInternalServiceException": { + "type": "structure", + "members": { + "message": { "shape": "TagrisExceptionMessage" } + }, + "exception": true, + "fault": true + }, + "TagrisInvalidArnException": { + "type": "structure", + "members": { + "message": { "shape": "TagrisExceptionMessage" }, + "sweepListItem": { "shape": "TagrisSweepListItem" } + }, + "exception": true + }, + "TagrisInvalidParameterException": { + "type": "structure", + "members": { + "message": { "shape": "TagrisExceptionMessage" } + }, + "exception": true + }, + "TagrisPartialResourcesExistResultsException": { + "type": "structure", + "members": { + "message": { "shape": "TagrisExceptionMessage" }, + "resourceExistenceInformation": { "shape": "TagrisSweepListResult" } + }, + "exception": true + }, + "TagrisStatus": { + "type": "string", + "enum": ["ACTIVE", "NOT_ACTIVE"] + }, + "TagrisSweepList": { + "type": "list", + "member": { "shape": "TagrisSweepListItem" } + }, + "TagrisSweepListItem": { + "type": "structure", + "members": { + "TagrisAccountId": { "shape": "TagrisAccountId" }, + "TagrisAmazonResourceName": { "shape": "TagrisAmazonResourceName" }, + "TagrisInternalId": { "shape": "TagrisInternalId" }, + "TagrisVersion": { "shape": "TagrisVersion" } + } + }, + "TagrisSweepListResult": { + "type": "map", + "key": { "shape": "TagrisAmazonResourceName" }, + "value": { "shape": "TagrisStatus" } + }, + "TagrisThrottledException": { + "type": "structure", + "members": { + "message": { "shape": "TagrisExceptionMessage" } + }, + "exception": true + }, + "TagrisVerifyResourcesExistInput": { + "type": "structure", + "required": ["TagrisSweepList"], + "members": { + "TagrisSweepList": { "shape": "TagrisSweepList" } + } + }, + "TagrisVerifyResourcesExistOutput": { + "type": "structure", + "required": ["TagrisSweepListResult"], + "members": { + "TagrisSweepListResult": { "shape": "TagrisSweepListResult" } + } + }, + "TagrisVersion": { "type": "long" }, + "Tags": { + "type": "map", + "key": { "shape": "TagKey" }, + "value": { "shape": "TagValue" }, + "max": 50, + "min": 1 + }, + "ThrottlingException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "code": { "shape": "ErrorCode" } + }, + "error": { + "httpStatusCode": 429, + "senderFault": true + }, + "exception": true + }, + "UntagResourceRequest": { + "type": "structure", + "required": ["resourceArn", "tagKeys"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "resourceArn": { + "shape": "Arn", + "location": "uri", + "locationName": "resourceArn" + }, + "tagKeys": { + "shape": "TagKeyList", + "location": "querystring", + "locationName": "tagKeys" + } + } + }, + "UntagResourceResponse": { + "type": "structure", + "members": {} + }, + "UpdateConnectionRequest": { + "type": "structure", + "required": ["id", "authenticationType"], + "members": { + "sqlworkbenchSource": { + "shape": "SqlworkbenchSource", + "location": "header", + "locationName": "sqlworkbench-source" + }, + "id": { + "shape": "UpdateConnectionRequestIdString", + "documentation": "

Id of the connection to update

" + }, + "name": { + "shape": "UpdateConnectionRequestNameString", + "documentation": "

Name of the connection

" + }, + "databaseName": { + "shape": "UpdateConnectionRequestDatabaseNameString", + "documentation": "

Name of the database used for this connection

" + }, + "authenticationType": { + "shape": "UpdateConnectionRequestAuthenticationTypeEnum", + "documentation": "

Number representing the type of authentication to use (2 = IAM, 3 = Username and Password, 4 = Federated connection)

" + }, + "secretArn": { + "shape": "UpdateConnectionRequestSecretArnString", + "documentation": "

secretArn for redshift cluster

" + }, + "clusterId": { + "shape": "UpdateConnectionRequestClusterIdString", + "documentation": "

Id of the cluster used for this connection

" + }, + "isServerless": { + "shape": "Boolean", + "documentation": "

Is serverless connection

" + }, + "dbUser": { + "shape": "DbUser", + "documentation": "

User of the database used for this connection

" + }, + "username": { + "shape": "DbUser", + "documentation": "

Username used in the Username_Password connection type

" + }, + "password": { + "shape": "UpdateConnectionRequestPasswordString", + "documentation": "

Password of the user used for this connection

" + }, + "host": { + "shape": "String", + "documentation": "

Host address used for creating secret for Username_Password connection type

" + }, + "databaseType": { "shape": "DatabaseType" }, + "connectableResourceIdentifier": { + "shape": "UpdateConnectionRequestConnectableResourceIdentifierString", + "documentation": "

Id of the connectable resource used for this connection

" + }, + "connectableResourceType": { + "shape": "UpdateConnectionRequestConnectableResourceTypeString", + "documentation": "

Type of the connectable resource used for this connection

" + } + } + }, + "UpdateConnectionRequestAuthenticationTypeEnum": { + "type": "string", + "enum": ["2", "3", "4", "5", "6", "7", "8"], + "max": 1, + "min": 1, + "sensitive": true + }, + "UpdateConnectionRequestClusterIdString": { + "type": "string", + "max": 63, + "min": 1 + }, + "UpdateConnectionRequestConnectableResourceIdentifierString": { + "type": "string", + "max": 63, + "min": 1, + "sensitive": true + }, + "UpdateConnectionRequestConnectableResourceTypeString": { + "type": "string", + "max": 63, + "min": 1 + }, + "UpdateConnectionRequestDatabaseNameString": { + "type": "string", + "max": 64, + "min": 1, + "sensitive": true + }, + "UpdateConnectionRequestIdString": { + "type": "string", + "max": 2048, + "min": 32 + }, + "UpdateConnectionRequestNameString": { + "type": "string", + "max": 512, + "min": 1, + "sensitive": true + }, + "UpdateConnectionRequestPasswordString": { + "type": "string", + "max": 64, + "min": 8, + "sensitive": true + }, + "UpdateConnectionRequestSecretArnString": { + "type": "string", + "max": 1000, + "min": 1 + }, + "UpdateConnectionResponse": { + "type": "structure", + "members": { + "data": { "shape": "Connection" } + } + }, + "UserSettings": { + "type": "string", + "sensitive": true + }, + "ValidationException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { "shape": "String" }, + "code": { "shape": "ErrorCode" } + }, + "error": { + "httpStatusCode": 400, + "senderFault": true + }, + "exception": true + }, + "statusCode": { + "type": "integer", + "box": true, + "max": 500, + "min": 100 + } + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/smusUtils.ts b/packages/core/src/sagemakerunifiedstudio/shared/smusUtils.ts new file mode 100644 index 00000000000..35858f0dc5a --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/smusUtils.ts @@ -0,0 +1,416 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getLogger } from '../../shared/logger/logger' +import { ToolkitError } from '../../shared/errors' +import { isSageMaker } from '../../shared/extensionUtilities' +import { getResourceMetadata } from './utils/resourceMetadataUtils' +import fetch from 'node-fetch' + +/** + * Represents SSO instance information retrieved from DataZone + */ +export interface SsoInstanceInfo { + issuerUrl: string + ssoInstanceId: string + clientId: string + region: string +} + +/** + * Response from DataZone /sso/login endpoint + */ +interface DataZoneSsoLoginResponse { + redirectUrl: string +} + +/** + * Credential expiry time constants for SMUS providers (in milliseconds) + */ +export const SmusCredentialExpiry = { + /** Domain Execution Role (DER) credentials expiry time: 10 minutes */ + derExpiryMs: 10 * 60 * 1000, + /** Project Role credentials expiry time: 10 minutes */ + projectExpiryMs: 10 * 60 * 1000, + /** Connection credentials expiry time: 10 minutes */ + connectionExpiryMs: 10 * 60 * 1000, +} as const + +/** + * Error codes for SMUS-related operations + */ +export const SmusErrorCodes = { + /** Error code for when no active SMUS connection is available */ + NoActiveConnection: 'NoActiveConnection', + /** Error code for when API calls timeout */ + ApiTimeout: 'ApiTimeout', + /** Error code for when SMUS login fails */ + SmusLoginFailed: 'SmusLoginFailed', + /** Error code for when redeeming access token fails */ + RedeemAccessTokenFailed: 'RedeemAccessTokenFailed', + /** Error code for when connection establish fails */ + FailedAuthConnecton: 'FailedAuthConnecton', + /** Error code for when user cancels an operation */ + UserCancelled: 'UserCancelled', + /** Error code for when domain account Id is missing */ + AccountIdNotFound: 'AccountIdNotFound', + /** Error code for when resource ARN is missing */ + ResourceArnNotFound: 'ResourceArnNotFound', + /** Error code for when fails to get domain account Id */ + GetDomainAccountIdFailed: 'GetDomainAccountIdFailed', + /** Error code for when fails to get project account Id */ + GetProjectAccountIdFailed: 'GetProjectAccountIdFailed', + /** Error code for when region is missing */ + RegionNotFound: 'RegionNotFound', +} as const + +/** + * Timeout constants for SMUS API calls (in milliseconds) + */ +export const SmusTimeouts = { + /** Default timeout for API calls: 10 seconds */ + apiCallTimeoutMs: 10 * 1000, +} as const + +/** + * Interface for AWS credential objects that need validation + */ +interface CredentialObject { + accessKeyId?: unknown + secretAccessKey?: unknown + sessionToken?: unknown + expiration?: unknown +} + +/** + * Validates AWS credential fields and throws appropriate errors if invalid + * @param credentials The credential object to validate + * @param errorCode The error code to use in ToolkitError + * @param contextMessage The context message for error messages (e.g., "API response", "project credential response") + * @throws ToolkitError if any credential field is invalid + */ +export function validateCredentialFields( + credentials: CredentialObject, + errorCode: string, + contextMessage: string, + validateExpireTime: boolean = false +): void { + if (!credentials.accessKeyId || typeof credentials.accessKeyId !== 'string') { + throw new ToolkitError(`Invalid accessKeyId in ${contextMessage}: ${typeof credentials.accessKeyId}`, { + code: errorCode, + }) + } + if (!credentials.secretAccessKey || typeof credentials.secretAccessKey !== 'string') { + throw new ToolkitError(`Invalid secretAccessKey in ${contextMessage}: ${typeof credentials.secretAccessKey}`, { + code: errorCode, + }) + } + if (!credentials.sessionToken || typeof credentials.sessionToken !== 'string') { + throw new ToolkitError(`Invalid sessionToken in ${contextMessage}: ${typeof credentials.sessionToken}`, { + code: errorCode, + }) + } + if (validateExpireTime) { + if (!credentials.expiration || !(credentials.expiration instanceof Date)) { + throw new ToolkitError(`Invalid expireTime in ${contextMessage}: ${typeof credentials.expiration}`, { + code: errorCode, + }) + } + } +} + +/** + * Utility class for SageMaker Unified Studio domain URL parsing and validation + */ +export class SmusUtils { + private static readonly logger = getLogger() + + /** + * Extracts the domain ID from a SageMaker Unified Studio domain URL + * @param domainUrl The SageMaker Unified Studio domain URL + * @returns The extracted domain ID or undefined if not found + */ + public static extractDomainIdFromUrl(domainUrl: string): string | undefined { + try { + // Domain URL format: https://dzd_d3hr1nfjbtwui1.sagemaker.us-east-2.on.aws + const url = new URL(domainUrl) + const hostname = url.hostname + + // Extract domain ID from hostname (dzd_d3hr1nfjbtwui1 or dzd-d3hr1nfjbtwui1) + const domainIdMatch = hostname.match(/^(dzd[-_][a-zA-Z0-9_-]{1,36})\./) + return domainIdMatch?.[1] + } catch (error) { + this.logger.error('Failed to extract domain ID from URL: %s', error as Error) + return undefined + } + } + + /** + * Extracts the AWS region from a SageMaker Unified Studio domain URL + * @param domainUrl The SageMaker Unified Studio domain URL + * @param fallbackRegion Fallback region if extraction fails (default: 'us-east-1') + * @returns The extracted AWS region or the fallback region if not found + */ + public static extractRegionFromUrl(domainUrl: string, fallbackRegion: string = 'us-east-1'): string { + try { + // Domain URL formats: + // - https://dzd_d3hr1nfjbtwui1.sagemaker.us-east-2.on.aws + // - https://dzd_4gickdfsxtoxg0.sagemaker-gamma.us-west-2.on.aws + const url = new URL(domainUrl) + const hostname = url.hostname + + // Extract region from hostname, handling both prod and non-prod stages + // Pattern matches: .sagemaker[-stage].{region}.on.aws + const regionMatch = hostname.match(/\.sagemaker(?:-[a-z]+)?\.([a-z0-9-]+)\.on\.aws$/) + return regionMatch?.[1] || fallbackRegion + } catch (error) { + this.logger.error('Failed to extract region from URL: %s', error as Error) + return fallbackRegion + } + } + + /** + * Extracts both domain ID and region from a SageMaker Unified Studio domain URL + * @param domainUrl The SageMaker Unified Studio domain URL + * @param fallbackRegion Fallback region if extraction fails (default: 'us-east-1') + * @returns Object containing domainId and region + */ + public static extractDomainInfoFromUrl( + domainUrl: string, + fallbackRegion: string = 'us-east-1' + ): { domainId: string | undefined; region: string } { + return { + domainId: this.extractDomainIdFromUrl(domainUrl), + region: this.extractRegionFromUrl(domainUrl, fallbackRegion), + } + } + + /** + * Validates the domain URL format for SageMaker Unified Studio + * @param value The URL to validate + * @returns Error message if invalid, undefined if valid + */ + public static validateDomainUrl(value: string): string | undefined { + if (!value || value.trim() === '') { + return 'Domain URL is required' + } + + const trimmedValue = value.trim() + + // Check HTTPS requirement + if (!trimmedValue.startsWith('https://')) { + return 'Domain URL must use HTTPS (https://)' + } + + // Check basic URL format + try { + const url = new URL(trimmedValue) + + // Check if it looks like a SageMaker Unified Studio domain + if (!url.hostname.includes('sagemaker') || !url.hostname.includes('on.aws')) { + return 'URL must be a valid SageMaker Unified Studio domain (e.g., https://dzd_xxxxxxxxx.sagemaker.us-east-1.on.aws)' + } + + // Extract domain ID to validate + const domainId = this.extractDomainIdFromUrl(trimmedValue) + + if (!domainId) { + return 'URL must contain a valid domain ID (starting with dzd- or dzd_)' + } + + return undefined // Valid + } catch (err) { + return 'Invalid URL format' + } + } + + /** + * Makes HTTP call to DataZone /sso/login endpoint + * @param domainUrl The SageMaker Unified Studio domain URL + * @param domainId The extracted domain ID + * @returns Promise resolving to the login response + * @throws ToolkitError if the API call fails + */ + private static async callDataZoneLogin(domainUrl: string, domainId: string): Promise { + const loginUrl = new URL('/sso/login', domainUrl) + const requestBody = { + domainId: domainId, + } + + try { + const response = await fetch(loginUrl.toString(), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'User-Agent': 'aws-toolkit-vscode', + }, + body: JSON.stringify(requestBody), + timeout: SmusTimeouts.apiCallTimeoutMs, + }) + + if (!response.ok) { + throw new ToolkitError(`SMUS login failed: ${response.status} ${response.statusText}`, { + code: SmusErrorCodes.SmusLoginFailed, + }) + } + + return (await response.json()) as DataZoneSsoLoginResponse + } catch (error) { + // Handle timeout errors specifically + if (error instanceof Error && (error.name === 'AbortError' || error.message.includes('timeout'))) { + throw new ToolkitError( + `DataZone login request timed out after ${SmusTimeouts.apiCallTimeoutMs / 1000} seconds`, + { + code: SmusErrorCodes.ApiTimeout, + cause: error, + } + ) + } + // Re-throw other errors as-is + throw error + } + } + + /** + * Gets SSO instance information by calling DataZone /sso/login endpoint + * This extracts the proper SSO instance ID and issuer URL needed for OAuth client registration + * + * @param domainUrl The SageMaker Unified Studio domain URL + * @returns Promise resolving to SSO instance information + * @throws ToolkitError if the API call fails or response is invalid + */ + public static async getSsoInstanceInfo(domainUrl: string): Promise { + try { + this.logger.info(`SMUS Auth: Getting SSO instance info from DataZone for domainurl: ${domainUrl}`) + + // Extract domain ID from the domain URL + const domainId = this.extractDomainIdFromUrl(domainUrl) + if (!domainId) { + throw new ToolkitError('Invalid domain URL format', { code: 'InvalidDomainUrl' }) + } + + // Call DataZone /sso/login endpoint to get redirect URL with SSO instance info + const loginData = await this.callDataZoneLogin(domainUrl, domainId) + if (!loginData.redirectUrl) { + throw new ToolkitError('No redirect URL received from DataZone login', { code: 'InvalidLoginResponse' }) + } + + // Parse the redirect URL to extract SSO instance information + const redirectUrl = new URL(loginData.redirectUrl) + const clientIdParam = redirectUrl.searchParams.get('client_id') + if (!clientIdParam) { + throw new ToolkitError('No client_id found in DataZone redirect URL', { code: 'InvalidRedirectUrl' }) + } + + // Decode the client_id ARN: arn:aws:sso::785498918019:application/ssoins-6684636af7e1a207/apl-5f60548b7f5677a2 + const decodedClientId = decodeURIComponent(clientIdParam) + const arnParts = decodedClientId.split('/') + if (arnParts.length < 2) { + throw new ToolkitError('Invalid client_id ARN format', { code: 'InvalidArnFormat' }) + } + + const ssoInstanceId = arnParts[1] // Extract ssoins-6684636af7e1a207 + const issuerUrl = `https://identitycenter.amazonaws.com/${ssoInstanceId}` + + // Extract region from domain URL + const region = this.extractRegionFromUrl(domainUrl) + + this.logger.info('SMUS Auth: Extracted SSO instance info: %s', ssoInstanceId) + + return { + issuerUrl, + ssoInstanceId, + clientId: decodedClientId, + region, + } + } catch (error) { + const errorMsg = error instanceof Error ? error.message : 'Unknown error' + this.logger.error('SMUS Auth: Failed to get SSO instance info: %s', errorMsg) + + if (error instanceof ToolkitError) { + throw error + } + + throw new ToolkitError(`Failed to get SSO instance info: ${errorMsg}`, { + code: 'SsoInstanceInfoFailed', + cause: error instanceof Error ? error : undefined, + }) + } + } + /** + * Extracts SSO ID from a user ID in the format "user-" + * @param userId The user ID to extract SSO ID from + * @returns The extracted SSO ID + * @throws Error if the userId format is invalid + */ + public static extractSSOIdFromUserId(userId: string): string { + const match = userId.match(/user-(.+)$/) + if (!match) { + this.logger.error(`Invalid UserId format: ${userId}`) + throw new Error(`Invalid UserId format: ${userId}`) + } + return match[1] + } + + /** + * Checks if we're in SMUS space environment (should hide certain UI elements) + * @returns True if in SMUS space environment with DataZone domain ID + */ + public static isInSmusSpaceEnvironment(): boolean { + const isSMUSspace = isSageMaker('SMUS') || isSageMaker('SMUS-SPACE-REMOTE-ACCESS') + const resourceMetadata = getResourceMetadata() + return isSMUSspace && !!resourceMetadata?.AdditionalMetadata?.DataZoneDomainId + } +} + +/** + * Extracts the account ID from a SageMaker ARN. + * Supports formats like: + * arn:aws:sagemaker:::app/* + * + * @param arn - The full SageMaker ARN string + * @returns The account ID from the ARN + * @throws If the ARN format is invalid + */ +export function extractAccountIdFromSageMakerArn(arn: string): string { + // Match the ARN components to extract account ID + const regex = /^arn:aws:sagemaker:(?[^:]+):(?\d+):(app|space)\/.+$/i + const match = arn.match(regex) + + if (!match?.groups) { + throw new ToolkitError(`Invalid SageMaker ARN format: "${arn}"`) + } + + return match.groups.accountId +} + +/** + * Extracts account ID from ResourceArn in SMUS space environment + * @returns Promise resolving to the account ID + * @throws ToolkitError if unable to extract account ID + */ +export async function extractAccountIdFromResourceMetadata(): Promise { + const logger = getLogger() + + try { + logger.debug('SMUS: Extracting account ID from ResourceArn in resource-metadata file') + + const resourceMetadata = getResourceMetadata()! + const resourceArn = resourceMetadata.ResourceArn + + if (!resourceArn) { + throw new Error('ResourceArn not found in metadata file') + } + + const accountId = extractAccountIdFromSageMakerArn(resourceArn) + logger.debug(`Successfully extracted account ID from resource-metadata file: ${accountId}`) + + return accountId + } catch (err) { + logger.error(`Failed to extract account ID from ResourceArn: %s`, err) + throw new Error('Failed to extract AWS account ID from ResourceArn in SMUS space environment') + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/telemetry.ts b/packages/core/src/sagemakerunifiedstudio/shared/telemetry.ts new file mode 100644 index 00000000000..ceeb4828b83 --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/telemetry.ts @@ -0,0 +1,122 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + SmusLogin, + SmusOpenRemoteConnection, + SmusRenderLakehouseNode, + SmusRenderS3Node, + SmusSignOut, + SmusStopSpace, + Span, +} from '../../shared/telemetry/telemetry' +import { SagemakerUnifiedStudioSpaceNode } from '../explorer/nodes/sageMakerUnifiedStudioSpaceNode' +import { SageMakerUnifiedStudioSpacesParentNode } from '../explorer/nodes/sageMakerUnifiedStudioSpacesParentNode' +import { SmusAuthenticationProvider } from '../auth/providers/smusAuthenticationProvider' +import { getLogger } from '../../shared/logger/logger' +import { getContext } from '../../shared/vscode/setContext' +import { ConnectionCredentialsProvider } from '../auth/providers/connectionCredentialsProvider' +import { DataZoneConnection, DataZoneClient } from './client/datazoneClient' + +/** + * Records space telemetry + */ +export async function recordSpaceTelemetry( + span: Span | Span, + node: SagemakerUnifiedStudioSpaceNode +) { + const logger = getLogger() + + try { + const parent = node.resource.getParent() as SageMakerUnifiedStudioSpacesParentNode + const authProvider = SmusAuthenticationProvider.fromContext() + const accountId = await authProvider.getDomainAccountId() + const projectId = parent?.getProjectId() + + // Get project account ID and region + let projectAccountId: string | undefined + let projectRegion: string | undefined + + if (projectId) { + projectAccountId = await authProvider.getProjectAccountId(projectId) + + // Get project region from tooling environment + const dzClient = await DataZoneClient.getInstance(authProvider) + const toolingEnv = await dzClient.getToolingEnvironment(projectId) + projectRegion = toolingEnv.awsAccountRegion + } + + span.record({ + smusSpaceKey: node.resource.DomainSpaceKey, + smusDomainRegion: node.resource.regionCode, + smusDomainId: parent?.getAuthProvider()?.activeConnection?.domainId, + smusDomainAccountId: accountId, + smusProjectId: projectId, + smusProjectAccountId: projectAccountId, + smusProjectRegion: projectRegion, + }) + } catch (err) { + logger.error(`Failed to record space telemetry: ${(err as Error).message}`) + } +} + +/** + * Records auth telemetry + */ +export async function recordAuthTelemetry( + span: Span | Span, + authProvider: SmusAuthenticationProvider, + domainId: string | undefined, + region: string | undefined +) { + const logger = getLogger() + + span.record({ + smusDomainId: domainId, + awsRegion: region, + }) + + try { + if (!region) { + throw new Error(`Region is undefined for domain ${domainId}`) + } + const accountId = await authProvider.getDomainAccountId() + span.record({ + smusDomainAccountId: accountId, + }) + } catch (err) { + logger.error( + `Failed to record Domain AccountId in data connection telemetry for domain ${domainId} in region ${region}: ${err}` + ) + } +} + +/** + * Records data connection telemetry for SMUS nodes + */ +export async function recordDataConnectionTelemetry( + span: Span | Span, + connection: DataZoneConnection, + connectionCredentialsProvider: ConnectionCredentialsProvider +) { + const logger = getLogger() + + try { + const isInSmusSpace = getContext('aws.smus.inSmusSpaceEnvironment') + const accountId = await connectionCredentialsProvider.getDomainAccountId() + span.record({ + smusToolkitEnv: isInSmusSpace ? 'smus_space' : 'local', + smusDomainId: connection.domainId, + smusDomainAccountId: accountId, + smusProjectId: connection.projectId, + smusConnectionId: connection.connectionId, + smusConnectionType: connection.type, + smusProjectRegion: connection.location?.awsRegion, + smusProjectAccountId: connection.location?.awsAccountId, + }) + } catch (err) { + logger.error(`Failed to record data connection telemetry: ${(err as Error).message}`) + } +} diff --git a/packages/core/src/sagemakerunifiedstudio/shared/utils/resourceMetadataUtils.ts b/packages/core/src/sagemakerunifiedstudio/shared/utils/resourceMetadataUtils.ts new file mode 100644 index 00000000000..61ce0430ecd --- /dev/null +++ b/packages/core/src/sagemakerunifiedstudio/shared/utils/resourceMetadataUtils.ts @@ -0,0 +1,93 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { fs } from '../../../shared/fs/fs' +import { getLogger } from '../../../shared/logger/logger' +import { isSageMaker } from '../../../shared/extensionUtilities' + +/** + * Resource metadata schema used by `resource-metadata.json` in SageMaker Unified Studio spaces + */ +export type ResourceMetadata = { + AppType?: string + DomainId?: string + SpaceName?: string + UserProfileName?: string + ExecutionRoleArn?: string + ResourceArn?: string + ResourceName?: string + AppImageVersion?: string + AdditionalMetadata?: { + DataZoneDomainId?: string + DataZoneDomainRegion?: string + DataZoneEndpoint?: string + DataZoneEnvironmentId?: string + DataZoneProjectId?: string + DataZoneScopeName?: string + DataZoneStage?: string + DataZoneUserId?: string + PrivateSubnets?: string + ProjectS3Path?: string + SecurityGroup?: string + } + ResourceArnCaseSensitive?: string + IpAddressType?: string +} & Record + +const resourceMetadataPath = '/opt/ml/metadata/resource-metadata.json' +let resourceMetadata: ResourceMetadata | undefined = undefined + +/** + * Gets the cached resource metadata (must be initialized with `initializeResourceMetadata()` first) + * @returns ResourceMetadata object or undefined if not yet initialized + */ +export function getResourceMetadata(): ResourceMetadata | undefined { + return resourceMetadata +} + +/** + * Initializes resource metadata by reading and parsing the resource-metadata.json file + */ +export async function initializeResourceMetadata(): Promise { + const logger = getLogger() + + if (!isSageMaker('SMUS') && !isSageMaker('SMUS-SPACE-REMOTE-ACCESS')) { + logger.debug(`Not in SageMaker Unified Studio space, skipping initialization of resource metadata`) + return + } + + try { + if (!(await resourceMetadataFileExists())) { + logger.debug(`Resource metadata file not found at: ${resourceMetadataPath}`) + } + + const fileContent = await fs.readFileText(resourceMetadataPath) + resourceMetadata = JSON.parse(fileContent) as ResourceMetadata + logger.debug(`Successfully read resource metadata from: ${resourceMetadataPath}`) + } catch (error) { + logger.error(`Failed to read or parse resource metadata file: ${error as Error}`) + } +} + +/** + * Checks if the resource-metadata.json file exists + * @returns True if the file exists, false otherwise + */ +export async function resourceMetadataFileExists(): Promise { + try { + return await fs.existsFile(resourceMetadataPath) + } catch (error) { + const logger = getLogger() + logger.error(`Failed to check if resource metadata file exists: ${error as Error}`) + return false + } +} + +/** + * Resets the cached resource metadata + */ +export function resetResourceMetadata(): void { + resourceMetadata = undefined +} diff --git a/packages/core/src/shared/activationReloadState.ts b/packages/core/src/shared/activationReloadState.ts new file mode 100644 index 00000000000..bcdefa925f3 --- /dev/null +++ b/packages/core/src/shared/activationReloadState.ts @@ -0,0 +1,46 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Runtime } from '@aws-sdk/client-lambda' +import globals from './extensionGlobals' + +export interface SamInitState { + template: string | undefined + readme: string | undefined + runtime: Runtime | undefined + architecture: string | undefined + isImage: boolean | undefined +} + +/** + * Manages state that needs to be persisted across extension restart to workflows that mutate the workspace. + */ +export class ActivationReloadState { + public getSamInitState(): SamInitState | undefined { + return { + template: globals.globalState.get('ACTIVATION_TEMPLATE_PATH_KEY'), + readme: globals.globalState.get('ACTIVATION_LAUNCH_PATH_KEY'), + runtime: globals.globalState.get('SAM_INIT_RUNTIME_KEY'), + architecture: globals.globalState.get('SAM_INIT_ARCH_KEY'), + isImage: globals.globalState.get('SAM_INIT_IMAGE_BOOLEAN_KEY'), + } + } + + public setSamInitState(state: SamInitState): void { + void globals.globalState.update('ACTIVATION_TEMPLATE_PATH_KEY', state.template) + void globals.globalState.update('ACTIVATION_LAUNCH_PATH_KEY', state.readme) + void globals.globalState.update('SAM_INIT_RUNTIME_KEY', state.runtime) + void globals.globalState.update('SAM_INIT_ARCH_KEY', state.architecture) + void globals.globalState.update('SAM_INIT_IMAGE_BOOLEAN_KEY', state.isImage) + } + + public clearSamInitState(): void { + void globals.globalState.update('ACTIVATION_TEMPLATE_PATH_KEY', undefined) + void globals.globalState.update('ACTIVATION_LAUNCH_PATH_KEY', undefined) + void globals.globalState.update('SAM_INIT_RUNTIME_KEY', undefined) + void globals.globalState.update('SAM_INIT_ARCH_KEY', undefined) + void globals.globalState.update('SAM_INIT_IMAGE_BOOLEAN_KEY', undefined) + } +} diff --git a/packages/core/src/shared/awsClientBuilder.ts b/packages/core/src/shared/awsClientBuilder.ts new file mode 100644 index 00000000000..849bdeeabd7 --- /dev/null +++ b/packages/core/src/shared/awsClientBuilder.ts @@ -0,0 +1,195 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Request, AWSError, Credentials } from 'aws-sdk' +import { CredentialsOptions } from 'aws-sdk/lib/credentials' +import { ServiceConfigurationOptions } from 'aws-sdk/lib/service' +import { AwsContext } from './awsContext' +import { DevSettings } from './settings' +import { getUserAgent } from './telemetry/util' +import { telemetry } from './telemetry/telemetry' +import { isLocalStackConnection } from '../auth/utils' + +/** Suppresses a very noisy warning printed by AWS SDK v2, which clutters local debugging output, CI logs, etc. */ +export function disableAwsSdkWarning() { + // See https://github.com/aws/aws-sdk-js/issues/4354 + require('aws-sdk/lib/maintenance_mode_message').suppress = true +} + +// These are not on the public API but are very useful for logging purposes. +// Tests guard against the possibility that these values change unexpectedly. +// Since the field names are not prepended with `_` to signify visibility, the +// fact that they're not in the API may have been an oversight. +interface RequestExtras { + readonly service: AWS.Service + readonly operation: string + readonly params?: any +} + +type RequestListener = (request: AWS.Request & RequestExtras) => void +export type ServiceOptions = ServiceConfigurationOptions & { + /** + * The frequency and (lack of) idempotency of events is highly dependent on the SDK implementation + * For example, 'error' may fire more than once for a single request + * + * Example usage: + * + * ```ts + * const service = await builder.createAwsService(FakeService, { + * onRequestSetup: [ + * req => { + * console.log('req: %O [%O]', req.operation, req.params) + * req.on('error', e => (errorCount += !e.originalError ? 1 : 0)) + * }, + * ], + * }) + * ``` + */ + onRequestSetup?: RequestListener | RequestListener[] +} + +export interface AWSClientBuilder { + /** + * Creates AWS service object of the given type, and sets options defaults. + * + * @param type AWS service type + * @param options AWS service configuration options + * @param region AWS region override + * @param userAgent Set the Toolkit user agent + * @returns + */ + createAwsService( + type: new (o: ServiceConfigurationOptions) => T, + options?: ServiceOptions, + region?: string, + userAgent?: boolean, + settings?: DevSettings + ): Promise +} + +export class DefaultAWSClientBuilder implements AWSClientBuilder { + public constructor(private readonly awsContext: AwsContext) {} + + public async createAwsService( + type: new (o: ServiceConfigurationOptions) => T, + options?: ServiceOptions, + region?: string, + userAgent: boolean = true, + settings = DevSettings.instance + ): Promise { + const onRequest = options?.onRequestSetup ?? [] + const listeners = Array.isArray(onRequest) ? onRequest : [onRequest] + const opt = { ...options } + delete opt.onRequestSetup + if (opt.credentialProvider) { + opt.credentials = await opt.credentialProvider.resolvePromise() + } + + if (!opt.credentials && !opt.token && !opt.credentialProvider) { + const shim = this.awsContext.credentialsShim + + if (!shim) { + throw new Error('Toolkit is not logged-in.') + } + + opt.credentials = new (class extends Credentials { + public constructor() { + // The class doesn't like being instantiated with empty creds + super({ accessKeyId: '???', secretAccessKey: '???' }) + } + + public override get(callback: (err?: AWSError) => void): void { + // Always try to fetch the latest creds first, attempting a refresh if needed + // A 'passive' refresh is attempted first, before trying an 'active' one if certain criteria are met + shim.get() + .then((creds) => { + this.loadCreds(creds) + this.needsRefresh() ? this.refresh(callback) : callback() + }) + .catch(callback) + } + + public override refresh(callback: (err?: AWSError) => void): void { + shim.refresh() + .then((creds) => { + this.loadCreds(creds) + // The SDK V2 sets `expired` on certain errors so we should only + // unset the flag after acquiring new credentials via `refresh` + this.expired = false + callback() + }) + .catch(callback) + } + + private loadCreds(creds: CredentialsOptions & { expiration?: Date }) { + this.accessKeyId = creds.accessKeyId + this.secretAccessKey = creds.secretAccessKey + this.sessionToken = creds.sessionToken ?? this.sessionToken + this.expireTime = creds.expiration ?? this.expireTime + } + })() + } + + if (!opt.region && region) { + opt.region = region + } + + if (userAgent && !opt.customUserAgent) { + opt.customUserAgent = getUserAgent({ includePlatform: true, includeClientId: true }) + } + + const apiConfig = (opt as { apiConfig?: { metadata?: Record } } | undefined)?.apiConfig + const serviceName = + apiConfig?.metadata?.serviceId?.toLowerCase() ?? + (type as unknown as { serviceIdentifier?: string }).serviceIdentifier + + // Get endpoint url from the active profile if there's no endpoint directly passed as a parameter + const endpointUrl = this.awsContext.getCredentialEndpointUrl() + if (!('endpoint' in opt) && endpointUrl !== undefined) { + opt.endpoint = endpointUrl + } + if (isLocalStackConnection()) { + // Disable host prefixes for LocalStack + opt.hostPrefixEnabled = false + } + // Then check if there's an endpoint in the dev settings + if (serviceName) { + opt.endpoint = settings.get('endpoints', {})[serviceName] ?? opt.endpoint + } + + const service = new type(opt) + const originalSetup = service.setupRequestListeners.bind(service) + + // Record request IDs to the current context, potentially overriding the field if + // multiple API calls are made in the same context. We only do failures as successes + // are generally uninteresting and noisy. + listeners.push((request) => { + request.on('error', (err) => { + if (!err.retryable) { + // TODO: update codegen so `record` enumerates all fields as a flat object instead of + // intersecting all of the definitions + interface RequestData { + requestId?: string + requestServiceType?: string + } + + telemetry.record({ + requestId: err.requestId, + requestServiceType: serviceName, + } satisfies RequestData as any) + } + }) + }) + + service.setupRequestListeners = (request: Request) => { + originalSetup(request) + for (const l of listeners) { + l(request as AWS.Request & RequestExtras) + } + } + + return service + } +} diff --git a/packages/core/src/shared/awsClientBuilderV3.ts b/packages/core/src/shared/awsClientBuilderV3.ts new file mode 100644 index 00000000000..4bc0f3ccbe6 --- /dev/null +++ b/packages/core/src/shared/awsClientBuilderV3.ts @@ -0,0 +1,320 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CredentialsShim } from '../auth/deprecated/loginManager' +import { AwsContext } from './awsContext' +import { + AwsCredentialIdentityProvider, + Logger, + RetryStrategyV2, + TokenIdentity, + TokenIdentityProvider, +} from '@smithy/types' +import { getUserAgent } from './telemetry/util' +import { DevSettings } from './settings' +import { + BuildHandler, + BuildMiddleware, + DeserializeHandler, + DeserializeMiddleware, + Handler, + FinalizeHandler, + FinalizeRequestMiddleware, + HandlerExecutionContext, + MetadataBearer, + MiddlewareStack, + Provider, + RequestHandlerMetadata, + RequestHandlerOutput, + RetryStrategy, + UserAgent, +} from '@aws-sdk/types' +import { S3Client } from '@aws-sdk/client-s3' +import { FetchHttpHandler } from '@smithy/fetch-http-handler' +import { HttpResponse, HttpRequest } from '@aws-sdk/protocol-http' +import { ConfiguredRetryStrategy } from '@smithy/util-retry' +import { telemetry } from './telemetry/telemetry' +import { getRequestId, getTelemetryReason, getTelemetryReasonDesc, getTelemetryResult } from './errors' +import { extensionVersion } from './vscode/env' +import { getLogger } from './logger/logger' +import { partialClone } from './utilities/collectionUtils' +import { selectFrom } from './utilities/tsUtils' +import { once } from './utilities/functionUtils' +import { isWeb } from './extensionGlobals' +import { isLocalStackConnection } from '../auth/utils' + +export type AwsClientConstructor = new (o: AwsClientOptions) => C +export type AwsCommandConstructor> = new ( + o: CommandInput +) => Command + +// AWS-SDKv3 does not export generic types for clients so we need to build them as needed +// https://github.com/aws/aws-sdk-js-v3/issues/5856#issuecomment-2096950979 +export interface AwsClient { + middlewareStack: { + add: MiddlewareStack['add'] + } + send( + command: AwsCommand, + options?: any + ): Promise + destroy: () => void +} + +export interface AwsCommand { + input: InputType + middlewareStack: any + resolveMiddleware: (stack: any, configuration: any, options: any) => Handler +} + +export interface AwsClientOptions { + credentials: AwsCredentialIdentityProvider + region: string | Provider + userAgent: UserAgent + requestHandler: { + metadata?: RequestHandlerMetadata + handle: (req: any, options?: any) => Promise> + destroy?: () => void + } + apiVersion: string + endpoint: string + retryStrategy: RetryStrategy | RetryStrategyV2 + logger: Logger + token: TokenIdentity | TokenIdentityProvider + forcePathStyle: boolean + hostPrefixEnabled: boolean +} + +interface AwsServiceOptions { + serviceClient: AwsClientConstructor + clientOptions?: Partial + region?: string + userAgent?: boolean + keepAlive?: boolean + settings?: DevSettings +} + +export class AWSClientBuilderV3 { + private serviceCache: Map = new Map() + public constructor(private readonly context: AwsContext) {} + + private getShim(): CredentialsShim { + const shim = this.context.credentialsShim + if (!shim) { + throw new Error('Toolkit is not logged-in.') + } + return shim + } + + private buildHttpHandler() { + const requestTimeout = 30000 + // HACK: avoid importing node-http-handler on web. + return isWeb() + ? new FetchHttpHandler({ keepAlive: true, requestTimeout }) + : new (require('@smithy/node-http-handler').NodeHttpHandler)({ + httpAgent: { keepAlive: true }, + httpsAgent: { keepAlive: true }, + requestTimeout, + }) + } + + private getHttpHandler = once(this.buildHttpHandler.bind(this)) + + private keyAwsService(serviceOptions: AwsServiceOptions): string { + // Serializing certain objects in the args allows us to detect when nested objects change (ex. new retry strategy, endpoints) + return [ + String(serviceOptions.serviceClient), + JSON.stringify(serviceOptions.clientOptions), + serviceOptions.region, + serviceOptions.userAgent ? '1' : '0', + this.context.getCredentialEndpointUrl(), // It gets the valid endpoint at the moment of creation + serviceOptions.settings ? JSON.stringify(serviceOptions.settings.get('endpoints', {})) : '', + ].join(':') + } + + public getAwsService(serviceOptions: AwsServiceOptions): C { + const key = this.keyAwsService(serviceOptions) + const cached = this.serviceCache.get(key) + if (cached) { + return cached as C + } + + const service = this.createAwsService(serviceOptions) + this.serviceCache.set(key, service) + return service as C + } + + public createAwsService(serviceOptions: AwsServiceOptions): C { + const opt = (serviceOptions.clientOptions ?? {}) as AwsClientOptions + const userAgent = serviceOptions.userAgent ?? true + const keepAlive = serviceOptions.keepAlive ?? true + + if (!opt.region && serviceOptions.region) { + opt.region = serviceOptions.region + } + + if (!opt.userAgent && userAgent) { + opt.userAgent = [[getUserAgent({ includePlatform: true, includeClientId: true }), extensionVersion]] + } + + if (!opt.retryStrategy) { + // Simple exponential backoff strategy as default. + opt.retryStrategy = new ConfiguredRetryStrategy(5, (attempt: number) => 1000 * 2 ** attempt) + } + + if (!opt.requestHandler) { + opt.requestHandler = this.getHttpHandler() + } + + if (!opt.credentials && !opt.token) { + const shim = this.getShim() + opt.credentials = async () => { + const creds = await shim.get() + if (creds.expiration && creds.expiration.getTime() < Date.now()) { + return shim.refresh() + } + return creds + } + } + // Get endpoint url from the active profile if there's no endpoint directly passed as a parameter + const endpointUrl = this.context.getCredentialEndpointUrl() + if (!('endpoint' in opt) && endpointUrl !== undefined) { + // Because we check that 'endpoint' doesn't exist in `opt`, TS complains when we actually add it + // @ts-expect-error TS2339 + opt.endpoint = endpointUrl + } + if (isLocalStackConnection()) { + // Disable host prefixes for LocalStack + opt.hostPrefixEnabled = false + // serviceClient name gets minified, but it's always consistent + if (serviceOptions.serviceClient.name === S3Client.name) { + // Use path-style S3 URLs for LocalStack + opt.forcePathStyle = true + } + } + const service = new serviceOptions.serviceClient(opt) + service.middlewareStack.add(telemetryMiddleware, { step: 'deserialize' }) + service.middlewareStack.add(loggingMiddleware, { step: 'finalizeRequest' }) + service.middlewareStack.add(getEndpointMiddleware(serviceOptions.settings), { step: 'build' }) + + if (keepAlive) { + service.middlewareStack.add(keepAliveMiddleware, { step: 'build' }) + } + return service + } + + public clearServiceCache() { + for (const client of this.serviceCache.values()) { + client.destroy() + } + this.serviceCache.clear() + } +} + +export function getServiceId(context: { clientName?: string; commandName?: string }): string { + return context.clientName?.toLowerCase().replace(/client$/, '') ?? 'unknown-service' +} + +/** + * Record request IDs to the current context, potentially overriding the field if + * multiple API calls are made in the same context. We only do failures as successes are generally uninteresting and noisy. + */ +export function recordErrorTelemetry(err: Error, serviceName?: string) { + telemetry.record({ + requestId: getRequestId(err), + requestServiceType: serviceName, + reasonDesc: getTelemetryReasonDesc(err), + reason: getTelemetryReason(err), + result: getTelemetryResult(err), + }) +} + +function logAndThrow(e: any, serviceId: string, errorMessageAppend: string): never { + if (e instanceof Error) { + recordErrorTelemetry(e, serviceId) + getLogger().error('API Response %s: %O', errorMessageAppend, e) + } + throw e +} + +const telemetryMiddleware: DeserializeMiddleware = + (next: DeserializeHandler, context: HandlerExecutionContext) => async (args: any) => + emitOnRequest(next, context, args) + +const loggingMiddleware: FinalizeRequestMiddleware = (next: FinalizeHandler) => async (args: any) => + logOnRequest(next, args) + +function getEndpointMiddleware(settings: DevSettings = DevSettings.instance): BuildMiddleware { + return (next: BuildHandler, context: HandlerExecutionContext) => async (args: any) => + overwriteEndpoint(next, context, settings, args) +} + +const keepAliveMiddleware: BuildMiddleware = (next: BuildHandler) => async (args: any) => + addKeepAliveHeader(next, args) + +export async function emitOnRequest(next: DeserializeHandler, context: HandlerExecutionContext, args: any) { + if (!HttpResponse.isInstance(args.request)) { + return next(args) + } + const serviceId = getServiceId(context as object) + const { hostname, path } = args.request + const logTail = `(${hostname} ${path})` + try { + const result = await next(args) + if (HttpResponse.isInstance(result.response)) { + // TODO: omit credentials / sensitive info from the telemetry. + const output = partialClone(result.output, 3) + getLogger().debug(`API Response %s: %O`, logTail, output) + } + return result + } catch (e: any) { + logAndThrow(e, serviceId, logTail) + } +} + +export async function logOnRequest(next: FinalizeHandler, args: any) { + const request = args.request + if (HttpRequest.isInstance(args.request)) { + const { hostname, path } = request + // TODO: omit credentials / sensitive info from the logs. + const input = partialClone(args.input, 3) + getLogger().debug(`API Request (%s %s): %O`, hostname, path, input) + } + return next(args) +} + +export function overwriteEndpoint( + next: BuildHandler, + context: HandlerExecutionContext, + settings: DevSettings, + args: any +) { + const request = args.request + if (HttpRequest.isInstance(request)) { + const serviceId = getServiceId(context) + const endpoint = serviceId ? settings.get('endpoints', {})[serviceId] : undefined + if (endpoint) { + const url = new URL(endpoint) + Object.assign(request, selectFrom(url, 'hostname', 'port', 'protocol', 'pathname')) + request.path = (request as typeof request & { pathname: string }).pathname + } + } + return next(args) +} + +/** + * Overwrite agents behavior and add the keepAliveHeader. This is needed due to + * https://github.com/microsoft/vscode/issues/173861. + * @param next + * @param args + * @returns + */ +export function addKeepAliveHeader(next: BuildHandler, args: any) { + const request = args.request + if (HttpRequest.isInstance(request)) { + request.headers['Connection'] = 'keep-alive' + } + return next(args) +} diff --git a/packages/core/src/shared/awsConsole.ts b/packages/core/src/shared/awsConsole.ts new file mode 100644 index 00000000000..db12e66727b --- /dev/null +++ b/packages/core/src/shared/awsConsole.ts @@ -0,0 +1,50 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { AWSResourceNode } from './treeview/nodes/awsResourceNode' +import { AWSTreeNodeBase } from './treeview/nodes/awsTreeNodeBase' +import { StackNameNode } from '../awsService/appBuilder/explorer/nodes/deployedStack' +import { openUrl } from './utilities/vsCodeUtils' + +export function getAwsConsoleUrl( + service: 'ecr' | 'cloudformation' | 'ec2-launch' | 'docdb', + region: string +): vscode.Uri { + switch (service) { + case 'ecr': + return vscode.Uri.parse(`https://${region}.console.aws.amazon.com/ecr/repositories?region=${region}`) + case 'cloudformation': + return vscode.Uri.parse(`https://${region}.console.aws.amazon.com/cloudformation/home?region=${region}`) + case 'ec2-launch': + return vscode.Uri.parse( + `https://${region}.console.aws.amazon.com/ec2/home?region=${region}#LaunchInstances:` + ) + case 'docdb': + return vscode.Uri.parse(`https://${region}.console.aws.amazon.com/docdb/home?region=${region}`) + default: + throw Error() + } +} + +export async function openAwsConsoleCommand(node: AWSResourceNode) { + // All AWSResourceNodes that this function will receive are also AWSTreeNodeBase, + // so we should be able to get the region from them. + const regionCode = (node as unknown as AWSTreeNodeBase).regionCode + const arn = node.arn + await openUrlInConsole(regionCode, arn) +} + +export async function openAwsCFNConsoleCommand(node: StackNameNode) { + const regionCode = node.resource.regionCode + const arn = node.resource.arn + await openUrlInConsole(regionCode, arn) +} + +async function openUrlInConsole(regionCode: string | undefined, arn: string | undefined) { + const regionQuery = regionCode ? `region=${regionCode}&` : '' + const url = `https://console.aws.amazon.com/go/view?${regionQuery}arn=${arn}&source=aws-toolkit-vscode` + await openUrl(vscode.Uri.parse(url), 'AppBuilderOpenConsole') +} diff --git a/src/shared/awsContext.ts b/packages/core/src/shared/awsContext.ts similarity index 88% rename from src/shared/awsContext.ts rename to packages/core/src/shared/awsContext.ts index 8879ef8905e..9acb9e994fe 100644 --- a/src/shared/awsContext.ts +++ b/packages/core/src/shared/awsContext.ts @@ -1,18 +1,19 @@ /*! - * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ import * as vscode from 'vscode' import * as AWS from '@aws-sdk/types' -import { getLogger } from '../shared/logger' +import { getLogger } from '../shared/logger/logger' import { ClassToInterfaceType } from './utilities/tsUtils' -import { CredentialsShim } from '../credentials/loginManager' +import { CredentialsShim } from '../auth/deprecated/loginManager' export interface AwsContextCredentials { readonly credentials: AWS.Credentials readonly credentialsId: string readonly accountId?: string readonly defaultRegion?: string + readonly endpointUrl?: string } /** AWS Toolkit context change */ @@ -77,7 +78,7 @@ export class DefaultAwsContext implements AwsContext { */ public async getCredentials(): Promise { return ( - this.shim?.get().catch(error => { + this.shim?.get().catch((error) => { getLogger().warn(`credentials: failed to retrieve latest credentials: ${error.message}`) return undefined }) ?? this.currentCredentials?.credentials @@ -106,6 +107,13 @@ export class DefaultAwsContext implements AwsContext { return this.currentCredentials?.defaultRegion ?? defaultRegion } + /** + * Gets the endpoint URL configured for the current credentials profile, if any. + */ + public getCredentialEndpointUrl(): string | undefined { + return this.currentCredentials?.endpointUrl + } + private emitEvent() { // TODO(jmkeyes): skip this if the state did not actually change. this._onDidChangeContext.fire({ diff --git a/src/shared/awsContextCommands.ts b/packages/core/src/shared/awsContextCommands.ts similarity index 79% rename from src/shared/awsContextCommands.ts rename to packages/core/src/shared/awsContextCommands.ts index 373283ef082..aaf439b3f02 100644 --- a/src/shared/awsContextCommands.ts +++ b/packages/core/src/shared/awsContextCommands.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -7,23 +7,24 @@ import * as vscode from 'vscode' import * as nls from 'vscode-nls' const localize = nls.loadMessageBundle() -import { asString, CredentialsId } from '../credentials/providers/credentials' +import { asString, CredentialsId } from '../auth/providers/credentials' import * as extensionConstants from './constants' import { UserCredentialsUtils } from './credentials/userCredentialsUtils' import * as localizedText from './localizedText' import { RegionProvider } from './regions/regionProvider' import { getIdeProperties } from './extensionUtilities' import { credentialHelpUrl } from './constants' -import { PromptSettings } from './settings' +import { ToolkitPromptSettings } from './settings' import { isNonNullable } from './utilities/tsUtils' -import { loadSharedCredentialsProfiles } from '../credentials/sharedCredentials' -import { CreateProfileWizard } from '../credentials/wizards/createProfile' -import { Profile } from './credentials/credentialsFile' -import { ProfileKey, staticCredentialsTemplate } from '../credentials/wizards/templates' -import { SharedCredentialsProvider } from '../credentials/providers/sharedCredentialsProvider' -import { Auth } from '../credentials/auth' +import { CreateProfileWizard } from '../auth/wizards/createProfile' +import { staticCredentialsTemplate } from '../auth/wizards/templates' +import { SharedCredentialsProvider } from '../auth/providers/sharedCredentialsProvider' +import { Auth } from '../auth/auth' import { CancellationError } from './utilities/timeoutUtils' import { ToolkitError } from './errors' +import { loadSharedCredentialsProfiles } from '../auth/credentials/sharedCredentials' +import { SharedCredentialsKeys } from '../auth/credentials/types' +import { openUrl } from './utilities/vsCodeUtils' /** * @deprecated @@ -31,7 +32,10 @@ import { ToolkitError } from './errors' export class AwsContextCommands { private readonly _regionProvider: RegionProvider - public constructor(regionProvider: RegionProvider, private readonly auth: Auth) { + public constructor( + regionProvider: RegionProvider, + private readonly auth: Auth + ) { this._regionProvider = regionProvider } @@ -61,7 +65,7 @@ export class AwsContextCommands { await this.editCredentials() if ( credentialsFiles.length === 0 && - (await PromptSettings.instance.isPromptEnabled('createCredentialsProfile')) && + ToolkitPromptSettings.instance.isPromptEnabled('createCredentialsProfile') && (await this.promptCredentialsSetup()) ) { await this.onCommandCreateCredentialsProfile() @@ -100,8 +104,8 @@ export class AwsContextCommands { return false // User canceled. } - const selected = result.map(res => res.detail).filter(isNonNullable) - if (selected.length !== currentRegions.size || selected.some(r => !currentRegions.has(r))) { + const selected = result.map((res) => res.detail).filter(isNonNullable) + if (selected.length !== currentRegions.size || selected.some((r) => !currentRegions.has(r))) { await this._regionProvider.updateExplorerRegions(selected) await vscode.commands.executeCommand('aws.refreshAwsExplorer', true) } @@ -116,12 +120,8 @@ export class AwsContextCommands { * @returns The profile name, or undefined if user cancelled */ public async promptAndCreateNewCredentialsFile(): Promise { - const profiles = {} as Record - for (const [k, v] of (await loadSharedCredentialsProfiles()).entries()) { - profiles[k] = v - } - - const wizard = new CreateProfileWizard(profiles, staticCredentialsTemplate) + const existingProfiles = await loadSharedCredentialsProfiles() + const wizard = new CreateProfileWizard(existingProfiles, staticCredentialsTemplate) const resp = await wizard.run() if (!resp) { return @@ -129,8 +129,8 @@ export class AwsContextCommands { await UserCredentialsUtils.generateCredentialsFile({ profileName: resp.name, - accessKey: resp.profile[ProfileKey.AccessKeyId]!, - secretKey: resp.profile[ProfileKey.SecretKey]!, + accessKey: resp.profile[SharedCredentialsKeys.AWS_ACCESS_KEY_ID]!, + secretKey: resp.profile[SharedCredentialsKeys.AWS_SECRET_ACCESS_KEY]!, }) const sharedProviderId: CredentialsId = { @@ -138,7 +138,7 @@ export class AwsContextCommands { credentialTypeId: resp.name, } - vscode.window.showInformationMessage( + void vscode.window.showInformationMessage( localize( 'AWS.message.prompt.credentials.definition.done', 'Created {0} credentials profile: {1}', @@ -167,9 +167,9 @@ export class AwsContextCommands { if (userResponse === localizedText.yes) { return true } else if (userResponse === localizedText.no) { - PromptSettings.instance.disablePrompt('createCredentialsProfile') + await ToolkitPromptSettings.instance.disablePrompt('createCredentialsProfile') } else if (userResponse === localizedText.help) { - vscode.env.openExternal(vscode.Uri.parse(credentialHelpUrl)) + await openUrl(vscode.Uri.parse(credentialHelpUrl)) return await this.promptCredentialsSetup() } @@ -205,7 +205,7 @@ export class AwsContextCommands { ) if (response && response === localizedText.viewDocs) { - await vscode.env.openExternal(vscode.Uri.parse(extensionConstants.aboutCredentialsFileUrl)) + await openUrl(vscode.Uri.parse(extensionConstants.aboutCredentialsFileUrl)) } } } diff --git a/packages/core/src/shared/clients/apiGateway.ts b/packages/core/src/shared/clients/apiGateway.ts new file mode 100644 index 00000000000..eeae7144712 --- /dev/null +++ b/packages/core/src/shared/clients/apiGateway.ts @@ -0,0 +1,83 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { ClientWrapper } from './clientWrapper' +import { + APIGatewayClient as ApiGatewayClientSDK, + GetResourcesCommand, + GetResourcesRequest, + GetRestApisCommand, + GetRestApisRequest, + GetStagesCommand, + Resource, + Resources, + RestApi, + RestApis, + Stages, + TestInvokeMethodCommand, + TestInvokeMethodRequest, + TestInvokeMethodResponse, +} from '@aws-sdk/client-api-gateway' + +export class ApiGatewayClient extends ClientWrapper { + public constructor(regionCode: string) { + super(regionCode, ApiGatewayClientSDK) + } + + public async *getResourcesForApi(apiId: string): AsyncIterableIterator { + const request: GetResourcesRequest = { + restApiId: apiId, + } + + do { + const response: Resources = await this.makeRequest(GetResourcesCommand, request) + + if (response.items !== undefined && response.items.length > 0) { + yield* response.items + } + + request.position = response.position + } while (request.position !== undefined) + } + + public async getStages(apiId: string): Promise { + return this.makeRequest(GetStagesCommand, { + restApiId: apiId, + }) + } + + public async *listApis(): AsyncIterableIterator { + const request: GetRestApisRequest = {} + + do { + const response: RestApis = await this.makeRequest(GetRestApisCommand, request) + + if (response.items !== undefined && response.items.length > 0) { + yield* response.items + } + + request.position = response.position + } while (request.position !== undefined) + } + + public async testInvokeMethod( + apiId: string, + resourceId: string, + method: string, + body: string, + pathWithQueryString: string | undefined + ): Promise { + const request: TestInvokeMethodRequest = { + restApiId: apiId, + resourceId: resourceId, + httpMethod: method, + body: body, + } + if (pathWithQueryString) { + request.pathWithQueryString = pathWithQueryString + } + + return this.makeRequest(TestInvokeMethodCommand, request) + } +} diff --git a/packages/core/src/shared/clients/apprunner.ts b/packages/core/src/shared/clients/apprunner.ts new file mode 100644 index 00000000000..a2179167c26 --- /dev/null +++ b/packages/core/src/shared/clients/apprunner.ts @@ -0,0 +1,117 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as AppRunner from '@aws-sdk/client-apprunner' +import { ClientWrapper } from './clientWrapper' +import { hasProps, RequiredProps } from '../utilities/tsUtils' +import { AsyncCollection } from '../utilities/asyncCollection' + +export type ServiceSummary = RequiredProps< + AppRunner.ServiceSummary, + 'ServiceName' | 'ServiceArn' | 'Status' | 'ServiceId' +> +export type ImageRepository = RequiredProps + +export type CodeConfigurationValues = RequiredProps +interface CodeConfiguration extends RequiredProps { + CodeConfigurationValues: CodeConfigurationValues +} +export interface CodeRepository extends RequiredProps { + SourceCodeVersion: RequiredProps + CodeConfiguration: CodeConfiguration +} +export interface SourceConfiguration extends AppRunner.SourceConfiguration { + CodeRepository?: CodeRepository + ImageRepository?: RequiredProps +} +export interface CreateServiceRequest extends RequiredProps { + SourceConfiguration: SourceConfiguration +} + +// Note: Many of the requests return a type of Service, but Service <: ServiceSummary. +type WithServiceSummary = Omit & { Service: ServiceSummary } +export class AppRunnerClient extends ClientWrapper { + public constructor(regionCode: string) { + super(regionCode, AppRunner.AppRunnerClient) + } + + public async createService( + request: CreateServiceRequest + ): Promise> { + return await this.makeRequest(AppRunner.CreateServiceCommand, request) + } + + public async listServices(request: AppRunner.ListServicesRequest): Promise { + return await this.makeRequest(AppRunner.ListServicesCommand, request) + } + + public paginateServices(request: AppRunner.ListServicesRequest): AsyncCollection { + return this.makePaginatedRequest( + AppRunner.paginateListServices, + request, + (page) => page.ServiceSummaryList + ).map((summaries) => summaries.filter(isServiceSummary)) + + function isServiceSummary(s: AppRunner.ServiceSummary): s is ServiceSummary { + return hasProps(s, 'ServiceName', 'ServiceArn', 'Status', 'ServiceId') + } + } + + public async pauseService( + request: AppRunner.PauseServiceRequest + ): Promise> { + return await this.makeRequest(AppRunner.PauseServiceCommand, request) + } + + public async resumeService( + request: AppRunner.ResumeServiceRequest + ): Promise> { + return await this.makeRequest(AppRunner.ResumeServiceCommand, request) + } + + public async updateService( + request: AppRunner.UpdateServiceRequest + ): Promise> { + return await this.makeRequest(AppRunner.UpdateServiceCommand, request) + } + + public async createConnection( + request: AppRunner.CreateConnectionRequest + ): Promise { + return await this.makeRequest(AppRunner.CreateConnectionCommand, request) + } + + public async listConnections( + request: AppRunner.ListConnectionsRequest = {} + ): Promise { + const result: AppRunner.ListConnectionsResponse = await this.makeRequest( + AppRunner.ListConnectionsCommand, + request + ) + return result.ConnectionSummaryList ?? [] + } + + public async describeService( + request: AppRunner.DescribeServiceRequest + ): Promise> { + return await this.makeRequest(AppRunner.DescribeServiceCommand, request) + } + + public async startDeployment( + request: AppRunner.StartDeploymentRequest + ): Promise { + return await this.makeRequest(AppRunner.StartDeploymentCommand, request) + } + + public async listOperations(request: AppRunner.ListOperationsRequest): Promise { + return await this.makeRequest(AppRunner.ListOperationsCommand, request) + } + + public async deleteService( + request: AppRunner.DeleteServiceRequest + ): Promise> { + return this.makeRequest(AppRunner.DeleteServiceCommand, request) + } +} diff --git a/packages/core/src/shared/clients/clientWrapper.ts b/packages/core/src/shared/clients/clientWrapper.ts new file mode 100644 index 00000000000..beb117a9bf6 --- /dev/null +++ b/packages/core/src/shared/clients/clientWrapper.ts @@ -0,0 +1,70 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import globals from '../extensionGlobals' +import { AwsClient, AwsClientConstructor, AwsCommand, AwsCommandConstructor } from '../awsClientBuilderV3' +import { PaginationConfiguration, Paginator } from '@aws-sdk/types' +import { AsyncCollection, toCollection } from '../utilities/asyncCollection' +import { isDefined } from '../utilities/tsUtils' + +type SDKPaginator = ( + config: Omit & { client: C }, + input: CommandInput, + ...rest: any[] +) => Paginator +export abstract class ClientWrapper implements vscode.Disposable { + protected client?: C + + public constructor( + public readonly regionCode: string, + private readonly clientType: AwsClientConstructor + ) {} + + protected getClient(ignoreCache: boolean = false) { + const args = { + serviceClient: this.clientType, + region: this.regionCode, + } + return ignoreCache + ? globals.sdkClientBuilderV3.createAwsService(args) + : globals.sdkClientBuilderV3.getAwsService(args) + } + + protected async makeRequest< + CommandInput extends object, + CommandOutput extends object, + CommandOptions extends CommandInput, + Command extends AwsCommand, + >(command: AwsCommandConstructor, commandOptions: CommandOptions): Promise { + return await this.getClient().send(new command(commandOptions)) + } + + protected makePaginatedRequest( + paginator: SDKPaginator, + input: CommandInput, + extractPage: (page: CommandOutput) => Output[] | undefined + ): AsyncCollection { + const p = paginator({ client: this.getClient() }, input) + const collection = toCollection(() => p) + .map(extractPage) + .filter(isDefined) + .map((o) => o.filter(isDefined)) + + return collection + } + + protected async getFirst( + paginator: SDKPaginator, + input: CommandInput, + extractPage: (page: CommandOutput) => Output[] | undefined + ): Promise { + const results = await this.makePaginatedRequest(paginator, input, extractPage).flatten().promise() + return results[0] + } + + public dispose() { + this.client?.destroy() + } +} diff --git a/packages/core/src/shared/clients/cloudControl.ts b/packages/core/src/shared/clients/cloudControl.ts new file mode 100644 index 00000000000..664ae2db70f --- /dev/null +++ b/packages/core/src/shared/clients/cloudControl.ts @@ -0,0 +1,116 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as CloudControl from '@aws-sdk/client-cloudcontrol' +import globals from '../extensionGlobals' +import { localize } from '../utilities/vsCodeUtils' +import { ClientWrapper } from './clientWrapper' + +export class CloudControlClient extends ClientWrapper { + public constructor(regionCode: string) { + super(regionCode, CloudControl.CloudControlClient) + } + + public async createResource(request: CloudControl.CreateResourceInput): Promise { + const createResponse: CloudControl.CreateResourceOutput = await this.makeRequest( + CloudControl.CreateResourceCommand, + request + ) + + await this.pollForCompletion(createResponse.ProgressEvent!) + return createResponse + } + + public async deleteResource(request: CloudControl.DeleteResourceInput): Promise { + const deleteResponse: CloudControl.DeleteResourceOutput = await this.makeRequest( + CloudControl.DeleteResourceCommand, + request + ) + + await this.pollForCompletion(deleteResponse.ProgressEvent!) + } + + public async listResources(request: CloudControl.ListResourcesInput): Promise { + return await this.makeRequest(CloudControl.ListResourcesCommand, request) + } + + public async getResource(request: CloudControl.GetResourceInput): Promise { + return await this.makeRequest(CloudControl.GetResourceCommand, request) + } + + public async updateResource(request: CloudControl.UpdateResourceInput): Promise { + const updateResponse: CloudControl.UpdateResourceOutput = await this.makeRequest( + CloudControl.UpdateResourceCommand, + request + ) + + await this.pollForCompletion(updateResponse.ProgressEvent!) + } + + private async getResourceRequestStatus( + request: CloudControl.GetResourceRequestStatusInput + ): Promise { + return await this.makeRequest(CloudControl.GetResourceRequestStatusCommand, request) + } + + private async pollForCompletion( + progressEvent: CloudControl.ProgressEvent, + baseDelay: number = 500, + maxRetries: number = 10 + ): Promise { + for (let i = 0; i < maxRetries; i++) { + const operationStatus = progressEvent.OperationStatus + + switch (operationStatus) { + case 'SUCCESS': + return + case 'FAILED': + throw new Error( + localize( + 'AWS.message.error.cloudControl.pollResourceStatus.failed', + 'Resource operation failed: {0} ({1})', + progressEvent.StatusMessage, + progressEvent.ErrorCode + ) + ) + case 'CANCEL_COMPLETE': + throw new Error( + localize( + 'AWS.message.error.cloudControl.pollResourceStatus.cancelled', + 'Resource operation cancelled: {0}', + progressEvent.StatusMessage + ) + ) + case 'IN_PROGRESS': + case 'CANCEL_IN_PROGRESS': + case 'PENDING': + break + default: + throw new Error( + localize( + 'AWS.message.error.cloudControl.pollResourceStatus.invalidOperationStatus', + 'Invalid resource operation status: {0}', + operationStatus + ) + ) + } + + if (i + 1 < maxRetries) { + await new Promise((resolve) => globals.clock.setTimeout(resolve, baseDelay * 2 ** i)) + const resourceRequestStatus = await this.getResourceRequestStatus({ + RequestToken: progressEvent.RequestToken, + }) + progressEvent = resourceRequestStatus.ProgressEvent! + } + } + throw new Error( + localize( + 'AWS.message.error.cloudControl.pollResourceStatus.timeout', + 'Failed to get terminal resource operation status for {0} before timeout. Please try again later', + progressEvent.Identifier + ) + ) + } +} diff --git a/packages/core/src/shared/clients/cloudFormation.ts b/packages/core/src/shared/clients/cloudFormation.ts new file mode 100644 index 00000000000..f04d2fdeb08 --- /dev/null +++ b/packages/core/src/shared/clients/cloudFormation.ts @@ -0,0 +1,94 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as CloudFormation from '@aws-sdk/client-cloudformation' +import { AsyncCollection } from '../utilities/asyncCollection' +import { hasProps, isNonNullable, RequiredProps } from '../utilities/tsUtils' +import { ClientWrapper } from './clientWrapper' + +export interface StackSummary + extends RequiredProps { + DriftInformation: RequiredProps +} + +export type StackResource = RequiredProps + +export interface DescribeStackResourcesOutput extends CloudFormation.DescribeStackResourcesOutput { + StackResources: StackResource[] +} +export class CloudFormationClient extends ClientWrapper { + public constructor(regionCode: string) { + super(regionCode, CloudFormation.CloudFormationClient) + } + + public async deleteStack(name: string): Promise { + return await this.makeRequest(CloudFormation.DeleteStackCommand, { StackName: name }) + } + + public async describeType(typeName: string): Promise { + return await this.makeRequest(CloudFormation.DescribeTypeCommand, { TypeName: typeName }) + } + + public async *listStacks( + statusFilter: CloudFormation.StackStatus[] = ['CREATE_COMPLETE', 'UPDATE_COMPLETE'] + ): AsyncIterableIterator { + const request: CloudFormation.ListStacksInput = { + StackStatusFilter: statusFilter, + } + + do { + const response: CloudFormation.ListStacksOutput = await this.makeRequest( + CloudFormation.ListStacksCommand, + request + ) + + const filteredResponse = response.StackSummaries?.filter(isStackSummary) + if (filteredResponse && filteredResponse.length > 0) { + yield* filteredResponse + } + + request.NextToken = response.NextToken + } while (request.NextToken) + } + + public listAllStacks(request: CloudFormation.ListStacksInput = {}): AsyncCollection { + return this.makePaginatedRequest(CloudFormation.paginateListStacks, request, (page) => page.StackSummaries).map( + (s) => s.filter(isStackSummary) + ) + } + + public async *listTypes(): AsyncIterableIterator { + const request: CloudFormation.ListTypesInput = { + DeprecatedStatus: 'LIVE', + Type: 'RESOURCE', + Visibility: 'PUBLIC', + } + + do { + const response: CloudFormation.ListTypesOutput = await this.makeRequest( + CloudFormation.ListTypesCommand, + request + ) + + if (response.TypeSummaries) { + yield* response.TypeSummaries + } + + request.NextToken = response.NextToken + } while (request.NextToken) + } + + public async describeStackResources(name: string): Promise { + return await this.makeRequest(CloudFormation.DescribeStackResourcesCommand, { StackName: name }) + } +} + +function isStackSummary(s: CloudFormation.StackSummary | undefined): s is StackSummary { + return ( + isNonNullable(s) && + hasProps(s, 'StackName', 'CreationTime', 'StackStatus', 'DriftInformation') && + hasProps(s.DriftInformation, 'StackDriftStatus') + ) +} diff --git a/packages/core/src/shared/clients/cloudWatchLogs.ts b/packages/core/src/shared/clients/cloudWatchLogs.ts new file mode 100644 index 00000000000..5e43470fa3a --- /dev/null +++ b/packages/core/src/shared/clients/cloudWatchLogs.ts @@ -0,0 +1,46 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as CloudWatchLogs from '@aws-sdk/client-cloudwatch-logs' +import { ClientWrapper } from './clientWrapper' + +export class CloudWatchLogsClient extends ClientWrapper { + public constructor(regionCode: string) { + super(regionCode, CloudWatchLogs.CloudWatchLogsClient) + } + + public async *describeLogGroups( + request: CloudWatchLogs.DescribeLogGroupsRequest = {} + ): AsyncIterableIterator { + do { + const response: CloudWatchLogs.DescribeLogGroupsResponse = await this.makeRequest( + CloudWatchLogs.DescribeLogGroupsCommand, + request + ) + if (response.logGroups) { + yield* response.logGroups + } + request.nextToken = response.nextToken + } while (request.nextToken) + } + + public async describeLogStreams( + request: CloudWatchLogs.DescribeLogStreamsRequest + ): Promise { + return await this.makeRequest(CloudWatchLogs.DescribeLogStreamsCommand, request) + } + + public async getLogEvents( + request: CloudWatchLogs.GetLogEventsRequest + ): Promise { + return await this.makeRequest(CloudWatchLogs.GetLogEventsCommand, request) + } + + public async filterLogEvents( + request: CloudWatchLogs.FilterLogEventsRequest + ): Promise { + return await this.makeRequest(CloudWatchLogs.FilterLogEventsCommand, request) + } +} diff --git a/packages/core/src/shared/clients/codecatalystClient.ts b/packages/core/src/shared/clients/codecatalystClient.ts new file mode 100644 index 00000000000..b7618fe01b0 --- /dev/null +++ b/packages/core/src/shared/clients/codecatalystClient.ts @@ -0,0 +1,971 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import globals from '../extensionGlobals' + +import * as vscode from 'vscode' +import * as nls from 'vscode-nls' +const localize = nls.loadMessageBundle() + +import * as logger from '../logger/logger' +import { CancellationError, Timeout, waitTimeout, waitUntil } from '../utilities/timeoutUtils' +import { isUserCancelledError } from '../../shared/errors' +import { showMessageWithCancel } from '../utilities/messages' +import { + assertHasProps, + ClassToInterfaceType, + hasProps, + isDefined, + isNonNullable, + RequiredProps, +} from '../utilities/tsUtils' +import { AsyncCollection, toCollection } from '../utilities/asyncCollection' +import { joinAll, pageableToCollection } from '../utilities/collectionUtils' +import { ToolkitError } from '../errors' +import { Uri } from 'vscode' +import { + CodeCatalystClient as CodeCatalystSDKClient, + CreateAccessTokenCommand, + CreateAccessTokenRequest, + CreateAccessTokenResponse, + CreateDevEnvironmentCommand, + CreateDevEnvironmentCommandOutput, + CreateDevEnvironmentRequest, + CreateProjectCommand, + CreateProjectRequest, + CreateSourceRepositoryBranchCommand, + CreateSourceRepositoryBranchRequest, + CreateSourceRepositoryBranchResponse, + DeleteDevEnvironmentCommand, + DeleteDevEnvironmentRequest, + DeleteDevEnvironmentResponse, + DevEnvironmentRepositorySummary, + DevEnvironmentSummary, + GetDevEnvironmentCommand, + GetDevEnvironmentRequest, + GetDevEnvironmentResponse, + GetProjectCommand, + GetProjectCommandOutput, + GetProjectRequest, + GetSourceRepositoryCloneUrlsCommand, + GetSourceRepositoryCloneUrlsRequest, + GetSourceRepositoryCloneUrlsResponse, + GetSpaceCommand, + GetSpaceCommandOutput, + GetSpaceRequest, + GetSubscriptionCommand, + GetSubscriptionRequest, + GetSubscriptionResponse, + GetUserDetailsCommand, + GetUserDetailsCommandOutput, + GetUserDetailsRequest, + GetUserDetailsResponse, + ListDevEnvironmentsCommand, + ListDevEnvironmentsRequest, + ListDevEnvironmentsResponse, + ListProjectsCommand, + ListProjectsRequest, + ListProjectsResponse, + ListSourceRepositoriesCommand, + ListSourceRepositoriesItem, + ListSourceRepositoriesRequest, + ListSourceRepositoriesResponse, + ListSourceRepositoryBranchesCommand, + ListSourceRepositoryBranchesItem, + ListSourceRepositoryBranchesRequest, + ListSpacesCommand, + ListSpacesRequest, + ListSpacesResponse, + PersistentStorage, + ProjectSummary, + SpaceSummary, + StartDevEnvironmentCommand, + StartDevEnvironmentRequest, + StartDevEnvironmentResponse, + StartDevEnvironmentSessionCommand, + StartDevEnvironmentSessionRequest, + StartDevEnvironmentSessionResponse, + StopDevEnvironmentCommand, + StopDevEnvironmentRequest, + StopDevEnvironmentResponse, + UpdateDevEnvironmentCommand, + UpdateDevEnvironmentRequest, + UpdateDevEnvironmentResponse, + VerifySessionCommand, + VerifySessionCommandOutput, +} from '@aws-sdk/client-codecatalyst' +import { SsoConnection } from '../../auth/connection' +import { DevSettings } from '../settings' +import { getServiceEnvVarConfig } from '../vscode/env' +import { AwsCommand, AwsCommandConstructor } from '../awsClientBuilderV3' +import { ClientWrapper } from './clientWrapper' +import { ServiceException } from '@aws-sdk/smithy-client' +import { AccessDeniedException } from '@aws-sdk/client-sso-oidc' +import { TokenIdentityProvider } from '@aws-sdk/types' + +const requiredDevEnvProps = [ + 'id', + 'status', + 'inactivityTimeoutMinutes', + 'repositories', + 'instanceType', + 'lastUpdatedTime', +] as const +type RequiredDevEnvProps = (typeof requiredDevEnvProps)[number] + +export interface CodeCatalystConfig { + readonly region: string + readonly endpoint: string + readonly hostname: string + readonly gitHostname: string +} + +export const defaultServiceConfig: CodeCatalystConfig = { + region: 'us-east-1', + endpoint: 'https://codecatalyst.global.api.aws', + hostname: 'codecatalyst.aws', + gitHostname: 'codecatalyst.aws', +} + +export function getCodeCatalystConfig(): CodeCatalystConfig { + return { + ...DevSettings.instance.getServiceConfig('codecatalystService', defaultServiceConfig), + + // Environment variable overrides + ...getServiceEnvVarConfig('codecatalyst', Object.keys(defaultServiceConfig)), + } +} + +interface CodeCatalystDevEnvironmentSummary extends RequiredProps { + readonly persistentStorage: RequiredProps + readonly repositories: RequiredProps[] +} + +export interface DevEnvironment extends CodeCatalystDevEnvironmentSummary { + readonly type: 'devEnvironment' + readonly id: string + readonly org: Pick + readonly project: Pick + readonly repositories: RequiredProps[] +} + +/** CodeCatalyst developer environment session. */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface CodeCatalystDevEnvSession extends StartDevEnvironmentResponse {} + +export interface CodeCatalystOrg extends SpaceSummary { + readonly type: 'org' + readonly name: string +} + +export interface CodeCatalystProject extends ProjectSummary { + readonly type: 'project' + readonly name: string + readonly org: Pick +} + +export interface CodeCatalystRepo extends ListSourceRepositoriesItem { + readonly type: 'repo' + readonly name: string + readonly org: Pick + readonly project: Pick +} + +export interface CodeCatalystBranch extends ListSourceRepositoryBranchesItem { + readonly type: 'branch' + readonly name: string + readonly repo: Pick + readonly org: Pick + readonly project: Pick +} + +export type CodeCatalystResource = + | CodeCatalystOrg + | CodeCatalystProject + | CodeCatalystRepo + | CodeCatalystBranch + | DevEnvironment + +function toDevEnv(spaceName: string, projectName: string, summary: CodeCatalystDevEnvironmentSummary): DevEnvironment { + return { + type: 'devEnvironment', + org: { name: spaceName }, + project: { name: projectName }, + ...summary, + } +} + +function toBranch( + org: string, + project: string, + repo: string, + branch: ListSourceRepositoryBranchesItem +): CodeCatalystBranch { + assertHasProps(branch, 'name') + + return { + type: 'branch', + repo: { name: repo }, + org: { name: org }, + project: { name: project }, + ...branch, + } +} + +function createCodeCatalystClient( + tokenProvider: TokenIdentityProvider, + regionCode: string, + endpoint: string +): CodeCatalystSDKClient { + // Avoid using cached client so that we can inject fresh bearer token provider. + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: CodeCatalystSDKClient, + clientOptions: { + region: regionCode, + endpoint: endpoint, + token: tokenProvider, + }, + }) +} + +export type UserDetails = RequiredProps + +// CodeCatalyst client has two variants: 'logged-in' and 'not logged-in' +// The 'not logged-in' variant is a subtype and has restricted functionality +// These characteristics appear in the Smithy model, but the SDK codegen is unable to model this + +export interface CodeCatalystClient extends ClassToInterfaceType { + readonly identity: { readonly id: string; readonly name: string } +} + +const onAccessDeniedExceptionEmitter = new vscode.EventEmitter() +export const onAccessDeniedException = onAccessDeniedExceptionEmitter.event + +interface AuthOptions { + showReauthPrompt?: boolean +} +export type CodeCatalystClientFactory = () => Promise +/** + * Factory to create a new `CodeCatalystClient`. Call `onCredentialsChanged()` before making requests. + */ +export async function createClient( + connection: SsoConnection, + regionCode = getCodeCatalystConfig().region, + endpoint = getCodeCatalystConfig().endpoint, + authOptions: AuthOptions = {} +): Promise { + const sdkv3Client = createCodeCatalystClient(getTokenProvider(connection), regionCode, endpoint) + const c = new CodeCatalystClientInternal(connection, sdkv3Client, regionCode) + try { + await c.verifySession() + } catch (e) { + if (!(e instanceof ToolkitError) || e.code !== 'AccessDeniedException') { + throw e + } + onAccessDeniedExceptionEmitter.fire(authOptions.showReauthPrompt ?? true) + + // Throw a "cancel" error to prevent further execution. + throw new ToolkitError('CodeCatalyst scope is expired', { code: 'ScopeExpiration', cancelled: true }) + } + + return c +} + +// TODO: move this to sso auth folder? +function getTokenProvider(connection: SsoConnection): TokenIdentityProvider { + return async (_) => { + const token = await connection.getToken() + return { + token: token.accessToken, + expiration: token.expiresAt, + } + } +} + +// XXX: the backend currently rejects empty strings for `alias` so the field must be removed if falsey +function fixAliasInRequest(request: T): T { + if (!request.alias) { + delete request.alias + } + + return request +} + +class CodeCatalystClientInternal extends ClientWrapper { + private userDetails?: UserDetails + private readonly log: logger.Logger + + /** + * Maps bearer tokens to CAWS identities via `verifySession` + * + * It's assumed that an identity will never change over the lifetime of a token + */ + private static identityCache = new Map() + + /** + * Maps CAWS identities to user details via `getUserDetails` + * + * User details _might_ change at some point, however, this is an uncommon occurence. + * Cached user details are cleared when the access token is refreshed. + */ + private static userDetailsCache = new Map() + + public constructor( + private readonly connection: SsoConnection, + private readonly sdkClientV3: CodeCatalystSDKClient, + regionCode: string + ) { + super(regionCode, CodeCatalystSDKClient) + this.log = logger.getLogger() + } + + protected override getClient(): CodeCatalystSDKClient { + return this.sdkClientV3 + } + + public get identity(): CodeCatalystClient['identity'] { + if (!this.userDetails) { + throw new Error('CodeCatalyst client is not connected') + } + + return { id: this.userDetails.userId, name: this.userDetails.userName } + } + + private async call< + CommandInput extends object, + CommandOutput extends object, + CommandOptions extends CommandInput, + Command extends AwsCommand, + >( + cmd: AwsCommandConstructor, + commandOptions: CommandOptions, + silent: true, + defaultVal: CommandOutput + ): Promise + private async call< + CommandInput extends object, + CommandOutput extends object, + CommandOptions extends CommandInput, + Command extends AwsCommand, + >( + cmd: AwsCommandConstructor, + commandOptions: CommandOptions, + silent: false + ): Promise + private async call< + CommandInput extends object, + CommandOutput extends object, + CommandOptions extends CommandInput, + Command extends AwsCommand, + >( + cmd: AwsCommandConstructor, + commandOptions: CommandOptions, + silent: boolean, + defaultVal?: CommandOutput + ): Promise { + const bearerToken = (await this.connection.getToken()).accessToken + + return new Promise(async (resolve, reject) => { + try { + const data = await this.makeRequest( + cmd, + commandOptions + ) + resolve(data) + } catch (e) { + const error = e as Error + if (error instanceof ServiceException && isAccessDeniedError(error)) { + CodeCatalystClientInternal.identityCache.delete(bearerToken) + } + if (silent && defaultVal !== undefined) { + resolve(defaultVal) + } else { + // SDKv3 does not have error codes so we use the name instead (see: https://github.com/aws/aws-sdk-js-v3/issues/759) + reject( + new ToolkitError(`CodeCatalyst: request failed with error`, { cause: error, code: error.name }) + ) + } + } + }) + + function isAccessDeniedError(e: ServiceException): boolean { + return ( + e.$response?.statusCode === 403 || + e.$response?.statusCode === 401 || + e.name === AccessDeniedException.name + ) + } + } + + /** + * Creates a PAT. + * + * @param args.name Name of the token + * @param args.expires PAT expires on this date, or undefined. + * @returns PAT secret + */ + public async createAccessToken( + args: CreateAccessTokenRequest + ): Promise> { + try { + return await this.call(CreateAccessTokenCommand, args, false) + } catch (e) { + if ((e as Error).name === 'ServiceQuotaExceededException') { + throw new ToolkitError('Access token limit exceeded', { cause: e as Error }) + } + throw e + } + } + + public async getSubscription(request: GetSubscriptionRequest): Promise { + return this.call(GetSubscriptionCommand, request, false) + } + + /** + * Gets identity properties of the current authenticated principal, and + * stores the id for use in later calls. + */ + public async verifySession(): Promise { + const id = await this.getUserId() + this.userDetails = CodeCatalystClientInternal.userDetailsCache.get(id) ?? (await this.getUserDetails({ id })) + CodeCatalystClientInternal.userDetailsCache.set(id, this.userDetails) + + return { ...this.userDetails } + } + + private async getUserId(): Promise { + const { accessToken, expiresAt } = await this.connection.getToken() + if (CodeCatalystClientInternal.identityCache.has(accessToken)) { + return CodeCatalystClientInternal.identityCache.get(accessToken)! + } + + const r: VerifySessionCommandOutput = await this.call(VerifySessionCommand, {}, false) + assertHasProps(r, 'identity') + + CodeCatalystClientInternal.identityCache.set(accessToken, r.identity) + setTimeout(() => { + CodeCatalystClientInternal.identityCache.delete(accessToken) + CodeCatalystClientInternal.userDetailsCache.delete(r.identity) + }, expiresAt.getTime() - Date.now()) + + return r.identity + } + + public async getBearerToken(): Promise { + return (await this.connection.getToken()).accessToken + } + + private async getUserDetails(args: GetUserDetailsRequest) { + const resp: GetUserDetailsCommandOutput = await this.call(GetUserDetailsCommand, args, false) + assertHasProps(resp, 'userId', 'userName', 'displayName', 'primaryEmail') + + return { ...resp, version: resp.version } as const + } + + public async getSpace(request: GetSpaceRequest): Promise { + const resp: GetSpaceCommandOutput = await this.call(GetSpaceCommand, request, false) + assertHasProps(resp, 'name', 'regionName') + return { ...resp, type: 'org' } + } + + public async getProject(request: RequiredProps): Promise { + const resp: GetProjectCommandOutput = await this.call(GetProjectCommand, request, false) + assertHasProps(resp, 'name') + return { ...resp, type: 'project', org: { name: request.spaceName } } + } + + /** + * Gets a list of all spaces (orgs) for the current CodeCatalyst user. + */ + public listSpaces(request: ListSpacesRequest = {}): AsyncCollection { + const requester: (request: ListSpacesRequest) => Promise = async (request) => + this.call(ListSpacesCommand, request, true, { items: [] }) + const collection = pageableToCollection(requester, request, 'nextToken', 'items').filter(isDefined) + // ts doesn't recognize nested assertion, so we add cast.This is safe because we assert it in the same line. + return collection.map((summaries) => summaries.filter(hasName).map(toOrg)) + + function toOrg>(s: T): CodeCatalystOrg { + return { + type: 'org', + ...s, + } + } + } + + /** + * Gets a list of all projects for the given CodeCatalyst user. + */ + public listProjects( + request: RequiredProps + ): AsyncCollection { + // Only get projects the user is a member of. + request.filters = [ + ...(request.filters ?? []), + { + key: 'hasAccessTo', + values: ['true'], + }, + ] + + const requester: (request: ListProjectsRequest) => Promise = (request) => + this.call(ListProjectsCommand, request, true, { items: [] }) + + const collection = pageableToCollection(requester, request, 'nextToken', 'items') + return collection.filter(isDefined).map((summaries) => summaries.filter(hasName).map(toProject)) + + function toProject>(s: T): CodeCatalystProject { + return { + type: 'project', + org: { name: request.spaceName }, + ...s, + } + } + } + + /** + * Gets a flat list of all devenvs for the given CodeCatalyst project. + */ + public listDevEnvironments(proj: CodeCatalystProject): AsyncCollection { + const initRequest = { spaceName: proj.org.name, projectName: proj.name } + const requester: (request: ListDevEnvironmentsRequest) => Promise = (request) => + this.call(ListDevEnvironmentsCommand, request, true, { items: [] }) + const collection = pageableToCollection(requester, initRequest, 'nextToken' as never, 'items').filter(isDefined) + // ts unable to recognize nested assertion here, so we need to cast. This is safe because we assert it in the same line. + return collection.map((envs) => { + const filteredEnvs = envs.filter(isValidEnvSummary) + const mappedEnvs = filteredEnvs.map((s) => toDevEnv(proj.org.name, proj.name, s)) + return mappedEnvs + }) + } + + /** + * Gets a flat list of all repos for the given CodeCatalyst user. + * @param thirdParty If you want to include 3P (eg github) results in + * your output. + */ + public listSourceRepositories( + request: RequiredProps, + thirdParty: boolean = true + ): AsyncCollection { + const requester = async (r: typeof request) => { + const allRepositories: Promise = this.call( + ListSourceRepositoriesCommand, + r, + true, + { items: [] } + ) + let finalRepositories = allRepositories + + // Filter out 3P repos + if (!thirdParty) { + finalRepositories = allRepositories.then(async (repos) => { + repos.items = await excludeThirdPartyRepos( + this, + request.spaceName, + request.projectName, + repos.items?.filter(hasName) + ) + return repos + }) + } + + return finalRepositories + } + + const collection = pageableToCollection(requester, request, 'nextToken', 'items') + return collection.filter(isDefined).map((summaries) => summaries.filter(hasName).map(toCodeCatalystRepo)) + + function toCodeCatalystRepo(s: RequiredProps): CodeCatalystRepo { + return { + type: 'repo', + org: { name: request.spaceName }, + project: { name: request.projectName }, + ...s, + } + } + } + + public listBranches( + request: RequiredProps< + ListSourceRepositoryBranchesRequest, + 'spaceName' | 'projectName' | 'sourceRepositoryName' + > + ): AsyncCollection { + const requester = async (r: typeof request) => + this.call(ListSourceRepositoryBranchesCommand, r, true, { items: [] }) + const collection = pageableToCollection(requester, request, 'nextToken' as never, 'items') + + return collection + .filter(isNonNullable) + .map((items) => + items.map((b) => toBranch(request.spaceName, request.projectName, request.sourceRepositoryName, b)) + ) + } + + /** + * Lists ALL of the given resource in the current account + * + * @param thirdParty If you want 3P repos in the result. + */ + public listResources(resourceType: 'org'): AsyncCollection + public listResources(resourceType: 'project'): AsyncCollection + public listResources(resourceType: 'repo', thirdParty?: boolean): AsyncCollection + public listResources(resourceType: 'devEnvironment'): AsyncCollection + public listResources( + resourceType: CodeCatalystResource['type'], + ...args: any[] + ): AsyncCollection { + function mapInner( + collection: AsyncCollection, + fn: (element: T) => AsyncCollection + ): AsyncCollection { + return toCollection(() => joinAll(collection.flatten().map(fn))) + } + + switch (resourceType) { + case 'org': + return this.listSpaces() + case 'project': + return mapInner(this.listResources('org'), (o) => this.listProjects({ spaceName: o.name })) + case 'repo': + return mapInner(this.listResources('project'), (p) => + this.listSourceRepositories({ projectName: p.name, spaceName: p.org.name }, ...args) + ) + case 'branch': + throw new Error('Listing branches is not currently supported') + case 'devEnvironment': + return mapInner(this.listResources('project'), (p) => this.listDevEnvironments(p)) + } + } + + public async createSourceBranch( + args: CreateSourceRepositoryBranchRequest + ): Promise { + return this.call(CreateSourceRepositoryBranchCommand, args, false) + } + + /** + * Gets the git source host URL for the given CodeCatalyst or third-party repo. + */ + public async getRepoCloneUrl(args: GetSourceRepositoryCloneUrlsRequest): Promise { + const r: GetSourceRepositoryCloneUrlsResponse = await this.call( + GetSourceRepositoryCloneUrlsCommand, + args, + false + ) + assertHasProps(r, 'https') + // The git extension skips over credential providers if the username is included in the authority + const uri = Uri.parse(r.https) + return uri.with({ authority: uri.authority.replace(/.*@/, '') }).toString() + } + + public async createDevEnvironment( + args: RequiredProps + ): Promise { + const request = fixAliasInRequest(args) + const response: CreateDevEnvironmentCommandOutput = await this.call(CreateDevEnvironmentCommand, request, false) + assertHasProps(response, 'id') + + return this.getDevEnvironment({ + id: response.id, + projectName: args.projectName, + spaceName: args.spaceName, + }) + } + + public async startDevEnvironment(args: StartDevEnvironmentRequest): Promise { + return this.call(StartDevEnvironmentCommand, args, false) + } + + public async createProject( + args: RequiredProps + ): Promise { + await this.call(CreateProjectCommand, args, false) + + return { ...args, name: args.displayName, type: 'project', org: { name: args.spaceName } } + } + + public async startDevEnvironmentSession( + args: StartDevEnvironmentSessionRequest + ): Promise { + const r: StartDevEnvironmentSessionResponse = await this.call(StartDevEnvironmentSessionCommand, args, false) + if (!r.sessionId) { + throw new TypeError('got falsy dev environment "sessionId"') + } + return { ...r, sessionId: r.sessionId } + } + + public async stopDevEnvironment(args: StopDevEnvironmentRequest): Promise { + return this.call(StopDevEnvironmentCommand, args, false) + } + + public async getDevEnvironment( + args: RequiredProps + ): Promise { + const a = { ...args } + delete (a as any).ides + delete (a as any).repositories + + const r: GetDevEnvironmentResponse = await this.call(GetDevEnvironmentCommand, a, false) + const summary = { ...args, ...r } + if (!isValidEnvSummary(summary)) { + throw new ToolkitError(`GetDevEnvironment failed due to response missing required properties`) + } + + return toDevEnv(args.spaceName, args.projectName, summary) + } + + public async deleteDevEnvironment(args: DeleteDevEnvironmentRequest): Promise { + return this.call(DeleteDevEnvironmentCommand, args, false) + } + + public updateDevEnvironment(args: UpdateDevEnvironmentRequest): Promise { + const request = fixAliasInRequest(args) + return this.call(UpdateDevEnvironmentCommand, request, false) + } + + /** + * Best-effort attempt to start a devenv given an ID, showing a progress notification with a cancel button + * TODO: may combine this progress stuff into some larger construct + * + * The cancel button does not abort the start, but rather alerts any callers that any operations that rely + * on the dev environment starting should not progress. + */ + public async startDevEnvironmentWithProgress( + args: RequiredProps, + timeout: Timeout = new Timeout(1000 * 60 * 60) + ): Promise { + // Track the status changes chronologically so that we can + // 1. reason about hysterisis (weird flip-flops) + // 2. have visibility in the logs + const statuses = new Array<{ status: string; start: number }>() + let alias: string | undefined + let startAttempts = 0 + + function statusesToString() { + let s = '' + for (let i = 0; i < statuses.length; i++) { + const item = statuses[i] + const nextItem = i < statuses.length - 1 ? statuses[i + 1] : undefined + const nextTime = nextItem ? nextItem.start : Date.now() + const elapsed = nextTime - item.start + s += `${s ? ' ' : ''}${item.status}/${elapsed}ms` + } + return `[${s}]` + } + + function getName(): string { + const fullname = alias ? alias : args.id + const shortname = fullname.substring(0, 19) + (fullname.length > 20 ? '…' : '') + return shortname + } + + function failedStartMsg(serviceMsg?: string) { + const LastSeenStatus = statuses[statuses.length - 1]?.status + const serviceMsg_ = serviceMsg ? `${serviceMsg}: ` : '' + return `Dev Environment failed to start (${LastSeenStatus}): ${serviceMsg_}${getName()}` + } + + const doLog = (kind: 'debug' | 'error' | 'info', msg: string) => { + const fmt = `${msg} (time: %ds${ + startAttempts <= 1 ? '' : ', startAttempts: ' + startAttempts.toString() + }): %s %s` + if (kind === 'debug') { + this.log.debug(fmt, timeout.elapsedTime / 1000, getName(), statusesToString()) + } else if (kind === 'error') { + this.log.error(fmt, timeout.elapsedTime / 1000, getName(), statusesToString()) + } else { + this.log.info(fmt, timeout.elapsedTime / 1000, getName(), statusesToString()) + } + } + + const progress = await showMessageWithCancel( + localize('AWS.CodeCatalyst.devenv.message', 'CodeCatalyst'), + timeout + ) + progress.report({ message: localize('AWS.CodeCatalyst.devenv.checking', 'Checking status...') }) + + try { + const devenv = await this.getDevEnvironment(args) + alias = devenv.alias + statuses.push({ status: devenv.status, start: Date.now() }) + if (devenv.status === 'RUNNING') { + doLog('debug', 'devenv RUNNING') + timeout.cancel() + // "Debounce" in case caller did not check if the environment was already running. + return devenv + } + } catch { + // Continue. + } + + doLog('debug', 'devenv not started, waiting') + + const pollDevEnv = waitUntil( + async () => { + if (timeout.completed) { + // TODO: need a better way to "cancel" a `waitUntil`. + throw new CancellationError('user') + } + + const LastSeenStatus = statuses[statuses.length - 1] + const elapsed = Date.now() - LastSeenStatus.start + const resp = await this.getDevEnvironment(args) + const serviceReason = (resp.statusReason ?? '').trim() + alias = resp.alias + + if ( + startAttempts > 2 && + elapsed > 10000 && + ['STOPPED', 'FAILED'].includes(LastSeenStatus.status) && + ['STOPPED', 'FAILED'].includes(resp.status) + ) { + const fails = statuses.filter((o) => o.status === 'FAILED').length + const code = fails === 0 ? 'BadDevEnvState' : 'FailedDevEnv' + + if (serviceReason !== '') { + // Service gave a status reason like "Compute limit exceeded", show it to the user. + throw new ToolkitError(failedStartMsg(resp.statusReason), { code: code }) + } + + // If still STOPPED/FAILED after 10+ seconds, don't keep retrying for 1 hour... + throw new ToolkitError(failedStartMsg(), { code: code }) + } else if (['STOPPED', 'FAILED'].includes(resp.status)) { + progress.report({ + message: localize('AWS.CodeCatalyst.devenv.resuming', 'Resuming Dev Environment...'), + }) + try { + startAttempts++ + await this.startDevEnvironment(args) + } catch (e) { + const err = e as ServiceException + // - ServiceQuotaExceededException: account billing limit reached + // - ValidationException: "… creation has failed, cannot start" + // - ConflictException: "Cannot start … because update process is still going on" + // (can happen after "Update Dev Environment") + if (err.name === 'ServiceQuotaExceededException') { + throw new ToolkitError('Dev Environment failed: quota exceeded', { + code: 'ServiceQuotaExceeded', + cause: err, + }) + } + doLog('info', `devenv not started (${err.name}), waiting`) + // Continue retrying... + } + } else if (resp.status === 'STOPPING') { + progress.report({ + message: localize('AWS.CodeCatalyst.devenv.stopping', 'Waiting for Dev Environment to stop...'), + }) + } else { + progress.report({ + message: localize('AWS.CodeCatalyst.devenv.starting', 'Opening Dev Environment...'), + }) + } + + if (LastSeenStatus?.status !== resp.status) { + statuses.push({ status: resp.status, start: Date.now() }) + if (resp.status !== 'RUNNING') { + doLog('debug', `devenv not started, waiting`) + } + } + return resp.status === 'RUNNING' ? resp : undefined + }, + // note: the `waitUntil` will resolve prior to the real timeout if it is refreshed + { interval: 1000, timeout: timeout.remainingTime, truthy: true } + ) + + const devenv = await waitTimeout(pollDevEnv, timeout).catch((e) => { + if (isUserCancelledError(e)) { + doLog('info', 'devenv failed to start (user cancelled)') + e.message = failedStartMsg() + throw e + } else if (e instanceof ToolkitError) { + doLog('error', 'devenv failed to start') + throw e + } + + doLog('error', 'devenv failed to start') + throw new ToolkitError(failedStartMsg(), { code: 'Unknown', cause: e }) + }) + + if (!devenv) { + doLog('error', 'devenv failed to start (timeout)') + throw new ToolkitError(failedStartMsg(), { code: 'Timeout' }) + } + doLog('info', 'devenv started') + + return devenv + } +} + +/** + * Returns only the first-party repos from the given + * list of repository items. + */ +export async function excludeThirdPartyRepos( + client: CodeCatalystClient, + spaceName: CodeCatalystOrg['name'], + projectName: CodeCatalystProject['name'], + items?: RequiredProps[] +): Promise { + if (items === undefined) { + return items + } + + // Filter out 3P repos. + return ( + await Promise.all( + items.map(async (item) => { + return (await isThirdPartyRepo(client, { + spaceName, + projectName, + sourceRepositoryName: item.name, + })) + ? undefined + : item + }) + ) + ).filter(isDefined) +} + +/** + * Determines if a repo is third party (3P) compared to first party (1P). + * + * 1P is CodeCatalyst, 3P is something like Github. + */ +export async function isThirdPartyRepo( + client: CodeCatalystClient, + codeCatalystRepo: GetSourceRepositoryCloneUrlsRequest +): Promise { + const url = await client.getRepoCloneUrl(codeCatalystRepo) + // TODO: Make more robust to work with getCodeCatalystConfig(). + return ( + !Uri.parse(url).authority.endsWith('codecatalyst.aws') && + !Uri.parse(url).authority.endsWith('caws.dev-tools.aws.dev') + ) +} + +// These type guard wrappers are needed because type assertions fail +// to travel up function scope (see: https://github.com/microsoft/TypeScript/issues/9998) + +function hasPersistentStorage( + s: T +): s is T & { persistentStorage: { sizeInGiB: number } } { + return hasProps(s, 'persistentStorage') && hasProps(s.persistentStorage, 'sizeInGiB') +} + +function hasRepositories( + s: T +): s is T & { repositories: RequiredProps[] } { + return hasProps(s, 'repositories') && s.repositories.every((r) => hasProps(r, 'repositoryName')) +} + +function hasName(s: T): s is RequiredProps { + return hasProps(s, 'name') +} + +function isValidEnvSummary(s: DevEnvironmentSummary): s is CodeCatalystDevEnvironmentSummary { + return hasProps(s, ...requiredDevEnvProps) && hasPersistentStorage(s) && hasRepositories(s) +} diff --git a/packages/core/src/shared/clients/codewhispererChatClient.ts b/packages/core/src/shared/clients/codewhispererChatClient.ts new file mode 100644 index 00000000000..93b7b3bceb4 --- /dev/null +++ b/packages/core/src/shared/clients/codewhispererChatClient.ts @@ -0,0 +1,23 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { CodeWhispererStreaming } from '@amzn/codewhisperer-streaming' +import { ConfiguredRetryStrategy } from '@smithy/util-retry' +import { getCodewhispererConfig } from '../../codewhisperer/client/codewhisperer' +import { AuthUtil } from '../../codewhisperer/util/authUtil' +import { getUserAgent } from '../telemetry/util' + +// Create a client for featureDev streaming based off of aws sdk v3 +export async function createCodeWhispererChatStreamingClient(): Promise { + const bearerToken = await AuthUtil.instance.getBearerToken() + const cwsprConfig = getCodewhispererConfig() + const streamingClient = new CodeWhispererStreaming({ + region: cwsprConfig.region, + endpoint: cwsprConfig.endpoint, + token: { token: bearerToken }, + customUserAgent: getUserAgent(), + retryStrategy: new ConfiguredRetryStrategy(1, (attempt: number) => 500 + attempt ** 10), + }) + return streamingClient +} diff --git a/packages/core/src/shared/clients/devenvClient.ts b/packages/core/src/shared/clients/devenvClient.ts new file mode 100644 index 00000000000..c62708b0b09 --- /dev/null +++ b/packages/core/src/shared/clients/devenvClient.ts @@ -0,0 +1,276 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import got, { HTTPError } from 'got' +import globals from '../extensionGlobals' +import { getLogger } from '../logger/logger' +import { getCodeCatalystDevEnvId } from '../vscode/env' +import { UserActivity } from '../extensionUtilities' + +const environmentAuthToken = '__MDE_ENV_API_AUTHORIZATION_TOKEN' +const environmentEndpoint = process.env['__MDE_ENVIRONMENT_API'] ?? 'http://127.0.0.1:1339' + +/** + * Client to the MDE quasi-IMDS localhost endpoint. + */ +export class DevEnvClient implements vscode.Disposable { + static #instance: DevEnvClient + private readonly timer + private LastSeenStatus = '' + private onStatusChangeFn: undefined | ((oldStatus: string, newStatus: string) => void) + + /** Singleton instance (to avoid multiple polling workers). */ + public static get instance() { + return (this.#instance ??= new this()) + } + + /** @internal */ + public constructor(private readonly endpoint: string = environmentEndpoint) { + if (!this.id) { + getLogger().debug('codecatalyst: DevEnvClient skipped (local)') + this.timer = undefined + } else { + getLogger().debug('codecatalyst: DevEnvClient started') + this.timer = globals.clock.setInterval(async () => { + const r = await this.getStatus() + if (this.LastSeenStatus !== r.status) { + const newStatus = r.status ?? 'NULL' + getLogger().info( + 'codecatalyst: DevEnvClient: status change (old=%s new=%s)%s%s', + this.LastSeenStatus, + newStatus, + r.actionId ? ` action=${r.actionId}` : '', + r.message ? `: "${r.message}"` : '' + ) + if (this.onStatusChangeFn) { + this.onStatusChangeFn(this.LastSeenStatus, newStatus) + } + this.LastSeenStatus = newStatus ?? 'NULL' + } + }, 1000) + } + } + + public onStatusChange(fn: (oldStatus: string, newStatus: string) => void) { + this.onStatusChangeFn = fn + } + + public dispose() { + if (this.timer) { + globals.clock.clearInterval(this.timer) + } + } + + public get id(): string | undefined { + return getCodeCatalystDevEnvId() + } + + public isCodeCatalystDevEnv(): boolean { + return !!this.id + } + + // Start an action + public async startDevfile(request: StartDevfileRequest): Promise { + await this.got.post('start', { json: request }) + } + + // Create a devfile for the project + public async createDevfile(request: CreateDevfileRequest): Promise { + const response = await this.got.post('devfile/create', { json: request }) + + return response.body + } + + // Get status and action type + // + // Example: + // { status: 'IMAGES-UPDATE-AVAILABLE', location: 'devfile.yaml' } + public async getStatus(): Promise { + const response = await this.got('status') + + return response.body + } + + private get authToken(): string | undefined { + return process.env[environmentAuthToken] + } + + private readonly got = got.extend({ + prefixUrl: this.endpoint, + responseType: 'json', + // `Authorization` _should_ have two parameters (RFC 7235), MDE should probably fix that + headers: { Authorization: this.authToken }, + }) + + /** + * Notifies the MDE API of user activity. + * + * WARNING: Use {@link DevEnvActivity} instead of calling this directly. + */ + async updateActivity(timestamp: number = Date.now()): Promise { + await this.got.put('activity', { json: { timestamp: timestamp.toString() } }) + return timestamp + } + + /** + * Gets the latest user activity timestamp from MDE API. + * + * WARNING: Use {@link DevEnvActivity} instead of calling this directly. + */ + async getActivity(): Promise { + const response = await this.got('activity') + return response.body.timestamp ? parseInt(response.body.timestamp) : undefined + } +} + +/** + * Posts user activity timestamps to the dev env "/activity" API, which are used by MDE to decide + * when the user was last active, and thus prevent auto-shutdown of the dev env. + */ +export class DevEnvActivity implements vscode.Disposable { + private activityUpdatedEmitter = new vscode.EventEmitter() + private ideActivityListener: vscode.Disposable | undefined + /** The last known activity timestamp, but there could be a newer one on the server. */ + private lastLocalActivity: number | undefined + private extensionUserActivity: UserActivity + private static _defaultUserActivity: UserActivity | undefined + + static readonly activityUpdateDelay = 10_000 + + /** Gets a new DevEnvActivity, or undefined if service is failed. */ + static async create( + client: DevEnvClient, + extensionUserActivity?: UserActivity + ): Promise { + try { + await client.getActivity() + getLogger().debug('codecatalyst: DevEnvActivity: Activity API is enabled') + } catch (e) { + const error = e instanceof HTTPError ? e.response.body : e + getLogger().error(`codecatalyst: DevEnvActivity: Activity API failed: %s`, error) + return undefined + } + + return new DevEnvActivity(client, extensionUserActivity) + } + + private constructor( + private readonly client: DevEnvClient, + extensionUserActivity?: UserActivity + ) { + this.extensionUserActivity = extensionUserActivity ?? this.defaultUserActivity + } + + private get defaultUserActivity(): UserActivity { + return (DevEnvActivity._defaultUserActivity ??= new UserActivity(DevEnvActivity.activityUpdateDelay)) + } + + /** Send activity timestamp to the MDE environment endpoint. */ + async sendActivityUpdate(timestamp: number = Date.now()): Promise { + await this.client.updateActivity() + getLogger().debug('codecatalyst: DevEnvActivity: heartbeat sent') + this.lastLocalActivity = timestamp + this.activityUpdatedEmitter.fire(timestamp) + return timestamp + } + + /** Get the latest activity timestamp from the Dev Env */ + async getLatestActivity(): Promise { + const lastServerActivity = await this.client.getActivity() + + // A single Dev Env can have multiple clients connected to it. + // So if one client updates the timestamp, it will be different from what the + // other clients assumed the last activity was. + if (lastServerActivity && lastServerActivity !== this.lastLocalActivity) { + this.activityUpdatedEmitter.fire(lastServerActivity) + } + + this.lastLocalActivity = lastServerActivity + return this.lastLocalActivity + } + + /** true, if the latest activity on the server is different from what this client has as the latest */ + async isLocalActivityStale(): Promise { + return (await this.getLatestActivity()) !== this.lastLocalActivity + } + + /** + * Subscribes to the "user activity sent" event. + * + * @param callback Called when event is fired. + * @param callback.timestamp Timestamp (milliseconds since 1970), see {@link Date.now} + */ + onActivityUpdate(callback: (timestamp: number) => any) { + this.activityUpdatedEmitter.event(callback) + } + + /** + * Sends an activity timestamp to Dev Env when there is user activity, throttled to once every {@link DevEnvActivity.activityUpdateDelay}. + */ + setUpdateActivityOnIdeActivity(doUpdate: boolean) { + this.ideActivityListener?.dispose() + this.ideActivityListener = undefined + + if (!doUpdate) { + // Stop updating the activity heartbeat + return + } + + if (this.ideActivityListener) { + return + } + + this.ideActivityListener = this.extensionUserActivity.onUserActivity(async () => { + await this.sendActivityUpdate() + }) + } + + dispose() { + this.setUpdateActivityOnIdeActivity(false) + } +} + +export interface GetActivityResponse { + timestamp?: string +} + +export interface UpdateActivityRequest { + timestamp?: string +} + +export interface GetStatusResponse { + actionId?: string + message?: string + status?: Status + location?: string // relative to the currently mounted project +} + +export interface CreateDevfileRequest { + path?: string +} + +export interface CreateDevfileResponse { + // Location of the created devfile. + location?: string +} + +export interface StartDevfileRequest { + // The devfile.yaml file path relative to /projects/ + location?: string + + // The home volumes will be deleted and created again with the content of the '/home' folder of each component container. + recreateHomeVolumes?: boolean +} + +export type Status = + | 'PENDING' + | 'STABLE' + | 'CHANGED' + /** + * The image on-disk in the DE is different from the one in the container registry. + * Client should call "/devfile/pull" to pull the latest image from the registry. + */ + | 'IMAGES-UPDATE-AVAILABLE' diff --git a/packages/core/src/shared/clients/docdbClient.ts b/packages/core/src/shared/clients/docdbClient.ts new file mode 100644 index 00000000000..54050101149 --- /dev/null +++ b/packages/core/src/shared/clients/docdbClient.ts @@ -0,0 +1,312 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import globals from '../extensionGlobals' +import { getLogger } from '../logger/logger' +import { getUserAgent } from '../telemetry/util' +import { ToolkitError } from '../errors' +import { InterfaceNoSymbol } from '../utilities/tsUtils' +import * as DocDB from '@aws-sdk/client-docdb' +import * as DocDBElastic from '@aws-sdk/client-docdb-elastic' + +function isElasticCluster(clusterId: string | undefined): boolean | undefined { + return clusterId?.includes(':docdb-elastic:') +} + +export const DocDBEngine = 'docdb' +export const DBStorageType = { Standard: 'standard', IOpt1: 'iopt1' } as const +export const MaxInstanceCount = 16 + +/** A list of Amazon DocumentDB clusters. */ +export type DBElasticCluster = DocDBElastic.Cluster & DocDBElastic.ClusterInList + +export interface DBInstance extends DocDB.DBInstance { + IsClusterWriter?: boolean +} + +export type DocumentDBClient = InterfaceNoSymbol + +export class DefaultDocumentDBClient { + static create(regionCode: string): DocumentDBClient { + return new DefaultDocumentDBClient(regionCode) + } + + private constructor(public readonly regionCode: string) {} + + private async getSdkConfig() { + const credentials = await globals.awsContext.getCredentials() + const endpointUrl = globals.awsContext.getCredentialEndpointUrl() + const config = { + customUserAgent: getUserAgent({ includePlatform: true, includeClientId: true }), + credentials: credentials, + region: this.regionCode, + } + if (endpointUrl !== undefined) { + return { ...config, endpoint: endpointUrl } + } + return config + } + + public async getClient(): Promise { + const config = await this.getSdkConfig() + return new DocDB.DocDBClient(config) + } + + public async getElasticClient(): Promise { + const config = await this.getSdkConfig() + return new DocDBElastic.DocDBElasticClient(config) + } + + private async executeCommand(command: any): Promise { + getLogger().debug(`docdbClient: ${command.constructor.name} called`) + const client = await this.getClient() + try { + return await client.send(command) + } finally { + client.destroy() + } + } + + private async executeElasticCommand( + command: any + ): Promise { + getLogger().debug(`docdbClient: ${command.constructor.name} called`) + const client = await this.getElasticClient() + try { + return await client.send(command) + } finally { + client.destroy() + } + } + + // Ideally, we would return AsyncCollection or iterator + public async listInstanceClassOptions( + engineVersion: string | undefined, + storageType: string | undefined + ): Promise { + getLogger().debug('docdbClient: ListInstanceClassOptions called') + const client = await this.getClient() + + try { + const instanceClasses: DocDB.OrderableDBInstanceOption[] = [] + const input = { + Engine: DocDBEngine, + EngineVersion: engineVersion, + } + const paginator = DocDB.paginateDescribeOrderableDBInstanceOptions({ client }, input) + for await (const page of paginator) { + instanceClasses.push(...(page.OrderableDBInstanceOptions ?? [])) + } + + return instanceClasses.filter((ic) => storageType === ic.StorageType || storageType === undefined) + } catch (e) { + throw ToolkitError.chain(e, 'Failed to get instance classes') + } finally { + client.destroy() + } + } + + public async listEngineVersions(): Promise { + const command = new DocDB.DescribeDBEngineVersionsCommand({ Engine: DocDBEngine }) + const response = await this.executeCommand(command) + return response.DBEngineVersions ?? [] + } + + public async listGlobalClusters(clusterId: string | undefined = undefined): Promise { + const input: DocDB.DescribeGlobalClustersCommandInput = { + Filters: [], + GlobalClusterIdentifier: clusterId, + } + const command = new DocDB.DescribeGlobalClustersCommand(input) + const response = await this.executeCommand(command) + return response.GlobalClusters ?? [] + } + + public async listElasticClusters(): Promise { + const command = new DocDBElastic.ListClustersCommand() + const response = await this.executeElasticCommand(command) + return response.clusters ?? [] + } + + public async listClusters(clusterId: string | undefined = undefined): Promise { + const input: DocDB.DescribeDBClustersCommandInput = { + Filters: [{ Name: 'engine', Values: [DocDBEngine] }], + DBClusterIdentifier: clusterId, + } + const command = new DocDB.DescribeDBClustersCommand(input) + const response = await this.executeCommand(command) + return response.DBClusters ?? [] + } + + public async listInstances(clusters: string[] = []): Promise { + const input: DocDB.DescribeDBInstancesCommandInput = {} + if (clusters?.length > 0) { + input.Filters = [{ Name: 'db-cluster-id', Values: clusters }] + } + const command = new DocDB.DescribeDBInstancesCommand(input) + const response = await this.executeCommand(command) + return response.DBInstances ?? [] + } + + public async getElasticCluster(clusterArn: string): Promise { + const command = new DocDBElastic.GetClusterCommand({ clusterArn }) + const response = await this.executeElasticCommand(command) + return response.cluster + } + + public async createCluster(input: DocDB.CreateDBClusterMessage): Promise { + const command = new DocDB.CreateDBClusterCommand(input) + const response = await this.executeCommand(command) + return response.DBCluster + } + + public async createElasticCluster( + input: DocDBElastic.CreateClusterInput + ): Promise { + const command = new DocDBElastic.CreateClusterCommand(input) + const response = await this.executeElasticCommand(command) + return response.cluster + } + + public async createGlobalCluster( + input: DocDB.CreateGlobalClusterCommandInput + ): Promise { + const command = new DocDB.CreateGlobalClusterCommand(input) + const response = await this.executeCommand(command) + return response.GlobalCluster + } + + public async modifyGlobalCluster( + input: DocDB.ModifyGlobalClusterCommandInput + ): Promise { + const command = new DocDB.ModifyGlobalClusterCommand(input) + const response = await this.executeCommand(command) + return response.GlobalCluster + } + + public async createClusterSnapshot( + input: DocDBElastic.CreateClusterSnapshotInput + ): Promise { + const command = new DocDBElastic.CreateClusterSnapshotCommand(input) + const response = await this.executeElasticCommand(command) + return response.snapshot + } + + public async modifyCluster(input: DocDB.ModifyDBClusterMessage): Promise { + const command = new DocDB.ModifyDBClusterCommand(input) + const response = await this.executeCommand(command) + return response.DBCluster + } + + public async deleteCluster(input: DocDB.DeleteDBClusterMessage): Promise { + const command = new DocDB.DeleteDBClusterCommand(input) + const response = await this.executeCommand(command) + return response.DBCluster + } + + public async deleteElasticCluster(clusterArn: string): Promise { + const command = new DocDBElastic.DeleteClusterCommand({ clusterArn }) + const response = await this.executeElasticCommand(command) + return response.cluster + } + + public async getInstance(instanceId: string): Promise { + const input: DocDB.DescribeDBInstancesCommandInput = { + DBInstanceIdentifier: instanceId, + } + const command = new DocDB.DescribeDBInstancesCommand(input) + const response = await this.executeCommand(command) + return response.DBInstances ? response.DBInstances[0] : undefined + } + + public async createInstance(input: DocDB.CreateDBInstanceMessage): Promise { + const command = new DocDB.CreateDBInstanceCommand(input) + const response = await this.executeCommand(command) + return response.DBInstance + } + + public async modifyInstance(input: DocDB.ModifyDBInstanceMessage): Promise { + const command = new DocDB.ModifyDBInstanceCommand(input) + const response = await this.executeCommand(command) + return response.DBInstance + } + + public async deleteInstance(input: DocDB.DeleteDBInstanceMessage): Promise { + const command = new DocDB.DeleteDBInstanceCommand(input) + const response = await this.executeCommand(command) + return response.DBInstance + } + + public async rebootInstance(instanceId: string): Promise { + const command = new DocDB.RebootDBInstanceCommand({ DBInstanceIdentifier: instanceId }) + const response = await this.executeCommand(command) + return !!response.DBInstance + } + + public async listResourceTags(arn: string): Promise | undefined> { + if (isElasticCluster(arn)) { + const command = new DocDBElastic.ListTagsForResourceCommand({ resourceArn: arn }) + const response = await this.executeElasticCommand(command) + return response.tags + } else { + const command = new DocDB.ListTagsForResourceCommand({ ResourceName: arn }) + const response = await this.executeCommand(command) + const tagMap = response.TagList?.reduce( + (map, tag) => { + map[tag.Key!] = tag.Value + return map + }, + {} as Record + ) + return tagMap + } + } + + public async addResourceTags(input: DocDBElastic.TagResourceCommandInput): Promise { + if (isElasticCluster(input.resourceArn)) { + const command = new DocDBElastic.TagResourceCommand(input) + await this.executeElasticCommand(command) + } else { + const command = new DocDB.AddTagsToResourceCommand({ + ResourceName: input.resourceArn, + Tags: Object.entries(input.tags ?? {}).map(([Key, Value]) => ({ Key, Value })), + }) + await this.executeCommand(command) + } + } + + public async removeResourceTags(input: DocDBElastic.UntagResourceCommandInput): Promise { + if (isElasticCluster(input.resourceArn)) { + const command = new DocDBElastic.UntagResourceCommand(input) + await this.executeElasticCommand(command) + } else { + const command = new DocDB.RemoveTagsFromResourceCommand({ + ResourceName: input.resourceArn, + TagKeys: input.tagKeys, + }) + await this.executeCommand(command) + } + } + + public async startCluster(clusterId: string) { + if (isElasticCluster(clusterId)) { + const command = new DocDBElastic.StartClusterCommand({ clusterArn: clusterId }) + await this.executeElasticCommand(command) + } else { + const command = new DocDB.StartDBClusterCommand({ DBClusterIdentifier: clusterId }) + await this.executeCommand(command) + } + } + + public async stopCluster(clusterId: string) { + if (isElasticCluster(clusterId)) { + const command = new DocDBElastic.StopClusterCommand({ clusterArn: clusterId }) + await this.executeElasticCommand(command) + } else { + const command = new DocDB.StopDBClusterCommand({ DBClusterIdentifier: clusterId }) + await this.executeCommand(command) + } + } +} diff --git a/packages/core/src/shared/clients/ec2.ts b/packages/core/src/shared/clients/ec2.ts new file mode 100644 index 00000000000..4abe9714401 --- /dev/null +++ b/packages/core/src/shared/clients/ec2.ts @@ -0,0 +1,261 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { + EC2Client, + Instance, + InstanceStateName, + GetConsoleOutputRequest, + Filter, + paginateDescribeInstances, + DescribeInstancesRequest, + Reservation, + Tag, + paginateDescribeInstanceStatus, + StartInstancesCommandOutput, + StartInstancesCommand, + StopInstancesCommand, + StopInstancesCommandOutput, + RebootInstancesCommand, + IamInstanceProfileAssociation, + paginateDescribeIamInstanceProfileAssociations, + IamInstanceProfile, + GetConsoleOutputCommand, + RebootInstancesCommandOutput, + GetConsoleOutputCommandOutput, +} from '@aws-sdk/client-ec2' +import { Timeout } from '../utilities/timeoutUtils' +import { showMessageWithCancel } from '../utilities/messages' +import { ToolkitError, isAwsError } from '../errors' +import { decodeBase64 } from '../utilities/textUtilities' +import { ClientWrapper } from './clientWrapper' +import { AsyncCollection } from '../utilities/asyncCollection' + +/** + * A wrapper around Instance where we can safely assume InstanceId field exists. + */ +export interface Ec2Instance extends Instance { + InstanceId: string + Name?: string + LastSeenStatus: InstanceStateName +} +/** + * A wrapper around Reservation where we can safely it is non empty + */ +export interface Ec2Reservation extends Reservation { + Instances: Ec2Instance[] +} + +interface InstanceWithId extends Instance { + InstanceId: string +} + +interface SafeEc2GetConsoleOutputResult extends GetConsoleOutputRequest { + Output: string + InstanceId: string +} + +export class Ec2Client extends ClientWrapper { + public constructor(public override readonly regionCode: string) { + super(regionCode, EC2Client) + } + + public getReservations(filters?: Filter[]): AsyncCollection { + const reservations = this.makePaginatedRequest( + paginateDescribeInstances, + filters ? { Filters: filters } : ({} satisfies DescribeInstancesRequest), + (page) => page.Reservations + ) + + return this.patchReservations(reservations) + } + + public getInstances(filters?: Filter[]): AsyncCollection { + return this.getReservations(filters) + .flatten() + .map((r) => r.Instances) + } + + /** Updates status and name in-place for displaying to humans. */ + public patchReservations( + reservationPages: AsyncCollection, + getStatus: (i: string) => Promise = this.getInstanceStatus.bind(this) + ): AsyncCollection { + return reservationPages.map(async (r) => await Promise.all(r.filter(isNotEmpty).map(patchReservation))) + + async function patchReservation(r: Reservation & { Instances: Instance[] }): Promise { + const namedInstances = r.Instances.filter(hasId).map(addName) + return { ...r, Instances: await Promise.all(namedInstances.map(addStatus)) } satisfies Reservation + } + + function hasId(i: Instance): i is InstanceWithId { + return i.InstanceId !== undefined + } + + function addName(i: I): I & { Name?: string } { + return instanceHasName(i) ? { ...i, Name: lookupTagKey(i.Tags, 'Name') } : i + } + + async function addStatus( + instance: I + ): Promise { + return { ...instance, LastSeenStatus: await getStatus(instance.InstanceId) } + } + + function isNotEmpty(r: Reservation): r is Reservation & { Instances: Instance[] } { + return r.Instances !== undefined && r.Instances.length > 0 + } + } + + public async getInstanceStatus(instanceId: string): Promise { + const instance = await this.getFirst( + paginateDescribeInstanceStatus, + { InstanceIds: [instanceId], IncludeAllInstances: true }, + (page) => page.InstanceStatuses + ) + + return instance.InstanceState!.Name! + } + + public async isInstanceRunning(instanceId: string): Promise { + const status = await this.getInstanceStatus(instanceId) + return status === 'running' + } + + public getInstancesFilter(instanceIds: string[]): Filter[] { + return [ + { + Name: 'instance-id', + Values: instanceIds, + }, + ] + } + + private handleStatusError(instanceId: string, err: unknown) { + if (isAwsError(err)) { + throw new ToolkitError(`EC2: failed to change status of instance ${instanceId}`, { + cause: err as Error, + }) + } else { + throw err + } + } + + public async assertNotInStatus( + instanceId: string, + targetStatus: string, + getStatus: (i: string) => Promise = this.getInstanceStatus.bind(this) + ) { + const isAlreadyInStatus = (await getStatus(instanceId)) === targetStatus + if (isAlreadyInStatus) { + throw new ToolkitError( + `EC2: Instance is currently ${targetStatus}. Unable to update status of ${instanceId}.` + ) + } + } + + public async startInstance(instanceId: string): Promise { + return await this.makeRequest(StartInstancesCommand, { InstanceIds: [instanceId] }) + } + + public async startInstanceWithCancel(instanceId: string): Promise { + const timeout = new Timeout(5000) + + await showMessageWithCancel(`EC2: Starting instance ${instanceId}`, timeout) + + try { + await this.assertNotInStatus(instanceId, 'running') + await this.startInstance(instanceId) + } catch (err) { + this.handleStatusError(instanceId, err) + } finally { + timeout.cancel() + } + } + + public async stopInstance(instanceId: string): Promise { + return await this.makeRequest(StopInstancesCommand, { InstanceIds: [instanceId] }) + } + + public async stopInstanceWithCancel(instanceId: string): Promise { + const timeout = new Timeout(5000) + + await showMessageWithCancel(`EC2: Stopping instance ${instanceId}`, timeout) + + try { + await this.assertNotInStatus(instanceId, 'stopped') + await this.stopInstance(instanceId) + } catch (err) { + this.handleStatusError(instanceId, err) + } finally { + timeout.cancel() + } + } + + public async rebootInstance(instanceId: string): Promise { + return await this.makeRequest(RebootInstancesCommand, { InstanceIds: [instanceId] }) + } + + public async rebootInstanceWithCancel(instanceId: string): Promise { + const timeout = new Timeout(5000) + + await showMessageWithCancel(`EC2: Rebooting instance ${instanceId}`, timeout) + + try { + await this.rebootInstance(instanceId) + } catch (err) { + this.handleStatusError(instanceId, err) + } finally { + timeout.cancel() + } + } + + /** + * Retrieve IAM Association for a given EC2 instance. + * @param instanceId target EC2 instance ID + * @returns IAM Association for instance + */ + private async getIamInstanceProfileAssociation(instanceId: string): Promise { + return await this.getFirst( + paginateDescribeIamInstanceProfileAssociations, + { Filters: this.getInstancesFilter([instanceId]) }, + (page) => page.IamInstanceProfileAssociations + ) + } + + /** + * Gets the IAM Instance Profile (not role) attached to given EC2 instance. + * @param instanceId target EC2 instance ID + * @returns IAM Instance Profile associated with instance or undefined if none exists. + */ + public async getAttachedIamInstanceProfile(instanceId: string): Promise { + const association = await this.getIamInstanceProfileAssociation(instanceId) + return association ? association.IamInstanceProfile : undefined + } + + public async getConsoleOutput(instanceId: string, latest: boolean): Promise { + const response: GetConsoleOutputCommandOutput = await this.makeRequest(GetConsoleOutputCommand, { + InstanceId: instanceId, + Latest: latest, + }) + + return { + ...response, + InstanceId: instanceId, + Output: response.Output ? decodeBase64(response.Output) : '', + } + } +} + +export function getNameOfInstance(instance: Instance): string | undefined { + return instanceHasName(instance) ? lookupTagKey(instance.Tags!, 'Name')! : undefined +} + +export function instanceHasName(instance: Instance): instance is Instance & { Tags: Tag[] } { + return instance.Tags !== undefined && instance.Tags.some((tag) => tag.Key === 'Name') +} + +function lookupTagKey(tags: Tag[], targetKey: string) { + return tags.filter((tag) => tag.Key === targetKey)[0].Value +} diff --git a/packages/core/src/shared/clients/ec2MetadataClient.ts b/packages/core/src/shared/clients/ec2MetadataClient.ts new file mode 100644 index 00000000000..72249efa6c9 --- /dev/null +++ b/packages/core/src/shared/clients/ec2MetadataClient.ts @@ -0,0 +1,97 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getLogger } from '../logger/logger' +import { ClassToInterfaceType } from '../utilities/tsUtils' +import { httpRequest } from '@smithy/credential-provider-imds' +import { RequestOptions } from 'http' + +export interface IamInfo { + Code: string + LastUpdated: string + InstanceProfileArn: string + InstanceProfileId: string +} + +export interface InstanceIdentity { + region: string +} + +export type Ec2MetadataClient = ClassToInterfaceType +export class DefaultEc2MetadataClient { + private static readonly metadataServiceTimeout: number = 500 + // AWS EC2 Instance Metadata Service (IMDS) constants + // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-metadata-v2-how-it-works.html + private static readonly metadataServiceHost: string = '169.254.169.254' + private static readonly tokenPath: string = '/latest/api/token' + + public constructor() {} + + public getInstanceIdentity(): Promise { + return this.invoke('/latest/dynamic/instance-identity/document') + } + + public getIamInfo(): Promise { + return this.invoke('/latest/meta-data/iam/info') + } + + public async invoke(path: string): Promise { + try { + // Try to get IMDSv2 token first + const token = await this.fetchMetadataToken() + const headers: Record = {} + if (token) { + headers['x-aws-ec2-metadata-token'] = token + } + + const response = await this.makeRequest(path, headers) + return JSON.parse(response.toString()) + } catch (tokenErr) { + getLogger().warn( + 'Ec2MetadataClient failed to fetch token. If this is an EC2 environment, then Toolkit will fall back to IMDSv1: %s', + tokenErr + ) + + // Fall back to IMDSv1 for legacy instances + try { + const response = await this.makeRequest(path, {}) + return JSON.parse(response.toString()) + } catch (err) { + throw new Error(`Ec2MetadataClient: failed to fetch "${path}": ${err}`) + } + } + } + + private async fetchMetadataToken(): Promise { + try { + const options: RequestOptions = { + host: DefaultEc2MetadataClient.metadataServiceHost, + path: DefaultEc2MetadataClient.tokenPath, + method: 'PUT', + headers: { + 'x-aws-ec2-metadata-token-ttl-seconds': '21600', + }, + timeout: DefaultEc2MetadataClient.metadataServiceTimeout, + } + + const response = await httpRequest(options) + return response.toString() + } catch (err) { + return undefined + } + } + + private async makeRequest(path: string, headers: Record): Promise { + const options: RequestOptions = { + host: DefaultEc2MetadataClient.metadataServiceHost, + path, + method: 'GET', + headers, + timeout: DefaultEc2MetadataClient.metadataServiceTimeout, + } + + return httpRequest(options) + } +} diff --git a/packages/core/src/shared/clients/ecrClient.ts b/packages/core/src/shared/clients/ecrClient.ts new file mode 100644 index 00000000000..f5e03d4db7a --- /dev/null +++ b/packages/core/src/shared/clients/ecrClient.ts @@ -0,0 +1,103 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + ECRClient, + DescribeImagesCommand, + DescribeRepositoriesCommand, + CreateRepositoryCommand, + DeleteRepositoryCommand, + BatchDeleteImageCommand, +} from '@aws-sdk/client-ecr' +import type { DescribeImagesRequest, DescribeRepositoriesRequest, Repository } from '@aws-sdk/client-ecr' +import globals from '../extensionGlobals' +import { AsyncCollection } from '../utilities/asyncCollection' +import { pageableToCollection } from '../utilities/collectionUtils' +import { assertHasProps, ClassToInterfaceType, isNonNullable, RequiredProps } from '../utilities/tsUtils' + +export type EcrRepository = RequiredProps + +export type EcrClient = ClassToInterfaceType +export class DefaultEcrClient { + public constructor(public readonly regionCode: string) {} + + public async *describeTags(repositoryName: string): AsyncIterableIterator { + const sdkClient = this.createSdkClient() + const request: DescribeImagesRequest = { repositoryName: repositoryName } + do { + const response = await sdkClient.send(new DescribeImagesCommand(request)) + if (response.imageDetails) { + for (const item of response.imageDetails) { + if (item.imageTags !== undefined) { + for (const tag of item.imageTags) { + yield tag + } + } + } + } + request.nextToken = response.nextToken + } while (request.nextToken) + } + + public async *describeRepositories(): AsyncIterableIterator { + const sdkClient = this.createSdkClient() + const request: DescribeRepositoriesRequest = {} + do { + const response = await sdkClient.send(new DescribeRepositoriesCommand(request)) + if (response.repositories) { + yield* response.repositories + .map((repo: Repository) => { + // If any of these are not present, the repo returned is not valid. repositoryUri/Arn + // are both based on name, and it's not possible to not have a name + if (!repo.repositoryArn || !repo.repositoryName || !repo.repositoryUri) { + return undefined + } else { + return { + repositoryName: repo.repositoryName, + repositoryUri: repo.repositoryUri, + repositoryArn: repo.repositoryArn, + } + } + }) + .filter((item: EcrRepository | undefined) => item !== undefined) as EcrRepository[] + } + request.nextToken = response.nextToken + } while (request.nextToken) + } + + public listAllRepositories(): AsyncCollection { + const requester = async (req: DescribeRepositoriesRequest) => + this.createSdkClient().send(new DescribeRepositoriesCommand(req)) + const collection = pageableToCollection(requester, {}, 'nextToken', 'repositories') + + return collection + .filter(isNonNullable) + .map((list: Repository[]) => list.map((repo: Repository) => (assertHasProps(repo), repo))) + } + + public async createRepository(repositoryName: string) { + const sdkClient = this.createSdkClient() + return sdkClient.send(new CreateRepositoryCommand({ repositoryName: repositoryName })) + } + + public async deleteRepository(repositoryName: string): Promise { + const sdkClient = this.createSdkClient() + await sdkClient.send(new DeleteRepositoryCommand({ repositoryName: repositoryName })) + } + + public async deleteTag(repositoryName: string, tag: string): Promise { + const sdkClient = this.createSdkClient() + await sdkClient.send( + new BatchDeleteImageCommand({ repositoryName: repositoryName, imageIds: [{ imageTag: tag }] }) + ) + } + + protected createSdkClient(): ECRClient { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: ECRClient, + clientOptions: { region: this.regionCode }, + }) + } +} diff --git a/packages/core/src/shared/clients/ecsClient.ts b/packages/core/src/shared/clients/ecsClient.ts new file mode 100644 index 00000000000..818cdc0dec5 --- /dev/null +++ b/packages/core/src/shared/clients/ecsClient.ts @@ -0,0 +1,159 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + Cluster, + DescribeClustersCommand, + DescribeServicesCommand, + DescribeTaskDefinitionCommand, + DescribeTaskDefinitionResponse, + DescribeTasksCommand, + DescribeTasksRequest, + ECSClient, + ExecuteCommandCommand, + ExecuteCommandRequest, + ExecuteCommandResponse, + ListClustersCommand, + ListClustersRequest, + ListServicesCommand, + ListServicesRequest, + ListTasksCommand, + ListTasksRequest, + RegisterTaskDefinitionCommand, + RegisterTaskDefinitionRequest, + Service, + Task, + UpdateServiceCommand, + UpdateServiceRequest, +} from '@aws-sdk/client-ecs' +import globals from '../extensionGlobals' +import { AsyncCollection } from '../utilities/asyncCollection' +import { pageableToCollection } from '../utilities/collectionUtils' +import { ClassToInterfaceType, isNonNullable } from '../utilities/tsUtils' + +export type EcsClient = ClassToInterfaceType + +export type EcsResourceAndToken = { + resource: Cluster[] | Service[] + nextToken?: string +} + +const maxResultsPerResponse = 10 +export class DefaultEcsClient { + public constructor(public readonly regionCode: string) {} + + public async getClusters(nextToken?: string): Promise { + const sdkClient = this.createSdkClient() + const clusterArnList = await sdkClient.send( + new ListClustersCommand({ maxResults: maxResultsPerResponse, nextToken }) + ) + if (clusterArnList.clusterArns?.length === 0) { + return { resource: [] } + } + const clusterResponse = await sdkClient.send( + new DescribeClustersCommand({ clusters: clusterArnList.clusterArns }) + ) + const response: EcsResourceAndToken = { + resource: clusterResponse.clusters!, + nextToken: clusterArnList.nextToken, + } + return response + } + + public listClusters(request: ListClustersRequest = {}): AsyncCollection { + const client = this.createSdkClient() + const requester = async (req: ListClustersRequest) => client.send(new ListClustersCommand(req)) + const collection = pageableToCollection(requester, request, 'nextToken', 'clusterArns') + + return collection.filter(isNonNullable).map(async (clusters) => { + if (clusters.length === 0) { + return [] + } + + const resp = await client.send(new DescribeClustersCommand({ clusters })) + return resp.clusters! + }) + } + + public async getServices(cluster: string, nextToken?: string): Promise { + const sdkClient = this.createSdkClient() + const serviceArnList = await sdkClient.send( + new ListServicesCommand({ cluster: cluster, maxResults: maxResultsPerResponse, nextToken }) + ) + if (serviceArnList.serviceArns?.length === 0) { + return { resource: [] } + } + const services = await this.describeServices(cluster, serviceArnList.serviceArns!) + const response: EcsResourceAndToken = { + resource: services, + nextToken: serviceArnList.nextToken, + } + return response + } + + public listServices(request: ListServicesRequest = {}): AsyncCollection { + const client = this.createSdkClient() + const requester = async (req: ListServicesRequest) => client.send(new ListServicesCommand(req)) + const collection = pageableToCollection(requester, request, 'nextToken', 'serviceArns') + + return collection.filter(isNonNullable).map(async (services) => { + if (services.length === 0) { + return [] + } + + const resp = await client.send(new DescribeServicesCommand({ cluster: request.cluster, services })) + return resp.services! + }) + } + + public async describeTaskDefinition(taskDefinition: string): Promise { + const sdkClient = this.createSdkClient() + return await sdkClient.send(new DescribeTaskDefinitionCommand({ taskDefinition })) + } + + public async listTasks(args: ListTasksRequest): Promise { + const sdkClient = this.createSdkClient() + const listTasksResponse = await sdkClient.send(new ListTasksCommand(args)) + return listTasksResponse.taskArns ?? [] + } + + public async updateService(request: UpdateServiceRequest): Promise { + const sdkClient = this.createSdkClient() + await sdkClient.send(new UpdateServiceCommand(request)) + } + + public async describeTasks(cluster: string, tasks: string[]): Promise { + const sdkClient = this.createSdkClient() + + const params: DescribeTasksRequest = { cluster, tasks } + const describedTasks = await sdkClient.send(new DescribeTasksCommand(params)) + return describedTasks.tasks ?? [] + } + + public async describeServices(cluster: string, services: string[]): Promise { + const sdkClient = this.createSdkClient() + return (await sdkClient.send(new DescribeServicesCommand({ cluster, services }))).services ?? [] + } + + protected createSdkClient(): ECSClient { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: ECSClient, + clientOptions: { region: this.regionCode }, + }) + } + + public async executeCommand(request: Omit): Promise { + const sdkClient = this.createSdkClient() + + // Currently the 'interactive' flag is required and needs to be true for ExecuteCommand: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ExecuteCommand.html + // This may change 'in the near future' as explained here: https://aws.amazon.com/blogs/containers/new-using-amazon-ecs-exec-access-your-containers-fargate-ec2/ + return await sdkClient.send(new ExecuteCommandCommand({ ...request, interactive: true })) + } + + public async registerTaskDefinition(request: RegisterTaskDefinitionRequest) { + const sdkClient = this.createSdkClient() + return sdkClient.send(new RegisterTaskDefinitionCommand(request)) + } +} diff --git a/packages/core/src/shared/clients/iam.ts b/packages/core/src/shared/clients/iam.ts new file mode 100644 index 00000000000..1ccb1f6c03c --- /dev/null +++ b/packages/core/src/shared/clients/iam.ts @@ -0,0 +1,136 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + AttachedPolicy, + AttachRolePolicyCommand, + AttachRolePolicyRequest, + CreateRoleCommand, + CreateRoleCommandOutput, + CreateRoleRequest, + CreateRoleResponse, + EvaluationResult, + GetInstanceProfileCommand, + GetInstanceProfileCommandOutput, + IAMClient, + ListRolesRequest, + paginateListAttachedRolePolicies, + paginateListRoles, + PutRolePolicyCommand, + PutRolePolicyCommandOutput, + Role, + SimulatePolicyResponse, + SimulatePrincipalPolicyCommand, + SimulatePrincipalPolicyRequest, +} from '@aws-sdk/client-iam' +import { AsyncCollection } from '../utilities/asyncCollection' +import { ToolkitError } from '../errors' +import { ClientWrapper } from './clientWrapper' + +export interface IamRole extends Role { + RoleName: string + Arn: string +} + +export interface IamCreateRoleResponse extends CreateRoleResponse { + Role: IamRole +} + +export class IamClient extends ClientWrapper { + public constructor(public override readonly regionCode: string) { + super(regionCode, IAMClient) + } + + public getRoles(request: ListRolesRequest = {}, maxPages: number = 500): AsyncCollection { + return this.makePaginatedRequest(paginateListRoles, request, (p) => p.Roles) + .limit(maxPages) + .map((roles) => roles.filter(hasRequiredFields)) + } + + /** Gets all roles. */ + public async resolveRoles(request: ListRolesRequest = {}): Promise { + return this.getRoles(request).flatten().promise() + } + + public async createRole(request: CreateRoleRequest): Promise { + const response: CreateRoleCommandOutput = await this.makeRequest(CreateRoleCommand, request) + if (!response.Role || !hasRequiredFields(response.Role)) { + throw new ToolkitError('Failed to create IAM role') + } + return response as IamCreateRoleResponse // Safe to assume by check above. + } + + public async attachRolePolicy(request: AttachRolePolicyRequest): Promise { + return await this.makeRequest(AttachRolePolicyCommand, request) + } + + public async simulatePrincipalPolicy(request: SimulatePrincipalPolicyRequest): Promise { + return await this.makeRequest(SimulatePrincipalPolicyCommand, request) + } + + /** + * Attempts to verify if a role has the provided permissions. + */ + public async getDeniedActions(request: SimulatePrincipalPolicyRequest): Promise { + const permissionResponse = await this.simulatePrincipalPolicy(request) + if (!permissionResponse.EvaluationResults) { + throw new Error('No evaluation results found') + } + + // Ignore deny from Organization SCP. These can result in false negatives. + // See https://github.com/aws/aws-sdk/issues/102 + return permissionResponse.EvaluationResults.filter( + (r) => r.EvalDecision !== 'allowed' && r.OrganizationsDecisionDetail?.AllowedByOrganizations !== false + ) + } + + public getFriendlyName(arn: string): string { + const tokens = arn.split('/') + if (tokens.length < 2) { + throw new Error(`Invalid IAM role ARN (expected format: arn:aws:iam::{id}/{name}): ${arn}`) + } + return tokens[tokens.length - 1] + } + + public listAttachedRolePolicies(arn: string): AsyncCollection { + return this.makePaginatedRequest( + paginateListAttachedRolePolicies, + { + RoleName: this.getFriendlyName(arn), + }, + (p) => p.AttachedPolicies + ) + } + + public async getIAMRoleFromInstanceProfile(instanceProfileArn: string): Promise { + const response: GetInstanceProfileCommandOutput = await this.makeRequest(GetInstanceProfileCommand, { + InstanceProfileName: this.getFriendlyName(instanceProfileArn), + }) + if ( + !response.InstanceProfile?.Roles || + response.InstanceProfile.Roles.length === 0 || + !hasRequiredFields(response.InstanceProfile.Roles[0]) + ) { + throw new ToolkitError(`Failed to find IAM role associated with Instance profile ${instanceProfileArn}`) + } + return response.InstanceProfile.Roles[0] + } + + public async putRolePolicy( + roleArn: string, + policyName: string, + policyDocument: string + ): Promise { + return await this.makeRequest(PutRolePolicyCommand, { + RoleName: this.getFriendlyName(roleArn), + PolicyName: policyName, + PolicyDocument: policyDocument, + }) + } +} + +function hasRequiredFields(role: Role): role is IamRole { + return role.RoleName !== undefined && role.Arn !== undefined +} diff --git a/packages/core/src/shared/clients/iotClient.ts b/packages/core/src/shared/clients/iotClient.ts new file mode 100644 index 00000000000..fc5581fffa4 --- /dev/null +++ b/packages/core/src/shared/clients/iotClient.ts @@ -0,0 +1,580 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as _ from 'lodash' +import { + AttachPolicyCommand, + AttachPolicyRequest, + AttachThingPrincipalCommand, + AttachThingPrincipalRequest, + CertificateDescription, + CreateKeysAndCertificateCommand, + CreateKeysAndCertificateRequest, + CreateKeysAndCertificateResponse, + CreatePolicyCommand, + CreatePolicyRequest, + CreatePolicyResponse, + CreatePolicyVersionCommand, + CreatePolicyVersionRequest, + CreateThingCommand, + CreateThingRequest, + CreateThingResponse, + DeleteCertificateCommand, + DeleteCertificateRequest, + DeletePolicyCommand, + DeletePolicyRequest, + DeletePolicyVersionCommand, + DeletePolicyVersionRequest, + DeleteThingCommand, + DeleteThingRequest, + DescribeCertificateCommand, + DescribeCertificateRequest, + DescribeCertificateResponse, + DescribeEndpointCommand, + DetachPolicyCommand, + DetachPolicyRequest, + DetachThingPrincipalCommand, + DetachThingPrincipalRequest, + GetPolicyVersionCommand, + GetPolicyVersionRequest, + GetPolicyVersionResponse, + IoTClient, + ListCertificatesCommand, + ListCertificatesRequest, + ListCertificatesResponse, + ListPoliciesCommand, + ListPoliciesRequest, + ListPoliciesResponse, + ListPolicyVersionsCommand, + ListPolicyVersionsRequest, + ListPrincipalPoliciesCommand, + ListPrincipalPoliciesRequest, + ListPrincipalPoliciesResponse, + ListPrincipalThingsCommand, + ListPrincipalThingsRequest, + ListTargetsForPolicyCommand, + ListTargetsForPolicyRequest, + ListThingPrincipalsCommand, + ListThingPrincipalsRequest, + ListThingPrincipalsResponse, + ListThingsCommand, + ListThingsRequest, + ListThingsResponse, + PolicyVersion, + SetDefaultPolicyVersionCommand, + SetDefaultPolicyVersionRequest, + UpdateCertificateCommand, + UpdateCertificateRequest, +} from '@aws-sdk/client-iot' +import { parse } from '@aws-sdk/util-arn-parser' +import { getLogger } from '../logger/logger' +import { InterfaceNoSymbol } from '../utilities/tsUtils' +import globals from '../extensionGlobals' + +const defaultMaxThings = 250 // 250 is the maximum allowed by the API + +/* ATS is recommended over the deprecated Verisign certificates */ +const iotEndpointType = 'iot:Data-ATS' + +export type IotThing = { readonly name: string; readonly arn: string } +export type IotCertificate = { + readonly id: string + readonly arn: string + readonly activeStatus: string + readonly creationDate: Date +} +export type IotPolicy = IotThing +export type IotClient = InterfaceNoSymbol + +const iotServiceArn = 'iot' +// Pattern to extract the certificate ID from the parsed ARN resource. +const certArnResourcePattern = /cert\/(\w+)/ + +export interface ListThingCertificatesResponse { + readonly certificates: CertificateDescription[] + readonly nextToken: string | undefined +} + +export class DefaultIotClient { + public constructor( + private readonly regionCode: string, + private readonly iotProvider: (regionCode: string) => IoTClient = createSdkClient + ) {} + + /** + * Lists Things owned by the client. + * + * @throws Error if there is an error calling IoT. + */ + public async listThings(request?: ListThingsRequest): Promise { + getLogger().debug('ListThings called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output: ListThingsResponse = await iot.send( + new ListThingsCommand({ + maxResults: request?.maxResults ?? defaultMaxThings, + nextToken: request?.nextToken, + }) + ) + + getLogger().debug('ListThings returned response: %O', output) + return output + } + + /** + * Creates an IoT Thing. + * + * @throws Error if there is an error calling IoT. + */ + public async createThing(request: CreateThingRequest): Promise { + getLogger().debug('CreateThing called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output: CreateThingResponse = await iot.send(new CreateThingCommand({ thingName: request.thingName })) + + getLogger().debug('CreateThing returned response: %O', output) + return output + } + + /** + * Deletes an IoT Thing. + * + * @throws Error if there is an error calling IoT. + */ + public async deleteThing(request: DeleteThingRequest): Promise { + getLogger().debug('DeleteThing called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send(new DeleteThingCommand({ thingName: request.thingName })) + + getLogger().debug('DeleteThing successful') + } + + /** + * Lists all IoT certificates in account. + * + * @throws Error if there is an error calling IoT. + */ + public async listCertificates(request: ListCertificatesRequest): Promise { + getLogger().debug('ListCertificates called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output: ListCertificatesResponse = await iot.send( + new ListCertificatesCommand({ + pageSize: request.pageSize ?? defaultMaxThings, + marker: request.marker, + ascendingOrder: request.ascendingOrder, + }) + ) + + getLogger().debug('ListCertificates returned response: %O', output) + return output + } + + /** + * Lists all principals attached to IoT Thing. + * + * Returns ARNS of principals that may be X.509 certificates, IAM + * users/groups/roles, or Amazon Cognito identities. + * + * @throws Error if there is an error calling IoT. + */ + public async listThingPrincipals(request: ListThingPrincipalsRequest): Promise { + const iot = this.iotProvider(this.regionCode) + + const output: ListThingPrincipalsResponse = await iot.send( + new ListThingPrincipalsCommand({ + thingName: request.thingName, + maxResults: request.maxResults ?? defaultMaxThings, + nextToken: request.nextToken, + }) + ) + return output + } + + /** + * Describes a certificate given the certificate ID. + * + * @throws Error if there is an error calling IoT. + */ + private async describeCertificate(request: DescribeCertificateRequest): Promise { + const iot = this.iotProvider(this.regionCode) + + const output: DescribeCertificateResponse = await iot.send(new DescribeCertificateCommand(request)) + + return output + } + + /** + * Lists all IoT certificates attached to IoT Thing. + * + * listThingPrincipals() returns ARNS of principals that may be X.509 + * certificates, IAM users/groups/roles, or Amazon Cognito identities. + * The list is filtered for certificates only, and describeCertificate() + * is called to get the information for each certificate. + * + * @throws Error if there is an error calling IoT. + */ + public async listThingCertificates(request: ListThingPrincipalsRequest): Promise { + getLogger().debug('ListThingCertificates called with request: %O', request) + + const output = await this.listThingPrincipals(request) + const iotPrincipals: string[] = output.principals ?? [] + const nextToken = output.nextToken + + const describedCerts = iotPrincipals.map(async (iotPrincipal) => { + const principalArn = parse(iotPrincipal) + const certIdFound = principalArn.resource.match(certArnResourcePattern) + if (principalArn.service !== iotServiceArn || !certIdFound) { + return undefined + } + const certId = certIdFound[1] + return this.describeCertificate({ certificateId: certId }) + }) + + const resolvedCerts = (await Promise.all(describedCerts)) + .filter((cert) => cert?.certificateDescription !== undefined) + .map((cert) => cert?.certificateDescription as CertificateDescription) + + const response: ListThingCertificatesResponse = { certificates: resolvedCerts, nextToken: nextToken } + getLogger().debug('ListThingCertificates returned response: %O', response) + return { certificates: resolvedCerts, nextToken: nextToken } + } + + /** + * Lists Things attached to specified certificate. + * + * The nextToken output is discarded, since this function is only used + * to determine if the number of attached Things is nonzero. + * + * @throws Error if there is an error calling IoT. + */ + public async listThingsForCert(request: ListPrincipalThingsRequest): Promise { + getLogger().debug('ListThingsForCert called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output = await iot.send( + new ListPrincipalThingsCommand({ + maxResults: request.maxResults ?? defaultMaxThings, + nextToken: request.nextToken, + principal: request.principal, + }) + ) + const iotThings: string[] = output.things ?? [] + + getLogger().debug('ListThingsForCert returned response: %O', iotThings) + return iotThings + } + + /** + * Creates an X.509 certificate with a 2048 bit RSA keypair. + * + * @throws Error if there is an error calling IoT. + */ + public async createCertificateAndKeys( + request: CreateKeysAndCertificateRequest + ): Promise { + getLogger().debug('CreateCertificate called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output: CreateKeysAndCertificateResponse = await iot.send(new CreateKeysAndCertificateCommand(request)) + + getLogger().debug('CreateCertificate succeeded') + return output + } + + /** + * Activates, deactivates, or revokes an IoT Certificate. + * + * @throws Error if there is an error calling IoT. + */ + public async updateCertificate(request: UpdateCertificateRequest): Promise { + getLogger().debug('UpdateCertificate called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send( + new UpdateCertificateCommand({ certificateId: request.certificateId, newStatus: request.newStatus }) + ) + + getLogger().debug('UpdateCertificate successful') + } + + /** + * Deletes the specified IoT Certificate. + * + * Note that a certificate cannot be deleted if it is ACTIVE, or has attached + * Things or policies. A certificate may be force deleted if it is INACTIVE + * and has no attached Things. + * + * @throws Error if there is an error calling IoT. + */ + public async deleteCertificate(request: DeleteCertificateRequest): Promise { + getLogger().debug('DeleteCertificate called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send(new DeleteCertificateCommand(request)) + + getLogger().debug('DeleteCertificate successful') + } + + /** + * Attaches the certificate specified by the principal to the specified Thing. + * + * @throws Error if there is an error calling IoT. + */ + public async attachThingPrincipal(request: AttachThingPrincipalRequest): Promise { + getLogger().debug('AttachThingPrincipal called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send(new AttachThingPrincipalCommand({ thingName: request.thingName, principal: request.principal })) + + getLogger().debug('AttachThingPrincipal successful') + } + + /** + * Detaches the certificate specified by the principal from the specified Thing. + * + * @throws Error if there is an error calling IoT. + */ + public async detachThingPrincipal(request: DetachThingPrincipalRequest): Promise { + getLogger().debug('DetachThingPrincipal called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send(new DetachThingPrincipalCommand({ thingName: request.thingName, principal: request.principal })) + + getLogger().debug('DetachThingPrincipal successful') + } + + /** + * Lists all IoT Policies. + * + * @throws Error if there is an error calling IoT. + */ + public async listPolicies(request: ListPoliciesRequest): Promise { + getLogger().debug('ListPolicies called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output: ListPoliciesResponse = await iot.send( + new ListPoliciesCommand({ + pageSize: request.pageSize ?? defaultMaxThings, + marker: request.marker, + ascendingOrder: request.ascendingOrder, + }) + ) + + getLogger().debug('ListPolicies returned response: %O', output) + return output + } + + /** + * Lists IoT policies for principal. + * + * @throws Error if there is an error calling IoT. + */ + public async listPrincipalPolicies(request: ListPrincipalPoliciesRequest): Promise { + getLogger().debug('ListPrincipalPolicies called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output: ListPrincipalPoliciesResponse = await iot.send( + new ListPrincipalPoliciesCommand({ + principal: request.principal, + pageSize: request.pageSize ?? defaultMaxThings, + marker: request.marker, + ascendingOrder: request.ascendingOrder, + }) + ) + + getLogger().debug('ListPrincipalPolicies returned response: %O', output) + return output + } + + /** + * Lists certificates attached to specified policy. + * + * The nextMarker output value is discarded, since this function is only + * used to determine if the number of attached targets is nonzero. + * + * @throws Error if there is an error calling IoT. + */ + public async listPolicyTargets(request: ListTargetsForPolicyRequest): Promise { + getLogger().debug('ListPolicyTargets called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output = await iot.send( + new ListTargetsForPolicyCommand({ + pageSize: request.pageSize ?? defaultMaxThings, + marker: request.marker, + policyName: request.policyName, + }) + ) + const arns: string[] = output.targets ?? [] + + getLogger().debug('ListPolicyTargets returned response: %O', arns) + return arns + } + + /** + * Attaches the specified policy to the specified target certificate. + * + * @throws Error if there is an error calling IoT. + */ + public async attachPolicy(request: AttachPolicyRequest): Promise { + getLogger().debug('AttachPolicy called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send(new AttachPolicyCommand({ policyName: request.policyName, target: request.target })) + + getLogger().debug('AttachPolicy successful') + } + + /** + * Detaches the specified policy to the specified target certificate. + * + * @throws Error if there is an error calling IoT. + */ + public async detachPolicy(request: DetachPolicyRequest): Promise { + getLogger().debug('DetachPolicy called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send(new DetachPolicyCommand({ policyName: request.policyName, target: request.target })) + + getLogger().debug('DetachPolicy successful') + } + + /** + * Creates an policy from the given policy document. + * + * @throws Error if there is an error calling IoT. + */ + public async createPolicy(request: CreatePolicyRequest): Promise { + getLogger().debug('CreatePolicy called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output: CreatePolicyResponse = await iot.send(new CreatePolicyCommand(request)) + getLogger().info(`Created policy: ${output.policyArn}`) + + getLogger().debug('CreatePolicy successful') + } + + /** + * Deletes an IoT Policy. + * + * Note that a policy cannot be deleted if it is attached to a certificate, + * or has non-default versions. A policy with non default versions must first + * delete versions with deletePolicyVersions() + * + * @throws Error if there is an error calling IoT. + */ + public async deletePolicy(request: DeletePolicyRequest): Promise { + getLogger().debug('DeletePolicy called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send(new DeletePolicyCommand({ policyName: request.policyName })) + + getLogger().debug('DeletePolicy successful') + } + + /** + * Retrieves the account's IoT device data endpoint. + * + * @throws Error if there is an error calling IoT. + */ + public async getEndpoint(): Promise { + getLogger().debug('GetEndpoint called') + const iot = this.iotProvider(this.regionCode) + + const output = await iot.send(new DescribeEndpointCommand({ endpointType: iotEndpointType })) + if (!output.endpointAddress) { + throw new Error('Failed to retrieve endpoint') + } + + getLogger().debug('GetEndpoint successful') + return output.endpointAddress + } + + /** + * Lists versions for an IoT Policy + * + * @throws Error if there is an error calling IoT. + */ + public async *listPolicyVersions(request: ListPolicyVersionsRequest): AsyncIterableIterator { + const iot = this.iotProvider(this.regionCode) + + const response = await iot.send(new ListPolicyVersionsCommand(request)) + + if (response.policyVersions) { + yield* response.policyVersions + } + } + + /** + * Creates a new version for an IoT policy + * + * @throws Error if there is an error calling IoT. + */ + public async createPolicyVersion(request: CreatePolicyVersionRequest): Promise { + getLogger().debug('CreatePolicyVersion called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output = await iot.send(new CreatePolicyVersionCommand(request)) + getLogger().info(`Created new version ${output.policyVersionId} of ${request.policyName}`) + + getLogger().debug('CreatePolicyVersion successful') + } + + /** + * Deletes an IoT Policy version. + * + * Note that a policy cannot be deleted if it is attached to a certificate, + * or has non-default versions. A policy with non default versions must first + * delete versions with deletePolicyVersions() + * + * @throws Error if there is an error calling IoT. + */ + public async deletePolicyVersion(request: DeletePolicyVersionRequest): Promise { + getLogger().debug('DeletePolicyVersion called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send(new DeletePolicyVersionCommand(request)) + + getLogger().debug('DeletePolicyVersion successful') + } + + /** + * Sets a default version for an Iot Policy. + * + * @throws Error if there is an error calling IoT. + */ + public async setDefaultPolicyVersion(request: SetDefaultPolicyVersionRequest): Promise { + getLogger().debug('SetDefaultPolicyVersion called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + await iot.send(new SetDefaultPolicyVersionCommand(request)) + + getLogger().debug('SetDefaultPolicyVersion successful') + } + + /** + * Downloads information including document for a specified policy version. + * + * @throws Error if there is an error calling IoT. + */ + public async getPolicyVersion(request: GetPolicyVersionRequest): Promise { + getLogger().debug('GetPolicyVersion called with request: %O', request) + const iot = this.iotProvider(this.regionCode) + + const output: GetPolicyVersionResponse = await iot.send(new GetPolicyVersionCommand(request)) + + getLogger().debug('GetPolicyVersion successful') + return output + } +} + +function createSdkClient(regionCode: string): IoTClient { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: IoTClient, + clientOptions: { region: regionCode }, + }) +} diff --git a/packages/core/src/shared/clients/lambdaClient.ts b/packages/core/src/shared/clients/lambdaClient.ts new file mode 100644 index 00000000000..fb73ce9c2d2 --- /dev/null +++ b/packages/core/src/shared/clients/lambdaClient.ts @@ -0,0 +1,351 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { BlobPayloadInputTypes } from '@smithy/types' +import { ToolkitError } from '../errors' +import globals from '../extensionGlobals' +import { getLogger } from '../logger/logger' +import { ClassToInterfaceType } from '../utilities/tsUtils' + +import { + LambdaClient as LambdaSdkClient, + GetFunctionCommand, + GetFunctionCommandOutput, + FunctionConfiguration, + InvocationResponse, + ListFunctionsRequest, + ListFunctionsResponse, + GetFunctionResponse, + GetLayerVersionResponse, + ListLayerVersionsRequest, + LayerVersionsListItem, + ListLayerVersionsResponse, + UpdateFunctionConfigurationRequest, + FunctionUrlConfig, + GetFunctionConfigurationCommand, + PublishVersionCommand, + UpdateFunctionConfigurationCommand, + UpdateFunctionCodeCommand, + ListFunctionUrlConfigsCommand, + ListLayerVersionsCommand, + GetLayerVersionCommand, + ListFunctionsCommand, + DeleteFunctionCommand, + InvokeCommand, + waitUntilFunctionUpdatedV2, + waitUntilFunctionActiveV2, +} from '@aws-sdk/client-lambda' +import { CancellationError } from '../utilities/timeoutUtils' +import { fromSSO } from '@aws-sdk/credential-provider-sso' +import { getIAMConnection } from '../../auth/utils' +import { NodeHttpHandler } from '@smithy/node-http-handler' + +export type LambdaClient = ClassToInterfaceType + +export class DefaultLambdaClient { + private readonly defaultTimeoutInMs: number + + public constructor( + public readonly regionCode: string, + public readonly userAgent: string | undefined = undefined + ) { + this.defaultTimeoutInMs = 5 * 60 * 1000 // 5 minutes (SDK default is 2 minutes) + } + + public async deleteFunction(name: string, qualifier?: string): Promise { + const sdkClient = await this.createSdkClient() + + await sdkClient.send( + new DeleteFunctionCommand({ + FunctionName: name, + Qualifier: qualifier, + }) + ) + } + + public async invoke(name: string, payload?: BlobPayloadInputTypes, version?: string): Promise { + const sdkClient = await this.createSdkClient() + + const response = await sdkClient.send( + new InvokeCommand({ + FunctionName: name, + LogType: 'Tail', + Payload: payload, + Qualifier: version, + }) + ) + + return response + } + + public async *listFunctions(): AsyncIterableIterator { + const client = await this.createSdkClient() + + const request: ListFunctionsRequest = {} + do { + const response: ListFunctionsResponse = await client.send(new ListFunctionsCommand(request)) + + if (response.Functions) { + yield* response.Functions + } + + request.Marker = response.NextMarker + } while (request.Marker) + } + + public async getFunction(name: string): Promise { + getLogger().debug(`GetFunction called for function: ${name}`) + const client = await this.createSdkClient() + + try { + const response = await client.send(new GetFunctionCommand({ FunctionName: name })) + // prune `Code` from logs so we don't reveal a signed link to customer resources. + getLogger().debug('GetFunction 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 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.send(new GetLayerVersionCommand({ LayerName: name, VersionNumber: version })) + // 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: ListLayerVersionsRequest = { LayerName: name } + do { + const response: ListLayerVersionsResponse = await client.send(new ListLayerVersionsCommand(request)) + + 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() + + try { + const response = await client.send(new ListFunctionUrlConfigsCommand({ FunctionName: name })) + // prune `Code` from logs so we don't reveal a signed link to customer resources. + getLogger().debug('GetFunctionUrlConfig returned response (code section pruned): %O', { + ...response, + Code: 'Pruned', + }) + return response.FunctionUrlConfigs ?? [] + } catch (e) { + throw ToolkitError.chain(e, 'Failed to get Lambda function URLs') + } + } + + public async updateFunctionCode(name: string, zipFile: Uint8Array): Promise { + getLogger().debug(`updateFunctionCode called for function: ${name}`) + const client = await this.createSdkClient() + + try { + const response = await client.send( + new UpdateFunctionCodeCommand({ + FunctionName: name, + Publish: true, + ZipFile: zipFile, + }) + ) + getLogger().debug('updateFunctionCode returned response: %O', response) + await waitUntilFunctionUpdatedV2({ client, maxWaitTime: 300 }, { FunctionName: name }) + + return response + } catch (e) { + getLogger().error('Failed to run updateFunctionCode: %s', e) + throw e + } + } + + public async updateFunctionConfiguration( + params: UpdateFunctionConfigurationRequest, + options: { + maxRetries?: number + initialDelayMs?: number + backoffMultiplier?: number + waitForUpdate?: boolean + } = {} + ): Promise { + const client = await this.createSdkClient() + const maxRetries = options.maxRetries ?? 5 + const initialDelayMs = options.initialDelayMs ?? 1000 + const backoffMultiplier = options.backoffMultiplier ?? 2 + // return until lambda update is completed + const waitForUpdate = options.waitForUpdate ?? false + + let retryCount = 0 + let lastError: any + + // there could be race condition, if function is being updated, wait and retry + while (retryCount <= maxRetries) { + try { + const response = await client.send(new UpdateFunctionConfigurationCommand(params)) + getLogger().debug('updateFunctionConfiguration returned response: %O', response) + if (waitForUpdate) { + // don't return if wait for result + break + } + return response + } catch (e) { + lastError = e + + // Check if this is an "update in progress" error + if (this.isUpdateInProgressError(e) && retryCount < maxRetries) { + const delayMs = initialDelayMs * Math.pow(backoffMultiplier, retryCount) + getLogger().info( + `Update in progress for Lambda function ${params.FunctionName}. ` + + `Retrying in ${delayMs}ms (attempt ${retryCount + 1}/${maxRetries})` + ) + + await new Promise((resolve) => setTimeout(resolve, delayMs)) + retryCount++ + } else { + getLogger().error('Failed to run updateFunctionConfiguration: %s', e) + throw e + } + } + } + + // check if lambda update is completed, use client.getFunctionConfiguration to poll until + // LastUpdateStatus is Successful or Failed + if (waitForUpdate) { + let lastUpdateStatus = 'InProgress' + while (lastUpdateStatus === 'InProgress') { + await new Promise((resolve) => setTimeout(resolve, 1000)) + const response = await client.send( + new GetFunctionConfigurationCommand({ FunctionName: params.FunctionName }) + ) + lastUpdateStatus = response.LastUpdateStatus ?? 'Failed' + if (lastUpdateStatus === 'Successful') { + return response + } else if (lastUpdateStatus === 'Failed') { + getLogger().error('Failed to update function configuration: %O', response) + throw new Error(`Failed to update function configuration: ${response.LastUpdateStatusReason}`) + } + } + } + + getLogger().error(`Failed to update function configuration after ${maxRetries} retries: %s`, lastError) + throw lastError + } + + public async publishVersion( + name: string, + options: { waitForUpdate?: boolean } = {} + ): Promise { + const client = await this.createSdkClient() + // return until lambda update is completed + const waitForUpdate = options.waitForUpdate ?? false + const response = await client.send( + new PublishVersionCommand({ + FunctionName: name, + }) + ) + + if (waitForUpdate) { + let state = 'Pending' + while (state === 'Pending') { + await new Promise((resolve) => setTimeout(resolve, 1000)) + const statusResponse = await client.send( + new GetFunctionConfigurationCommand({ FunctionName: name, Qualifier: response.Version }) + ) + state = statusResponse.State ?? 'Failed' + if (state === 'Active' || state === 'InActive') { + // version creation finished + return statusResponse + } else if (state === 'Failed') { + getLogger().error('Failed to create Version: %O', statusResponse) + throw new Error(`Failed to create Version: ${statusResponse.LastUpdateStatusReason}`) + } + } + } + + return response + } + + private isUpdateInProgressError(error: any): boolean { + return ( + error?.message && + error.message.includes( + 'The operation cannot be performed at this time. An update is in progress for resource:' + ) + ) + } + + public async waitForActive( + functionName: string, + waiter?: { maxWaitTime?: number; minDelay?: number; maxDelay?: number } + ): Promise { + const sdkClient = await this.createSdkClient() + + await waitUntilFunctionActiveV2( + { + client: sdkClient, + maxWaitTime: waiter?.maxWaitTime ?? 600, + minDelay: waiter?.minDelay ?? 1, + maxDelay: waiter?.maxDelay ?? 120, + }, + { FunctionName: functionName } + ) + } + + private async createSdkClient(): Promise { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: LambdaSdkClient, + userAgent: !this.userAgent, + clientOptions: { + userAgent: this.userAgent ? [[this.userAgent]] : undefined, + region: this.regionCode, + requestHandler: new NodeHttpHandler({ + requestTimeout: this.defaultTimeoutInMs, + }), + }, + }) + } +} + +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/clients/qDeveloperChatClient.ts b/packages/core/src/shared/clients/qDeveloperChatClient.ts new file mode 100644 index 00000000000..ee98a78e356 --- /dev/null +++ b/packages/core/src/shared/clients/qDeveloperChatClient.ts @@ -0,0 +1,25 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { QDeveloperStreaming } from '@amzn/amazon-q-developer-streaming-client' +import { getCodewhispererConfig } from '../../codewhisperer/client/codewhisperer' +import { getUserAgent } from '../telemetry/util' +import { ConfiguredRetryStrategy } from '@smithy/util-retry' +import { AuthUtil } from '../../codewhisperer/util/authUtil' + +// Create a client for featureDev streaming based off of aws sdk v3 +export async function createQDeveloperStreamingClient(): Promise { + const cwsprConfig = getCodewhispererConfig() + const credentials = await AuthUtil.instance.getCredentials() + const streamingClient = new QDeveloperStreaming({ + region: cwsprConfig.region, + endpoint: cwsprConfig.endpoint, + credentials: credentials, + customUserAgent: getUserAgent(), + // SETTING max attempts to 0 FOR BETA. RE-ENABLE FOR RE-INVENT + // Implement exponential back off starting with a base of 500ms (500 + attempt^10) + retryStrategy: new ConfiguredRetryStrategy(0, (attempt: number) => 500 + attempt ** 10), + }) + return streamingClient +} diff --git a/packages/core/src/shared/clients/redshiftClient.ts b/packages/core/src/shared/clients/redshiftClient.ts new file mode 100644 index 00000000000..5464f58f1d2 --- /dev/null +++ b/packages/core/src/shared/clients/redshiftClient.ts @@ -0,0 +1,300 @@ +/* eslint-disable header/header */ +/*! + * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + ClusterCredentials, + ClustersMessage, + DescribeClustersCommand, + DescribeClustersMessage, + GetClusterCredentialsCommand, + GetClusterCredentialsMessage, + RedshiftClient, +} from '@aws-sdk/client-redshift' +import { + DescribeStatementCommand, + DescribeStatementRequest, + ExecuteStatementCommand, + GetStatementResultCommand, + GetStatementResultRequest, + GetStatementResultResponse, + ListDatabasesCommand, + ListDatabasesRequest, + ListDatabasesResponse, + ListSchemasCommand, + ListSchemasRequest, + ListSchemasResponse, + ListTablesCommand, + ListTablesRequest, + ListTablesResponse, + RedshiftDataClient, +} from '@aws-sdk/client-redshift-data' +import { + GetCredentialsCommand, + GetCredentialsRequest, + GetCredentialsResponse, + ListWorkgroupsCommand, + ListWorkgroupsRequest, + ListWorkgroupsResponse, + RedshiftServerlessClient, +} from '@aws-sdk/client-redshift-serverless' +import globals from '../extensionGlobals' +import { ConnectionParams, ConnectionType, RedshiftWarehouseType } from '../../awsService/redshift/models/models' +import { sleep } from '../utilities/timeoutUtils' +import { SecretsManagerClient } from './secretsManagerClient' +import { ToolkitError } from '../errors' +import { getLogger } from '../logger/logger' + +export interface ExecuteQueryResponse { + statementResultResponse: GetStatementResultResponse + executionId: string +} + +// Type definition for Provisioned and Serverless +export class DefaultRedshiftClient { + public constructor( + public readonly regionCode: string, + private readonly redshiftDataClientProvider: ( + regionCode: string + ) => RedshiftDataClient = createRedshiftDataClient, + private readonly redshiftClientProvider: (regionCode: string) => RedshiftClient = createRedshiftSdkClient, + private readonly redshiftServerlessClientProvider: ( + regionCode: string + ) => RedshiftServerlessClient = createRedshiftServerlessSdkClient + ) {} + + // eslint-disable-next-line require-yield + public async describeProvisionedClusters(nextToken?: string): Promise { + const redshiftClient = this.redshiftClientProvider(this.regionCode) + const request: DescribeClustersMessage = { + Marker: nextToken, + MaxRecords: 20, + } + const response = await redshiftClient.send(new DescribeClustersCommand(request)) + if (response.Clusters) { + response.Clusters = response.Clusters.filter( + (cluster) => cluster.ClusterAvailabilityStatus?.toLowerCase() === 'available' + ) + } + return response + } + + public async listServerlessWorkgroups(nextToken?: string): Promise { + const redshiftServerlessClient = this.redshiftServerlessClientProvider(this.regionCode) + const request: ListWorkgroupsRequest = { + nextToken: nextToken, + maxResults: 20, + } + const response = await redshiftServerlessClient.send(new ListWorkgroupsCommand(request)) + if (response.workgroups) { + response.workgroups = response.workgroups.filter( + (workgroup) => workgroup.status?.toLowerCase() === 'available' + ) + } + return response + } + + public async listDatabases(connectionParams: ConnectionParams, nextToken?: string): Promise { + const redshiftDataClient = this.redshiftDataClientProvider(this.regionCode) + const warehouseType = connectionParams.warehouseType + const warehouseIdentifier = connectionParams.warehouseIdentifier + const input: ListDatabasesRequest = { + ClusterIdentifier: warehouseType === RedshiftWarehouseType.PROVISIONED ? warehouseIdentifier : undefined, + Database: connectionParams.database, + DbUser: + warehouseType === RedshiftWarehouseType.PROVISIONED && + connectionParams.connectionType !== ConnectionType.DatabaseUser + ? connectionParams.username + : undefined, + WorkgroupName: warehouseType === RedshiftWarehouseType.SERVERLESS ? warehouseIdentifier : undefined, + NextToken: nextToken, + SecretArn: + connectionParams.connectionType === ConnectionType.DatabaseUser || connectionParams.secret + ? connectionParams.secret + : undefined, + } + return redshiftDataClient.send(new ListDatabasesCommand(input)) + } + public async listSchemas(connectionParams: ConnectionParams, nextToken?: string): Promise { + const redshiftDataClient = this.redshiftDataClientProvider(this.regionCode) + const warehouseType = connectionParams.warehouseType + const warehouseIdentifier = connectionParams.warehouseIdentifier + const input: ListSchemasRequest = { + ClusterIdentifier: warehouseType === RedshiftWarehouseType.PROVISIONED ? warehouseIdentifier : undefined, + Database: connectionParams.database, + DbUser: + connectionParams.username && connectionParams.connectionType !== ConnectionType.DatabaseUser + ? connectionParams.username + : undefined, + WorkgroupName: warehouseType === RedshiftWarehouseType.SERVERLESS ? warehouseIdentifier : undefined, + NextToken: nextToken, + SecretArn: + connectionParams.connectionType === ConnectionType.DatabaseUser || connectionParams.secret + ? connectionParams.secret + : undefined, + } + return redshiftDataClient.send(new ListSchemasCommand(input)) + } + + public async listTables( + connectionParams: ConnectionParams, + schemaName: string, + nextToken?: string + ): Promise { + const redshiftDataClient = this.redshiftDataClientProvider(this.regionCode) + const warehouseType = connectionParams.warehouseType + const warehouseIdentifier = connectionParams.warehouseIdentifier + const input: ListTablesRequest = { + ClusterIdentifier: warehouseType === RedshiftWarehouseType.PROVISIONED ? warehouseIdentifier : undefined, + DbUser: + connectionParams.username && connectionParams.connectionType !== ConnectionType.DatabaseUser + ? connectionParams.username + : undefined, + Database: connectionParams.database, + WorkgroupName: warehouseType === RedshiftWarehouseType.SERVERLESS ? warehouseIdentifier : undefined, + SchemaPattern: schemaName, + NextToken: nextToken, + SecretArn: + connectionParams.connectionType === ConnectionType.DatabaseUser || connectionParams.secret + ? connectionParams.secret + : undefined, + } + const ListTablesResponse = redshiftDataClient.send(new ListTablesCommand(input)) + return ListTablesResponse + } + + public async executeQuery( + connectionParams: ConnectionParams, + queryToExecute: string, + nextToken?: string, + executionId?: string + ): Promise { + const redshiftData = this.redshiftDataClientProvider(this.regionCode) + // if executionId is not passed in, that means that we're executing and retrieving the results of the query for the first time. + if (!executionId) { + const execution = await redshiftData.send( + new ExecuteStatementCommand({ + ClusterIdentifier: + connectionParams.warehouseType === RedshiftWarehouseType.PROVISIONED + ? connectionParams.warehouseIdentifier + : undefined, + WorkgroupName: + connectionParams.warehouseType === RedshiftWarehouseType.SERVERLESS + ? connectionParams.warehouseIdentifier + : undefined, + Database: connectionParams.database, + Sql: queryToExecute, + DbUser: + connectionParams.username && connectionParams.connectionType !== ConnectionType.DatabaseUser + ? connectionParams.username + : undefined, + SecretArn: + connectionParams.connectionType === ConnectionType.DatabaseUser || connectionParams.secret + ? connectionParams.secret + : undefined, + }) + ) + + executionId = execution.Id + type Status = 'RUNNING' | 'FAILED' | 'FINISHED' + let status: Status = 'RUNNING' + while (status === 'RUNNING') { + const describeStatementResponse = await redshiftData.send( + new DescribeStatementCommand({ Id: executionId } as DescribeStatementRequest) + ) + if (describeStatementResponse.Status === 'FAILED' || describeStatementResponse.Status === 'FINISHED') { + status = describeStatementResponse.Status + if (status === 'FAILED') { + throw new Error( + `Failed to run query: '${queryToExecute}': '${describeStatementResponse.Error}'` + ) + } else if (status === 'FINISHED' && !describeStatementResponse.HasResultSet) { + return undefined + } + break + } else { + await sleep(1000) + } + } + } + const result = await redshiftData.send( + new GetStatementResultCommand({ Id: executionId, NextToken: nextToken } as GetStatementResultRequest) + ) + + return { statementResultResponse: result, executionId: executionId } as ExecuteQueryResponse + } + + public async getTempCredentials( + warehouseType: RedshiftWarehouseType, + connectionParams: ConnectionParams + ): Promise { + if (warehouseType === RedshiftWarehouseType.PROVISIONED) { + const redshiftClient = this.redshiftClientProvider(this.regionCode) + const getClusterCredentialsRequest: GetClusterCredentialsMessage = { + DbUser: connectionParams.username!, + DbName: connectionParams.database, + ClusterIdentifier: connectionParams.warehouseIdentifier, + } + return redshiftClient.send(new GetClusterCredentialsCommand(getClusterCredentialsRequest)) + } else { + const redshiftServerless = this.redshiftServerlessClientProvider(this.regionCode) + const getCredentialsRequest: GetCredentialsRequest = { + dbName: connectionParams.database, + workgroupName: connectionParams.warehouseIdentifier, + } + return redshiftServerless.send(new GetCredentialsCommand(getCredentialsRequest)) + } + } + public genUniqueId(connectionParams: ConnectionParams): string { + const epochDate = Date.now() + return `${epochDate}-${connectionParams.warehouseIdentifier}` + } + + public async createSecretFromConnectionParams(connectionParams: ConnectionParams): Promise { + /* + create a secrete arn for the username and password entered through the Database Username and Password authentication + */ + const secretsManagerClient = new SecretsManagerClient(this.regionCode) + const username = connectionParams.username + const password = connectionParams.password + if (username && password) { + const secretString = this.genUniqueId(connectionParams) + try { + const response = await secretsManagerClient?.createSecret(secretString, username, password) + if (response && response.ARN) { + return response.ARN + } + throw new ToolkitError('Secret Arn not created') + } catch (error) { + getLogger().error( + `Redshift: Error creating secret in AWS Secrets Manager - ${(error as Error).message}` + ) + throw error + } + } else { + throw new ToolkitError('Username or Password not present') + } + } +} + +function createRedshiftSdkClient(regionCode: string): RedshiftClient { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: RedshiftClient, + clientOptions: { region: regionCode }, + }) +} + +function createRedshiftServerlessSdkClient(regionCode: string): RedshiftServerlessClient { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: RedshiftServerlessClient, + clientOptions: { region: regionCode }, + }) +} +function createRedshiftDataClient(regionCode: string): RedshiftDataClient { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: RedshiftDataClient, + clientOptions: { region: regionCode }, + }) +} diff --git a/packages/core/src/shared/clients/s3.ts b/packages/core/src/shared/clients/s3.ts new file mode 100644 index 00000000000..247e85731c6 --- /dev/null +++ b/packages/core/src/shared/clients/s3.ts @@ -0,0 +1,784 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as url from 'url' +import _ from 'lodash' +import { inspect } from 'util' +import { getLogger } from '../logger/logger' +import { bufferToStream, DefaultFileStreams, FileStreams, pipe } from '../utilities/streamUtilities' +import { assertHasProps, InterfaceNoSymbol, RequiredProps } from '../utilities/tsUtils' +import { Readable } from 'stream' +import globals, { isWeb } from '../extensionGlobals' +import { defaultPartition } from '../regions/regionProvider' +import { AsyncCollection } from '../utilities/asyncCollection' +import { StreamingBlobTypes } from '@smithy/types' +import { + _Object, + BucketLocationConstraint, + CreateBucketCommand, + DeleteBucketCommand, + GetObjectCommand, + HeadObjectCommand, + HeadObjectOutput, + ListObjectsV2Command, + ListObjectsV2Output, + PutObjectCommand, + S3Client as S3ClientSDK, + Bucket, + paginateListBuckets, + ListObjectVersionsCommand, + ListObjectVersionsOutput, + DeleteObjectCommand, + DeleteObjectsCommand, + DeleteObjectsOutput, + GetObjectOutput, + _Error, + GetObjectCommandOutput, +} from '@aws-sdk/client-s3' +import { getSignedUrl } from '@aws-sdk/s3-request-presigner' +import { Progress, Upload } from '@aws-sdk/lib-storage' +import { ClientWrapper } from './clientWrapper' +import { ToolkitError } from '../errors' + +export const DEFAULT_MAX_KEYS = 300 // eslint-disable-line @typescript-eslint/naming-convention +export const DEFAULT_DELIMITER = '/' // eslint-disable-line @typescript-eslint/naming-convention +export const defaultPrefix = '' + +export type Folder = InterfaceNoSymbol +export type S3Bucket = Bucket & { Name: string; BucketRegion: string; Arn: string } + +interface S3Object { + readonly key: string + readonly versionId?: string +} + +export interface ContinuationToken { + readonly keyMarker: string + readonly versionIdMarker?: string +} + +export interface CreateBucketRequest { + readonly bucketName: string +} + +export interface CreateBucketResponse { + readonly bucket: S3Bucket +} + +export interface ListBucketsResponse { + readonly buckets: S3Bucket[] +} + +export interface ListFilesRequest { + readonly bucketName: string + readonly folderPath?: string + readonly continuationToken?: string + readonly maxResults?: number // Defaults to DEFAULT_MAX_KEYS +} + +export interface ListFilesResponse { + readonly files: File[] + readonly folders: Folder[] + readonly continuationToken?: string +} + +export interface CreateFolderRequest { + readonly bucketName: string + readonly path: string +} + +export interface CreateFolderResponse { + readonly folder: Folder +} + +export interface DownloadFileRequest { + readonly bucketName: string + readonly key: string + readonly progressListener?: (loadedBytes: number) => void + readonly saveLocation: vscode.Uri +} + +export interface SignedUrlRequest { + readonly bucketName: string + readonly key: string + readonly time: number +} + +export interface UploadFileRequest { + readonly bucketName: string + readonly key: string + readonly content: vscode.Uri | Uint8Array + readonly contentType?: string + readonly progressListener?: (loadedBytes: number) => void +} + +export interface HeadObjectRequest { + readonly bucketName: string + readonly key: string +} + +export interface CharsetRequest { + readonly key: string + readonly bucketName: string +} + +export interface ListObjectVersionsRequest { + readonly bucketName: string + readonly continuationToken?: ContinuationToken + readonly maxResults?: number // Defaults to DEFAULT_MAX_KEYS +} + +export interface ListObjectVersionsResponse { + readonly objects: S3Object[] + readonly continuationToken?: ContinuationToken +} + +export interface DeleteObjectRequest { + readonly bucketName: string + readonly key: string +} + +export interface DeleteObjectsRequest { + readonly bucketName: string + readonly objects: { key: string; versionId?: string }[] +} + +export interface DeleteObjectsResponse { + readonly errors: _Error[] +} + +export interface DeleteBucketRequest { + readonly bucketName: string +} + +export interface GetObjectRequest { + readonly bucketName: string + readonly key: string +} + +export interface GetObjectResponse { + readonly objectBody: StreamingBlobTypes +} + +export class S3Client extends ClientWrapper { + public constructor( + regionCode: string, + private readonly partitionId = globals.regionProvider.getPartitionId(regionCode) ?? defaultPartition, + private readonly fileStreams: FileStreams = new DefaultFileStreams() + ) { + super(regionCode, S3ClientSDK) + } + + protected getCreateBucketConfiguration() { + return this.regionCode === 'us-east-1' + ? undefined + : { LocationConstraint: this.regionCode as BucketLocationConstraint } + } + + /** + * Creates a bucket in the region of the client. + * + * @throws Error if there is an error calling S3. + */ + public async createBucket(request: CreateBucketRequest): Promise { + getLogger().debug('CreateBucket called with request: %O', request) + await this.makeRequest(CreateBucketCommand, { + Bucket: request.bucketName, + CreateBucketConfiguration: this.getCreateBucketConfiguration(), + }) + + const response: CreateBucketResponse = { + bucket: toBucket(request.bucketName, this.regionCode, this.partitionId), + } + getLogger().debug('CreateBucket returned response: %O', response) + return response + } + + /** + * Empties and deletes a bucket. + * + * Note that this just repeatedly calls list and delete to empty the bucket before deletion. + * Failures during the emptying or deleting step can leave the bucket in a state where + * some (or all) objects are deleted, but the bucket remains. + * + * @throws Error if there is an error calling S3 to empty or delete the bucket. + */ + public async deleteBucket(request: DeleteBucketRequest): Promise { + getLogger().debug('DeleteBucket called with request: %O', request) + const { bucketName } = request + + await this.emptyBucket(bucketName) + await this.makeRequest(DeleteBucketCommand, { Bucket: bucketName }) + + getLogger().debug('DeleteBucket succeeded') + } + + /** + * Creates a folder. + * + * The folder's bucket should reside in the same region as the one configured for the client. + * + * Note that folders don't actually exist in S3. + * Everything in S3 is an object with a key residing in a bucket. + * However, S3 allows you to emulate folders by providing a key with delimiters (slashes) in its name. + * + * To creating empty "folders", you upload an empty object with a trailing slash. + * + * Creation of folders isn't strictly necessary, as you can just upload keys with delimiters. + * However, empty folders make it easier to work with S3 as if it were a filesystem like in the UI. + * + * @throws Error if there is an error calling S3. + */ + public async createFolder(request: CreateFolderRequest): Promise { + getLogger().debug('CreateFolder called with request: %O', request) + await this.makeRequest(PutObjectCommand, { Bucket: request.bucketName, Key: request.path, Body: '' }) + + const folder = new DefaultFolder({ + path: request.path, + partitionId: this.partitionId, + bucketName: request.bucketName, + }) + + const response: CreateFolderResponse = { folder } + getLogger().debug('CreateFolder returned response: %O', response) + return response + } + + /** + * Downloads a file to disk. + * + * The file's bucket should reside in the same region as the one configured for the client. + * + * Pipes the response (read) stream into the file (write) stream. + * + * @throws Error if there is an error calling S3 or piping between streams. + */ + public async downloadFile(request: DownloadFileRequest): Promise { + getLogger().debug( + 'DownloadFile called for bucketName: %s, key: %s, saveLocation: %s', + request.bucketName, + request.key, + request.saveLocation + ) + + const readStream = await this.downloadFileStream(request.bucketName, request.key) + const writeStream = this.fileStreams.createWriteStream(request.saveLocation) + + await pipe(readStream, writeStream, request.progressListener) + + getLogger().debug('DownloadFile succeeded') + } + + /** + * Lighter version of {@link downloadFile} that just returns the stream. + */ + public async downloadFileStream(bucketName: string, key: string): Promise { + // GetObject response body is now a `StreamingBlobPayloadOutputTypes` from @smithy/types. + // this is a general type for web/node streams, therefore we must cast to nodes streaming type. + const response: GetObjectCommandOutput = await this.makeRequest(GetObjectCommand, { + Bucket: bucketName, + Key: key, + }) + + if (isWeb()) { + throw new ToolkitError('S3: downloading files is not supported in web.') + } + + return (response.Body as Readable) ?? new Readable() + } + + public async headObject(request: HeadObjectRequest): Promise { + getLogger().debug('HeadObject called with request: %O', request) + return this.makeRequest(HeadObjectCommand, { Bucket: request.bucketName, Key: request.key }) + } + + /** + * Generates a presigned URL for the given file in S3. + * Takes a valid time option, which must be in seconds. This is the time the URL will be valid for + * + * @returns the string of the link to the presigned URL + */ + public async getSignedUrlForObject(request: SignedUrlRequest): Promise { + return await getSignedUrl( + this.getClient(), + new GetObjectCommand({ Bucket: request.bucketName, Key: request.key }), + { + expiresIn: request.time, + } + ) + } + + public linkProgressListenerToUpload( + upload: { on: (event: 'httpUploadProgress', listener: (progress: Progress) => void) => void }, + progressListener: (loadedBytes: number) => void + ) { + let lastLoaded = 0 + upload.on('httpUploadProgress', (progress) => { + if (progress.loaded) { + progressListener(progress.loaded - lastLoaded) + lastLoaded = progress.loaded + } + }) + } + + /** + * Uploads a file from disk. + * + * The destination bucket should reside in the same region as the one configured for the client. + * + * Pipes the file (read) stream into the request (write) stream. + * Assigns the target content type based on the mime type of the file. + * + * @returns The Upload stream + * @throws Error if there is an error calling S3 or piping between streams. + */ + public async uploadFile(request: UploadFileRequest): Promise { + getLogger().debug('UploadFile called for bucketName: %s, key: %s', request.bucketName, request.key) + // Upload example from: https://docs.aws.amazon.com/code-library/latest/ug/s3_example_s3_Scenario_UsingLargeFiles_section.html + const readStream = + request.content instanceof vscode.Uri + ? this.fileStreams.createReadStream(request.content) + : bufferToStream(request.content) + + const managedUpload = new Upload({ + client: this.getClient(), + params: { + Bucket: request.bucketName, + Key: request.key, + Body: readStream, + ContentType: request.contentType, + }, + }) + + const progressListener = request.progressListener + if (progressListener) { + this.linkProgressListenerToUpload(managedUpload, progressListener) + } + + return managedUpload + } + + /** + * Lists all buckets owned by the client. + * + * + * @throws Error if there is an error calling S3. + */ + private paginateBuckets(filterRegion: boolean = true): AsyncCollection { + return this.makePaginatedRequest( + paginateListBuckets, + filterRegion ? { BucketRegion: this.regionCode } : {}, + (page) => page.Buckets + ) + } + + // TODO: replace calls to listBucketsIterable and listBuckets with calls to this function once "Bucket" type is unified. + private listValidBuckets( + paginateBuckets: () => AsyncCollection = this.paginateBuckets.bind(this) + ): AsyncCollection { + const partitionId = this.partitionId + return paginateBuckets().map(async (page) => page.filter(hasName).filter(hasRegion).map(addArn)) + + function hasName(b: B): b is B & { Name: string } { + return b.Name !== undefined + } + + function hasRegion(b: B): b is B & { BucketRegion: string } { + return b.BucketRegion !== undefined + } + + function addArn(b: B): S3Bucket { + return toBucket(b.Name, b.BucketRegion, partitionId) + } + } + + public listBucketsIterable(): AsyncCollection & { readonly region: string }> { + return this.listValidBuckets() + .flatten() + .map((b) => { + return { + region: b.BucketRegion, + ...b, + } + }) + } + + public async listBuckets( + paginateBuckets: () => AsyncCollection = this.paginateBuckets.bind(this) + ): Promise { + getLogger().debug('ListBuckets called') + + const toDefaultBucket = (b: Bucket & { Name: string; BucketRegion: string }) => + toBucket(b.Name, b.BucketRegion, this.partitionId) + const buckets = await this.listValidBuckets(paginateBuckets).flatten().map(toDefaultBucket).promise() + const response = { buckets } + getLogger().debug('ListBuckets returned response: %O', response) + return response + } + + private async listObjectsV2(request: ListFilesRequest): Promise { + return await this.makeRequest(ListObjectsV2Command, { + Bucket: request.bucketName, + Delimiter: DEFAULT_DELIMITER, + MaxKeys: request.maxResults ?? DEFAULT_MAX_KEYS, + /** + * Set '' as the default prefix to ensure that the bucket's content will be displayed + * when the user has at least list access to the root of the bucket + * https://github.com/aws/aws-toolkit-vscode/issues/4643 + * @default '' + */ + Prefix: request.folderPath ?? defaultPrefix, + ContinuationToken: request.continuationToken, + }) + } + + private extractFilesFromResponse( + listObjectsRsp: ListObjectsV2Output, + bucketName: string, + folderPath: string | undefined + ): File[] { + const bucket = toBucket(bucketName, this.regionCode, this.partitionId) + return _(listObjectsRsp.Contents) + .reject((file) => file.Key === folderPath) + .map((file) => { + assertHasProps(file, 'Key') + return toFile(bucket, file) + }) + .value() + } + + private extractFoldersFromResponse(listObjectsRsp: ListObjectsV2Output, bucketName: string): Folder[] { + return _(listObjectsRsp.CommonPrefixes) + .map((prefix) => prefix.Prefix) + .compact() + .map((path) => new DefaultFolder({ path, partitionId: this.partitionId, bucketName })) + .value() + } + + public listFilesFromResponse( + listObjectsRsp: ListObjectsV2Output, + bucketName: string, + folderPath: string | undefined + ) { + const files = this.extractFilesFromResponse(listObjectsRsp, bucketName, folderPath) + const folders = this.extractFoldersFromResponse(listObjectsRsp, bucketName) + return { + files, + folders, + continuationToken: listObjectsRsp.NextContinuationToken, + } + } + + /** + * Lists files and folders in a folder or inside the bucket root. + * + * The bucket should reside in the same region as the one configured for the client. + * + * Returns the first {@link ListFilesRequest#maxResults} objects (the first "page"). + * If there are more results, returns a continuation token that can be passed in a subsequent call + * to get the next "page" of results. + * + * Note that folders don't actually exist in S3. + * Everything in S3 is an object with a key residing in a bucket. + * However, S3 lets you limit results to those residing at a specific "path" specified by delimiters (slashes). + * The list of sub-paths is returned in the result set and can be used in subsequent calls. + * + * A consequence of the fact that folders don't exist is that folders and files are intermingled across all + * of the pages. + * It's not possible to retrieve an exhaustive list of all folders without traversing all of the pages. + * + * @throws Error if there is an error calling S3. + */ + public async listFiles(request: ListFilesRequest): Promise { + getLogger().debug('ListFiles called with request: %O', request) + const output = await this.listObjectsV2(request) + const response = this.listFilesFromResponse(output, request.bucketName, request.folderPath) + + getLogger().debug('ListFiles returned response: %O', response) + return response + } + + /** + * Lists versions of all objects inside a bucket. + * + * The bucket should reside in the same region as the one configured for the client. + * + * Returns the first {@link ListObjectVersionsRequest#maxResults} versions (the first "page"). + * If there are more results, returns a continuation token that can be passed in a subsequent call + * to get the next "page" of results. + * + * @throws Error if there is an error calling S3. + */ + public async listObjectVersions(request: ListObjectVersionsRequest): Promise { + getLogger().debug('ListObjectVersions called with request: %O', request) + + const output: ListObjectVersionsOutput = await this.makeRequest(ListObjectVersionsCommand, { + Bucket: request.bucketName, + MaxKeys: request.maxResults ?? DEFAULT_MAX_KEYS, + KeyMarker: request.continuationToken?.keyMarker, + VersionIdMarker: request.continuationToken?.versionIdMarker, + }) + const response = this.processListObjectVersionsResponse(output) + getLogger().debug('ListObjectVersions returned response: %O', response) + return response + } + + public processListObjectVersionsResponse(output: ListObjectVersionsOutput) { + return { + objects: (output.Versions ?? []).map((version) => ({ + key: version.Key!, + versionId: version.VersionId, + })), + continuationToken: output.IsTruncated + ? { keyMarker: output.NextKeyMarker!, versionIdMarker: output.NextVersionIdMarker } + : undefined, + } + } + + /** + * Returns an async iterable over all pages of {@link listObjectVersions}. + * + * @throws Error from the iterable if there is an error calling S3. + */ + public async *listObjectVersionsIterable( + request: ListObjectVersionsRequest, + listObjectVersions: ( + request: ListObjectVersionsRequest + ) => Promise = this.listObjectVersions.bind(this) + ): AsyncIterableIterator { + let continuationToken: ContinuationToken | undefined = request.continuationToken + do { + const listObjectVersionsResponse: ListObjectVersionsResponse = await listObjectVersions({ + bucketName: request.bucketName, + maxResults: request.maxResults, + continuationToken, + }) + continuationToken = listObjectVersionsResponse.continuationToken + + yield listObjectVersionsResponse + } while (continuationToken) + } + + /** + * Deletes an object from a bucket. + * + * The bucket should reside in the same region as the one configured for the client. + * + * @throws Error if there is an error calling S3. + */ + public async deleteObject(request: DeleteObjectRequest): Promise { + getLogger().debug('DeleteObject called with request: %O', request) + await this.makeRequest(DeleteObjectCommand, { Bucket: request.bucketName, Key: request.key }) + getLogger().debug('DeleteObject succeeded') + } + + /** + * Deletes objects from a bucket. + * + * The bucket should reside in the same region as the one configured for the client. + * + * Returns a list of Errors that occurred if the delete was only partially completed. + * + * @throws Error if there is an error calling S3, beyond the partial errors mentioned above. + */ + public async deleteObjects(request: DeleteObjectsRequest): Promise { + getLogger().debug('DeleteObjects called with request: %O', request) + + const output: DeleteObjectsOutput = await this.makeRequest(DeleteObjectsCommand, { + Bucket: request.bucketName, + Delete: { + Objects: request.objects.map(({ key: Key, versionId: VersionId }) => ({ Key, VersionId })), + Quiet: true, + }, + }) + + const response: DeleteObjectsResponse = { errors: output.Errors ?? [] } + getLogger().debug('DeleteObjects returned response: %O', response) + return response + } + + /** + * Empties a bucket by repeatedly listing and deleting all versions of all objects inside. + * + * Note that this just repeatedly calls list object versions and delete objects to empty the bucket. + * Failures can leave the bucket in a state where only some objects are deleted. + * + * @throws Error if there is an error listing or deleting. + */ + private async emptyBucket(bucketName: string): Promise { + for await (const { objects } of this.listObjectVersionsIterable({ bucketName })) { + if (_(objects).isEmpty()) { + continue + } + + const deleteObjectsResponse = await this.deleteObjects({ bucketName, objects }) + if (!_(deleteObjectsResponse.errors).isEmpty()) { + const e = new Error(inspect(deleteObjectsResponse.errors[0])) + getLogger().error('Failed to delete %d objects: %O...', deleteObjectsResponse.errors.length, e) + throw e + } + } + } + + /** + * Gets an object's body from a bucket. + * + * @throws Error if there is an error calling S3. + */ + public async getObject(request: GetObjectRequest): Promise { + getLogger().debug('GetObject called with request: %O', request) + const output: GetObjectOutput = await this.makeRequest(GetObjectCommand, { + Bucket: request.bucketName, + Key: request.key, + }) + + const response: GetObjectResponse = { objectBody: output.Body! } + getLogger().debug('GetObject returned response: %O', response) + return response + } +} + +/** + * @deprecated This should be refactored the same way as {@link toFile} + */ +export class DefaultFolder { + public readonly name: string + public readonly path: string + public readonly arn: string + + public constructor({ partitionId, bucketName, path }: { partitionId: string; bucketName: string; path: string }) { + this.path = path + this.arn = buildArn({ partitionId, bucketName, key: path }) + this.name = _(this.path).split(DEFAULT_DELIMITER).dropRight()!.last()! + } + + public [inspect.custom](): string { + return `Folder (name=${this.name}, path=${this.path}, arn=${this.arn})` + } +} + +export interface File extends _Object, HeadObjectOutput { + readonly name: string + readonly key: string + readonly arn: string + readonly lastModified?: Date + readonly sizeBytes?: number + readonly eTag?: string +} + +export function toFile(bucket: S3Bucket, resp: RequiredProps<_Object, 'Key'>, delimiter = DEFAULT_DELIMITER): File { + return { + key: resp.Key, + arn: `${bucket.Arn}/${resp.Key}`, + name: resp.Key.split(delimiter).pop()!, + eTag: resp.ETag, + lastModified: resp.LastModified, + sizeBytes: resp.Size, + ...resp, + } +} + +export function toBucket(bucketName: string, region: string, partitionId: string): S3Bucket { + return { + Name: bucketName, + BucketRegion: region, + Arn: buildArn({ partitionId, bucketName }), + } +} + +function buildArn({ partitionId, bucketName, key }: { partitionId: string; bucketName: string; key?: string }) { + if (key === undefined) { + return `arn:${partitionId}:s3:::${bucketName}` + } + + return `arn:${partitionId}:s3:::${bucketName}/${key}` +} + +/** + * A URI parser that can parse out information about an S3 URI + * Adapted from + * @see https://github.com/frantz/amazon-s3-uri/ + */ +export function parseS3Uri(uri: string): [region: string, bucket: string, key: string] { + const endpointPattern = /^(.+\.)?s3[.-]([a-z0-9-]+)\./ + const defaultRegion = 'us-east-1' // Default region for URI parsing, if region is not found + const parsedUri = url.parse(uri) + let bucket: string | undefined = undefined + let region: string = defaultRegion + let key: string | undefined = undefined + + if (parsedUri.protocol === 's3:') { + bucket = parsedUri.host ?? undefined + if (!bucket) { + throw new Error(`Invalid S3 URI: no bucket: ${uri}`) + } + if (!parsedUri.pathname || parsedUri.pathname.length <= 1) { + // s3://bucket or s3://bucket/ + key = undefined + } else { + // s3://bucket/key + // Remove the leading '/'. + key = parsedUri.pathname.substring(1) + } + if (key !== undefined) { + key = decodeURIComponent(key) + } + return [region, bucket, key!] + } + + if (!parsedUri.host) { + throw new Error(`Invalid S3 URI: no hostname: ${uri}`) + } + + const matches = parsedUri.host.match(endpointPattern) + if (!matches) { + throw new Error(`Invalid S3 URI: hostname does not appear to be a valid S3 endpoint: ${uri}`) + } + + const prefix = matches[1] + if (!prefix) { + if (parsedUri.pathname === '/') { + bucket = undefined + key = undefined + } else { + const index = parsedUri.pathname!.indexOf('/', 1) + if (index === -1) { + // https://s3.amazonaws.com/bucket + bucket = parsedUri.pathname!.substring(1) ?? undefined + key = undefined + } else if (index === parsedUri.pathname!.length - 1) { + // https://s3.amazonaws.com/bucket/ + bucket = parsedUri.pathname!.substring(1, index) + key = undefined + } else { + // https://s3.amazonaws.com/bucket/key + bucket = parsedUri.pathname!.substring(1, index) + key = parsedUri.pathname!.substring(index + 1) + } + } + } else { + // Remove the trailing '.' from the prefix to get the bucket. + bucket = prefix.substring(0, prefix.length - 1) + + if (!parsedUri.pathname || parsedUri.pathname === '/') { + key = undefined + } else { + // Remove the leading '/'. + key = parsedUri.pathname.substring(1) + } + } + + if (matches[2] !== 'amazonaws') { + region = matches[2] + } else { + region = defaultRegion + } + + if (key !== undefined) { + key = decodeURIComponent(key) + } + return [region, bucket!, key!] +} diff --git a/packages/core/src/shared/clients/sagemaker.ts b/packages/core/src/shared/clients/sagemaker.ts new file mode 100644 index 00000000000..fda420effef --- /dev/null +++ b/packages/core/src/shared/clients/sagemaker.ts @@ -0,0 +1,380 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { + AppDetails, + AppType, + CreateAppCommand, + CreateAppCommandInput, + CreateAppCommandOutput, + DeleteAppCommand, + DeleteAppCommandInput, + DeleteAppCommandOutput, + DescribeAppCommand, + DescribeAppCommandInput, + DescribeAppCommandOutput, + DescribeDomainCommand, + DescribeDomainCommandInput, + DescribeDomainCommandOutput, + DescribeDomainResponse, + DescribeSpaceCommand, + DescribeSpaceCommandInput, + DescribeSpaceCommandOutput, + ListAppsCommandInput, + ListSpacesCommandInput, + ResourceSpec, + SageMakerClient, + SpaceDetails, + UpdateSpaceCommand, + UpdateSpaceCommandInput, + UpdateSpaceCommandOutput, + paginateListApps, + paginateListSpaces, +} from '@amzn/sagemaker-client' +import { isEmpty } from 'lodash' +import { sleep } from '../utilities/timeoutUtils' +import { ClientWrapper } from './clientWrapper' +import { AsyncCollection } from '../utilities/asyncCollection' +import { + InstanceTypeError, + InstanceTypeMinimum, + InstanceTypeInsufficientMemory, + InstanceTypeInsufficientMemoryMessage, + InstanceTypeNotSelectedMessage, +} from '../../awsService/sagemaker/constants' +import { getDomainSpaceKey } from '../../awsService/sagemaker/utils' +import { getLogger } from '../logger/logger' +import { ToolkitError } from '../errors' +import { yes, no, continueText, cancel } from '../localizedText' +import { AwsCredentialIdentity } from '@aws-sdk/types' +import globals from '../extensionGlobals' + +const appTypeSettingsMap: Record = { + [AppType.JupyterLab as string]: 'JupyterLabAppSettings', + [AppType.CodeEditor as string]: 'CodeEditorAppSettings', +} as const + +export interface SagemakerSpaceApp extends SpaceDetails { + App?: AppDetails + DomainSpaceKey: string +} + +export class SagemakerClient extends ClientWrapper { + public constructor( + public override readonly regionCode: string, + private readonly credentialsProvider?: () => Promise + ) { + super(regionCode, SageMakerClient) + } + + protected override getClient(ignoreCache: boolean = false) { + if (!this.client || ignoreCache) { + const args = { + serviceClient: SageMakerClient, + region: this.regionCode, + clientOptions: { + endpoint: `https://sagemaker.${this.regionCode}.amazonaws.com`, + region: this.regionCode, + ...(this.credentialsProvider && { credentials: this.credentialsProvider }), + }, + } + this.client = globals.sdkClientBuilderV3.createAwsService(args) + } + return this.client + } + + public override dispose() { + getLogger().debug('SagemakerClient: Disposing client %O', this.client) + this.client?.destroy() + this.client = undefined + } + + 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) + } + + public describeApp(request: DescribeAppCommandInput): Promise { + return this.makeRequest(DescribeAppCommand, request) + } + + 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: DescribeSpaceCommandOutput + + // Get existing space details + try { + spaceDetails = await this.describeSpace({ + DomainId: domainId, + SpaceName: spaceName, + }) + } catch (err) { + throw this.handleStartSpaceError(err) + } + + // Get app type + const appType = spaceDetails.SpaceSettings?.AppType + if (!appType || !(appType in appTypeSettingsMap)) { + throw new ToolkitError(`Unsupported AppType "${appType}" for space "${spaceName}"`) + } + + // Get app resource spec + const requestedResourceSpec = + appType === AppType.JupyterLab + ? spaceDetails.SpaceSettings?.JupyterLabAppSettings?.DefaultResourceSpec + : spaceDetails.SpaceSettings?.CodeEditorAppSettings?.DefaultResourceSpec + + let instanceType = requestedResourceSpec?.InstanceType + + // Is InstanceType defined and has enough memory? + if (instanceType && instanceType in InstanceTypeInsufficientMemory) { + // Prompt user to select one with sufficient memory (1 level up from their chosen one) + const response = await vscode.window.showErrorMessage( + InstanceTypeInsufficientMemoryMessage( + spaceDetails.SpaceName || '', + instanceType, + InstanceTypeInsufficientMemory[instanceType] + ), + yes, + no + ) + + if (response === no) { + throw new ToolkitError('InstanceType has insufficient memory.', { code: InstanceTypeError }) + } + + instanceType = InstanceTypeInsufficientMemory[instanceType] + } else if (!instanceType) { + // Prompt user to select the minimum supported instance type + const response = await vscode.window.showErrorMessage( + InstanceTypeNotSelectedMessage(spaceDetails.SpaceName || ''), + continueText, + cancel + ) + + if (response === cancel) { + throw new ToolkitError('InstanceType not defined.', { code: InstanceTypeError }) + } + + instanceType = InstanceTypeMinimum + } + + // First, update the space if needed + const needsRemoteAccess = + !spaceDetails.SpaceSettings?.RemoteAccess || spaceDetails.SpaceSettings?.RemoteAccess === 'DISABLED' + const instanceTypeChanged = requestedResourceSpec?.InstanceType !== instanceType + + if (needsRemoteAccess || instanceTypeChanged) { + const updateSpaceRequest: UpdateSpaceCommandInput = { + DomainId: domainId, + SpaceName: spaceName, + SpaceSettings: { + ...(needsRemoteAccess && { RemoteAccess: 'ENABLED' }), + ...(instanceTypeChanged && { + [appTypeSettingsMap[appType]]: { + DefaultResourceSpec: { + InstanceType: instanceType, + }, + }, + }), + }, + } + + try { + getLogger().debug('SagemakerClient: Updating space: domainId=%s, spaceName=%s', domainId, spaceName) + await this.updateSpace(updateSpaceRequest) + await this.waitForSpaceInService(spaceName, domainId) + } catch (err) { + throw this.handleStartSpaceError(err) + } + } + + const resourceSpec: ResourceSpec = { + // Default values + SageMakerImageArn: 'arn:aws:sagemaker:us-west-2:542918446943:image/sagemaker-distribution-cpu', + SageMakerImageVersionAlias: '3.2.0', + + // The existing resource spec + ...requestedResourceSpec, + + // The instance type user has chosen + InstanceType: instanceType, + } + + const cleanedResourceSpec = + resourceSpec && 'EnvironmentArn' in resourceSpec + ? { ...resourceSpec, EnvironmentArn: undefined, EnvironmentVersionArn: undefined } + : resourceSpec + + // Second, create the App + const createAppRequest: CreateAppCommandInput = { + DomainId: domainId, + SpaceName: spaceName, + AppType: appType, + AppName: 'default', + ResourceSpec: cleanedResourceSpec, + } + + try { + getLogger().debug('SagemakerClient: Creating app: domainId=%s, spaceName=%s', domainId, spaceName) + await this.createApp(createAppRequest) + } catch (err) { + throw this.handleStartSpaceError(err) + } + } + + public async listSpaceApps(domainId?: string): Promise> { + // Create options object conditionally if domainId is provided + const options = domainId ? { DomainIdEquals: domainId } : undefined + + const appMap: Map = await this.listApps(options) + .flatten() + .filter((app) => !!app.DomainId && !!app.SpaceName) + .filter((app) => app.AppType === AppType.JupyterLab || app.AppType === AppType.CodeEditor) + .toMap((app) => getDomainSpaceKey(app.DomainId || '', app.SpaceName || '')) + + const spaceApps: Map = await this.listSpaces(options) + .flatten() + .filter((space) => !!space.DomainId && !!space.SpaceName) + .map((space) => { + const key = getDomainSpaceKey(space.DomainId || '', space.SpaceName || '') + return { ...space, App: appMap.get(key), DomainSpaceKey: key } + }) + .toMap((space) => getDomainSpaceKey(space.DomainId || '', space.SpaceName || '')) + return spaceApps + } + + public async fetchSpaceAppsAndDomains( + domainId?: string, + filterSmusDomains: boolean = true + ): Promise<[Map, Map]> { + try { + const spaceApps = await this.listSpaceApps(domainId) + // Get de-duped list of domain IDs for all of the spaces + const domainIds: string[] = domainId + ? [domainId] + : [...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 only if filterSmusDomains is true + .filter( + ([_, spaceApp]) => + !filterSmusDomains || + isEmpty(domainsMap.get(spaceApp.DomainId || '')?.DomainSettings?.UnifiedStudioSettings) + ) + ) + + return [filteredSpaceApps, domainsMap] + } 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/clients/schemaClient.ts b/packages/core/src/shared/clients/schemaClient.ts new file mode 100644 index 00000000000..238b0d46810 --- /dev/null +++ b/packages/core/src/shared/clients/schemaClient.ts @@ -0,0 +1,191 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + DescribeCodeBindingCommand, + DescribeCodeBindingResponse, + DescribeSchemaCommand, + DescribeSchemaResponse, + GetCodeBindingSourceCommand, + GetCodeBindingSourceResponse, + ListRegistriesCommand, + ListRegistriesRequest, + ListRegistriesResponse, + ListSchemasCommand, + ListSchemasRequest, + ListSchemasResponse, + ListSchemaVersionsCommand, + ListSchemaVersionsRequest, + ListSchemaVersionsResponse, + PutCodeBindingCommand, + PutCodeBindingResponse, + RegistrySummary, + SchemasClient, + SchemaSummary, + SchemaVersionSummary, + SearchSchemasCommand, + SearchSchemasRequest, + SearchSchemasResponse, + SearchSchemaSummary, +} from '@aws-sdk/client-schemas' +import globals from '../extensionGlobals' + +import { ClassToInterfaceType } from '../utilities/tsUtils' + +export type SchemaClient = ClassToInterfaceType +export class DefaultSchemaClient { + public constructor(public readonly regionCode: string) {} + + public async *listRegistries(): AsyncIterableIterator { + const client = this.createSdkClient() + + const request: ListRegistriesRequest = {} + + do { + const response: ListRegistriesResponse = await client.send(new ListRegistriesCommand(request)) + + if (response.Registries) { + yield* response.Registries + } + + request.NextToken = response.NextToken + } while (request.NextToken) + } + + public async *listSchemas(registryName: string): AsyncIterableIterator { + const client = this.createSdkClient() + + const request: ListSchemasRequest = { + RegistryName: registryName, + } + + do { + const response: ListSchemasResponse = await client.send(new ListSchemasCommand(request)) + + if (response.Schemas) { + yield* response.Schemas + } + + request.NextToken = response.NextToken + } while (request.NextToken) + } + + public async describeSchema( + registryName: string, + schemaName: string, + schemaVersion?: string + ): Promise { + const client = this.createSdkClient() + + return await client.send( + new DescribeSchemaCommand({ + RegistryName: registryName, + SchemaName: schemaName, + SchemaVersion: schemaVersion, + }) + ) + } + + public async *listSchemaVersions( + registryName: string, + schemaName: string + ): AsyncIterableIterator { + const client = this.createSdkClient() + + const request: ListSchemaVersionsRequest = { + RegistryName: registryName, + SchemaName: schemaName, + } + + do { + const response: ListSchemaVersionsResponse = await client.send(new ListSchemaVersionsCommand(request)) + + if (response.SchemaVersions) { + yield* response.SchemaVersions + } + + request.NextToken = response.NextToken + } while (request.NextToken) + } + + public async *searchSchemas(keywords: string, registryName: string): AsyncIterableIterator { + const client = this.createSdkClient() + + const request: SearchSchemasRequest = { + Keywords: keywords, + RegistryName: registryName, + } + + do { + const response: SearchSchemasResponse = await client.send(new SearchSchemasCommand(request)) + + if (response.Schemas) { + yield* response.Schemas + } + + request.NextToken = response.NextToken + } while (request.NextToken) + } + + public async getCodeBindingSource( + language: string, + registryName: string, + schemaName: string, + schemaVersion: string + ): Promise { + const client = this.createSdkClient() + + return await client.send( + new GetCodeBindingSourceCommand({ + Language: language, + RegistryName: registryName, + SchemaName: schemaName, + SchemaVersion: schemaVersion, + }) + ) + } + + public async putCodeBinding( + language: string, + registryName: string, + schemaName: string, + schemaVersion: string + ): Promise { + const client = this.createSdkClient() + + return await client.send( + new PutCodeBindingCommand({ + Language: language, + RegistryName: registryName, + SchemaName: schemaName, + SchemaVersion: schemaVersion, + }) + ) + } + public async describeCodeBinding( + language: string, + registryName: string, + schemaName: string, + schemaVersion: string + ): Promise { + const client = this.createSdkClient() + + return await client.send( + new DescribeCodeBindingCommand({ + Language: language, + RegistryName: registryName, + SchemaName: schemaName, + SchemaVersion: schemaVersion, + }) + ) + } + + private createSdkClient(): SchemasClient { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: SchemasClient, + clientOptions: { region: this.regionCode }, + }) + } +} diff --git a/packages/core/src/shared/clients/secretsManagerClient.ts b/packages/core/src/shared/clients/secretsManagerClient.ts new file mode 100644 index 00000000000..af9af46d55d --- /dev/null +++ b/packages/core/src/shared/clients/secretsManagerClient.ts @@ -0,0 +1,73 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + CreateSecretCommand, + CreateSecretRequest, + CreateSecretResponse, + ListSecretsCommand, + ListSecretsRequest, + ListSecretsResponse, + SecretsManagerClient as SecretsManagerSdkClient, +} from '@aws-sdk/client-secrets-manager' +import globals from '../extensionGlobals' +import { productName } from '../constants' + +export class SecretsManagerClient { + public constructor( + public readonly regionCode: string, + private readonly secretsManagerClientProvider: ( + regionCode: string + ) => SecretsManagerSdkClient = createSecretsManagerClient + ) {} + + /** + * Lists the secrets that are stored by Secrets Manager + * @param filter tagged key filter value + * @returns a list of the secrets + */ + public async listSecrets(filter: string): Promise { + const secretsManagerClient = this.secretsManagerClientProvider(this.regionCode) + const request: ListSecretsRequest = { + IncludePlannedDeletion: false, + Filters: [ + { + Key: 'tag-key', + Values: [filter], + }, + ], + SortOrder: 'desc', + } + return secretsManagerClient.send(new ListSecretsCommand(request)) + } + + public async createSecret(secretString: string, username: string, password: string): Promise { + const secretsManagerClient = this.secretsManagerClientProvider(this.regionCode) + const request: CreateSecretRequest = { + Description: `Database secret created with ${productName}`, + Name: secretString ? secretString : '', + SecretString: JSON.stringify({ username, password }), + Tags: [ + { + Key: 'Service', + Value: 'Redshift', + }, + { + Key: 'Request-Source', + Value: productName, + }, + ], + ForceOverwriteReplicaSecret: true, + } + return secretsManagerClient.send(new CreateSecretCommand(request)) + } +} + +function createSecretsManagerClient(regionCode: string): SecretsManagerSdkClient { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: SecretsManagerSdkClient, + clientOptions: { region: regionCode }, + }) +} diff --git a/packages/core/src/shared/clients/ssm.ts b/packages/core/src/shared/clients/ssm.ts new file mode 100644 index 00000000000..3565e9c7a6d --- /dev/null +++ b/packages/core/src/shared/clients/ssm.ts @@ -0,0 +1,113 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + SSMClient, + Session, + StartSessionCommand, + TerminateSessionCommand, + TerminateSessionResponse, + StartSessionCommandOutput, + DescribeInstanceInformationCommandInput, + InstanceInformation, + SendCommandCommand, + SendCommandCommandOutput, + waitUntilCommandExecuted, + SessionState, + paginateDescribeInstanceInformation, + paginateDescribeSessions, +} from '@aws-sdk/client-ssm' +import { WaiterState } from '@smithy/util-waiter' +import { ToolkitError } from '../errors' +import { ClientWrapper } from './clientWrapper' + +export class SsmClient extends ClientWrapper { + public constructor(public override readonly regionCode: string) { + super(regionCode, SSMClient) + } + + public async terminateSession(session: Session): Promise { + const sessionId = session.SessionId! + return await this.terminateSessionFromId(sessionId) + } + + public async terminateSessionFromId(sessionId: string): Promise { + return await this.makeRequest(TerminateSessionCommand, { SessionId: sessionId }) + } + + public async startSession( + target: string, + document?: string, + reason?: string, + parameters?: Record + ): Promise { + return await this.makeRequest(StartSessionCommand, { + Target: target, + DocumentName: document, + Reason: reason, + Parameters: parameters, + }) + } + + public async describeInstance(target: string): Promise { + return await this.getFirst( + paginateDescribeInstanceInformation, + { + InstanceInformationFilterList: [{ key: 'InstanceIds', valueSet: [target] }], + } satisfies DescribeInstanceInformationCommandInput, + (page) => page.InstanceInformationList + ) + } + + public async getTargetPlatformName(target: string): Promise { + const instanceInformation = await this.describeInstance(target) + return instanceInformation.PlatformName! + } + + public async sendCommand( + target: string, + documentName: string, + parameters: Record + ): Promise { + return await this.makeRequest(SendCommandCommand, { + InstanceIds: [target], + DocumentName: documentName, + Parameters: parameters, + }) + } + + private async waitForCommand(commandId: string, target: string) { + const result = await waitUntilCommandExecuted( + { client: this.getClient(), maxWaitTime: 30 }, + { CommandId: commandId, InstanceId: target } + ) + if (result.state !== WaiterState.SUCCESS) { + throw new ToolkitError(`Command ${commandId} failed to execute on target ${target}`) + } + } + + public async sendCommandAndWait( + target: string, + documentName: string, + parameters: Record + ): Promise { + const response = await this.sendCommand(target, documentName, parameters) + try { + await this.waitForCommand(response.Command!.CommandId!, target) + return response + } catch (err) { + throw new ToolkitError(`Failed in sending command to target ${target}`, { cause: err as Error }) + } + } + + public async getInstanceAgentPingStatus(target: string): Promise { + const instanceInformation = await this.describeInstance(target) + return instanceInformation ? instanceInformation.PingStatus! : 'Inactive' + } + + public async describeSessions(state: SessionState) { + return this.makePaginatedRequest(paginateDescribeSessions, { State: state }, (page) => page.Sessions) + } +} diff --git a/packages/core/src/shared/clients/ssmDocumentClient.ts b/packages/core/src/shared/clients/ssmDocumentClient.ts new file mode 100644 index 00000000000..581cb0bc219 --- /dev/null +++ b/packages/core/src/shared/clients/ssmDocumentClient.ts @@ -0,0 +1,145 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + CreateDocumentCommand, + CreateDocumentRequest, + CreateDocumentResult, + DeleteDocumentCommand, + DeleteDocumentRequest, + DeleteDocumentResult, + DescribeDocumentCommand, + DescribeDocumentRequest, + DescribeDocumentResult, + DocumentFormat, + DocumentIdentifier, + DocumentVersionInfo, + GetDocumentCommand, + GetDocumentRequest, + GetDocumentResult, + ListDocumentsCommand, + ListDocumentsRequest, + ListDocumentsResult, + ListDocumentVersionsCommand, + ListDocumentVersionsRequest, + ListDocumentVersionsResult, + SSMClient, + UpdateDocumentCommand, + UpdateDocumentDefaultVersionCommand, + UpdateDocumentDefaultVersionRequest, + UpdateDocumentDefaultVersionResult, + UpdateDocumentRequest, + UpdateDocumentResult, +} from '@aws-sdk/client-ssm' +import globals from '../extensionGlobals' + +import { ClassToInterfaceType } from '../utilities/tsUtils' + +export type SsmDocumentClient = ClassToInterfaceType +export class DefaultSsmDocumentClient { + public constructor(public readonly regionCode: string) {} + + public async deleteDocument(documentName: string): Promise { + const client = this.createSdkClient() + + const request: DeleteDocumentRequest = { + Name: documentName, + } + + return await client.send(new DeleteDocumentCommand(request)) + } + + public async *listDocuments(request: ListDocumentsRequest = {}): AsyncIterableIterator { + const client = this.createSdkClient() + + do { + const response: ListDocumentsResult = await client.send(new ListDocumentsCommand(request)) + + if (response.DocumentIdentifiers) { + yield* response.DocumentIdentifiers + } + + request.NextToken = response.NextToken + } while (request.NextToken) + } + + public async *listDocumentVersions(documentName: string): AsyncIterableIterator { + const client = this.createSdkClient() + + const request: ListDocumentVersionsRequest = { + Name: documentName, + } + + do { + const response: ListDocumentVersionsResult = await client.send(new ListDocumentVersionsCommand(request)) + + if (response.DocumentVersions) { + yield* response.DocumentVersions + } + + request.NextToken = response.NextToken + } while (request.NextToken) + } + + public async describeDocument(documentName: string, documentVersion?: string): Promise { + const client = this.createSdkClient() + + const request: DescribeDocumentRequest = { + Name: documentName, + DocumentVersion: documentVersion, + } + + return await client.send(new DescribeDocumentCommand(request)) + } + + public async getDocument( + documentName: string, + documentVersion?: string, + documentFormat?: DocumentFormat + ): Promise { + const client = this.createSdkClient() + + const request: GetDocumentRequest = { + Name: documentName, + DocumentVersion: documentVersion, + DocumentFormat: documentFormat, + } + + return await client.send(new GetDocumentCommand(request)) + } + + public async createDocument(request: CreateDocumentRequest): Promise { + const client = this.createSdkClient() + + return await client.send(new CreateDocumentCommand(request)) + } + + public async updateDocument(request: UpdateDocumentRequest): Promise { + const client = this.createSdkClient() + + return await client.send(new UpdateDocumentCommand(request)) + } + + public async updateDocumentVersion( + documentName: string, + documentVersion: string + ): Promise { + const client = this.createSdkClient() + + const request: UpdateDocumentDefaultVersionRequest = { + Name: documentName, + DocumentVersion: documentVersion, + } + + return await client.send(new UpdateDocumentDefaultVersionCommand(request)) + } + + private createSdkClient(): SSMClient { + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: SSMClient, + clientOptions: { region: this.regionCode }, + }) + } +} diff --git a/packages/core/src/shared/clients/stepFunctions.ts b/packages/core/src/shared/clients/stepFunctions.ts new file mode 100644 index 00000000000..22483307654 --- /dev/null +++ b/packages/core/src/shared/clients/stepFunctions.ts @@ -0,0 +1,68 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + CreateStateMachineCommand, + CreateStateMachineCommandInput, + CreateStateMachineCommandOutput, + DescribeStateMachineCommand, + DescribeStateMachineCommandInput, + DescribeStateMachineCommandOutput, + ListStateMachinesCommand, + ListStateMachinesCommandInput, + ListStateMachinesCommandOutput, + SFNClient, + StartExecutionCommand, + StartExecutionCommandInput, + StartExecutionCommandOutput, + StateMachineListItem, + TestStateCommand, + TestStateCommandInput, + TestStateCommandOutput, + UpdateStateMachineCommand, + UpdateStateMachineCommandInput, + UpdateStateMachineCommandOutput, +} from '@aws-sdk/client-sfn' +import { ClientWrapper } from './clientWrapper' + +export class StepFunctionsClient extends ClientWrapper { + public constructor(regionCode: string) { + super(regionCode, SFNClient) + } + + public async *listStateMachines( + request: ListStateMachinesCommandInput = {} + ): AsyncIterableIterator { + do { + const response: ListStateMachinesCommandOutput = await this.makeRequest(ListStateMachinesCommand, request) + if (response.stateMachines) { + yield* response.stateMachines + } + request.nextToken = response.nextToken + } while (request.nextToken) + } + + public async getStateMachineDetails( + request: DescribeStateMachineCommandInput + ): Promise { + return this.makeRequest(DescribeStateMachineCommand, request) + } + + public async executeStateMachine(request: StartExecutionCommandInput): Promise { + return this.makeRequest(StartExecutionCommand, request) + } + + public async createStateMachine(request: CreateStateMachineCommandInput): Promise { + return this.makeRequest(CreateStateMachineCommand, request) + } + + public async updateStateMachine(request: UpdateStateMachineCommandInput): Promise { + return this.makeRequest(UpdateStateMachineCommand, request) + } + + public async testState(request: TestStateCommandInput): Promise { + return this.makeRequest(TestStateCommand, request) + } +} diff --git a/packages/core/src/shared/clients/stsClient.ts b/packages/core/src/shared/clients/stsClient.ts new file mode 100644 index 00000000000..f3a225882a5 --- /dev/null +++ b/packages/core/src/shared/clients/stsClient.ts @@ -0,0 +1,62 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { STSClient, AssumeRoleCommand, GetCallerIdentityCommand } from '@aws-sdk/client-sts' +import type { AssumeRoleRequest, AssumeRoleResponse, GetCallerIdentityResponse } from '@aws-sdk/client-sts' +import { AwsCredentialIdentityProvider } from '@smithy/types' +import { Credentials } from '@aws-sdk/types' +import globals from '../extensionGlobals' +import { ClassToInterfaceType } from '../utilities/tsUtils' + +export type { GetCallerIdentityResponse } +export type StsClient = ClassToInterfaceType + +// Helper function to convert Credentials to AwsCredentialIdentityProvider +function toCredentialProvider(credentials: Credentials | AwsCredentialIdentityProvider): AwsCredentialIdentityProvider { + if (typeof credentials === 'function') { + return credentials + } + // Convert static credentials to provider function + return async () => credentials +} + +export class DefaultStsClient { + public constructor( + public readonly regionCode: string, + private readonly credentials?: Credentials | AwsCredentialIdentityProvider, + private readonly endpointUrl?: string + ) {} + + public async assumeRole(request: AssumeRoleRequest): Promise { + const sdkClient = this.createSdkClient() + const response = await sdkClient.send(new AssumeRoleCommand(request)) + return response + } + + public async getCallerIdentity(): Promise { + const sdkClient = this.createSdkClient() + const response = await sdkClient.send(new GetCallerIdentityCommand({})) + return response + } + + private createSdkClient(): STSClient { + const clientOptions: { region: string; endpoint?: string; credentials?: AwsCredentialIdentityProvider } = { + region: this.regionCode, + } + + if (this.endpointUrl) { + clientOptions.endpoint = this.endpointUrl + } + + if (this.credentials) { + clientOptions.credentials = toCredentialProvider(this.credentials) + } + + return globals.sdkClientBuilderV3.createAwsService({ + serviceClient: STSClient, + clientOptions, + }) + } +} diff --git a/packages/core/src/shared/cloudformation/activation.ts b/packages/core/src/shared/cloudformation/activation.ts new file mode 100644 index 00000000000..86cbf05698b --- /dev/null +++ b/packages/core/src/shared/cloudformation/activation.ts @@ -0,0 +1,96 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger } from '../logger/logger' +import { isToolkitActive, localize } from '../utilities/vsCodeUtils' + +import { AsyncCloudFormationTemplateRegistry, CloudFormationTemplateRegistry } from '../fs/templateRegistry' +import { getIdeProperties } from '../extensionUtilities' +import { NoopWatcher } from '../fs/watchedFiles' +import { createStarterTemplateFile } from './cloudformation' +import * as CloudFormation from './cloudformation' +import { Commands } from '../vscode/commands2' +import globals from '../extensionGlobals' +import { SamCliSettings } from '../sam/cli/samCliSettings' +import { Timeout } from '../utilities/timeoutUtils' + +/** + * Creates a CloudFormationTemplateRegistry which retains the state of CloudFormation templates in a workspace. + * This also assigns a FileSystemWatcher which will update the registry on any change to tracked templates. + * + * @param extensionContext VS Code extension context + */ +export async function activate(extensionContext: vscode.ExtensionContext): Promise { + try { + const registry = new CloudFormationTemplateRegistry() + extensionContext.subscriptions.push(registry) + setTemplateRegistryInGlobals(registry) + } catch (e) { + void vscode.window.showErrorMessage( + localize( + 'AWS.codelens.failToInitialize', + 'Failed to activate template registry. {0}} will not appear on SAM template files.', + getIdeProperties().codelenses + ) + ) + getLogger().error('Failed to activate template registry: %s', e) + // This prevents us from breaking for any reason later if it fails to load. Since + // Noop watcher is always empty, we will get back empty arrays with no issues. + globals.templateRegistry = (async () => new NoopWatcher() as unknown as CloudFormationTemplateRegistry)() + } + // If setting it up worked, add it to subscriptions so it is cleaned up at exit + extensionContext.subscriptions.push( + Commands.register('aws.cloudFormation.newTemplate', () => createStarterTemplateFile(false)), + Commands.register('aws.sam.newTemplate', () => createStarterTemplateFile(true)) + ) +} + +/** + * Sets the `templateRegistry` property in the `globals` variable, + * where the value of the property depends on whether the registry + * is fully set up. + * + * This function exists to resolve the registry setup taking a long time + * and slowing down the extension starting up. + */ +function setTemplateRegistryInGlobals(registry: CloudFormationTemplateRegistry) { + const registrySetupFunc = async ( + registry: CloudFormationTemplateRegistry, + cancel: Timeout, + onItem?: (total: number, i: number, cancelled: boolean) => void + ) => { + registry.addExcludedPattern(CloudFormation.devfileExcludePattern) + registry.addExcludedPattern(CloudFormation.templateFileExcludePattern) + registry.addWatchPatterns([CloudFormation.templateFileGlobPattern]) + registry.watchUntitledFiles() + await registry.rebuild(cancel, onItem) + return registry + } + + const asyncRegistry = new AsyncCloudFormationTemplateRegistry(registry, registrySetupFunc) + + Object.defineProperty(globals, 'templateRegistry', { + set(newInstance: CloudFormationTemplateRegistry) { + this.cfnInstance = newInstance + }, + async get() { + // This condition handles testing scenarios where we may have + // already set a mock object before activation. + // Though in prod nothing should be calling this 'set' function. + if (this.cfnInstance) { + return this.cfnInstance + } + + // prevent eager load if codelenses are off + const config = SamCliSettings.instance + if (config.get('enableCodeLenses', false) || isToolkitActive()) { + return await asyncRegistry.getInstance() + } + + return new NoopWatcher() + }, + }) +} diff --git a/packages/core/src/shared/cloudformation/cloudformation.ts b/packages/core/src/shared/cloudformation/cloudformation.ts new file mode 100644 index 00000000000..690a5aee73c --- /dev/null +++ b/packages/core/src/shared/cloudformation/cloudformation.ts @@ -0,0 +1,914 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { promises as nodefs } from 'fs' // eslint-disable-line no-restricted-imports +import * as vscode from 'vscode' +import { schema } from 'yaml-cfn' +import * as yaml from 'js-yaml' +import * as filesystemUtilities from '../filesystemUtilities' +import fs from '../../shared/fs/fs' +import { getLogger } from '../logger/logger' +import { lambdaPackageTypeImage } from '../constants' +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' +export const ecrRepositoryType = 'AWS::ECR::Repository' +export const snsTopicType = 'AWS::SNS::Topic' +export const sqsQueueType = 'AWS::SQS::Queue' + +export const templateFileGlobPattern = '**/*.{yaml,yml,json,template}' +export const templateFileRegexPattern = /.*\.(yaml|yml|json|template)$/i +export const devfileExcludePattern = /.*devfile\.(yaml|yml)/i +/** + * Match any file path that contains a .aws-sam folder. The way this works is: + * match anything that starts with a '/' or '\', then '.aws-sam', then either + * a '/' or '\' followed by any number of characters or end of a string (so it + * matches both /.aws-sam or /.aws-sam/) + */ +export const templateFileExcludePattern = /.*[/\\]\.aws-sam([/\\].*|$)/ + +export function isZipLambdaResource( + resource?: ZipResourceProperties | ImageResourceProperties +): resource is ZipResourceProperties { + return resource?.PackageType !== 'Image' +} + +export function isImageLambdaResource( + resource?: ZipResourceProperties | ImageResourceProperties +): resource is ImageResourceProperties { + return resource?.PackageType === 'Image' +} + +export function validateZipLambdaProperties({ + Handler, + CodeUri, + Runtime, + ...rest +}: Partial): ZipResourceProperties { + if (!Handler) { + throw new Error('Missing value: Handler') + } + + if (!CodeUri) { + throw new Error('Missing value: CodeUri') + } + + if (!Runtime) { + throw new Error('Missing value: Runtime') + } + + return { + Handler, + CodeUri, + Runtime, + ...rest, + } +} + +export interface LambdaResourceProperties { + MemorySize?: number | Ref + Timeout?: number | Ref + Environment?: Environment + Events?: Events + PackageType?: 'Image' | 'Zip' + Architectures?: ('x86_64' | 'arm64')[] + [key: string]: any +} + +export interface ZipResourceProperties extends LambdaResourceProperties { + Handler: string | Ref + CodeUri: string | Ref + Runtime?: string | Ref + PackageType?: 'Zip' +} + +export interface ImageResourceProperties extends LambdaResourceProperties { + PackageType: 'Image' + ImageConfig?: ImageConfig +} + +export interface ImageConfig { + EntryPoint?: string[] + Command?: string[] + WorkingDirectory?: string +} + +export interface Ref { + Ref: string +} + +export interface Environment { + Variables?: Variables +} + +export interface ApiEventProperties { + Path?: string + Method?: 'delete' | 'get' | 'head' | 'options' | 'patch' | 'post' | 'put' | 'any' + Payload?: { + json?: { + [k: string]: string | number | boolean + } + } +} + +export interface Event { + Type?: 'Api' | 'HttpApi' + Properties?: ApiEventProperties +} + +export interface Events { + [key: string]: Event +} + +export interface Variables { + [key: string]: any +} + +export type ResourceType = + | typeof LAMBDA_FUNCTION_TYPE + | typeof SERVERLESS_FUNCTION_TYPE + | typeof SERVERLESS_API_TYPE + | string + +export interface Resource { + Type: ResourceType + Properties?: ZipResourceProperties | ImageResourceProperties + Metadata?: SamImageMetadata + // Any other properties are fine to have, we just copy them transparently + [key: string]: any +} + +export interface SamImageMetadata { + Dockerfile: string + DockerContext: string + // we only care about the two above, but really anything can go here + [key: string]: any +} + +// TODO: Can we automatically detect changes to the CFN spec and apply them here? +// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#parameters-section-structure-properties +export type ParameterType = + | 'String' + | 'Number' + | 'List' + | 'CommaDelimitedList' + | AWSSpecificParameterType + | SSMParameterType + +type ThingType = 'number' | 'string' | 'array' +const samParamArrayTypes = ['List', 'CommaDelimitedList'] + +// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#aws-specific-parameter-types +type AWSSpecificParameterType = + | 'AWS::EC2::AvailabilityZone::Name' + | 'AWS::EC2::Image::Id' + | 'AWS::EC2::KeyPair::KeyName' + | 'AWS::EC2::SecurityGroup::GroupName' + | 'AWS::EC2::SecurityGroup::Id' + | 'AWS::EC2::Subnet::Id' + | 'AWS::EC2::Volume::Id' + | 'AWS::EC2::VPC::Id' + | 'AWS::Route53::HostedZone::Id' + | 'List' + | 'List' + | 'List' + | 'List' + | 'List' + | 'List' + | 'List' + | 'List' + | 'List' + +// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#aws-ssm-parameter-types +type SSMParameterType = + | 'AWS::SSM::Parameter::Name' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::Value>' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::ValueAWS::EC2::SecurityGroup::GroupName<>' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::Value' + | 'AWS::SSM::Parameter::Value>' + | 'AWS::SSM::Parameter::Value>' + | 'AWS::SSM::Parameter::Value>' + | 'AWS::SSM::Parameter::Value>' + | 'AWS::SSM::Parameter::Value>' + | 'AWS::SSM::Parameter::Value>' + | 'AWS::SSM::Parameter::Value>' + | 'AWS::SSM::Parameter::Value>' + | 'AWS::SSM::Parameter::Value>' + +export interface Parameter { + Type: ParameterType + AllowedPattern?: string + AllowValues?: string[] + ConstraintDescription?: string + Default?: any + Description?: string + MaxLength?: number + MaxValue?: number + MinLength?: number + MinValue?: number + NoEcho?: boolean +} + +export interface Template { + AWSTemplateFormatVersion?: string + + Transform?: { properties: any } | string + + Parameters?: { + [key: string]: Parameter | undefined + } + + Globals?: TemplateGlobals + + Resources?: TemplateResources +} + +// Globals section of the template. Provides default values for functions, APIs, HTTP APIs, and SimpleTables. +// https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-template-anatomy-globals.html#sam-specification-template-anatomy-globals-supported-resources-and-properties +export interface TemplateGlobals { + Function?: FunctionGlobals + Api?: ApiGlobals + HttpApi?: HttpApiGlobals + SimpleTable?: SimpleTableGlobals +} + +type GlobalType = string | number | Record | string[] | undefined + +type FunctionKeys = + | 'Handler' + | 'Runtime' + | 'CodeUri' + | 'DeadLetterQueue' + | 'Description' + | 'MemorySize' + | 'Timeout' + | 'VpcConfig' + | 'Environment' + | 'Tags' + | 'Tracing' + | 'KmsKeyArn' + | 'Layers' + | 'AutoPublishAlias' + | 'DeploymentPreference' + | 'PermissionsBoundary' + | 'ReservedConcurrentExecutions' + | 'EventInvokeConfig' +const functionKeysSet: Set = new Set([ + 'Handler', + 'Runtime', + 'CodeUri', + 'DeadLetterQueue', + 'Description', + 'MemorySize', + 'Timeout', + 'VpcConfig', + 'Environment', + 'Tags', + 'Tracing', + 'KmsKeyArn', + 'Layers', + 'AutoPublishAlias', + 'DeploymentPreference', + 'PermissionsBoundary', + 'ReservedConcurrentExecutions', + 'EventInvokeConfig', +]) +type FunctionGlobals = { + [key in FunctionKeys]?: GlobalType +} + +type ApiKeys = + | 'Auth' + | 'Name' + | 'DefinitionUri' + | 'CacheClusterEnabled' + | 'CacheClusterSize' + | 'Variables' + | 'EndpointConfiguration' + | 'MethodSettings' + | 'BinaryMediaTypes' + | 'MinimumCompressionSize' + | 'Cors' + | 'GatewayResponses' + | 'AccessLogSetting' + | 'CanarySetting' + | 'TracingEnabled' + | 'OpenApiVersion' + | 'Domain' +const apiKeysSet: Set = new Set([ + 'Auth', + 'Name', + 'DefinitionUri', + 'CacheClusterEnabled', + 'CacheClusterSize', + 'Variables', + 'EndpointConfiguration', + 'MethodSettings', + 'BinaryMediaTypes', + 'MinimumCompressionSize', + 'Cors', + 'GatewayResponses', + 'AccessLogSetting', + 'CanarySetting', + 'TracingEnabled', + 'OpenApiVersion', + 'Domain', +]) +type ApiGlobals = { + [key in ApiKeys]?: GlobalType +} + +type HttpApiKeys = + | 'Auth' + | 'CorsConfiguration' + | 'AccessLogSettings' + | 'Tags' + | 'DefaultRouteSettings' + | 'RouteSettings' + | 'Domain' +const HttpApiKeysSet: Set = new Set([ + 'Auth', + 'CorsConfiguration', + 'AccessLogSettings', + 'Tags', + 'DefaultRouteSettings', + 'RouteSettings', + 'Domain', +]) +type HttpApiGlobals = { + [key in HttpApiKeys]?: GlobalType +} + +type SimpleTableKeys = 'SSESpecification' +const simpleTableKeysSet: Set = new Set(['SSESpecification']) +type SimpleTableGlobals = { + [key in SimpleTableKeys]?: GlobalType +} + +function globalPropForKey(key: string): keyof TemplateGlobals | undefined { + if (functionKeysSet.has(key)) { + return 'Function' + } else if (apiKeysSet.has(key)) { + return 'Api' + } else if (HttpApiKeysSet.has(key)) { + return 'HttpApi' + } else if (simpleTableKeysSet.has(key)) { + return 'SimpleTable' + } else { + return undefined + } +} + +export interface TemplateResources { + [key: string]: Resource | undefined +} + +/** Returns true if the given name or path is a valid CloudFormation or SAM filename. */ +export function isValidFilename(filename: string | vscode.Uri): boolean { + filename = typeof filename === 'string' ? filename : filename.fsPath + filename = filename.trim() + if (!templateFileRegexPattern.test(filename)) { + return false + } + // Note: intentionally _not_ checking `templateFileExcludePattern` here, because while excluding + // template files in .aws-sam/ is relevant for the workspace scan, it's irrelevant if such + // a file was opened explicitly by the user. + return !devfileExcludePattern.test(filename) +} + +export async function load(filename: string, validate: boolean = true): Promise