diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 740894b7edb..eb1f7b7c52f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -352,7 +352,7 @@ jobs: uses: actions/download-artifact@v4 continue-on-error: true with: - name: ${{ matrix.name }}-e2e-test-results-${{ matrix.group }}-${{ env.PREVIOUS_ATTEMPT }} + name: ${{ matrix.name }}-e2e-test-results-${{ matrix.os }}-${{ matrix.group }}-${{ env.PREVIOUS_ATTEMPT }} path: workspaces/${{ matrix.path }}/test-results - name: install packages @@ -413,7 +413,7 @@ jobs: uses: actions/upload-artifact@v4 if: always() with: - name: ${{ matrix.name }}I-e2e-test-results-${{ matrix.os }}-${{ matrix.group }}-${{ github.run_attempt }} + name: ${{ matrix.name }}-e2e-test-results-${{ matrix.os }}-${{ matrix.group }}-${{ github.run_attempt }} path: workspaces/${{ matrix.path }}/test-results/** retention-days: 5 include-hidden-files: true diff --git a/common/config/rush/.pnpmfile.cjs b/common/config/rush/.pnpmfile.cjs index 60edcb80b49..7f687d02b7b 100644 --- a/common/config/rush/.pnpmfile.cjs +++ b/common/config/rush/.pnpmfile.cjs @@ -57,6 +57,9 @@ module.exports = { if (pkg.dependencies['form-data']) { pkg.dependencies['form-data'] = '^4.0.4'; } + if (pkg.dependencies['min-document']) { + pkg.dependencies['min-document'] = '^2.19.1'; + } } if (pkg.devDependencies) { @@ -97,6 +100,9 @@ module.exports = { if (pkg.devDependencies['form-data']) { pkg.devDependencies['form-data'] = '^4.0.4'; } + if (pkg.devDependencies['min-document']) { + pkg.devDependencies['min-document'] = '^2.19.1'; + } } return pkg; diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 4b5dff04ef9..5588c7c0fae 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -145,7 +145,7 @@ importers: version: 5.8.3 webpack: specifier: ^5.94.0 - version: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) + version: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) webpack-cli: specifier: ^5.1.4 version: 5.1.4(webpack@5.102.1) @@ -321,7 +321,7 @@ importers: version: 5.8.3 webpack: specifier: ^5.94.0 - version: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) + version: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) webpack-cli: specifier: ~5.1.4 version: 5.1.4(webpack-dev-server@5.2.2)(webpack@5.102.1) @@ -448,10 +448,10 @@ importers: dependencies: '@ai-sdk/amazon-bedrock': specifier: ^3.0.25 - version: 3.0.51(zod@4.1.11) + version: 3.0.52(zod@4.1.11) '@ai-sdk/anthropic': specifier: ^2.0.20 - version: 2.0.41(zod@4.1.11) + version: 2.0.42(zod@4.1.11) '@types/lodash': specifier: ^4.14.200 version: 4.17.17 @@ -481,7 +481,7 @@ importers: version: link:../../wso2-platform/wso2-platform-core ai: specifier: ^5.0.56 - version: 5.0.87(zod@4.1.11) + version: 5.0.89(zod@4.1.11) cors-anywhere: specifier: ^0.4.4 version: 0.4.4 @@ -710,7 +710,7 @@ importers: version: 4.7.8 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + version: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) joi: specifier: ^17.13.3 version: 17.13.3 @@ -756,31 +756,31 @@ importers: version: 7.27.2(@babel/core@7.27.7) '@rollup/plugin-commonjs': specifier: ^28.0.3 - version: 28.0.9(rollup@4.52.5) + version: 28.0.9(rollup@4.53.1) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.52.5) + version: 6.1.0(rollup@4.53.1) '@rollup/plugin-node-resolve': specifier: ^16.0.1 - version: 16.0.3(rollup@4.52.5) + version: 16.0.3(rollup@4.53.1) '@storybook/addon-actions': specifier: ^6.5.16 version: 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addon-essentials': specifier: ^6.5.16 - version: 6.5.16(@babel/core@7.27.7)(@storybook/builder-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1) + version: 6.5.16(@babel/core@7.27.7)(@storybook/builder-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1) '@storybook/addon-links': specifier: ^6.5.16 version: 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/builder-webpack5': specifier: ^6.5.16 - version: 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + version: 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/manager-webpack5': specifier: ^6.5.9 - version: 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + version: 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/react': specifier: ^6.5.16 - version: 6.5.16(@babel/core@7.27.7)(@storybook/builder-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1) + version: 6.5.16(@babel/core@7.27.7)(@storybook/builder-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1) '@types/classnames': specifier: ^2.2.9 version: 2.3.4 @@ -810,7 +810,7 @@ importers: version: 10.0.0 '@types/webpack': specifier: ^5.28.5 - version: 5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + version: 5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) babel-loader: specifier: ^10.0.0 version: 10.0.0(@babel/core@7.27.7)(webpack@5.102.1) @@ -837,7 +837,7 @@ importers: version: 11.0.3 react-scripts-ts: specifier: ^3.1.0 - version: 3.1.0(@swc/core@1.14.0(@swc/helpers@0.5.17))(babel-core@7.0.0-bridge.0(@babel/core@7.27.7))(babel-runtime@6.26.0)(typescript@5.8.3)(webpack-cli@6.0.1) + version: 3.1.0(@swc/core@1.15.0(@swc/helpers@0.5.17))(babel-core@7.0.0-bridge.0(@babel/core@7.27.7))(babel-runtime@6.26.0)(typescript@5.8.3)(webpack-cli@6.0.1) react-test-renderer: specifier: ^19.1.0 version: 19.1.1(react@18.2.0) @@ -846,16 +846,16 @@ importers: version: 6.0.1 rollup: specifier: ^4.41.0 - version: 4.52.5 + version: 4.53.1 rollup-plugin-import-css: specifier: ^3.5.8 - version: 3.5.8(rollup@4.52.5) + version: 3.5.8(rollup@4.53.1) rollup-plugin-peer-deps-external: specifier: ^2.2.4 - version: 2.2.4(rollup@4.52.5) + version: 2.2.4(rollup@4.53.1) rollup-plugin-postcss: specifier: ^4.0.2 - version: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + version: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) rollup-plugin-scss: specifier: ^4.0.1 version: 4.0.1 @@ -864,7 +864,7 @@ importers: version: 2.0.0 rollup-plugin-typescript2: specifier: ^0.36.0 - version: 0.36.0(rollup@4.52.5)(typescript@5.8.3) + version: 0.36.0(rollup@4.53.1)(typescript@5.8.3) sass: specifier: ^1.89.0 version: 1.93.3 @@ -906,7 +906,7 @@ importers: version: 5.8.3 webpack: specifier: ^5.94.0 - version: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + version: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-cli: specifier: ^6.0.1 version: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.102.1) @@ -1046,7 +1046,7 @@ importers: version: 5.0.1(react-hook-form@7.56.4(react@18.2.0)) '@tanstack/query-core': specifier: ^5.77.1 - version: 5.90.6 + version: 5.90.7 '@tanstack/react-query': specifier: 5.77.1 version: 5.77.1(react@18.2.0) @@ -1218,7 +1218,7 @@ importers: version: 5.8.3 webpack: specifier: ^5.94.0 - version: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) + version: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) webpack-cli: specifier: ^5.1.4 version: 5.1.4(webpack-dev-server@5.2.2)(webpack@5.102.1) @@ -1285,7 +1285,7 @@ importers: version: 7.27.1(@babel/core@7.27.7) '@storybook/react': specifier: ^6.3.7 - version: 6.5.16(@babel/core@7.27.7)(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17)))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1) + version: 6.5.16(@babel/core@7.27.7)(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17)))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1) '@testing-library/dom': specifier: ~10.4.0 version: 10.4.1 @@ -1324,7 +1324,7 @@ importers: version: 3.0.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + version: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) jest-environment-jsdom: specifier: 29.7.0 version: 29.7.0 @@ -1336,7 +1336,7 @@ importers: version: 19.1.1(react@18.2.0) ts-jest: specifier: 29.3.4 - version: 29.3.4(@babel/core@7.27.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)))(typescript@5.8.3) + version: 29.3.4(@babel/core@7.27.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)))(typescript@5.8.3) typescript: specifier: 5.8.3 version: 5.8.3 @@ -1403,7 +1403,7 @@ importers: version: 7.27.1(@babel/core@7.27.7) '@storybook/react': specifier: ^6.5.16 - version: 6.5.16(@babel/core@7.27.7)(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17)))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1) + version: 6.5.16(@babel/core@7.27.7)(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17)))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1) '@testing-library/dom': specifier: ~10.4.0 version: 10.4.1 @@ -1442,7 +1442,7 @@ importers: version: 3.0.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + version: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) jest-environment-jsdom: specifier: 29.7.0 version: 29.7.0 @@ -1454,7 +1454,7 @@ importers: version: 19.1.1(react@18.2.0) ts-jest: specifier: 29.3.4 - version: 29.3.4(@babel/core@7.27.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)))(typescript@5.8.3) + version: 29.3.4(@babel/core@7.27.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)))(typescript@5.8.3) typescript: specifier: 5.8.3 version: 5.8.3 @@ -1484,7 +1484,7 @@ importers: version: 6.7.4(lodash@4.17.21)(react@18.2.0)(resize-observer-polyfill@1.5.1) '@tanstack/query-core': specifier: ^5.77.1 - version: 5.90.6 + version: 5.90.7 '@tanstack/react-query': specifier: 5.77.1 version: 5.77.1(react@18.2.0) @@ -2273,7 +2273,7 @@ importers: version: 5.8.3 webpack: specifier: ^5.94.0 - version: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) + version: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) webpack-cli: specifier: ^5.1.4 version: 5.1.4(webpack-dev-server@5.2.2)(webpack@5.102.1) @@ -2495,6 +2495,9 @@ importers: '@wso2/font-wso2-vscode': specifier: workspace:* version: link:../../common-libs/font-wso2-vscode + toml: + specifier: ^3.0.0 + version: 3.0.0 xstate: specifier: ^4.38.3 version: 4.38.3 @@ -2540,7 +2543,7 @@ importers: version: 11.0.3 mocha: specifier: ^11.2.2 - version: 11.7.4 + version: 11.7.5 source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -2686,10 +2689,10 @@ importers: version: 6.0.0 mocha: specifier: ^11.5.0 - version: 11.7.4 + version: 11.7.5 terser-webpack-plugin: specifier: ^5.3.10 - version: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + version: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) ts-loader: specifier: ~9.5.2 version: 9.5.4(typescript@5.8.3)(webpack@5.102.1) @@ -2698,7 +2701,7 @@ importers: version: 5.8.3 vscode-extension-tester: specifier: ^8.14.1 - version: 8.14.1(mocha@11.7.4)(typescript@5.8.3) + version: 8.14.1(mocha@11.7.5)(typescript@5.8.3) webpack: specifier: ^5.94.0 version: 5.102.1(webpack-cli@6.0.1) @@ -2776,7 +2779,7 @@ importers: version: 4.0.1 swagger-ui-react: specifier: ^5.22.0 - version: 5.30.1(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 5.30.2(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) timezone-support: specifier: ^3.1.0 version: 3.1.0 @@ -3068,7 +3071,7 @@ importers: version: 9.1.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3))(typescript@5.8.3) '@storybook/react-vite': specifier: ^9.0.12 - version: 9.1.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.52.5)(storybook@9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3))(typescript@5.8.3) + version: 9.1.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.53.1)(storybook@9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3))(typescript@5.8.3) '@types/lodash': specifier: ~4.17.16 version: 4.17.17 @@ -3125,7 +3128,7 @@ importers: dependencies: '@modelcontextprotocol/inspector': specifier: ^0.17.2 - version: 0.17.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.18)(@types/react-dom@18.2.0)(@types/react@18.2.0)(typescript@5.8.3) + version: 0.17.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.18)(@types/react-dom@18.2.0)(@types/react@18.2.0)(typescript@5.8.3) devDependencies: '@types/mocha': specifier: ^10.0.3 @@ -3159,7 +3162,7 @@ importers: version: 8.57.1 mocha: specifier: ^11.2.2 - version: 11.7.4 + version: 11.7.5 open: specifier: 10.2.0 version: 10.2.0 @@ -3174,7 +3177,7 @@ importers: version: 5.8.3 webpack: specifier: ^5.94.0 - version: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) + version: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) webpack-cli: specifier: ^5.1.4 version: 5.1.4(webpack@5.102.1) @@ -3302,7 +3305,7 @@ importers: version: 6.7.4(lodash@4.17.21)(react@18.2.0)(resize-observer-polyfill@1.5.1) '@tanstack/query-core': specifier: ^5.76.2 - version: 5.90.6 + version: 5.90.7 '@tanstack/react-query': specifier: 5.76.2 version: 5.76.2(react@18.2.0) @@ -3567,7 +3570,7 @@ importers: version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) '@storybook/react-webpack5': specifier: ^8.6.14 - version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(@swc/core@1.14.0(@swc/helpers@0.5.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) + version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(@swc/core@1.15.0(@swc/helpers@0.5.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) '@storybook/test': specifier: ^8.6.14 version: 8.6.14(storybook@8.6.14(prettier@3.5.3)) @@ -3612,7 +3615,7 @@ importers: version: 3.0.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + version: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) jest-environment-jsdom: specifier: 29.7.0 version: 29.7.0 @@ -3627,7 +3630,7 @@ importers: version: 8.6.14(prettier@3.5.3) ts-jest: specifier: 29.3.4 - version: 29.3.4(@babel/core@7.27.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)))(typescript@5.8.3) + version: 29.3.4(@babel/core@7.27.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)))(typescript@5.8.3) typescript: specifier: 5.8.3 version: 5.8.3 @@ -3646,6 +3649,9 @@ importers: ../../workspaces/mi/mi-extension: dependencies: + '@ai-sdk/anthropic': + specifier: ^2.0.35 + version: 2.0.42(zod@3.25.76) '@apidevtools/json-schema-ref-parser': specifier: 12.0.2 version: 12.0.2 @@ -3661,6 +3667,9 @@ importers: '@types/fs-extra': specifier: ~11.0.4 version: 11.0.4 + '@types/handlebars': + specifier: ^4.1.0 + version: 4.1.0 '@types/json-schema': specifier: 7.0.15 version: 7.0.15 @@ -3700,6 +3709,9 @@ importers: adm-zip: specifier: ~0.5.16 version: 0.5.16 + ai: + specifier: ^5.0.76 + version: 5.0.89(zod@3.25.76) axios: specifier: ~1.12.0 version: 1.12.2 @@ -3721,6 +3733,9 @@ importers: fs-extra: specifier: ~11.3.0 version: 11.3.2 + handlebars: + specifier: ^4.7.8 + version: 4.7.8 json-schema: specifier: 0.4.0 version: 0.4.0 @@ -3730,6 +3745,9 @@ importers: jsonix: specifier: ~3.0.0 version: 3.0.0 + jwt-decode: + specifier: ^4.0.0 + version: 4.0.0 lodash: specifier: ~4.17.21 version: 4.17.21 @@ -3774,7 +3792,7 @@ importers: version: 1.51.0 vscode-extension-tester: specifier: ~8.14.1 - version: 8.14.1(mocha@11.7.4)(typescript@5.8.3) + version: 8.14.1(mocha@11.7.5)(typescript@5.8.3) vscode-languageclient: specifier: ^9.0.1 version: 9.0.1 @@ -3793,6 +3811,9 @@ importers: xstate: specifier: ^4.38.3 version: 4.38.3 + zod: + specifier: ^3.24.1 + version: 3.25.76 devDependencies: '@playwright/test': specifier: ~1.55.1 @@ -3835,7 +3856,7 @@ importers: version: 11.0.3 mocha: specifier: ^11.4.0 - version: 11.7.4 + version: 11.7.5 playwright-core: specifier: ~1.55.1 version: 1.55.1 @@ -3938,7 +3959,7 @@ importers: version: 0.6.1(@types/webpack@5.28.5(webpack-cli@5.1.4))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1)(webpack@5.102.1) '@tanstack/query-core': specifier: ^5.76.0 - version: 5.90.6 + version: 5.90.7 '@tanstack/react-query': specifier: 5.76.1 version: 5.76.1(react@18.2.0) @@ -4116,7 +4137,7 @@ importers: version: 3.17.5 webpack: specifier: ^5.94.0 - version: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) + version: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) webpack-cli: specifier: ~5.1.4 version: 5.1.4(webpack-dev-server@5.2.2)(webpack@5.102.1) @@ -4196,7 +4217,7 @@ importers: dependencies: '@aws-sdk/client-s3': specifier: ^3.817.0 - version: 3.922.0 + version: 3.927.0 '@vscode-logging/logger': specifier: ^2.0.0 version: 2.0.0 @@ -4302,10 +4323,10 @@ importers: version: 6.0.0 mocha: specifier: ^11.5.0 - version: 11.7.4 + version: 11.7.5 terser-webpack-plugin: specifier: ^5.3.14 - version: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + version: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) ts-loader: specifier: ~9.5.2 version: 9.5.4(typescript@5.8.3)(webpack@5.102.1) @@ -4314,7 +4335,7 @@ importers: version: 5.8.3 vscode-extension-tester: specifier: ^8.14.1 - version: 8.14.1(mocha@11.7.4)(typescript@5.8.3) + version: 8.14.1(mocha@11.7.5)(typescript@5.8.3) webpack: specifier: ^5.94.0 version: 5.102.1(webpack-cli@6.0.1) @@ -4389,7 +4410,7 @@ importers: version: 4.0.1 swagger-ui-react: specifier: ^5.22.0 - version: 5.30.1(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 5.30.2(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) timezone-support: specifier: ^3.1.0 version: 3.1.0 @@ -4456,7 +4477,7 @@ importers: version: 4.0.0(webpack@5.102.1) tailwindcss: specifier: ^4.1.7 - version: 4.1.16 + version: 4.1.17 ts-loader: specifier: ^9.5.2 version: 9.5.4(typescript@5.8.3)(webpack@5.102.1) @@ -4478,20 +4499,20 @@ packages: '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - '@ai-sdk/amazon-bedrock@3.0.51': - resolution: {integrity: sha512-nPyUZDH9XS5iqgxM2ZPK2kSE1M1UoXdWaqWRqZ7k90BZu32VEreL1Z3pTTo58qEj9OuOkdlQ1oO5DujIE7VDLA==} + '@ai-sdk/amazon-bedrock@3.0.52': + resolution: {integrity: sha512-dCse5ShXxeKB0IBg2/uac3DaHaFHYh6xoDBGjtlxeosHAppHwkal4OA5tgy6uj3Zp9bRZg4ZlkR77zdw3HC4ug==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/anthropic@2.0.41': - resolution: {integrity: sha512-ZQebpyE6rM3JoeEyhJXUNDiRfVegw8ZrxT+rB8yurxI5JXDnlGpYQvSPmdR8TQfMbps4YkggfbcOwMeEZaTS+g==} + '@ai-sdk/anthropic@2.0.42': + resolution: {integrity: sha512-5BcXMx6VTYPeA4csd1SvJgpCn5Nu9qHqsNqOr1e/R7UHq83Vv4j4OcgbFwdWgaW/wihNla5B+y4OGqTFIw216w==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/gateway@2.0.6': - resolution: {integrity: sha512-FmhR6Tle09I/RUda8WSPpJ57mjPWzhiVVlB50D+k+Qf/PBW0CBtnbAUxlNSR5v+NIZNLTK3C56lhb23ntEdxhQ==} + '@ai-sdk/gateway@2.0.7': + resolution: {integrity: sha512-/AI5AKi4vOK9SEb8Z1dfXkhsJ5NAfWsoJQc96B/mzn2KIrjw5occOjIwD06scuhV9xWlghCoXJT1sQD9QH/tyg==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -4549,44 +4570,44 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.922.0': - resolution: {integrity: sha512-SZRaZUUAHCWfEyBf4SRSPd29ko4uFoJpfd0E/w1meE68XhFB52FTtz/71UqYcwqZmN+s7oUNFFZT+DE/dnQSEA==} + '@aws-sdk/client-s3@3.927.0': + resolution: {integrity: sha512-LwjZH7/WDFw2++ntRtJMMlkZy+BTMaQQv+S8m3amfRo4iF4KJKRE2q3+QOKX2Xpvnw5IEHkmLa+oEanGlk2t1g==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso@3.922.0': - resolution: {integrity: sha512-jdHs7uy7cSpiMvrxhYmqHyJxgK7hyqw4plG8OQ4YTBpq0SbfAxdoOuOkwJ1IVUUQho4otR1xYYjiX/8e8J8qwQ==} + '@aws-sdk/client-sso@3.927.0': + resolution: {integrity: sha512-O+e+jo6ei7U/BA7lhT4mmPCWmeR9dFgGUHVwCwJ5c/nCaSaHQ+cb7j2h8WPXERu0LhPSFyj1aD5dk3jFIwNlbg==} engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.922.0': - resolution: {integrity: sha512-EvfP4cqJfpO3L2v5vkIlTkMesPtRwWlMfsaW6Tpfm7iYfBOuTi6jx60pMDMTyJNVfh6cGmXwh/kj1jQdR+w99Q==} + '@aws-sdk/core@3.927.0': + resolution: {integrity: sha512-QOtR9QdjNeC7bId3fc/6MnqoEezvQ2Fk+x6F+Auf7NhOxwYAtB1nvh0k3+gJHWVGpfxN1I8keahRZd79U68/ag==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.922.0': - resolution: {integrity: sha512-WikGQpKkROJSK3D3E7odPjZ8tU7WJp5/TgGdRuZw3izsHUeH48xMv6IznafpRTmvHcjAbDQj4U3CJZNAzOK/OQ==} + '@aws-sdk/credential-provider-env@3.927.0': + resolution: {integrity: sha512-bAllBpmaWINpf0brXQWh/hjkBctapknZPYb3FJRlBHytEGHi7TpgqBXi8riT0tc6RVWChhnw58rQz22acOmBuw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.922.0': - resolution: {integrity: sha512-i72DgHMK7ydAEqdzU0Duqh60Q8W59EZmRJ73y0Y5oFmNOqnYsAI+UXyOoCsubp+Dkr6+yOwAn1gPt1XGE9Aowg==} + '@aws-sdk/credential-provider-http@3.927.0': + resolution: {integrity: sha512-jEvb8C7tuRBFhe8vZY9vm9z6UQnbP85IMEt3Qiz0dxAd341Hgu0lOzMv5mSKQ5yBnTLq+t3FPKgD9tIiHLqxSQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.922.0': - resolution: {integrity: sha512-bVF+pI5UCLNkvbiZr/t2fgTtv84s8FCdOGAPxQiQcw5qOZywNuuCCY3wIIchmQr6GJr8YFkEp5LgDCac5EC5aQ==} + '@aws-sdk/credential-provider-ini@3.927.0': + resolution: {integrity: sha512-WvliaKYT7bNLiryl/FsZyUwRGBo/CWtboekZWvSfloAb+0SKFXWjmxt3z+Y260aoaPm/LIzEyslDHfxqR9xCJQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-node@3.922.0': - resolution: {integrity: sha512-agCwaD6mBihToHkjycL8ObIS2XOnWypWZZWhJSoWyHwFrhEKz1zGvgylK9Dc711oUfU+zU6J8e0JPKNJMNb3BQ==} + '@aws-sdk/credential-provider-node@3.927.0': + resolution: {integrity: sha512-M6BLrI+WHQ7PUY1aYu2OkI/KEz9aca+05zyycACk7cnlHlZaQ3vTFd0xOqF+A1qaenQBuxApOTs7Z21pnPUo9Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.922.0': - resolution: {integrity: sha512-1DZOYezT6okslpvMW7oA2q+y17CJd4fxjNFH0jtThfswdh9CtG62+wxenqO+NExttq0UMaKisrkZiVrYQBTShw==} + '@aws-sdk/credential-provider-process@3.927.0': + resolution: {integrity: sha512-rvqdZIN3TRhLKssufN5G2EWLMBct3ZebOBdwr0tuOoPEdaYflyXYYUScu+Beb541CKfXaFnEOlZokq12r7EPcQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.922.0': - resolution: {integrity: sha512-nbD3G3hShTYxLCkKMqLkLPtKwAAfxdY/k9jHtZmVBFXek2T6tQrqZHKxlAu+fd23Ga4/Aik7DLQQx1RA1a5ipg==} + '@aws-sdk/credential-provider-sso@3.927.0': + resolution: {integrity: sha512-XrCuncze/kxZE6WYEWtNMGtrJvJtyhUqav4xQQ9PJcNjxCUYiIRv7Gwkt7cuwJ1HS+akQj+JiZmljAg97utfDw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.922.0': - resolution: {integrity: sha512-wjGIhgMHGGQfQTdFaJphNOKyAL8wZs6znJdHADPVURmgR+EWLyN/0fDO1u7wx8xaLMZpbHIFWBEvf9TritR/cQ==} + '@aws-sdk/credential-provider-web-identity@3.927.0': + resolution: {integrity: sha512-Oh/aFYjZQsIiZ2PQEgTNvqEE/mmOYxZKZzXV86qrU3jBUfUUBvprUZc684nBqJbSKPwM5jCZtxiRYh+IrZDE7A==} engines: {node: '>=18.0.0'} '@aws-sdk/middleware-bucket-endpoint@3.922.0': @@ -4597,8 +4618,8 @@ packages: resolution: {integrity: sha512-xmnLWMtmHJHJBupSWMUEW1gyxuRIeQ1Ov2xa8Tqq77fPr4Ft2AluEwiDMaZIMHoAvpxWKEEt9Si59Li7GIA+bQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.922.0': - resolution: {integrity: sha512-G363np7YcJhf+gBucskdv8cOTbs2TRwocEzRupuqDIooGDlLBlfJrvwehdgtWR8l53yjJR3zcHvGrVPTe2h8Nw==} + '@aws-sdk/middleware-flexible-checksums@3.927.0': + resolution: {integrity: sha512-f6R2Rn5gl+B7S3BOCKjv5ZwI1RsHXXHf8pecRW3n1EZjDR/BA5TiUso57DC2I9myR53qp2gADsgQ248tQdZb2g==} engines: {node: '>=18.0.0'} '@aws-sdk/middleware-host-header@3.922.0': @@ -4617,32 +4638,32 @@ packages: resolution: {integrity: sha512-TtSCEDonV/9R0VhVlCpxZbp/9sxQvTTRKzIf8LxW3uXpby6Wl8IxEciBJlxmSkoqxh542WRcko7NYODlvL/gDA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-s3@3.922.0': - resolution: {integrity: sha512-ygg8lME1oFAbsH42ed2wtGqfHLoT5irgx6VC4X98j79fV1qXEwwwbqMsAiMQ/HJehpjqAFRVsHox3MHLN48Z5A==} + '@aws-sdk/middleware-sdk-s3@3.927.0': + resolution: {integrity: sha512-kl39er2nUDIw21jxniBxCOnsw1m6gz7juuIn1cIyOAkUyPkkDpQT9+vTFpJcyNDkW+USxykBNe7HIXNiCKLyUg==} engines: {node: '>=18.0.0'} '@aws-sdk/middleware-ssec@3.922.0': resolution: {integrity: sha512-eHvSJZTSRJO+/tjjGD6ocnPc8q9o3m26+qbwQTu/4V6yOJQ1q+xkDZNqwJQphL+CodYaQ7uljp8g1Ji/AN3D9w==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.922.0': - resolution: {integrity: sha512-N4Qx/9KP3oVQBJOrSghhz8iZFtUC2NNeSZt88hpPhbqAEAtuX8aD8OzVcpnAtrwWqy82Yd2YTxlkqMGkgqnBsQ==} + '@aws-sdk/middleware-user-agent@3.927.0': + resolution: {integrity: sha512-sv6St9EgEka6E7y19UMCsttFBZ8tsmz2sstgRd7LztlX3wJynpeDUhq0gtedguG1lGZY/gDf832k5dqlRLUk7g==} engines: {node: '>=18.0.0'} - '@aws-sdk/nested-clients@3.922.0': - resolution: {integrity: sha512-uYvKCF1TGh/MuJ4TMqmUM0Csuao02HawcseG4LUDyxdUsd/EFuxalWq1Cx4fKZQ2K8F504efZBjctMAMNY+l7A==} + '@aws-sdk/nested-clients@3.927.0': + resolution: {integrity: sha512-Oy6w7+fzIdr10DhF/HpfVLy6raZFTdiE7pxS1rvpuj2JgxzW2y6urm2sYf3eLOpMiHyuG4xUBwFiJpU9CCEvJA==} engines: {node: '>=18.0.0'} - '@aws-sdk/region-config-resolver@3.922.0': - resolution: {integrity: sha512-44Y/rNNwhngR2KHp6gkx//TOr56/hx6s4l+XLjOqH7EBCHL7XhnrT1y92L+DLiroVr1tCSmO8eHQwBv0Y2+mvw==} + '@aws-sdk/region-config-resolver@3.925.0': + resolution: {integrity: sha512-FOthcdF9oDb1pfQBRCfWPZhJZT5wqpvdAS5aJzB1WDZ+6EuaAhLzLH/fW1slDunIqq1PSQGG3uSnVglVVOvPHQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/signature-v4-multi-region@3.922.0': - resolution: {integrity: sha512-mmsgEEL5pE+A7gFYiJMDBCLVciaXq4EFI5iAP7bPpnHvOplnNOYxVy2IreKMllGvrfjVyLnwxzZYlo5zZ65FWg==} + '@aws-sdk/signature-v4-multi-region@3.927.0': + resolution: {integrity: sha512-P0TZxFhNxj2V9LtR9vk8b3RVbnKt7HkPRptnZafpKjvG6VhWch8bDmrEveCIT8XP2vSUc/5O6a7S3MuPPgnTJA==} engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.922.0': - resolution: {integrity: sha512-/inmPnjZE0ZBE16zaCowAvouSx05FJ7p6BQYuzlJ8vxEU0sS0Hf8fvhuiRnN9V9eDUPIBY+/5EjbMWygXL4wlQ==} + '@aws-sdk/token-providers@3.927.0': + resolution: {integrity: sha512-JRdaprkZjZ6EY4WVwsZaEjPUj9W9vqlSaFDm4oD+IbwlY4GjAXuUQK6skKcvVyoOsSTvJp/CaveSws2FiWUp9Q==} engines: {node: '>=18.0.0'} '@aws-sdk/types@3.922.0': @@ -4664,8 +4685,8 @@ packages: '@aws-sdk/util-user-agent-browser@3.922.0': resolution: {integrity: sha512-qOJAERZ3Plj1st7M4Q5henl5FRpE30uLm6L9edZqZXGR6c7ry9jzexWamWVpQ4H4xVAVmiO9dIEBAfbq4mduOA==} - '@aws-sdk/util-user-agent-node@3.922.0': - resolution: {integrity: sha512-NrPe/Rsr5kcGunkog0eBV+bY0inkRELsD2SacC4lQZvZiXf8VJ2Y7j+Yq1tB+h+FPLsdt3v9wItIvDf/laAm0Q==} + '@aws-sdk/util-user-agent-node@3.927.0': + resolution: {integrity: sha512-5Ty+29jBTHg1mathEhLJavzA7A7vmhephRYGenFzo8rApLZh+c+MCAqjddSjdDzcf5FH+ydGGnIrj4iIfbZIMQ==} engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -4699,8 +4720,8 @@ packages: resolution: {integrity: sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==} engines: {node: '>=20.0.0'} - '@azure/core-rest-pipeline@1.22.1': - resolution: {integrity: sha512-UVZlVLfLyz6g3Hy7GNDpooMQonUygH7ghdiSASOOHy97fKj/mPLqgDX7aidOijn+sCMU+WU8NjlPlNTgnvbcGA==} + '@azure/core-rest-pipeline@1.22.2': + resolution: {integrity: sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==} engines: {node: '>=20.0.0'} '@azure/core-tracing@1.3.1': @@ -4719,8 +4740,8 @@ packages: resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} engines: {node: '>=20.0.0'} - '@azure/msal-browser@4.26.0': - resolution: {integrity: sha512-Ie3SZ4IMrf9lSwWVzzJrhTPE+g9+QDUfeor1LKMBQzcblp+3J/U1G8hMpNSfLL7eA5F/DjjPXkATJ5JRUdDJLA==} + '@azure/msal-browser@4.26.1': + resolution: {integrity: sha512-GGCIsZXxyNm5QcQZ4maA9q+9UWmM+/87G+ybvPkrE32el1URSa9WYt0t67ks3/P0gspZX9RoEqyLqJ/X/JDnBQ==} engines: {node: '>=0.8.0'} '@azure/msal-common@15.13.1': @@ -5645,8 +5666,8 @@ packages: '@codemirror/lint@6.8.5': resolution: {integrity: sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==} - '@codemirror/merge@6.11.1': - resolution: {integrity: sha512-NleJ//mSmcal3jRdm9WwOVMUaJWvP2h69K96z3xTDJnde/nsMnLt9qfKUBkycWm5iO3/g4Zd69XTuTFErTZ72A==} + '@codemirror/merge@6.11.2': + resolution: {integrity: sha512-NO5EJd2rLRbwVWLgMdhIntDIhfDtMOKYEZgqV5WnkNUS2oXOCVWLPjG/kgl/Jth2fGiOuG947bteqxP9nBXmMg==} '@codemirror/search@6.5.11': resolution: {integrity: sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==} @@ -6596,8 +6617,8 @@ packages: engines: {node: '>=22.7.5'} hasBin: true - '@modelcontextprotocol/sdk@1.21.0': - resolution: {integrity: sha512-YFBsXJMFCyI1zP98u7gezMFKX4lgu/XpoZJk7ufI6UlFKXLj2hAMUuRlQX/nrmIPOmhRrG6tw2OQ2ZA/ZlXYpQ==} + '@modelcontextprotocol/sdk@1.21.1': + resolution: {integrity: sha512-UyLFcJLDvUuZbGnaQqXFT32CpPpGj7VS19roLut6gkQVhb439xUzYWbsUvdI3ZPL+2hnFosuugtYWE0Mcs1rmQ==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -7186,8 +7207,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-label@2.1.7': - resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} + '@radix-ui/react-label@2.1.8': + resolution: {integrity: sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -7316,6 +7337,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-primitive@2.1.4': + resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-roving-focus@1.1.11': resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} peerDependencies: @@ -7386,6 +7420,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-switch@1.2.6': resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} peerDependencies: @@ -7638,6 +7681,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-visually-hidden@1.2.4': + resolution: {integrity: sha512-kaeiyGCe844dkb9AVF+rb4yTyb1LiLN/e3es3nLiRyN4dC8AduBYPMnnNlDjX2VDOcvDEiPnRNMJeWCfsX0txg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/rect@1.0.1': resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} @@ -7791,113 +7847,113 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.52.5': - resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} + '@rollup/rollup-android-arm-eabi@4.53.1': + resolution: {integrity: sha512-bxZtughE4VNVJlL1RdoSE545kc4JxL7op57KKoi59/gwuU5rV6jLWFXXc8jwgFoT6vtj+ZjO+Z2C5nrY0Cl6wA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.5': - resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} + '@rollup/rollup-android-arm64@4.53.1': + resolution: {integrity: sha512-44a1hreb02cAAfAKmZfXVercPFaDjqXCK+iKeVOlJ9ltvnO6QqsBHgKVPTu+MJHSLLeMEUbeG2qiDYgbFPU48g==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.5': - resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} + '@rollup/rollup-darwin-arm64@4.53.1': + resolution: {integrity: sha512-usmzIgD0rf1syoOZ2WZvy8YpXK5G1V3btm3QZddoGSa6mOgfXWkkv+642bfUUldomgrbiLQGrPryb7DXLovPWQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.5': - resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} + '@rollup/rollup-darwin-x64@4.53.1': + resolution: {integrity: sha512-is3r/k4vig2Gt8mKtTlzzyaSQ+hd87kDxiN3uDSDwggJLUV56Umli6OoL+/YZa/KvtdrdyNfMKHzL/P4siOOmg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.5': - resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} + '@rollup/rollup-freebsd-arm64@4.53.1': + resolution: {integrity: sha512-QJ1ksgp/bDJkZB4daldVmHaEQkG4r8PUXitCOC2WRmRaSaHx5RwPoI3DHVfXKwDkB+Sk6auFI/+JHacTekPRSw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.5': - resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} + '@rollup/rollup-freebsd-x64@4.53.1': + resolution: {integrity: sha512-J6ma5xgAzvqsnU6a0+jgGX/gvoGokqpkx6zY4cWizRrm0ffhHDpJKQgC8dtDb3+MqfZDIqs64REbfHDMzxLMqQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': - resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.1': + resolution: {integrity: sha512-JzWRR41o2U3/KMNKRuZNsDUAcAVUYhsPuMlx5RUldw0E4lvSIXFUwejtYz1HJXohUmqs/M6BBJAUBzKXZVddbg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.5': - resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} + '@rollup/rollup-linux-arm-musleabihf@4.53.1': + resolution: {integrity: sha512-L8kRIrnfMrEoHLHtHn+4uYA52fiLDEDyezgxZtGUTiII/yb04Krq+vk3P2Try+Vya9LeCE9ZHU8CXD6J9EhzHQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.5': - resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} + '@rollup/rollup-linux-arm64-gnu@4.53.1': + resolution: {integrity: sha512-ysAc0MFRV+WtQ8li8hi3EoFi7us6d1UzaS/+Dp7FYZfg3NdDljGMoVyiIp6Ucz7uhlYDBZ/zt6XI0YEZbUO11Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.5': - resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} + '@rollup/rollup-linux-arm64-musl@4.53.1': + resolution: {integrity: sha512-UV6l9MJpDbDZZ/fJvqNcvO1PcivGEf1AvKuTcHoLjVZVFeAMygnamCTDikCVMRnA+qJe+B3pSbgX2+lBMqgBhA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.5': - resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} + '@rollup/rollup-linux-loong64-gnu@4.53.1': + resolution: {integrity: sha512-UDUtelEprkA85g95Q+nj3Xf0M4hHa4DiJ+3P3h4BuGliY4NReYYqwlc0Y8ICLjN4+uIgCEvaygYlpf0hUj90Yg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.5': - resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} + '@rollup/rollup-linux-ppc64-gnu@4.53.1': + resolution: {integrity: sha512-vrRn+BYhEtNOte/zbc2wAUQReJXxEx2URfTol6OEfY2zFEUK92pkFBSXRylDM7aHi+YqEPJt9/ABYzmcrS4SgQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.5': - resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} + '@rollup/rollup-linux-riscv64-gnu@4.53.1': + resolution: {integrity: sha512-gto/1CxHyi4A7YqZZNznQYrVlPSaodOBPKM+6xcDSCMVZN/Fzb4K+AIkNz/1yAYz9h3Ng+e2fY9H6bgawVq17w==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.5': - resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} + '@rollup/rollup-linux-riscv64-musl@4.53.1': + resolution: {integrity: sha512-KZ6Vx7jAw3aLNjFR8eYVcQVdFa/cvBzDNRFM3z7XhNNunWjA03eUrEwJYPk0G8V7Gs08IThFKcAPS4WY/ybIrQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.5': - resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} + '@rollup/rollup-linux-s390x-gnu@4.53.1': + resolution: {integrity: sha512-HvEixy2s/rWNgpwyKpXJcHmE7om1M89hxBTBi9Fs6zVuLU4gOrEMQNbNsN/tBVIMbLyysz/iwNiGtMOpLAOlvA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.5': - resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} + '@rollup/rollup-linux-x64-gnu@4.53.1': + resolution: {integrity: sha512-E/n8x2MSjAQgjj9IixO4UeEUeqXLtiA7pyoXCFYLuXpBA/t2hnbIdxHfA7kK9BFsYAoNU4st1rHYdldl8dTqGA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.5': - resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} + '@rollup/rollup-linux-x64-musl@4.53.1': + resolution: {integrity: sha512-IhJ087PbLOQXCN6Ui/3FUkI9pWNZe/Z7rEIVOzMsOs1/HSAECCvSZ7PkIbkNqL/AZn6WbZvnoVZw/qwqYMo4/w==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.5': - resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + '@rollup/rollup-openharmony-arm64@4.53.1': + resolution: {integrity: sha512-0++oPNgLJHBblreu0SFM7b3mAsBJBTY0Ksrmu9N6ZVrPiTkRgda52mWR7TKhHAsUb9noCjFvAw9l6ZO1yzaVbA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.5': - resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} + '@rollup/rollup-win32-arm64-msvc@4.53.1': + resolution: {integrity: sha512-VJXivz61c5uVdbmitLkDlbcTk9Or43YC2QVLRkqp86QoeFSqI81bNgjhttqhKNMKnQMWnecOCm7lZz4s+WLGpQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.5': - resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} + '@rollup/rollup-win32-ia32-msvc@4.53.1': + resolution: {integrity: sha512-NmZPVTUOitCXUH6erJDzTQ/jotYw4CnkMDjCYRxNHVD9bNyfrGoIse684F9okwzKCV4AIHRbUkeTBc9F2OOH5Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.5': - resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} + '@rollup/rollup-win32-x64-gnu@4.53.1': + resolution: {integrity: sha512-2SNj7COIdAf6yliSpLdLG8BEsp5lgzRehgfkP0Av8zKfQFKku6JcvbobvHASPJu4f3BFxej5g+HuQPvqPhHvpQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.5': - resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + '@rollup/rollup-win32-x64-msvc@4.53.1': + resolution: {integrity: sha512-rLarc1Ofcs3DHtgSzFO31pZsCh8g05R2azN1q3fF+H423Co87My0R+tazOEvYVKXSLh8C4LerMK41/K7wlklcg==} cpu: [x64] os: [win32] @@ -8030,8 +8086,8 @@ packages: resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.4.1': - resolution: {integrity: sha512-BciDJ5hkyYEGBBKMbjGB1A/Zq8bYZ41Zo9BMnGdKF6QD1fY4zIkYx6zui/0CHaVGnv6h0iy8y4rnPX9CPCAPyQ==} + '@smithy/config-resolver@4.4.2': + resolution: {integrity: sha512-4Jys0ni2tB2VZzgslbEgszZyMdTkPOFGA8g+So/NjR8oy6Qwaq4eSwsrRI+NMtb0Dq4kqCzGUu/nGUx7OM/xfw==} engines: {node: '>=18.0.0'} '@smithy/core@3.17.2': @@ -8190,8 +8246,8 @@ packages: resolution: {integrity: sha512-GwaGjv/QLuL/QHQaqhf/maM7+MnRFQQs7Bsl6FlaeK6lm6U7mV5AAnVabw68cIoMl5FQFyKK62u7RWRzWL25OQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.7': - resolution: {integrity: sha512-6hinjVqec0WYGsqN7h9hL/ywfULmJJNXGXnNZW7jrIn/cFuC/aVlVaiDfBIJEvKcOrmN8/EgsW69eY0gXABeHw==} + '@smithy/util-defaults-mode-node@4.2.8': + resolution: {integrity: sha512-gIoTf9V/nFSIZ0TtgDNLd+Ws59AJvijmMDYrOozoMHPJaG9cMRdqNO50jZTlbM6ydzQYY8L/mQ4tKSw/TB+s6g==} engines: {node: '>=18.0.0'} '@smithy/util-endpoints@3.2.4': @@ -9184,95 +9240,95 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - '@swagger-api/apidom-ast@1.0.0-rc.1': - resolution: {integrity: sha512-hsAySkWlIjgkQEDu1YEbvnxdEC3rD9bjQf7UYm0vzkvL5PNDd6lHLhxb825bQAfXQjw7WOxtV7eNrgqRQohMDg==} + '@swagger-api/apidom-ast@1.0.0-rc.3': + resolution: {integrity: sha512-lGxvtanmQYqepjVWwPROR/97BIP3sUtwzoHbMSMag2/C3+Un8p6Xz8+I+1sPG2UOBlvDsQe3Di0hlSET7EFwAQ==} - '@swagger-api/apidom-core@1.0.0-rc.1': - resolution: {integrity: sha512-vlguVts28oYBjCU5ZYfnX6yAFys/dZ1PUZqpYevMIGi8lEvxEfoxKEaUQa1Lr974cfKaVGBs8gNNhvDKXbH/jA==} + '@swagger-api/apidom-core@1.0.0-rc.3': + resolution: {integrity: sha512-cRf+HzoXl3iDPc7alVxdPbLb1TqRePqsxI0id2KaB8HYbyxTUy3ygqY/jmxGtfAAK0Ba85Bw8j4N0crw23vLTg==} - '@swagger-api/apidom-error@1.0.0-rc.1': - resolution: {integrity: sha512-74tTb6QX8VeAvu/9XipXd4Ly3N3q+yJez+lGZD7Qa11E00AhNpzqH7swgZKutLEfq1tHxyGWE1A6xF8IiU4CJg==} + '@swagger-api/apidom-error@1.0.0-rc.3': + resolution: {integrity: sha512-E9WsxzR9wwD4+1zmZm9PVvxXBAYxMtGJjpRYR/FthvxhIwx+Vsey2h5k7FPS8yJsawIrdGPQtdiFMLPvnQXUFg==} - '@swagger-api/apidom-json-pointer@1.0.0-rc.1': - resolution: {integrity: sha512-fNDQozPRuD9ReYcCnIqr5jU0faFDUl3VrUtfeLl3YevxNB+onZkUidUvzUJgDjZK9Se567BgL0rK9hnEO/Q8qw==} + '@swagger-api/apidom-json-pointer@1.0.0-rc.3': + resolution: {integrity: sha512-cj83L5ntai/RJcZV0++lQiCHPWE6lTy62bGC2lQ0yi/kyCc+Ig+Sn08qpiLSrkQ4OooK85X+wgAy6pMK+Vt/8Q==} - '@swagger-api/apidom-ns-api-design-systems@1.0.0-rc.1': - resolution: {integrity: sha512-gV6vQHpdtVKtrV+uUCPwsSL5nX5zD/3vR7dSYE0Lii7f7RkpIXAgQViZSbv7+h8TB20DNobGt+JZH/gGaY+Oxg==} + '@swagger-api/apidom-ns-api-design-systems@1.0.0-rc.3': + resolution: {integrity: sha512-JB06VDEKPvyOcJ9qIJmr2vI2FSWjdZh+BiRExZPW4tv/mTvdOxt1n38WA+mKzfFHQuoTR4ork/wR481CjAfGGQ==} - '@swagger-api/apidom-ns-arazzo-1@1.0.0-rc.1': - resolution: {integrity: sha512-Bx3PMLp+613EgSsLLg6Ucg3FtbO2i1bVcFZXgImun5pYNfmtQu21ELfWKj8ty/Ts2zR1VKOn5+i9DyMOH/zpsA==} + '@swagger-api/apidom-ns-arazzo-1@1.0.0-rc.3': + resolution: {integrity: sha512-Um0MGGsGLQWvnASDoguSuE5X/NpS/9RlXlOHHG5nqzG2cdTlifRcN5tiz7H997162+ahEsD5aHD6tUKWOPCLtQ==} - '@swagger-api/apidom-ns-asyncapi-2@1.0.0-rc.1': - resolution: {integrity: sha512-Vvo1f/H3mUuTny1d+XPudSattDWdHP1VhowxAOAFrnLVM4qvFbeBdzWjmTPEaeRsOz+Vq6rJOC4DPmHmtkR+oQ==} + '@swagger-api/apidom-ns-asyncapi-2@1.0.0-rc.3': + resolution: {integrity: sha512-UFmnbvEsN7jVvS/8V7X37UPvn8uxdqYBhDzdPSivjxpu/5Ag5Q1P2gHJnO6K2EfTCFL4S1qDObW2TUFdV1b6pg==} - '@swagger-api/apidom-ns-json-schema-2019-09@1.0.0-rc.1': - resolution: {integrity: sha512-1va09+kSTpNKc9oKs0rk2FWP2wk9AAdOcdmLpPEbzMnThQD1DHeBCk5OMStGZlaROxKWMPVZ5EmKy6rTRXvEIQ==} + '@swagger-api/apidom-ns-json-schema-2019-09@1.0.0-rc.3': + resolution: {integrity: sha512-fxQo/GK5NGdx4gN2snj4DpBcDc8bORLehTUqcwp33ikJ2PGugtpV3IQrBjxSWP05PyLOZAMpq1SM9gkCPgZNRA==} - '@swagger-api/apidom-ns-json-schema-2020-12@1.0.0-rc.1': - resolution: {integrity: sha512-ixNci2lwVD0yC4lUrmOOhgE/denI8keGVnHXYokbq0QxlQWuwuVzjVEtVMdmEaX3JaYVmEI5tr8K9MPW1zso1A==} + '@swagger-api/apidom-ns-json-schema-2020-12@1.0.0-rc.3': + resolution: {integrity: sha512-iDPbua9HajFwkH9vFUIbkmKVI/VXKuV9G+jLGkyBlF/Zu++1Rv6CstBt+F9CgNThSUqkKt3YA9Rcd82uh1+HnQ==} - '@swagger-api/apidom-ns-json-schema-draft-4@1.0.0-rc.1': - resolution: {integrity: sha512-kLGANNv8oAWWvnVqWat/AqOEo6XBfdMF3I7BLL2eZFBE8bhTbFMvmAvUfnwcehYo3K4vT+J60DWrwqYBoGSSUQ==} + '@swagger-api/apidom-ns-json-schema-draft-4@1.0.0-rc.3': + resolution: {integrity: sha512-8lft8qCo/KAHqiUpfwUMifP9JDhuhXKMNYSSahP2SN0PnbujoS1h3DOXtpR9/+0N6fKPUT8I6GLEwgq8TX2yvA==} - '@swagger-api/apidom-ns-json-schema-draft-6@1.0.0-rc.1': - resolution: {integrity: sha512-UzoTSrPOh+dwzSKZmawBwhWg4xGgpdNBmtV7jDJGEyFGsEkPvDBvViq+4sfMxO/BGoqPCD/jdt4yF16AKRxLiw==} + '@swagger-api/apidom-ns-json-schema-draft-6@1.0.0-rc.3': + resolution: {integrity: sha512-IDC+98ur+7L3YaZZnnCytx9+cihElj24CcjX/X2mOBqOTaAwZ/Exb7LiBnvUswV1lOE2X2CX4donRemjk+e32Q==} - '@swagger-api/apidom-ns-json-schema-draft-7@1.0.0-rc.1': - resolution: {integrity: sha512-3alW6gJKeb+DzTu+LYpYyEc5swo3oP8aoatOcVceWo/A/568zfIW0wWssf9WoasI42jEktV17z4A6ZwT6PzYbA==} + '@swagger-api/apidom-ns-json-schema-draft-7@1.0.0-rc.3': + resolution: {integrity: sha512-P0dk9WhH7CINBCh1u8GfcQFycrZcw3qCXug0w6M0wiSrjqZv+Mv/AI68dc0Rb+Dzshe4aZy0bZFjAQb3NHfrSg==} - '@swagger-api/apidom-ns-openapi-2@1.0.0-rc.1': - resolution: {integrity: sha512-SJ79fGH+WA7IYEXOJFPjXCB5bg6uoJDmkEYxMtZpN0Q+juFSkMcquh3jVf0j0y+6gFe/MZjIFDHxiBdeJarOig==} + '@swagger-api/apidom-ns-openapi-2@1.0.0-rc.3': + resolution: {integrity: sha512-zwriSfjG+qiPWBHLZRyfdZa305xrB24aZjiAY8r2ikZsdQhC/WHI+e6YqeVCkJwkLzA/oZgrlmyci0mvtkFDQA==} - '@swagger-api/apidom-ns-openapi-3-0@1.0.0-rc.1': - resolution: {integrity: sha512-TC2EBxBFJWD5pbZKUcbySqCt2nQmeP60ooS4f4Nl5r6vB/BeNbuO4FmO7CDI8OXD7b4J2+ro5KrXMs1EOQ3kVA==} + '@swagger-api/apidom-ns-openapi-3-0@1.0.0-rc.3': + resolution: {integrity: sha512-RCufXt7ja7fqFS/EqWOMZ54J4uEnqPQkCXMwwCqUrFHXQ7nGN1J9nmwj2hFQUFYraajmtnk2dNByO46+XefV1w==} - '@swagger-api/apidom-ns-openapi-3-1@1.0.0-rc.1': - resolution: {integrity: sha512-IY87MhqFBJnzhPWlr/OEVUa3iDjZuiwlyoWX4lw2jbKX+mLDrceGG5nqZawDACAjTjvtsjJcFP81D2VmjHVT5Q==} + '@swagger-api/apidom-ns-openapi-3-1@1.0.0-rc.3': + resolution: {integrity: sha512-Nc28G/ikbypcXVricv8+PGEGXKAmOwZjkBxB3wN5D4+D0+AiUy1lV07Z7+xFWdql65Y5WWxxfU2/Ej01Bnqt4Q==} - '@swagger-api/apidom-parser-adapter-api-design-systems-json@1.0.0-rc.1': - resolution: {integrity: sha512-1/koF8VwJHzFwk6FMWen39vpMUNcoCMXVY6MjMGag0h37LY5YAByl0LcYzLa33cvm5KCa23Aa75cu7Ns0SR1HQ==} + '@swagger-api/apidom-parser-adapter-api-design-systems-json@1.0.0-rc.3': + resolution: {integrity: sha512-ZXKuMd6nqBrpCqTJmbd2pS46ZmL8bIra1KqWVjcvkA/E032nmgDeaT78Cf0Ulha6j+CAzcwL0AnR7GrtFpSfSw==} - '@swagger-api/apidom-parser-adapter-api-design-systems-yaml@1.0.0-rc.1': - resolution: {integrity: sha512-Gjx1gojtYvGFqKnGttv84ba0RCkY7Xa+12kj9HVik8G+YVzUN78Qt8yu96ak0oXFlY1Ai8MQb5siC8YH4pC8Dg==} + '@swagger-api/apidom-parser-adapter-api-design-systems-yaml@1.0.0-rc.3': + resolution: {integrity: sha512-Qg1yTPPzGF3EhlqcxIZeDVBxxvZzylGM6CTHg5cltGOSoFQ7+NJFE9Ktvk0gbVaFUyElFduCno9FvIfzxPlj8g==} - '@swagger-api/apidom-parser-adapter-arazzo-json-1@1.0.0-rc.1': - resolution: {integrity: sha512-RHIly3bprJELMlt91UFqmMbAtIxDyHi8DM27YVXRjrX7zncP6QKyevcg2ajEe8UaNtkCFvPZW9h0gDh/ZW6ZYQ==} + '@swagger-api/apidom-parser-adapter-arazzo-json-1@1.0.0-rc.3': + resolution: {integrity: sha512-T7MbfTSDqdHgSr+cSC6gcGIsiwK3NXmdo28ZUv6LWsgcWDj2zw2Jie+7rXQaDN3JFEL34M/BIcMLyvrG7gYN/Q==} - '@swagger-api/apidom-parser-adapter-arazzo-yaml-1@1.0.0-rc.1': - resolution: {integrity: sha512-a+FweCIFAAnjvEWpMAd93xczbYX7AU4prwAMJ3QpFszltq2K7HKWUN1mMRplYPg5SSRLZojymdyMlu1evuP2Sg==} + '@swagger-api/apidom-parser-adapter-arazzo-yaml-1@1.0.0-rc.3': + resolution: {integrity: sha512-mUmxQVXPoemP2ak/77g/o8kpP2DNd1EDjteuyGHyw1EHk/t4xYPAP05rQ2DfIQ5yVHmxBKRDQ15kfVNEpfUfYQ==} - '@swagger-api/apidom-parser-adapter-asyncapi-json-2@1.0.0-rc.1': - resolution: {integrity: sha512-IKJ95OH35dW1+yGYDoE8uE3movG9z8Nht2QW8Ja75/H/jAFYGCxj56ZborEIiZxp83ItFqxQFn+ZUvwD7bDZew==} + '@swagger-api/apidom-parser-adapter-asyncapi-json-2@1.0.0-rc.3': + resolution: {integrity: sha512-K2BaslenC4ouPyzOQSB7wQPSsIGKGIj4VfP4M9y3fJaX9dIi+z3kzYQV7NFhZHAnq6pVybIDA44FLHF/WLCxUg==} - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@1.0.0-rc.1': - resolution: {integrity: sha512-cVu2Ue1U809HiGeAR/54yF42y4UKiWh45sEKzkXPYJUqRUd2Ewyo5KHtlckjNnCDRILZEhaPaZFpxURSbyUeSg==} + '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@1.0.0-rc.3': + resolution: {integrity: sha512-xJezoi5d+RtV7sG9VRcfpbLlJwaR6GoJr2S8lbsnMUkk/B2vZGdRbA2Fc67REQIJTEfxXcU8T3+5m8j0WrG9Xw==} - '@swagger-api/apidom-parser-adapter-json@1.0.0-rc.1': - resolution: {integrity: sha512-pmWOuZFxSNdbV1xNV0IoIrRiweaVl9yGAiEtiYH0BzbD+yGQSxi1ltMkZDVoyBPbe2NtygFDRaINSDLwuYpUYA==} + '@swagger-api/apidom-parser-adapter-json@1.0.0-rc.3': + resolution: {integrity: sha512-Y0dfIYvQE+OLjormlx6RjmA6ymNA6+nkqJC/6qkFt+4fSjfOiXwbOOnfZp9pJXb2ssmDDdrPTFc3ninx5k7jNw==} - '@swagger-api/apidom-parser-adapter-openapi-json-2@1.0.0-rc.1': - resolution: {integrity: sha512-+OsFBsD9PPqtcgETXU7l00/TMOfbtM+gvafJIdS/a+O1NQ2upAujQB3ArIB3sry3vloorjKmPyY6ZK/8rEKhNA==} + '@swagger-api/apidom-parser-adapter-openapi-json-2@1.0.0-rc.3': + resolution: {integrity: sha512-yaMS11FZVJLF062s+dch1kmUvBqdIS6mwAg/4XUL7XwSYat6pnV2ONCqdcUO9JSc9KJMZQiVAZjAZSj096ssNg==} - '@swagger-api/apidom-parser-adapter-openapi-json-3-0@1.0.0-rc.1': - resolution: {integrity: sha512-FEUJ+RaXKMP6LHMVeVyUPKdqjEqMSEZVhpvZt3Kh5fvnZvdgWngqs4gUjxO+dQCDVWkBxH/29uXm2eghdaM2Lw==} + '@swagger-api/apidom-parser-adapter-openapi-json-3-0@1.0.0-rc.3': + resolution: {integrity: sha512-5OdImG3eEgYpFvSo0EiZVvJJahk+f6cm5WZNn9lVdRlmxmtpzKM3UNfIYcBgVcAcLvfi8g6G7xRzD1DshaS8sw==} - '@swagger-api/apidom-parser-adapter-openapi-json-3-1@1.0.0-rc.1': - resolution: {integrity: sha512-pcfPj3FW2IWPYmU5kE0YB7npqV2vN+DvqUsw1GcDzsb8y2IdkzagHtMPZkM/KrfHFmhsoHm5YNpYC+Vvd2g61Q==} + '@swagger-api/apidom-parser-adapter-openapi-json-3-1@1.0.0-rc.3': + resolution: {integrity: sha512-UWlH29DOqKfHF2zwv7r5b7pgrc7Yxdus7FjYWA8p8yoIB02xDwHBaH4KhccIAXkm1qNMo+4TwSKFvO/boE8LMA==} - '@swagger-api/apidom-parser-adapter-openapi-yaml-2@1.0.0-rc.1': - resolution: {integrity: sha512-ckt6b1P+iwYkTMibixpo0oKWFm0wOGf88gslMMCo1xNaLVJnjxiadTQ/lNJd58CBJiQeN/dziTkRqGcFDqV9JQ==} + '@swagger-api/apidom-parser-adapter-openapi-yaml-2@1.0.0-rc.3': + resolution: {integrity: sha512-kSWzmalm98ScImQHHtpTBDAIEzLsfE24Pe1IIJP1TaI2rk1AuxzaCsqMl6NQIlnIEawghPOXlG0hLsgtswn/Jg==} - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@1.0.0-rc.1': - resolution: {integrity: sha512-JFyNwcj43cmps18Y+iqyna3uufyib8eLku+z4EhKFRPCuGFQ2bjsfVCFSP+Sv6sJATlagRRcfont+Q0BgNjwvw==} + '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@1.0.0-rc.3': + resolution: {integrity: sha512-IRxjOgmGpaA1ay/NITOqk3TKTXnGiJtNP8KsPm//i+HkGcg87lZEvRDflB2Z70aRofKncXM2rCMAEqFqV7A9ug==} - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@1.0.0-rc.1': - resolution: {integrity: sha512-kLRZYxJdix+irs0HTXJ223rj4Ou8AXo9IHiSf44KTuAZ/bsuakb0P8xROHg5MWTTEHYMfDrdLX+LaUo3b2GFyA==} + '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@1.0.0-rc.3': + resolution: {integrity: sha512-uvDMPiKt7uZSAOUVe+q/AygTFXw1odxxu5mi5voQM3/0KbR/vlt8f1dO9sQkys+G6ped2nL4r8B0p6bXR8uAMQ==} - '@swagger-api/apidom-parser-adapter-yaml-1-2@1.0.0-rc.1': - resolution: {integrity: sha512-XmRG/5lmoRusCupHEf10OeK1SQnSym4N1OrK+c3OTfN1GGX60Gxu2XCZ70pafJDuu+cvo/F8Db8UX3UOHapjwA==} + '@swagger-api/apidom-parser-adapter-yaml-1-2@1.0.0-rc.3': + resolution: {integrity: sha512-IiLIw74NRpRwi2YkV1hzmHC5JvvAm/TdeVYZoYK0QxeT2Ozr6MvhnUnRFjjSL3wcmku9+rLz2d8EGL2kO46qRA==} - '@swagger-api/apidom-reference@1.0.0-rc.1': - resolution: {integrity: sha512-Xj4aYrawCseCf6N6UuGSIaboN60ERmQVcKqXs/rybQz1gnD2AVqb8gklC2sUdOIUyN+ehDy+HDSM8I+yP32J0w==} + '@swagger-api/apidom-reference@1.0.0-rc.3': + resolution: {integrity: sha512-xZ9B6lGpdlHGSZGEhYe/MAyULCN4d+w4LKK5P1C/i6W6AU4iDEMjMjSawRV9ptJcObnu9ArEe92rgI7XS6s0TQ==} '@swaggerexpert/cookie@2.0.2': resolution: {integrity: sha512-DPI8YJ0Vznk4CT+ekn3rcFNq1uQwvUHZhH6WvTSPD0YKBIlMS9ur2RYKghXuxxOiqOam/i4lHJH4xTIiTgs3Mg==} @@ -9282,68 +9338,68 @@ packages: resolution: {integrity: sha512-qMx1nOrzoB+PF+pzb26Q4Tc2sOlrx9Ba2UBNX9hB31Omrq+QoZ2Gly0KLrQWw4Of1AQ4J9lnD+XOdwOdcdXqqw==} engines: {node: '>=12.20.0'} - '@swc/core-darwin-arm64@1.14.0': - resolution: {integrity: sha512-uHPC8rlCt04nvYNczWzKVdgnRhxCa3ndKTBBbBpResOZsRmiwRAvByIGh599j+Oo6Z5eyTPrgY+XfJzVmXnN7Q==} + '@swc/core-darwin-arm64@1.15.0': + resolution: {integrity: sha512-TBKWkbnShnEjlIbO4/gfsrIgAqHBVqgPWLbWmPdZ80bF393yJcLgkrb7bZEnJs6FCbSSuGwZv2rx1jDR2zo6YA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.14.0': - resolution: {integrity: sha512-2SHrlpl68vtePRknv9shvM9YKKg7B9T13tcTg9aFCwR318QTYo+FzsKGmQSv9ox/Ua0Q2/5y2BNjieffJoo4nA==} + '@swc/core-darwin-x64@1.15.0': + resolution: {integrity: sha512-f5JKL1v1H56CIZc1pVn4RGPOfnWqPwmuHdpf4wesvXunF1Bx85YgcspW5YxwqG5J9g3nPU610UFuExJXVUzOiQ==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.14.0': - resolution: {integrity: sha512-SMH8zn01dxt809svetnxpeg/jWdpi6dqHKO3Eb11u4OzU2PK7I5uKS6gf2hx5LlTbcJMFKULZiVwjlQLe8eqtg==} + '@swc/core-linux-arm-gnueabihf@1.15.0': + resolution: {integrity: sha512-duK6nG+WyuunnfsfiTUQdzC9Fk8cyDLqT9zyXvY2i2YgDu5+BH5W6wM5O4mDNCU5MocyB/SuF5YDF7XySnowiQ==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.14.0': - resolution: {integrity: sha512-q2JRu2D8LVqGeHkmpVCljVNltG0tB4o4eYg+dElFwCS8l2Mnt9qurMCxIeo9mgoqz0ax+k7jWtIRHktnVCbjvQ==} + '@swc/core-linux-arm64-gnu@1.15.0': + resolution: {integrity: sha512-ITe9iDtTRXM98B91rvyPP6qDVbhUBnmA/j4UxrHlMQ0RlwpqTjfZYZkD0uclOxSZ6qIrOj/X5CaoJlDUuQ0+Cw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.14.0': - resolution: {integrity: sha512-uofpVoPCEUjYIv454ZEZ3sLgMD17nIwlz2z7bsn7rl301Kt/01umFA7MscUovFfAK2IRGck6XB+uulMu6aFhKQ==} + '@swc/core-linux-arm64-musl@1.15.0': + resolution: {integrity: sha512-Q5ldc2bzriuzYEoAuqJ9Vr3FyZhakk5hiwDbniZ8tlEXpbjBhbOleGf9/gkhLaouDnkNUEazFW9mtqwUTRdh7Q==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.14.0': - resolution: {integrity: sha512-quTTx1Olm05fBfv66DEBuOsOgqdypnZ/1Bh3yGXWY7ANLFeeRpCDZpljD9BSjdsNdPOlwJmEUZXMHtGm3v1TZQ==} + '@swc/core-linux-x64-gnu@1.15.0': + resolution: {integrity: sha512-pY4is+jEpOxlYCSnI+7N8Oxbap9TmTz5YT84tUvRTlOlTBwFAUlWFCX0FRwWJlsfP0TxbqhIe8dNNzlsEmJbXQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.14.0': - resolution: {integrity: sha512-caaNAu+aIqT8seLtCf08i8C3/UC5ttQujUjejhMcuS1/LoCKtNiUs4VekJd2UGt+pyuuSrQ6dKl8CbCfWvWeXw==} + '@swc/core-linux-x64-musl@1.15.0': + resolution: {integrity: sha512-zYEt5eT8y8RUpoe7t5pjpoOdGu+/gSTExj8PV86efhj6ugB3bPlj3Y85ogdW3WMVXr4NvwqvzdaYGCZfXzSyVg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.14.0': - resolution: {integrity: sha512-EeW3jFlT3YNckJ6V/JnTfGcX7UHGyh6/AiCPopZ1HNaGiXVCKHPpVQZicmtyr/UpqxCXLrTgjHOvyMke7YN26A==} + '@swc/core-win32-arm64-msvc@1.15.0': + resolution: {integrity: sha512-zC1rmOgFH5v2BCbByOazEqs0aRNpTdLRchDExfcCfgKgeaD+IdpUOqp7i3VG1YzkcnbuZjMlXfM0ugpt+CddoA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.14.0': - resolution: {integrity: sha512-dPai3KUIcihV5hfoO4QNQF5HAaw8+2bT7dvi8E5zLtecW2SfL3mUZipzampXq5FHll0RSCLzlrXnSx+dBRZIIQ==} + '@swc/core-win32-ia32-msvc@1.15.0': + resolution: {integrity: sha512-7t9U9KwMwQblkdJIH+zX1V4q1o3o41i0HNO+VlnAHT5o+5qHJ963PHKJ/pX3P2UlZnBCY465orJuflAN4rAP9A==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.14.0': - resolution: {integrity: sha512-nm+JajGrTqUA6sEHdghDlHMNfH1WKSiuvljhdmBACW4ta4LC3gKurX2qZuiBARvPkephW9V/i5S8QPY1PzFEqg==} + '@swc/core-win32-x64-msvc@1.15.0': + resolution: {integrity: sha512-VE0Zod5vcs8iMLT64m5QS1DlTMXJFI/qSgtMDRx8rtZrnjt6/9NW8XUaiPJuRu8GluEO1hmHoyf1qlbY19gGSQ==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.14.0': - resolution: {integrity: sha512-oExhY90bes5pDTVrei0xlMVosTxwd/NMafIpqsC4dMbRYZ5KB981l/CX8tMnGsagTplj/RcG9BeRYmV6/J5m3w==} + '@swc/core@1.15.0': + resolution: {integrity: sha512-8SnJV+JV0rYbfSiEiUvYOmf62E7QwsEG+aZueqSlKoxFt0pw333+bgZSQXGUV6etXU88nxur0afVMaINujBMSw==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -9379,8 +9435,8 @@ packages: '@tanstack/query-core@5.77.1': resolution: {integrity: sha512-nfxVhy4UynChMFfN4NxwI8pktV9R3Zt/ROxOAe6pdOf8CigDLn26p+ex1YW5uien26BBICLmN0dTvIELHCs5vw==} - '@tanstack/query-core@5.90.6': - resolution: {integrity: sha512-AnZSLF26R8uX+tqb/ivdrwbVdGemdEDm1Q19qM6pry6eOZ6bEYiY7mWhzXT1YDIPTNEVcZ5kYP9nWjoxDLiIVw==} + '@tanstack/query-core@5.90.7': + resolution: {integrity: sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ==} '@tanstack/query-persist-client-core@4.27.0': resolution: {integrity: sha512-A+dPA7zG0MJOMDeBc/2WcKXW4wV2JMkeBVydobPW9G02M4q0yAj7vI+7SmM2dFuXyIvxXp4KulCywN6abRKDSQ==} @@ -10369,8 +10425,8 @@ packages: resolution: {integrity: sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typespec/ts-http-runtime@0.3.1': - resolution: {integrity: sha512-SnbaqayTVFEA6/tYumdF0UmybY0KHyKwGPBXnyckFlrrKdhWFrL3a2HIPXHjht5ZOElKGcXfD2D63P36btb+ww==} + '@typespec/ts-http-runtime@0.3.2': + resolution: {integrity: sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==} engines: {node: '>=20.0.0'} '@uiw/codemirror-extensions-basic-setup@4.23.14': @@ -10792,8 +10848,8 @@ packages: resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} engines: {node: '>=12'} - ai@5.0.87: - resolution: {integrity: sha512-9Cjx7o8IY9zAczigX0Tk/BaQwjPe/M6DpEjejKSBNrf8mOPIvyM+pJLqJSC10IsKci3FPsnaizJeJhoetU1Wfw==} + ai@5.0.89: + resolution: {integrity: sha512-8Nq+ZojGacQrupoJEQLrTDzT5VtR3gyp5AaqFSV3tzsAXlYQ9Igb7QE3yeoEdzOk5IRfDwWL7mDCUD+oBg1hDA==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -10875,8 +10931,8 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} - ansi-escapes@7.1.1: - resolution: {integrity: sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q==} + ansi-escapes@7.2.0: + resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} engines: {node: '>=18'} ansi-html-community@0.0.8: @@ -11637,8 +11693,8 @@ packages: balanced-match@2.0.0: resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} - bare-events@2.8.1: - resolution: {integrity: sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ==} + bare-events@2.8.2: + resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} peerDependencies: bare-abort-controller: '*' peerDependenciesMeta: @@ -11681,8 +11737,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.8.23: - resolution: {integrity: sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==} + baseline-browser-mapping@2.8.25: + resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==} hasBin: true basic-auth@2.0.1: @@ -11999,11 +12055,11 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-db@1.0.30001753: - resolution: {integrity: sha512-M08EdkYtgpwpvFLscj+mmtjDYaqFVYqbCMIDlsKnw86CxaT2bjlDAEaxtktDiv70uFQtwNV2Ou2UZhQogpr4Ew==} + caniuse-db@1.0.30001754: + resolution: {integrity: sha512-xiQRtIvVj33+nRR+rMUaDS+GTbU2J47APUoxmlTXimjJ0f20AY2dszjjo6eBli96r68al3d9OUnztZJmFIhN6w==} - caniuse-lite@1.0.30001753: - resolution: {integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==} + caniuse-lite@1.0.30001754: + resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} canvas@3.2.0: resolution: {integrity: sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA==} @@ -13330,8 +13386,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.244: - resolution: {integrity: sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==} + electron-to-chromium@1.5.249: + resolution: {integrity: sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg==} email-addresses@5.0.0: resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} @@ -14245,8 +14301,8 @@ packages: resolution: {integrity: sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==} deprecated: flatten is deprecated in favor of utility frameworks such as lodash. - flow-parser@0.289.0: - resolution: {integrity: sha512-w4sVnH6ddNAIxokoz0mGyiIIdzvqncFhAYW+RmkPbPSSTYozG6yhqAixzaWeBCQf2qqXJTlHkoKPnf/BAj8Ofw==} + flow-parser@0.290.0: + resolution: {integrity: sha512-9qXeNyrHPIoRK23kX7HNp275RYMy2y1AWb37y86ZTH/2UvfrofBis18aBunzfTIXkRpeD0F/w/uAKFhLUpboqQ==} engines: {node: '>=0.4.0'} flush-write-stream@1.1.1: @@ -17339,8 +17395,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - min-document@2.19.0: - resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} + min-document@2.19.1: + resolution: {integrity: sha512-8lqe85PkqQJzIcs2iD7xW/WSxcncC3/DPVbTOafKNJDIMXwGfwXS350mH4SJslomntN2iYtFBuC0yNO3CEap6g==} min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} @@ -17457,8 +17513,8 @@ packages: engines: {node: '>= 14.0.0'} hasBin: true - mocha@11.7.4: - resolution: {integrity: sha512-1jYAaY8x0kAZ0XszLWu14pzsf4KV740Gld4HXkhNTXwcHx4AUEDkPzgEHg9CM5dVcW+zv036tjpsEbLraPJj4w==} + mocha@11.7.5: + resolution: {integrity: sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true @@ -17525,8 +17581,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nan@2.23.0: - resolution: {integrity: sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==} + nan@2.23.1: + resolution: {integrity: sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==} nano-spawn@2.0.0: resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==} @@ -18210,8 +18266,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-scurry@2.0.0: - resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} engines: {node: 20 || >=22} path-to-regexp@0.1.12: @@ -19463,6 +19519,12 @@ packages: peerDependencies: react: '>= 0.14.0' + react-syntax-highlighter@16.1.0: + resolution: {integrity: sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==} + engines: {node: '>= 16.20.2'} + peerDependencies: + react: '>= 0.14.0' + react-test-renderer@19.1.1: resolution: {integrity: sha512-aGRXI+zcBTtg0diHofc7+Vy97nomBs9WHHFY1Csl3iV0x6xucjNYZZAkiVKGiNYUv23ecOex5jE67t8ZzqYObA==} peerDependencies: @@ -19624,6 +19686,9 @@ packages: refractor@3.6.0: resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + refractor@5.0.0: + resolution: {integrity: sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==} + regenerate-unicode-properties@10.2.2: resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} engines: {node: '>=4'} @@ -19968,8 +20033,8 @@ packages: resolution: {integrity: sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==} hasBin: true - rollup@4.52.5: - resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} + rollup@4.53.1: + resolution: {integrity: sha512-n2I0V0lN3E9cxxMqBCT3opWOiQBzRN7UG60z/WDKqdX2zHUS/39lezBcsckZFsV6fUTSnfqI7kHf60jDAPGKug==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -20103,8 +20168,8 @@ packages: sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} - sax@1.4.2: - resolution: {integrity: sha512-FySGAa0RGcFiN6zfrO9JvK1r7TB59xuzCcTHOBXBNoKgDejlOQCR2KL/FGk3/iDlsqyYg1ELZpOmlg09B01Czw==} + sax@1.4.3: + resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} saxes@3.1.11: resolution: {integrity: sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==} @@ -20995,8 +21060,8 @@ packages: react: '>=16.8.0 <19' react-dom: '>=16.8.0 <19' - swagger-ui-react@5.30.1: - resolution: {integrity: sha512-W3HP5vHkLy+f+N7sKv/zNuUArWypBjFXUIbvYyYQ0Ke50yUvW1WhQvogIp8FCi/y1/kp20nnEfTVxSG1CtvZqw==} + swagger-ui-react@5.30.2: + resolution: {integrity: sha512-0tS9GOcswKuQrIpCyvDoCDs6xS8B6MRC+iE7P99WfVXDhAIU+U7iFHuS4e7zucSh9qXvcL7KsXs623c+4oBe6w==} peerDependencies: react: '>=16.8.0 <20' react-dom: '>=16.8.0 <20' @@ -21043,8 +21108,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tailwindcss@4.1.16: - resolution: {integrity: sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==} + tailwindcss@4.1.17: + resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} tapable@0.2.9: resolution: {integrity: sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==} @@ -21131,8 +21196,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - terser@5.44.0: - resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} + terser@5.44.1: + resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} engines: {node: '>=10'} hasBin: true @@ -22905,9 +22970,9 @@ snapshots: '@adobe/css-tools@4.4.4': {} - '@ai-sdk/amazon-bedrock@3.0.51(zod@4.1.11)': + '@ai-sdk/amazon-bedrock@3.0.52(zod@4.1.11)': dependencies: - '@ai-sdk/anthropic': 2.0.41(zod@4.1.11) + '@ai-sdk/anthropic': 2.0.42(zod@4.1.11) '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.16(zod@4.1.11) '@smithy/eventstream-codec': 4.2.4 @@ -22915,19 +22980,39 @@ snapshots: aws4fetch: 1.0.20 zod: 4.1.11 - '@ai-sdk/anthropic@2.0.41(zod@4.1.11)': + '@ai-sdk/anthropic@2.0.42(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.16(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/anthropic@2.0.42(zod@4.1.11)': dependencies: '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.16(zod@4.1.11) zod: 4.1.11 - '@ai-sdk/gateway@2.0.6(zod@4.1.11)': + '@ai-sdk/gateway@2.0.7(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.16(zod@3.25.76) + '@vercel/oidc': 3.0.3 + zod: 3.25.76 + + '@ai-sdk/gateway@2.0.7(zod@4.1.11)': dependencies: '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.16(zod@4.1.11) '@vercel/oidc': 3.0.3 zod: 4.1.11 + '@ai-sdk/provider-utils@3.0.16(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@standard-schema/spec': 1.0.0 + eventsource-parser: 3.0.6 + zod: 3.25.76 + '@ai-sdk/provider-utils@3.0.16(zod@4.1.11)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -23009,31 +23094,31 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.922.0': + '@aws-sdk/client-s3@3.927.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.922.0 - '@aws-sdk/credential-provider-node': 3.922.0 + '@aws-sdk/core': 3.927.0 + '@aws-sdk/credential-provider-node': 3.927.0 '@aws-sdk/middleware-bucket-endpoint': 3.922.0 '@aws-sdk/middleware-expect-continue': 3.922.0 - '@aws-sdk/middleware-flexible-checksums': 3.922.0 + '@aws-sdk/middleware-flexible-checksums': 3.927.0 '@aws-sdk/middleware-host-header': 3.922.0 '@aws-sdk/middleware-location-constraint': 3.922.0 '@aws-sdk/middleware-logger': 3.922.0 '@aws-sdk/middleware-recursion-detection': 3.922.0 - '@aws-sdk/middleware-sdk-s3': 3.922.0 + '@aws-sdk/middleware-sdk-s3': 3.927.0 '@aws-sdk/middleware-ssec': 3.922.0 - '@aws-sdk/middleware-user-agent': 3.922.0 - '@aws-sdk/region-config-resolver': 3.922.0 - '@aws-sdk/signature-v4-multi-region': 3.922.0 + '@aws-sdk/middleware-user-agent': 3.927.0 + '@aws-sdk/region-config-resolver': 3.925.0 + '@aws-sdk/signature-v4-multi-region': 3.927.0 '@aws-sdk/types': 3.922.0 '@aws-sdk/util-endpoints': 3.922.0 '@aws-sdk/util-user-agent-browser': 3.922.0 - '@aws-sdk/util-user-agent-node': 3.922.0 + '@aws-sdk/util-user-agent-node': 3.927.0 '@aws-sdk/xml-builder': 3.921.0 - '@smithy/config-resolver': 4.4.1 + '@smithy/config-resolver': 4.4.2 '@smithy/core': 3.17.2 '@smithy/eventstream-serde-browser': 4.2.4 '@smithy/eventstream-serde-config-resolver': 4.3.4 @@ -23059,7 +23144,7 @@ snapshots: '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 '@smithy/util-defaults-mode-browser': 4.3.5 - '@smithy/util-defaults-mode-node': 4.2.7 + '@smithy/util-defaults-mode-node': 4.2.8 '@smithy/util-endpoints': 3.2.4 '@smithy/util-middleware': 4.2.4 '@smithy/util-retry': 4.2.4 @@ -23071,21 +23156,21 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.922.0': + '@aws-sdk/client-sso@3.927.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.922.0 + '@aws-sdk/core': 3.927.0 '@aws-sdk/middleware-host-header': 3.922.0 '@aws-sdk/middleware-logger': 3.922.0 '@aws-sdk/middleware-recursion-detection': 3.922.0 - '@aws-sdk/middleware-user-agent': 3.922.0 - '@aws-sdk/region-config-resolver': 3.922.0 + '@aws-sdk/middleware-user-agent': 3.927.0 + '@aws-sdk/region-config-resolver': 3.925.0 '@aws-sdk/types': 3.922.0 '@aws-sdk/util-endpoints': 3.922.0 '@aws-sdk/util-user-agent-browser': 3.922.0 - '@aws-sdk/util-user-agent-node': 3.922.0 - '@smithy/config-resolver': 4.4.1 + '@aws-sdk/util-user-agent-node': 3.927.0 + '@smithy/config-resolver': 4.4.2 '@smithy/core': 3.17.2 '@smithy/fetch-http-handler': 5.3.5 '@smithy/hash-node': 4.2.4 @@ -23105,7 +23190,7 @@ snapshots: '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 '@smithy/util-defaults-mode-browser': 4.3.5 - '@smithy/util-defaults-mode-node': 4.2.7 + '@smithy/util-defaults-mode-node': 4.2.8 '@smithy/util-endpoints': 3.2.4 '@smithy/util-middleware': 4.2.4 '@smithy/util-retry': 4.2.4 @@ -23114,7 +23199,7 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.922.0': + '@aws-sdk/core@3.927.0': dependencies: '@aws-sdk/types': 3.922.0 '@aws-sdk/xml-builder': 3.921.0 @@ -23130,17 +23215,17 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.922.0': + '@aws-sdk/credential-provider-env@3.927.0': dependencies: - '@aws-sdk/core': 3.922.0 + '@aws-sdk/core': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/property-provider': 4.2.4 '@smithy/types': 4.8.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.922.0': + '@aws-sdk/credential-provider-http@3.927.0': dependencies: - '@aws-sdk/core': 3.922.0 + '@aws-sdk/core': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/fetch-http-handler': 5.3.5 '@smithy/node-http-handler': 4.4.4 @@ -23151,15 +23236,15 @@ snapshots: '@smithy/util-stream': 4.5.5 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.922.0': + '@aws-sdk/credential-provider-ini@3.927.0': dependencies: - '@aws-sdk/core': 3.922.0 - '@aws-sdk/credential-provider-env': 3.922.0 - '@aws-sdk/credential-provider-http': 3.922.0 - '@aws-sdk/credential-provider-process': 3.922.0 - '@aws-sdk/credential-provider-sso': 3.922.0 - '@aws-sdk/credential-provider-web-identity': 3.922.0 - '@aws-sdk/nested-clients': 3.922.0 + '@aws-sdk/core': 3.927.0 + '@aws-sdk/credential-provider-env': 3.927.0 + '@aws-sdk/credential-provider-http': 3.927.0 + '@aws-sdk/credential-provider-process': 3.927.0 + '@aws-sdk/credential-provider-sso': 3.927.0 + '@aws-sdk/credential-provider-web-identity': 3.927.0 + '@aws-sdk/nested-clients': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/credential-provider-imds': 4.2.4 '@smithy/property-provider': 4.2.4 @@ -23169,14 +23254,14 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.922.0': + '@aws-sdk/credential-provider-node@3.927.0': dependencies: - '@aws-sdk/credential-provider-env': 3.922.0 - '@aws-sdk/credential-provider-http': 3.922.0 - '@aws-sdk/credential-provider-ini': 3.922.0 - '@aws-sdk/credential-provider-process': 3.922.0 - '@aws-sdk/credential-provider-sso': 3.922.0 - '@aws-sdk/credential-provider-web-identity': 3.922.0 + '@aws-sdk/credential-provider-env': 3.927.0 + '@aws-sdk/credential-provider-http': 3.927.0 + '@aws-sdk/credential-provider-ini': 3.927.0 + '@aws-sdk/credential-provider-process': 3.927.0 + '@aws-sdk/credential-provider-sso': 3.927.0 + '@aws-sdk/credential-provider-web-identity': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/credential-provider-imds': 4.2.4 '@smithy/property-provider': 4.2.4 @@ -23186,20 +23271,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.922.0': + '@aws-sdk/credential-provider-process@3.927.0': dependencies: - '@aws-sdk/core': 3.922.0 + '@aws-sdk/core': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/property-provider': 4.2.4 '@smithy/shared-ini-file-loader': 4.3.4 '@smithy/types': 4.8.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.922.0': + '@aws-sdk/credential-provider-sso@3.927.0': dependencies: - '@aws-sdk/client-sso': 3.922.0 - '@aws-sdk/core': 3.922.0 - '@aws-sdk/token-providers': 3.922.0 + '@aws-sdk/client-sso': 3.927.0 + '@aws-sdk/core': 3.927.0 + '@aws-sdk/token-providers': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/property-provider': 4.2.4 '@smithy/shared-ini-file-loader': 4.3.4 @@ -23208,10 +23293,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.922.0': + '@aws-sdk/credential-provider-web-identity@3.927.0': dependencies: - '@aws-sdk/core': 3.922.0 - '@aws-sdk/nested-clients': 3.922.0 + '@aws-sdk/core': 3.927.0 + '@aws-sdk/nested-clients': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/property-provider': 4.2.4 '@smithy/shared-ini-file-loader': 4.3.4 @@ -23237,12 +23322,12 @@ snapshots: '@smithy/types': 4.8.1 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.922.0': + '@aws-sdk/middleware-flexible-checksums@3.927.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.922.0 + '@aws-sdk/core': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/is-array-buffer': 4.2.0 '@smithy/node-config-provider': 4.3.4 @@ -23280,9 +23365,9 @@ snapshots: '@smithy/types': 4.8.1 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.922.0': + '@aws-sdk/middleware-sdk-s3@3.927.0': dependencies: - '@aws-sdk/core': 3.922.0 + '@aws-sdk/core': 3.927.0 '@aws-sdk/types': 3.922.0 '@aws-sdk/util-arn-parser': 3.893.0 '@smithy/core': 3.17.2 @@ -23303,9 +23388,9 @@ snapshots: '@smithy/types': 4.8.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.922.0': + '@aws-sdk/middleware-user-agent@3.927.0': dependencies: - '@aws-sdk/core': 3.922.0 + '@aws-sdk/core': 3.927.0 '@aws-sdk/types': 3.922.0 '@aws-sdk/util-endpoints': 3.922.0 '@smithy/core': 3.17.2 @@ -23313,21 +23398,21 @@ snapshots: '@smithy/types': 4.8.1 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.922.0': + '@aws-sdk/nested-clients@3.927.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.922.0 + '@aws-sdk/core': 3.927.0 '@aws-sdk/middleware-host-header': 3.922.0 '@aws-sdk/middleware-logger': 3.922.0 '@aws-sdk/middleware-recursion-detection': 3.922.0 - '@aws-sdk/middleware-user-agent': 3.922.0 - '@aws-sdk/region-config-resolver': 3.922.0 + '@aws-sdk/middleware-user-agent': 3.927.0 + '@aws-sdk/region-config-resolver': 3.925.0 '@aws-sdk/types': 3.922.0 '@aws-sdk/util-endpoints': 3.922.0 '@aws-sdk/util-user-agent-browser': 3.922.0 - '@aws-sdk/util-user-agent-node': 3.922.0 - '@smithy/config-resolver': 4.4.1 + '@aws-sdk/util-user-agent-node': 3.927.0 + '@smithy/config-resolver': 4.4.2 '@smithy/core': 3.17.2 '@smithy/fetch-http-handler': 5.3.5 '@smithy/hash-node': 4.2.4 @@ -23347,7 +23432,7 @@ snapshots: '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 '@smithy/util-defaults-mode-browser': 4.3.5 - '@smithy/util-defaults-mode-node': 4.2.7 + '@smithy/util-defaults-mode-node': 4.2.8 '@smithy/util-endpoints': 3.2.4 '@smithy/util-middleware': 4.2.4 '@smithy/util-retry': 4.2.4 @@ -23356,27 +23441,27 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.922.0': + '@aws-sdk/region-config-resolver@3.925.0': dependencies: '@aws-sdk/types': 3.922.0 - '@smithy/config-resolver': 4.4.1 + '@smithy/config-resolver': 4.4.2 '@smithy/node-config-provider': 4.3.4 '@smithy/types': 4.8.1 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.922.0': + '@aws-sdk/signature-v4-multi-region@3.927.0': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.922.0 + '@aws-sdk/middleware-sdk-s3': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/protocol-http': 5.3.4 '@smithy/signature-v4': 5.3.4 '@smithy/types': 4.8.1 tslib: 2.8.1 - '@aws-sdk/token-providers@3.922.0': + '@aws-sdk/token-providers@3.927.0': dependencies: - '@aws-sdk/core': 3.922.0 - '@aws-sdk/nested-clients': 3.922.0 + '@aws-sdk/core': 3.927.0 + '@aws-sdk/nested-clients': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/property-provider': 4.2.4 '@smithy/shared-ini-file-loader': 4.3.4 @@ -23413,9 +23498,9 @@ snapshots: bowser: 2.12.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.922.0': + '@aws-sdk/util-user-agent-node@3.927.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.922.0 + '@aws-sdk/middleware-user-agent': 3.927.0 '@aws-sdk/types': 3.922.0 '@smithy/node-config-provider': 4.3.4 '@smithy/types': 4.8.1 @@ -23451,7 +23536,7 @@ snapshots: dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.1 - '@azure/core-rest-pipeline': 1.22.1 + '@azure/core-rest-pipeline': 1.22.2 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 @@ -23459,14 +23544,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/core-rest-pipeline@1.22.1': + '@azure/core-rest-pipeline@1.22.2': dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.1 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 - '@typespec/ts-http-runtime': 0.3.1 + '@typespec/ts-http-runtime': 0.3.2 tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -23478,7 +23563,7 @@ snapshots: '@azure/core-util@1.13.1': dependencies: '@azure/abort-controller': 2.1.2 - '@typespec/ts-http-runtime': 0.3.1 + '@typespec/ts-http-runtime': 0.3.2 tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -23488,11 +23573,11 @@ snapshots: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.1 '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.1 + '@azure/core-rest-pipeline': 1.22.2 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 - '@azure/msal-browser': 4.26.0 + '@azure/msal-browser': 4.26.1 '@azure/msal-node': 3.8.1 open: 10.2.0 tslib: 2.8.1 @@ -23501,12 +23586,12 @@ snapshots: '@azure/logger@1.3.0': dependencies: - '@typespec/ts-http-runtime': 0.3.1 + '@typespec/ts-http-runtime': 0.3.2 tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@azure/msal-browser@4.26.0': + '@azure/msal-browser@4.26.1': dependencies: '@azure/msal-common': 15.13.1 @@ -25497,7 +25582,7 @@ snapshots: '@codemirror/view': 6.38.6 crelt: 1.0.6 - '@codemirror/merge@6.11.1': + '@codemirror/merge@6.11.2': dependencies: '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 @@ -26049,7 +26134,7 @@ snapshots: '@radix-ui/react-dialog': 1.1.15(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.2.4(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/codemirror': 5.60.17 clsx: 1.2.1 codemirror: 5.65.20 @@ -26232,7 +26317,7 @@ snapshots: - supports-color - utf-8-validate - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3))': + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -26246,7 +26331,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -26895,7 +26980,7 @@ snapshots: dependencies: '@codemirror/lang-markdown': 6.5.0 '@codemirror/language-data': 6.5.2 - '@codemirror/merge': 6.11.1 + '@codemirror/merge': 6.11.2 '@codemirror/state': 6.5.2 '@codemirror/view': 6.38.6 '@codesandbox/sandpack-react': 2.20.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -27052,7 +27137,7 @@ snapshots: '@modelcontextprotocol/inspector-cli@0.17.2': dependencies: - '@modelcontextprotocol/sdk': 1.21.0 + '@modelcontextprotocol/sdk': 1.21.1 commander: 13.1.0 spawn-rx: 5.1.2 transitivePeerDependencies: @@ -27061,14 +27146,14 @@ snapshots: '@modelcontextprotocol/inspector-client@0.17.2(@types/react-dom@18.2.0)(@types/react@18.2.0)': dependencies: - '@modelcontextprotocol/sdk': 1.21.0 + '@modelcontextprotocol/sdk': 1.21.1 '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dialog': 1.1.15(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-icons': 1.3.2(react@18.3.1) - '@radix-ui/react-label': 2.1.7(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-label': 2.1.8(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-popover': 1.1.15(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-select': 2.2.6(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.2.0)(react@18.3.1) + '@radix-ui/react-slot': 1.2.4(@types/react@18.2.0)(react@18.3.1) '@radix-ui/react-switch': 1.2.6(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tabs': 1.1.13(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-toast': 1.2.15(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -27094,7 +27179,7 @@ snapshots: '@modelcontextprotocol/inspector-server@0.17.2': dependencies: - '@modelcontextprotocol/sdk': 1.21.0 + '@modelcontextprotocol/sdk': 1.21.1 cors: 2.8.5 express: 5.1.0 shell-quote: 1.8.3 @@ -27107,18 +27192,18 @@ snapshots: - supports-color - utf-8-validate - '@modelcontextprotocol/inspector@0.17.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.18)(@types/react-dom@18.2.0)(@types/react@18.2.0)(typescript@5.8.3)': + '@modelcontextprotocol/inspector@0.17.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.18)(@types/react-dom@18.2.0)(@types/react@18.2.0)(typescript@5.8.3)': dependencies: '@modelcontextprotocol/inspector-cli': 0.17.2 '@modelcontextprotocol/inspector-client': 0.17.2(@types/react-dom@18.2.0)(@types/react@18.2.0) '@modelcontextprotocol/inspector-server': 0.17.2 - '@modelcontextprotocol/sdk': 1.21.0 + '@modelcontextprotocol/sdk': 1.21.1 concurrently: 9.2.1 node-fetch: 3.3.2 open: 10.2.0 shell-quote: 1.8.3 spawn-rx: 5.1.2 - ts-node: 10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3) + ts-node: 10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3) zod: 3.25.76 transitivePeerDependencies: - '@cfworker/json-schema' @@ -27132,7 +27217,7 @@ snapshots: - typescript - utf-8-validate - '@modelcontextprotocol/sdk@1.21.0': + '@modelcontextprotocol/sdk@1.21.1': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) @@ -27349,7 +27434,7 @@ snapshots: dependencies: playwright: 1.55.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.17(@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1)(webpack@5.102.1)': + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1)(webpack@5.102.1)': dependencies: ansi-html: 0.0.9 core-js-pure: 3.46.0 @@ -27359,14 +27444,14 @@ snapshots: react-refresh: 0.11.0 schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) optionalDependencies: - '@types/webpack': 5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + '@types/webpack': 5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) type-fest: 4.41.0 webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.102.1) webpack-hot-middleware: 2.26.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.17(@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17)))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17)))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)))': dependencies: ansi-html: 0.0.9 core-js-pure: 3.46.0 @@ -27376,11 +27461,11 @@ snapshots: react-refresh: 0.11.0 schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optionalDependencies: - '@types/webpack': 5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17)) + '@types/webpack': 5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17)) type-fest: 4.41.0 - webpack-dev-server: 5.2.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + webpack-dev-server: 5.2.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) webpack-hot-middleware: 2.26.1 '@pmmmwh/react-refresh-webpack-plugin@0.5.17(@types/webpack@5.28.5(webpack-cli@4.10.0))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1)(webpack@5.102.1)': @@ -28120,9 +28205,9 @@ snapshots: optionalDependencies: '@types/react': 18.2.0 - '@radix-ui/react-label@2.1.7(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-label@2.1.8(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: @@ -28382,6 +28467,24 @@ snapshots: '@types/react': 18.2.0 '@types/react-dom': 18.2.0 + '@radix-ui/react-primitive@2.1.4(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-slot': 1.2.4(@types/react@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.0 + '@types/react-dom': 18.2.0 + + '@radix-ui/react-primitive@2.1.4(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.2.4(@types/react@18.2.0)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.0 + '@types/react-dom': 18.2.0 + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -28606,6 +28709,20 @@ snapshots: optionalDependencies: '@types/react': 18.2.0 + '@radix-ui/react-slot@1.2.4(@types/react@18.2.0)(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.0)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.0 + + '@radix-ui/react-slot@1.2.4(@types/react@18.2.0)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.0)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.0 + '@radix-ui/react-switch@1.2.6(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -29058,6 +29175,15 @@ snapshots: '@types/react': 18.2.0 '@types/react-dom': 18.2.0 + '@radix-ui/react-visually-hidden@1.2.4(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.0 + '@types/react-dom': 18.2.0 + '@radix-ui/rect@1.0.1': dependencies: '@babel/runtime': 7.28.4 @@ -29166,9 +29292,9 @@ snapshots: resolve: 1.22.11 rollup: 1.32.1 - '@rollup/plugin-commonjs@28.0.9(rollup@4.52.5)': + '@rollup/plugin-commonjs@28.0.9(rollup@4.53.1)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0(rollup@4.53.1) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) @@ -29176,28 +29302,28 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.3 optionalDependencies: - rollup: 4.52.5 + rollup: 4.53.1 '@rollup/plugin-json@4.1.0(rollup@1.32.1)': dependencies: '@rollup/pluginutils': 3.1.0(rollup@1.32.1) rollup: 1.32.1 - '@rollup/plugin-json@6.1.0(rollup@4.52.5)': + '@rollup/plugin-json@6.1.0(rollup@4.53.1)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0(rollup@4.53.1) optionalDependencies: - rollup: 4.52.5 + rollup: 4.53.1 - '@rollup/plugin-node-resolve@16.0.3(rollup@4.52.5)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.53.1)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0(rollup@4.53.1) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.11 optionalDependencies: - rollup: 4.52.5 + rollup: 4.53.1 '@rollup/plugin-node-resolve@9.0.0(rollup@1.32.1)': dependencies: @@ -29227,78 +29353,78 @@ snapshots: estree-walker: 2.0.2 picomatch: 2.3.1 - '@rollup/pluginutils@5.3.0(rollup@4.52.5)': + '@rollup/pluginutils@5.3.0(rollup@4.53.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.52.5 + rollup: 4.53.1 - '@rollup/rollup-android-arm-eabi@4.52.5': + '@rollup/rollup-android-arm-eabi@4.53.1': optional: true - '@rollup/rollup-android-arm64@4.52.5': + '@rollup/rollup-android-arm64@4.53.1': optional: true - '@rollup/rollup-darwin-arm64@4.52.5': + '@rollup/rollup-darwin-arm64@4.53.1': optional: true - '@rollup/rollup-darwin-x64@4.52.5': + '@rollup/rollup-darwin-x64@4.53.1': optional: true - '@rollup/rollup-freebsd-arm64@4.52.5': + '@rollup/rollup-freebsd-arm64@4.53.1': optional: true - '@rollup/rollup-freebsd-x64@4.52.5': + '@rollup/rollup-freebsd-x64@4.53.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + '@rollup/rollup-linux-arm-gnueabihf@4.53.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.5': + '@rollup/rollup-linux-arm-musleabihf@4.53.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.5': + '@rollup/rollup-linux-arm64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.5': + '@rollup/rollup-linux-arm64-musl@4.53.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.5': + '@rollup/rollup-linux-loong64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.5': + '@rollup/rollup-linux-ppc64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.5': + '@rollup/rollup-linux-riscv64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.5': + '@rollup/rollup-linux-riscv64-musl@4.53.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.5': + '@rollup/rollup-linux-s390x-gnu@4.53.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.5': + '@rollup/rollup-linux-x64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-x64-musl@4.52.5': + '@rollup/rollup-linux-x64-musl@4.53.1': optional: true - '@rollup/rollup-openharmony-arm64@4.52.5': + '@rollup/rollup-openharmony-arm64@4.53.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.5': + '@rollup/rollup-win32-arm64-msvc@4.53.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.5': + '@rollup/rollup-win32-ia32-msvc@4.53.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.5': + '@rollup/rollup-win32-x64-gnu@4.53.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.5': + '@rollup/rollup-win32-x64-msvc@4.53.1': optional: true '@rtsao/scc@1.1.0': {} @@ -29461,7 +29587,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/config-resolver@4.4.1': + '@smithy/config-resolver@4.4.2': dependencies: '@smithy/node-config-provider': 4.3.4 '@smithy/types': 4.8.1 @@ -29719,9 +29845,9 @@ snapshots: '@smithy/types': 4.8.1 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.7': + '@smithy/util-defaults-mode-node@4.2.8': dependencies: - '@smithy/config-resolver': 4.4.1 + '@smithy/config-resolver': 4.4.2 '@smithy/credential-provider-imds': 4.2.4 '@smithy/node-config-provider': 4.3.4 '@smithy/property-provider': 4.2.4 @@ -29960,13 +30086,13 @@ snapshots: storybook: 9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3) ts-dedent: 2.2.0 - '@storybook/addon-controls@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': + '@storybook/addon-controls@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': dependencies: '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/api': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/client-logger': 6.5.16 '@storybook/components': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/node-logger': 6.5.16 '@storybook/store': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -30074,7 +30200,7 @@ snapshots: storybook: 9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3) ts-dedent: 2.2.0 - '@storybook/addon-docs@6.5.16(@babel/core@7.27.7)(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1)': + '@storybook/addon-docs@6.5.16(@babel/core@7.27.7)(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1)': dependencies: '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.7) '@babel/preset-env': 7.27.2(@babel/core@7.27.7) @@ -30083,7 +30209,7 @@ snapshots: '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/api': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/components': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/core-events': 6.5.16 '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/docs-tools': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -30261,29 +30387,29 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@storybook/addon-essentials@6.5.16(@babel/core@7.27.7)(@storybook/builder-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1)': + '@storybook/addon-essentials@6.5.16(@babel/core@7.27.7)(@storybook/builder-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1)': dependencies: '@babel/core': 7.27.7 '@storybook/addon-actions': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addon-backgrounds': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/addon-controls': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) - '@storybook/addon-docs': 6.5.16(@babel/core@7.27.7)(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1) + '@storybook/addon-controls': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/addon-docs': 6.5.16(@babel/core@7.27.7)(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1) '@storybook/addon-measure': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addon-outline': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addon-toolbars': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addon-viewport': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/api': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/node-logger': 6.5.16 core-js: 3.46.0 regenerator-runtime: 0.13.11 ts-dedent: 2.2.0 optionalDependencies: - '@storybook/builder-webpack5': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/builder-webpack5': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) transitivePeerDependencies: - '@storybook/mdx2-csf' - '@swc/core' @@ -30935,7 +31061,7 @@ snapshots: storybook: 9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3) ts-dedent: 2.2.0 - '@storybook/builder-webpack4@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': + '@storybook/builder-webpack4@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': dependencies: '@babel/core': 7.27.7 '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -30945,7 +31071,7 @@ snapshots: '@storybook/client-api': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/client-logger': 6.5.16 '@storybook/components': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) '@storybook/core-events': 6.5.16 '@storybook/node-logger': 6.5.16 '@storybook/preview-web': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -30957,33 +31083,33 @@ snapshots: '@types/node': 16.18.126 '@types/webpack': 4.41.40 autoprefixer: 9.8.8 - babel-loader: 8.4.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + babel-loader: 8.4.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) case-sensitive-paths-webpack-plugin: 2.4.0 core-js: 3.46.0 - css-loader: 3.6.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - file-loader: 6.2.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + css-loader: 3.6.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + file-loader: 6.2.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) find-up: 5.0.0 fork-ts-checker-webpack-plugin: 4.1.6 glob: 7.2.3 glob-promise: 3.4.0(glob@7.2.3) global: 4.4.0 - html-webpack-plugin: 4.5.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + html-webpack-plugin: 4.5.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) pnp-webpack-plugin: 1.6.4(typescript@5.8.3) postcss: 7.0.39 postcss-flexbugs-fixes: 4.2.1 - postcss-loader: 4.3.0(postcss@7.0.39)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - raw-loader: 4.0.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + postcss-loader: 4.3.0(postcss@7.0.39)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + raw-loader: 4.0.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) stable: 0.1.8 - style-loader: 1.3.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - terser-webpack-plugin: 4.2.3(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + style-loader: 1.3.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + terser-webpack-plugin: 4.2.3(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))))(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))))(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) - webpack-dev-middleware: 3.7.3(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - webpack-filter-warnings-plugin: 1.2.1(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) + webpack-dev-middleware: 3.7.3(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + webpack-filter-warnings-plugin: 1.2.1(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.2.2 optionalDependencies: @@ -30997,7 +31123,7 @@ snapshots: - vue-template-compiler - webpack-cli - '@storybook/builder-webpack4@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': + '@storybook/builder-webpack4@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': dependencies: '@babel/core': 7.27.7 '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -31007,7 +31133,7 @@ snapshots: '@storybook/client-api': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/client-logger': 6.5.16 '@storybook/components': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/core-events': 6.5.16 '@storybook/node-logger': 6.5.16 '@storybook/preview-web': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -31043,7 +31169,7 @@ snapshots: ts-dedent: 2.2.0 url-loader: 4.1.1(file-loader@6.2.0(webpack@5.102.1))(webpack@5.102.1) util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-dev-middleware: 3.7.3(webpack@5.102.1) webpack-filter-warnings-plugin: 1.2.1(webpack@5.102.1) webpack-hot-middleware: 2.26.1 @@ -31307,7 +31433,7 @@ snapshots: - vue-template-compiler - webpack-cli - '@storybook/builder-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': + '@storybook/builder-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': dependencies: '@babel/core': 7.27.7 '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -31317,7 +31443,7 @@ snapshots: '@storybook/client-api': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/client-logger': 6.5.16 '@storybook/components': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/core-events': 6.5.16 '@storybook/node-logger': 6.5.16 '@storybook/preview-web': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -31342,10 +31468,10 @@ snapshots: react-dom: 18.2.0(react@18.2.0) stable: 0.1.8 style-loader: 2.0.0(webpack@5.102.1) - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) ts-dedent: 2.2.0 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-dev-middleware: 4.3.0(webpack@5.102.1) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.4.6 @@ -31396,7 +31522,7 @@ snapshots: react-dom: 18.2.0(react@18.2.0) stable: 0.1.8 style-loader: 2.0.0(webpack@5.102.1) - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) ts-dedent: 2.2.0 util-deprecate: 1.0.2 webpack: 5.102.1(webpack-cli@4.10.0) @@ -31434,7 +31560,7 @@ snapshots: '@storybook/router': 7.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/store': 7.4.6 '@storybook/theming': 7.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@swc/core': 1.14.0(@swc/helpers@0.5.17) + '@swc/core': 1.15.0(@swc/helpers@0.5.17) '@types/node': 16.18.126 '@types/semver': 7.7.1 babel-loader: 9.2.1(@babel/core@7.27.7)(webpack@5.102.1) @@ -31453,13 +31579,13 @@ snapshots: react-dom: 18.2.0(react@18.2.0) semver: 7.7.3 style-loader: 3.3.4(webpack@5.102.1) - swc-loader: 0.2.6(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + swc-loader: 0.2.6(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) ts-dedent: 2.2.0 url: 0.11.4 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4) webpack-dev-middleware: 6.1.3(webpack@5.102.1) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.5.0 @@ -31495,33 +31621,33 @@ snapshots: '@storybook/router': 7.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@storybook/store': 7.4.6 '@storybook/theming': 7.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@swc/core': 1.14.0(@swc/helpers@0.5.17) + '@swc/core': 1.15.0(@swc/helpers@0.5.17) '@types/node': 16.18.126 '@types/semver': 7.7.1 - babel-loader: 9.2.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + babel-loader: 9.2.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) babel-plugin-named-exports-order: 0.0.2 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 constants-browserify: 1.0.0 - css-loader: 6.11.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + css-loader: 6.11.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) express: 4.21.2 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) fs-extra: 11.3.2 - html-webpack-plugin: 5.6.4(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + html-webpack-plugin: 5.6.4(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) path-browserify: 1.0.1 process: 0.11.10 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) semver: 7.7.3 - style-loader: 3.3.4(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - swc-loader: 0.2.6(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + style-loader: 3.3.4(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + swc-loader: 0.2.6(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) ts-dedent: 2.2.0 url: 0.11.4 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) - webpack-dev-middleware: 6.1.3(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) + webpack-dev-middleware: 6.1.3(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.5.0 optionalDependencies: @@ -31537,7 +31663,7 @@ snapshots: - uglify-js - webpack-cli - '@storybook/builder-webpack5@8.6.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/builder-webpack5@8.6.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)': dependencies: '@storybook/core-webpack': 8.6.14(storybook@8.6.14(prettier@3.5.3)) '@types/semver': 7.7.1 @@ -31545,23 +31671,23 @@ snapshots: case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.4.3 constants-browserify: 1.0.0 - css-loader: 6.11.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + css-loader: 6.11.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) es-module-lexer: 1.7.0 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - html-webpack-plugin: 5.6.4(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + html-webpack-plugin: 5.6.4(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) magic-string: 0.30.21 path-browserify: 1.0.1 process: 0.11.10 semver: 7.7.3 storybook: 8.6.14(prettier@3.5.3) - style-loader: 3.3.4(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + style-loader: 3.3.4(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) ts-dedent: 2.2.0 url: 0.11.4 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) - webpack-dev-middleware: 6.1.3(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) + webpack-dev-middleware: 6.1.3(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.2 optionalDependencies: @@ -31591,7 +31717,7 @@ snapshots: semver: 7.7.3 storybook: 9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3) style-loader: 3.3.4(webpack@5.102.1) - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) ts-dedent: 2.2.0 url: 0.11.4 util: 0.12.5 @@ -31898,7 +32024,7 @@ snapshots: dependencies: storybook: 9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3) - '@storybook/core-client@6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)))': + '@storybook/core-client@6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)))': dependencies: '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/channel-postmessage': 6.5.16 @@ -31922,7 +32048,7 @@ snapshots: ts-dedent: 2.2.0 unfetch: 4.2.0 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optionalDependencies: typescript: 5.8.3 @@ -31950,7 +32076,7 @@ snapshots: ts-dedent: 2.2.0 unfetch: 4.2.0 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) optionalDependencies: typescript: 5.8.3 @@ -31987,7 +32113,7 @@ snapshots: '@storybook/client-logger': 7.4.6 '@storybook/preview-api': 7.4.6 - '@storybook/core-common@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': + '@storybook/core-common@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': dependencies: '@babel/core': 7.27.7 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.27.7) @@ -32015,7 +32141,7 @@ snapshots: '@storybook/semver': 7.3.2 '@types/node': 16.18.126 '@types/pretty-hrtime': 1.0.3 - babel-loader: 8.4.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + babel-loader: 8.4.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) babel-plugin-macros: 3.1.0 babel-plugin-polyfill-corejs3: 0.1.7(@babel/core@7.27.7) chalk: 4.1.2 @@ -32023,7 +32149,7 @@ snapshots: express: 4.21.2 file-system-cache: 1.1.0 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@9.27.0(jiti@2.6.1))(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + fork-ts-checker-webpack-plugin: 6.5.3(eslint@9.27.0(jiti@2.6.1))(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) fs-extra: 9.1.0 glob: 7.2.3 handlebars: 4.7.8 @@ -32040,7 +32166,7 @@ snapshots: telejson: 6.0.8 ts-dedent: 2.2.0 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -32052,7 +32178,7 @@ snapshots: - vue-template-compiler - webpack-cli - '@storybook/core-common@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': + '@storybook/core-common@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': dependencies: '@babel/core': 7.27.7 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.27.7) @@ -32105,7 +32231,7 @@ snapshots: telejson: 6.0.8 ts-dedent: 2.2.0 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -32447,20 +32573,20 @@ snapshots: dependencies: ts-dedent: 2.2.0 - '@storybook/core-server@6.5.16(@storybook/builder-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': + '@storybook/core-server@6.5.16(@storybook/builder-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': dependencies: '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-webpack4': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/builder-webpack4': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/core-events': 6.5.16 '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/csf-tools': 6.5.16 - '@storybook/manager-webpack4': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/manager-webpack4': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/node-logger': 6.5.16 '@storybook/semver': 7.3.2 '@storybook/store': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/telemetry': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/telemetry': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@types/node': 16.18.126 '@types/node-fetch': 2.6.13 '@types/pretty-hrtime': 1.0.3 @@ -32493,12 +32619,12 @@ snapshots: ts-dedent: 2.2.0 util-deprecate: 1.0.2 watchpack: 2.4.4 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) ws: 8.18.3 x-default-browser: 0.4.0 optionalDependencies: - '@storybook/builder-webpack5': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) - '@storybook/manager-webpack5': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/builder-webpack5': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/manager-webpack5': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) typescript: 5.8.3 transitivePeerDependencies: - '@storybook/mdx2-csf' @@ -32579,20 +32705,20 @@ snapshots: - vue-template-compiler - webpack-cli - '@storybook/core-server@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': + '@storybook/core-server@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': dependencies: '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-webpack4': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) - '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) + '@storybook/builder-webpack4': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) + '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) '@storybook/core-events': 6.5.16 '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/csf-tools': 6.5.16 - '@storybook/manager-webpack4': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) + '@storybook/manager-webpack4': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) '@storybook/node-logger': 6.5.16 '@storybook/semver': 7.3.2 '@storybook/store': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/telemetry': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) + '@storybook/telemetry': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) '@types/node': 16.18.126 '@types/node-fetch': 2.6.13 '@types/pretty-hrtime': 1.0.3 @@ -32625,7 +32751,7 @@ snapshots: ts-dedent: 2.2.0 util-deprecate: 1.0.2 watchpack: 2.4.4 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) ws: 8.18.3 x-default-browser: 0.4.0 optionalDependencies: @@ -32904,16 +33030,16 @@ snapshots: storybook: 9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3) ts-dedent: 2.2.0 - '@storybook/core@6.5.16(@storybook/builder-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1)': + '@storybook/core@6.5.16(@storybook/builder-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1)': dependencies: '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1) - '@storybook/core-server': 6.5.16(@storybook/builder-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-server': 6.5.16(@storybook/builder-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) optionalDependencies: - '@storybook/builder-webpack5': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) - '@storybook/manager-webpack5': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/builder-webpack5': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/manager-webpack5': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) typescript: 5.8.3 transitivePeerDependencies: - '@storybook/mdx2-csf' @@ -32952,13 +33078,13 @@ snapshots: - vue-template-compiler - webpack-cli - '@storybook/core@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)))': + '@storybook/core@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)))': dependencies: - '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - '@storybook/core-server': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) + '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + '@storybook/core-server': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -33253,29 +33379,29 @@ snapshots: dependencies: storybook: 9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3) - '@storybook/manager-webpack4@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': + '@storybook/manager-webpack4@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': dependencies: '@babel/core': 7.27.7 '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.27.7) '@babel/preset-react': 7.27.1(@babel/core@7.27.7) '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) + '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) '@storybook/node-logger': 6.5.16 '@storybook/theming': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/ui': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/node': 16.18.126 '@types/webpack': 4.41.40 - babel-loader: 8.4.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + babel-loader: 8.4.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) case-sensitive-paths-webpack-plugin: 2.4.0 chalk: 4.1.2 core-js: 3.46.0 - css-loader: 3.6.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + css-loader: 3.6.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) express: 4.21.2 - file-loader: 6.2.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + file-loader: 6.2.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) find-up: 5.0.0 fs-extra: 9.1.0 - html-webpack-plugin: 4.5.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + html-webpack-plugin: 4.5.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) node-fetch: 2.6.13(encoding@0.1.13) pnp-webpack-plugin: 1.6.4(typescript@5.8.3) react: 18.2.0 @@ -33283,14 +33409,14 @@ snapshots: read-pkg-up: 7.0.1 regenerator-runtime: 0.13.11 resolve-from: 5.0.0 - style-loader: 1.3.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + style-loader: 1.3.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) telejson: 6.0.8 - terser-webpack-plugin: 4.2.3(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + terser-webpack-plugin: 4.2.3(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))))(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))))(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) - webpack-dev-middleware: 3.7.3(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) + webpack-dev-middleware: 3.7.3(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) webpack-virtual-modules: 0.2.2 optionalDependencies: typescript: 5.8.3 @@ -33304,14 +33430,14 @@ snapshots: - vue-template-compiler - webpack-cli - '@storybook/manager-webpack4@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': + '@storybook/manager-webpack4@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': dependencies: '@babel/core': 7.27.7 '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.27.7) '@babel/preset-react': 7.27.1(@babel/core@7.27.7) '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/node-logger': 6.5.16 '@storybook/theming': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/ui': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -33340,7 +33466,7 @@ snapshots: ts-dedent: 2.2.0 url-loader: 4.1.1(file-loader@6.2.0(webpack@5.102.1))(webpack@5.102.1) util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-dev-middleware: 3.7.3(webpack@5.102.1) webpack-virtual-modules: 0.2.2 optionalDependencies: @@ -33559,14 +33685,14 @@ snapshots: - vue-template-compiler - webpack-cli - '@storybook/manager-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': + '@storybook/manager-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': dependencies: '@babel/core': 7.27.7 '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.27.7) '@babel/preset-react': 7.27.1(@babel/core@7.27.7) '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/core-client': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/node-logger': 6.5.16 '@storybook/theming': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/ui': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -33589,10 +33715,10 @@ snapshots: resolve-from: 5.0.0 style-loader: 2.0.0(webpack@5.102.1) telejson: 6.0.8 - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) ts-dedent: 2.2.0 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-dev-middleware: 4.3.0(webpack@5.102.1) webpack-virtual-modules: 0.4.6 optionalDependencies: @@ -33638,7 +33764,7 @@ snapshots: resolve-from: 5.0.0 style-loader: 2.0.0(webpack@5.102.1) telejson: 6.0.8 - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) ts-dedent: 2.2.0 util-deprecate: 1.0.2 webpack: 5.102.1(webpack-cli@4.10.0) @@ -33770,11 +33896,11 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(@swc/core@1.14.0(@swc/helpers@0.5.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/preset-react-webpack@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(@swc/core@1.15.0(@swc/helpers@0.5.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)': dependencies: '@storybook/core-webpack': 8.6.14(storybook@8.6.14(prettier@3.5.3)) '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) '@types/semver': 7.7.1 find-up: 5.0.0 magic-string: 0.30.21 @@ -33785,7 +33911,7 @@ snapshots: semver: 7.7.3 storybook: 8.6.14(prettier@3.5.3) tsconfig-paths: 4.2.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -33922,7 +34048,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/react-docgen-typescript-plugin@1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)))': + '@storybook/react-docgen-typescript-plugin@1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)))': dependencies: debug: 4.4.3(supports-color@8.1.1) endent: 2.1.0 @@ -33932,7 +34058,7 @@ snapshots: react-docgen-typescript: 2.4.0(typescript@5.8.3) tslib: 2.8.1 typescript: 5.8.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) transitivePeerDependencies: - supports-color @@ -33946,11 +34072,11 @@ snapshots: react-docgen-typescript: 2.4.0(typescript@5.8.3) tslib: 2.8.1 typescript: 5.8.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) transitivePeerDependencies: - supports-color - '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)))': + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)))': dependencies: debug: 4.4.3(supports-color@8.1.1) endent: 2.1.0 @@ -33960,7 +34086,7 @@ snapshots: react-docgen-typescript: 2.4.0(typescript@5.8.3) tslib: 2.8.1 typescript: 5.8.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) transitivePeerDependencies: - supports-color @@ -34018,10 +34144,10 @@ snapshots: react-dom: 19.1.0(react@19.1.0) storybook: 9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3) - '@storybook/react-vite@9.1.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.52.5)(storybook@9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/react-vite@9.1.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.53.1)(storybook@9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3))(typescript@5.8.3)': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.8.3) - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0(rollup@4.53.1) '@storybook/builder-vite': 9.1.16(storybook@9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3)) '@storybook/react': 9.1.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@9.1.16(@testing-library/dom@10.4.1)(prettier@3.5.3))(typescript@5.8.3) find-up: 7.0.0 @@ -34095,10 +34221,10 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/react-webpack5@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(@swc/core@1.14.0(@swc/helpers@0.5.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/react-webpack5@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(@swc/core@1.15.0(@swc/helpers@0.5.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)': dependencies: - '@storybook/builder-webpack5': 8.6.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) - '@storybook/preset-react-webpack': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(@swc/core@1.14.0(@swc/helpers@0.5.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) + '@storybook/builder-webpack5': 8.6.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) + '@storybook/preset-react-webpack': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(@swc/core@1.15.0(@swc/helpers@0.5.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -34133,15 +34259,15 @@ snapshots: - uglify-js - webpack-cli - '@storybook/react@6.5.16(@babel/core@7.27.7)(@storybook/builder-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1)': + '@storybook/react@6.5.16(@babel/core@7.27.7)(@storybook/builder-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1)': dependencies: '@babel/preset-flow': 7.27.1(@babel/core@7.27.7) '@babel/preset-react': 7.27.1(@babel/core@7.27.7) - '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1)(webpack@5.102.1) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2)(webpack-hot-middleware@2.26.1)(webpack@5.102.1) '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/client-logger': 6.5.16 - '@storybook/core': 6.5.16(@storybook/builder-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core': 6.5.16(@storybook/builder-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@storybook/manager-webpack5@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1))(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)(webpack@5.102.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/docs-tools': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/node-logger': 6.5.16 @@ -34172,11 +34298,11 @@ snapshots: require-from-string: 2.0.2 ts-dedent: 2.2.0 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) optionalDependencies: '@babel/core': 7.27.7 - '@storybook/builder-webpack5': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) - '@storybook/manager-webpack5': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/builder-webpack5': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/manager-webpack5': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) typescript: 5.8.3 transitivePeerDependencies: - '@storybook/mdx2-csf' @@ -34261,19 +34387,19 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/react@6.5.16(@babel/core@7.27.7)(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17)))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1)': + '@storybook/react@6.5.16(@babel/core@7.27.7)(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17)))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(require-from-string@2.0.2)(type-fest@4.41.0)(typescript@5.8.3)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1)': dependencies: '@babel/preset-flow': 7.27.1(@babel/core@7.27.7) '@babel/preset-react': 7.27.1(@babel/core@7.27.7) - '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17)))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17)))(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))))(webpack-hot-middleware@2.26.1)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) '@storybook/addons': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/client-logger': 6.5.16 - '@storybook/core': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) + '@storybook/core': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/docs-tools': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/node-logger': 6.5.16 - '@storybook/react-docgen-typescript-plugin': 1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + '@storybook/react-docgen-typescript-plugin': 1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) '@storybook/semver': 7.3.2 '@storybook/store': 6.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/estree': 0.0.51 @@ -34300,7 +34426,7 @@ snapshots: require-from-string: 2.0.2 ts-dedent: 2.2.0 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optionalDependencies: '@babel/core': 7.27.7 typescript: 5.8.3 @@ -34712,10 +34838,10 @@ snapshots: '@storybook/client-logger': 7.4.6 '@storybook/preview-api': 7.4.6 - '@storybook/telemetry@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': + '@storybook/telemetry@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)': dependencies: '@storybook/client-logger': 6.5.16 - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3) chalk: 4.1.2 core-js: 3.46.0 detect-package-manager: 2.0.1 @@ -34739,10 +34865,10 @@ snapshots: - vue-template-compiler - webpack-cli - '@storybook/telemetry@6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': + '@storybook/telemetry@6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(encoding@0.1.13)(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1)': dependencies: '@storybook/client-logger': 6.5.16 - '@storybook/core-common': 6.5.16(@swc/core@1.14.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) + '@storybook/core-common': 6.5.16(@swc/core@1.15.0(@swc/helpers@0.5.17))(eslint@9.27.0(jiti@2.6.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.8.3)(webpack-cli@6.0.1) chalk: 4.1.2 core-js: 3.46.0 detect-package-manager: 2.0.1 @@ -35007,20 +35133,20 @@ snapshots: regenerator-runtime: 0.13.11 resolve-from: 5.0.0 - '@swagger-api/apidom-ast@1.0.0-rc.1': + '@swagger-api/apidom-ast@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-error': 1.0.0-rc.1 + '@swagger-api/apidom-error': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) unraw: 3.0.0 - '@swagger-api/apidom-core@1.0.0-rc.1': + '@swagger-api/apidom-core@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 + '@swagger-api/apidom-ast': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 '@types/ramda': 0.30.2 minim: 0.23.8 ramda: 0.30.1 @@ -35028,213 +35154,213 @@ snapshots: short-unique-id: 5.3.2 ts-mixer: 6.0.4 - '@swagger-api/apidom-error@1.0.0-rc.1': + '@swagger-api/apidom-error@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-json-pointer@1.0.0-rc.1': + '@swagger-api/apidom-json-pointer@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 '@swaggerexpert/json-pointer': 2.10.2 - '@swagger-api/apidom-ns-api-design-systems@1.0.0-rc.1': + '@swagger-api/apidom-ns-api-design-systems@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 optional: true - '@swagger-api/apidom-ns-arazzo-1@1.0.0-rc.1': + '@swagger-api/apidom-ns-arazzo-1@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-json-schema-2020-12': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-json-schema-2020-12': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 optional: true - '@swagger-api/apidom-ns-asyncapi-2@1.0.0-rc.1': + '@swagger-api/apidom-ns-asyncapi-2@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-json-schema-draft-7': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-json-schema-draft-7': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 optional: true - '@swagger-api/apidom-ns-json-schema-2019-09@1.0.0-rc.1': + '@swagger-api/apidom-ns-json-schema-2019-09@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 - '@swagger-api/apidom-ns-json-schema-draft-7': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 + '@swagger-api/apidom-ns-json-schema-draft-7': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 - '@swagger-api/apidom-ns-json-schema-2020-12@1.0.0-rc.1': + '@swagger-api/apidom-ns-json-schema-2020-12@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 - '@swagger-api/apidom-ns-json-schema-2019-09': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 + '@swagger-api/apidom-ns-json-schema-2019-09': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 - '@swagger-api/apidom-ns-json-schema-draft-4@1.0.0-rc.1': + '@swagger-api/apidom-ns-json-schema-draft-4@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.0.0-rc.1 - '@swagger-api/apidom-core': 1.0.0-rc.1 + '@swagger-api/apidom-ast': 1.0.0-rc.3 + '@swagger-api/apidom-core': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 - '@swagger-api/apidom-ns-json-schema-draft-6@1.0.0-rc.1': + '@swagger-api/apidom-ns-json-schema-draft-6@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 - '@swagger-api/apidom-ns-json-schema-draft-4': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 + '@swagger-api/apidom-ns-json-schema-draft-4': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 - '@swagger-api/apidom-ns-json-schema-draft-7@1.0.0-rc.1': + '@swagger-api/apidom-ns-json-schema-draft-7@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 - '@swagger-api/apidom-ns-json-schema-draft-6': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 + '@swagger-api/apidom-ns-json-schema-draft-6': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 - '@swagger-api/apidom-ns-openapi-2@1.0.0-rc.1': + '@swagger-api/apidom-ns-openapi-2@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 - '@swagger-api/apidom-ns-json-schema-draft-4': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 + '@swagger-api/apidom-ns-json-schema-draft-4': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 optional: true - '@swagger-api/apidom-ns-openapi-3-0@1.0.0-rc.1': + '@swagger-api/apidom-ns-openapi-3-0@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 - '@swagger-api/apidom-ns-json-schema-draft-4': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 + '@swagger-api/apidom-ns-json-schema-draft-4': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 - '@swagger-api/apidom-ns-openapi-3-1@1.0.0-rc.1': + '@swagger-api/apidom-ns-openapi-3-1@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.0.0-rc.1 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-json-pointer': 1.0.0-rc.1 - '@swagger-api/apidom-ns-json-schema-2020-12': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-3-0': 1.0.0-rc.1 + '@swagger-api/apidom-ast': 1.0.0-rc.3 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-json-pointer': 1.0.0-rc.3 + '@swagger-api/apidom-ns-json-schema-2020-12': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-3-0': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) ts-mixer: 6.0.4 - '@swagger-api/apidom-parser-adapter-api-design-systems-json@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-api-design-systems-json@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-api-design-systems': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-api-design-systems': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-api-design-systems-yaml@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-api-design-systems-yaml@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-api-design-systems': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-api-design-systems': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-arazzo-json-1@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-arazzo-json-1@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-arazzo-1': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-arazzo-1': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-arazzo-yaml-1@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-arazzo-yaml-1@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-arazzo-1': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-arazzo-1': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-asyncapi-json-2@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-asyncapi-json-2@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-asyncapi-2': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-asyncapi-2': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-asyncapi-2': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-asyncapi-2': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-json@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-json@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.0.0-rc.1 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 + '@swagger-api/apidom-ast': 1.0.0-rc.3 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) @@ -35243,78 +35369,78 @@ snapshots: web-tree-sitter: 0.24.5 optional: true - '@swagger-api/apidom-parser-adapter-openapi-json-2@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-openapi-json-2@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-2': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-2': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-openapi-json-3-0@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-openapi-json-3-0@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-3-0': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-3-0': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-openapi-json-3-1@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-openapi-json-3-1@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-openapi-yaml-2@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-openapi-yaml-2@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-2': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-2': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-3-0': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-3-0': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.3 '@types/ramda': 0.30.2 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optional: true - '@swagger-api/apidom-parser-adapter-yaml-1-2@1.0.0-rc.1': + '@swagger-api/apidom-parser-adapter-yaml-1-2@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.0.0-rc.1 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 + '@swagger-api/apidom-ast': 1.0.0-rc.3 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 '@tree-sitter-grammars/tree-sitter-yaml': 0.7.1(tree-sitter@0.22.4) '@types/ramda': 0.30.2 ramda: 0.30.1 @@ -35323,11 +35449,11 @@ snapshots: web-tree-sitter: 0.24.5 optional: true - '@swagger-api/apidom-reference@1.0.0-rc.1': + '@swagger-api/apidom-reference@1.0.0-rc.3': dependencies: '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 '@types/ramda': 0.30.2 axios: 1.12.2 minimatch: 7.4.6 @@ -35335,26 +35461,26 @@ snapshots: ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) optionalDependencies: - '@swagger-api/apidom-json-pointer': 1.0.0-rc.1 - '@swagger-api/apidom-ns-arazzo-1': 1.0.0-rc.1 - '@swagger-api/apidom-ns-asyncapi-2': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-2': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-3-0': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-api-design-systems-json': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-api-design-systems-yaml': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-arazzo-json-1': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-arazzo-yaml-1': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-asyncapi-json-2': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-openapi-json-2': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-openapi-json-3-0': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-openapi-json-3-1': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-openapi-yaml-2': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1': 1.0.0-rc.1 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.1 + '@swagger-api/apidom-json-pointer': 1.0.0-rc.3 + '@swagger-api/apidom-ns-arazzo-1': 1.0.0-rc.3 + '@swagger-api/apidom-ns-asyncapi-2': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-2': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-3-0': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-api-design-systems-json': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-api-design-systems-yaml': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-arazzo-json-1': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-arazzo-yaml-1': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-asyncapi-json-2': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-json': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-openapi-json-2': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-openapi-json-3-0': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-openapi-json-3-1': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-openapi-yaml-2': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1': 1.0.0-rc.3 + '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.0.0-rc.3 transitivePeerDependencies: - debug @@ -35366,51 +35492,51 @@ snapshots: dependencies: apg-lite: 1.0.5 - '@swc/core-darwin-arm64@1.14.0': + '@swc/core-darwin-arm64@1.15.0': optional: true - '@swc/core-darwin-x64@1.14.0': + '@swc/core-darwin-x64@1.15.0': optional: true - '@swc/core-linux-arm-gnueabihf@1.14.0': + '@swc/core-linux-arm-gnueabihf@1.15.0': optional: true - '@swc/core-linux-arm64-gnu@1.14.0': + '@swc/core-linux-arm64-gnu@1.15.0': optional: true - '@swc/core-linux-arm64-musl@1.14.0': + '@swc/core-linux-arm64-musl@1.15.0': optional: true - '@swc/core-linux-x64-gnu@1.14.0': + '@swc/core-linux-x64-gnu@1.15.0': optional: true - '@swc/core-linux-x64-musl@1.14.0': + '@swc/core-linux-x64-musl@1.15.0': optional: true - '@swc/core-win32-arm64-msvc@1.14.0': + '@swc/core-win32-arm64-msvc@1.15.0': optional: true - '@swc/core-win32-ia32-msvc@1.14.0': + '@swc/core-win32-ia32-msvc@1.15.0': optional: true - '@swc/core-win32-x64-msvc@1.14.0': + '@swc/core-win32-x64-msvc@1.15.0': optional: true - '@swc/core@1.14.0(@swc/helpers@0.5.17)': + '@swc/core@1.15.0(@swc/helpers@0.5.17)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.25 optionalDependencies: - '@swc/core-darwin-arm64': 1.14.0 - '@swc/core-darwin-x64': 1.14.0 - '@swc/core-linux-arm-gnueabihf': 1.14.0 - '@swc/core-linux-arm64-gnu': 1.14.0 - '@swc/core-linux-arm64-musl': 1.14.0 - '@swc/core-linux-x64-gnu': 1.14.0 - '@swc/core-linux-x64-musl': 1.14.0 - '@swc/core-win32-arm64-msvc': 1.14.0 - '@swc/core-win32-ia32-msvc': 1.14.0 - '@swc/core-win32-x64-msvc': 1.14.0 + '@swc/core-darwin-arm64': 1.15.0 + '@swc/core-darwin-x64': 1.15.0 + '@swc/core-linux-arm-gnueabihf': 1.15.0 + '@swc/core-linux-arm64-gnu': 1.15.0 + '@swc/core-linux-arm64-musl': 1.15.0 + '@swc/core-linux-x64-gnu': 1.15.0 + '@swc/core-linux-x64-musl': 1.15.0 + '@swc/core-win32-arm64-msvc': 1.15.0 + '@swc/core-win32-ia32-msvc': 1.15.0 + '@swc/core-win32-x64-msvc': 1.15.0 '@swc/helpers': 0.5.17 '@swc/counter@0.1.3': {} @@ -35437,7 +35563,7 @@ snapshots: '@tanstack/query-core@5.77.1': {} - '@tanstack/query-core@5.90.6': {} + '@tanstack/query-core@5.90.7': {} '@tanstack/query-persist-client-core@4.27.0': dependencies: @@ -36134,11 +36260,11 @@ snapshots: - webpack-cli optional: true - '@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17))': + '@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17))': dependencies: '@types/node': 22.15.35 tapable: 2.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) transitivePeerDependencies: - '@swc/core' - esbuild @@ -36146,11 +36272,11 @@ snapshots: - webpack-cli optional: true - '@types/webpack@5.28.5(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1)': + '@types/webpack@5.28.5(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1)': dependencies: '@types/node': 22.15.35 tapable: 2.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) transitivePeerDependencies: - '@swc/core' - esbuild @@ -36821,7 +36947,7 @@ snapshots: '@typescript-eslint/types': 8.46.3 eslint-visitor-keys: 4.2.1 - '@typespec/ts-http-runtime@0.3.1': + '@typespec/ts-http-runtime@0.3.2': dependencies: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -37251,7 +37377,7 @@ snapshots: '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack@5.102.1)': dependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.102.1) optionalDependencies: webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.102.1) @@ -37380,9 +37506,17 @@ snapshots: clean-stack: 4.2.0 indent-string: 5.0.0 - ai@5.0.87(zod@4.1.11): + ai@5.0.89(zod@3.25.76): + dependencies: + '@ai-sdk/gateway': 2.0.7(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.16(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + zod: 3.25.76 + + ai@5.0.89(zod@4.1.11): dependencies: - '@ai-sdk/gateway': 2.0.6(zod@4.1.11) + '@ai-sdk/gateway': 2.0.7(zod@4.1.11) '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.16(zod@4.1.11) '@opentelemetry/api': 1.9.0 @@ -37479,7 +37613,7 @@ snapshots: dependencies: type-fest: 0.21.3 - ansi-escapes@7.1.1: + ansi-escapes@7.2.0: dependencies: environment: 1.1.0 @@ -37770,7 +37904,7 @@ snapshots: autoprefixer@10.4.21(postcss@8.5.6): dependencies: browserslist: 4.27.0 - caniuse-lite: 1.0.30001753 + caniuse-lite: 1.0.30001754 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -37780,7 +37914,7 @@ snapshots: autoprefixer@6.7.7: dependencies: browserslist: 1.7.7 - caniuse-db: 1.0.30001753 + caniuse-db: 1.0.30001754 normalize-range: 0.1.2 num2fraction: 1.2.2 postcss: 5.2.18 @@ -37789,7 +37923,7 @@ snapshots: autoprefixer@7.1.6: dependencies: browserslist: 2.11.3 - caniuse-lite: 1.0.30001753 + caniuse-lite: 1.0.30001754 normalize-range: 0.1.2 num2fraction: 1.2.2 postcss: 6.0.23 @@ -37798,7 +37932,7 @@ snapshots: autoprefixer@9.8.8: dependencies: browserslist: 4.27.0 - caniuse-lite: 1.0.30001753 + caniuse-lite: 1.0.30001754 normalize-range: 0.1.2 num2fraction: 1.2.2 picocolors: 0.2.1 @@ -38020,7 +38154,7 @@ snapshots: dependencies: '@babel/core': 7.27.7 find-up: 5.0.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) babel-loader@7.1.2(babel-core@7.0.0-bridge.0(@babel/core@7.27.7))(webpack@5.102.1): dependencies: @@ -38028,7 +38162,7 @@ snapshots: find-cache-dir: 1.0.0 loader-utils: 1.4.2 mkdirp: 0.5.6 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) babel-loader@7.1.2(babel-core@7.0.0-bridge.0(@babel/core@7.28.5))(webpack@5.102.1): dependencies: @@ -38038,14 +38172,14 @@ snapshots: mkdirp: 0.5.6 webpack: 5.102.1(webpack-cli@4.10.0) - babel-loader@8.4.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + babel-loader@8.4.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: '@babel/core': 7.27.7 find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) babel-loader@8.4.1(@babel/core@7.27.7)(webpack@5.102.1): dependencies: @@ -38054,14 +38188,14 @@ snapshots: loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) - babel-loader@9.2.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + babel-loader@9.2.1(@babel/core@7.27.7)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: '@babel/core': 7.27.7 find-cache-dir: 4.0.0 schema-utils: 4.3.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) babel-loader@9.2.1(@babel/core@7.27.7)(webpack@5.102.1): dependencies: @@ -38606,13 +38740,13 @@ snapshots: balanced-match@2.0.0: {} - bare-events@2.8.1: {} + bare-events@2.8.2: {} bare-fs@4.5.0: dependencies: - bare-events: 2.8.1 + bare-events: 2.8.2 bare-path: 3.0.0 - bare-stream: 2.7.0(bare-events@2.8.1) + bare-stream: 2.7.0(bare-events@2.8.2) bare-url: 2.3.2 fast-fifo: 1.3.2 transitivePeerDependencies: @@ -38628,11 +38762,11 @@ snapshots: bare-os: 3.6.2 optional: true - bare-stream@2.7.0(bare-events@2.8.1): + bare-stream@2.7.0(bare-events@2.8.2): dependencies: streamx: 2.23.0 optionalDependencies: - bare-events: 2.8.1 + bare-events: 2.8.2 transitivePeerDependencies: - bare-abort-controller - react-native-b4a @@ -38647,7 +38781,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.8.23: {} + baseline-browser-mapping@2.8.25: {} basic-auth@2.0.1: dependencies: @@ -38800,19 +38934,19 @@ snapshots: browserslist@1.7.7: dependencies: - caniuse-db: 1.0.30001753 - electron-to-chromium: 1.5.244 + caniuse-db: 1.0.30001754 + electron-to-chromium: 1.5.249 browserslist@2.11.3: dependencies: - caniuse-lite: 1.0.30001753 - electron-to-chromium: 1.5.244 + caniuse-lite: 1.0.30001754 + electron-to-chromium: 1.5.249 browserslist@4.27.0: dependencies: - baseline-browser-mapping: 2.8.23 - caniuse-lite: 1.0.30001753 - electron-to-chromium: 1.5.244 + baseline-browser-mapping: 2.8.25 + caniuse-lite: 1.0.30001754 + electron-to-chromium: 1.5.249 node-releases: 2.0.27 update-browserslist-db: 1.1.4(browserslist@4.27.0) @@ -39052,20 +39186,20 @@ snapshots: caniuse-api@1.6.1: dependencies: browserslist: 1.7.7 - caniuse-db: 1.0.30001753 + caniuse-db: 1.0.30001754 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 caniuse-api@3.0.0: dependencies: browserslist: 4.27.0 - caniuse-lite: 1.0.30001753 + caniuse-lite: 1.0.30001754 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-db@1.0.30001753: {} + caniuse-db@1.0.30001754: {} - caniuse-lite@1.0.30001753: {} + caniuse-lite@1.0.30001754: {} canvas@3.2.0: dependencies: @@ -39408,7 +39542,7 @@ snapshots: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.0)(react@18.3.1) '@radix-ui/react-dialog': 1.1.15(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-id': 1.1.1(@types/react@18.2.0)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -39660,7 +39794,7 @@ snapshots: schema-utils: 4.3.3 serialize-javascript: 6.0.2 tinyglobby: 0.2.15 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) copyfiles@2.4.1: dependencies: @@ -39781,13 +39915,13 @@ snapshots: dependencies: capture-stack-trace: 1.0.2 - create-jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): + create-jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -39873,7 +40007,7 @@ snapshots: postcss-value-parser: 3.3.1 source-list-map: 2.0.1 - css-loader@3.6.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + css-loader@3.6.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: camelcase: 5.3.1 cssesc: 3.0.0 @@ -39888,7 +40022,7 @@ snapshots: postcss-value-parser: 4.2.0 schema-utils: 2.7.1 semver: 6.3.1 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) css-loader@3.6.0(webpack@5.102.1): dependencies: @@ -39905,7 +40039,7 @@ snapshots: postcss-value-parser: 4.2.0 schema-utils: 2.7.1 semver: 6.3.1 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) css-loader@5.2.7(webpack@5.102.1): dependencies: @@ -39921,7 +40055,7 @@ snapshots: semver: 7.7.3 webpack: 5.102.1(webpack-cli@5.1.4) - css-loader@6.11.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + css-loader@6.11.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -39932,7 +40066,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.3 optionalDependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) css-loader@6.11.0(webpack@5.102.1): dependencies: @@ -39958,7 +40092,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.3 optionalDependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) css-select@4.3.0: dependencies: @@ -40599,7 +40733,7 @@ snapshots: dependencies: jake: 10.9.4 - electron-to-chromium@1.5.244: {} + electron-to-chromium@1.5.249: {} email-addresses@5.0.0: {} @@ -41236,7 +41370,7 @@ snapshots: '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@modelcontextprotocol/sdk': 1.21.0 + '@modelcontextprotocol/sdk': 1.21.1 '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 ajv: 6.12.6 @@ -41397,7 +41531,7 @@ snapshots: events-universal@1.0.1: dependencies: - bare-events: 2.8.1 + bare-events: 2.8.2 transitivePeerDependencies: - bare-abort-controller @@ -41639,7 +41773,7 @@ snapshots: async: 2.6.4 loader-utils: 1.4.2 schema-utils: 0.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-sources: 1.4.3 extract-zip@1.7.0: @@ -41811,19 +41945,19 @@ snapshots: dependencies: loader-utils: 1.4.2 schema-utils: 0.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) - file-loader@6.2.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + file-loader@6.2.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) file-loader@6.2.0(webpack@5.102.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) file-system-cache@1.1.0: dependencies: @@ -41996,7 +42130,7 @@ snapshots: flatten@1.0.3: {} - flow-parser@0.289.0: {} + flow-parser@0.290.0: {} flush-write-stream@1.1.1: dependencies: @@ -42042,7 +42176,7 @@ snapshots: lodash.startswith: 4.2.1 minimatch: 3.1.2 typescript: 5.8.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) fork-ts-checker-webpack-plugin@4.1.6: dependencies: @@ -42094,7 +42228,7 @@ snapshots: optionalDependencies: eslint: 9.27.0(jiti@2.6.1) - fork-ts-checker-webpack-plugin@6.5.3(eslint@9.27.0(jiti@2.6.1))(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + fork-ts-checker-webpack-plugin@6.5.3(eslint@9.27.0(jiti@2.6.1))(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: '@babel/code-frame': 7.27.1 '@types/json-schema': 7.0.15 @@ -42110,7 +42244,7 @@ snapshots: semver: 7.7.3 tapable: 1.1.3 typescript: 5.8.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optionalDependencies: eslint: 9.27.0(jiti@2.6.1) @@ -42130,11 +42264,11 @@ snapshots: semver: 7.7.3 tapable: 1.1.3 typescript: 5.8.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) optionalDependencies: eslint: 9.27.0(jiti@2.6.1) - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.8.3)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: '@babel/code-frame': 7.27.1 chalk: 4.1.2 @@ -42149,7 +42283,7 @@ snapshots: semver: 7.7.3 tapable: 2.3.0 typescript: 5.8.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) fork-ts-checker-webpack-plugin@8.0.0(typescript@5.8.3)(webpack@5.102.1): dependencies: @@ -42183,7 +42317,7 @@ snapshots: semver: 7.7.3 tapable: 2.3.0 typescript: 5.8.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) form-data-encoder@2.1.4: {} @@ -42308,7 +42442,7 @@ snapshots: fsevents@1.2.13: dependencies: bindings: 1.5.0 - nan: 2.23.0 + nan: 2.23.1 optional: true fsevents@2.3.2: @@ -42530,7 +42664,7 @@ snapshots: minimatch: 10.1.1 minipass: 7.1.2 package-json-from-dist: 1.0.1 - path-scurry: 2.0.0 + path-scurry: 2.0.1 glob@5.0.15: dependencies: @@ -42587,7 +42721,7 @@ snapshots: global@4.4.0: dependencies: - min-document: 2.19.0 + min-document: 2.19.1 process: 0.11.10 globals@12.4.0: @@ -43067,7 +43201,7 @@ snapshots: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.44.0 + terser: 5.44.1 html-minifier@3.5.21: dependencies: @@ -43099,9 +43233,9 @@ snapshots: lodash: 4.17.21 pretty-error: 2.1.2 toposort: 1.0.7 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) - html-webpack-plugin@4.5.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + html-webpack-plugin@4.5.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: '@types/html-minifier-terser': 5.1.2 '@types/tapable': 1.0.12 @@ -43112,7 +43246,7 @@ snapshots: pretty-error: 2.1.2 tapable: 1.1.3 util.promisify: 1.0.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) html-webpack-plugin@4.5.2(webpack@5.102.1): dependencies: @@ -43125,9 +43259,9 @@ snapshots: pretty-error: 2.1.2 tapable: 1.1.3 util.promisify: 1.0.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) - html-webpack-plugin@5.6.4(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + html-webpack-plugin@5.6.4(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -43135,7 +43269,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.3.0 optionalDependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) html-webpack-plugin@5.6.4(webpack@5.102.1): dependencies: @@ -43145,7 +43279,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.3.0 optionalDependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) htmlparser2@10.0.0: dependencies: @@ -44085,16 +44219,16 @@ snapshots: - supports-color - utf-8-validate - jest-cli@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): + jest-cli@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + create-jest: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -44158,7 +44292,7 @@ snapshots: - supports-color - utf-8-validate - jest-config@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): + jest-config@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): dependencies: '@babel/core': 7.27.7 '@jest/test-sequencer': 29.7.0 @@ -44184,7 +44318,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 22.15.35 - ts-node: 10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3) + ts-node: 10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -44423,10 +44557,7 @@ snapshots: pretty-format: 25.5.0 throat: 5.0.0 transitivePeerDependencies: - - bufferutil - - canvas - supports-color - - utf-8-validate jest-leak-detector@25.5.0: dependencies: @@ -44946,12 +45077,12 @@ snapshots: - supports-color - utf-8-validate - jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): + jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + jest-cli: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -45014,7 +45145,7 @@ snapshots: '@babel/register': 7.28.3(@babel/core@7.27.7) babel-core: 7.0.0-bridge.0(@babel/core@7.27.7) chalk: 4.1.2 - flow-parser: 0.289.0 + flow-parser: 0.290.0 graceful-fs: 4.2.11 micromatch: 4.0.8 neo-async: 2.6.2 @@ -45041,7 +45172,7 @@ snapshots: '@babel/register': 7.28.3(@babel/core@7.27.7) babel-core: 7.0.0-bridge.0(@babel/core@7.27.7) chalk: 4.1.2 - flow-parser: 0.289.0 + flow-parser: 0.290.0 graceful-fs: 4.2.11 micromatch: 4.0.8 neo-async: 2.6.2 @@ -45074,7 +45205,7 @@ snapshots: pn: 1.1.0 request: 2.88.2 request-promise-native: 1.0.9(request@2.88.2) - sax: 1.4.2 + sax: 1.4.3 symbol-tree: 3.2.4 tough-cookie: 2.5.0 w3c-hr-time: 1.0.2 @@ -45164,7 +45295,7 @@ snapshots: nwmatcher: 1.4.4 parse5: 1.5.1 request: 2.88.2 - sax: 1.4.2 + sax: 1.4.3 symbol-tree: 3.2.4 tough-cookie: 2.5.0 webidl-conversions: 4.0.2 @@ -45566,7 +45697,7 @@ snapshots: log-update@6.1.0: dependencies: - ansi-escapes: 7.1.1 + ansi-escapes: 7.2.0 cli-cursor: 5.0.0 slice-ansi: 7.1.2 strip-ansi: 7.1.2 @@ -46575,7 +46706,7 @@ snapshots: mimic-response@4.0.0: {} - min-document@2.19.0: + min-document@2.19.1: dependencies: dom-walk: 0.1.2 @@ -46719,7 +46850,7 @@ snapshots: yargs-parser: 20.2.9 yargs-unparser: 2.0.0 - mocha@11.7.4: + mocha@11.7.5: dependencies: browser-stdout: 1.3.1 chokidar: 4.0.3 @@ -46809,7 +46940,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nan@2.23.0: {} + nan@2.23.1: {} nano-spawn@2.0.0: {} @@ -47544,7 +47675,7 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-scurry@2.0.0: + path-scurry@2.0.1: dependencies: lru-cache: 11.2.2 minipass: 7.1.2 @@ -47832,13 +47963,13 @@ snapshots: postcss-load-options: 1.2.0 postcss-load-plugins: 2.3.0 - postcss-load-config@3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): + postcss-load-config@3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: postcss: 8.5.6 - ts-node: 10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3) + ts-node: 10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3) postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.1): dependencies: @@ -47865,7 +47996,7 @@ snapshots: postcss-load-config: 1.2.0 schema-utils: 0.3.0 - postcss-loader@4.3.0(postcss@7.0.39)(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + postcss-loader@4.3.0(postcss@7.0.39)(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: cosmiconfig: 7.1.0 klona: 2.0.6 @@ -47873,7 +48004,7 @@ snapshots: postcss: 7.0.39 schema-utils: 3.3.0 semver: 7.7.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) postcss-loader@4.3.0(postcss@7.0.39)(webpack@5.102.1): dependencies: @@ -47883,7 +48014,7 @@ snapshots: postcss: 7.0.39 schema-utils: 3.3.0 semver: 7.7.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.8.3)(webpack@5.102.1): dependencies: @@ -48545,17 +48676,17 @@ snapshots: iconv-lite: 0.7.0 unpipe: 1.0.0 - raw-loader@4.0.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + raw-loader@4.0.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) raw-loader@4.0.2(webpack@5.102.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) rc-config-loader@4.1.3: dependencies: @@ -49049,7 +49180,7 @@ snapshots: optionalDependencies: '@types/react': 18.2.0 - react-scripts-ts@3.1.0(@swc/core@1.14.0(@swc/helpers@0.5.17))(babel-core@7.0.0-bridge.0(@babel/core@7.27.7))(babel-runtime@6.26.0)(typescript@5.8.3)(webpack-cli@6.0.1): + react-scripts-ts@3.1.0(@swc/core@1.15.0(@swc/helpers@0.5.17))(babel-core@7.0.0-bridge.0(@babel/core@7.27.7))(babel-runtime@6.26.0)(typescript@5.8.3)(webpack-cli@6.0.1): dependencies: autoprefixer: 7.1.6 babel-jest: 20.0.3 @@ -49085,7 +49216,7 @@ snapshots: typescript: 5.8.3 uglifyjs-webpack-plugin: 1.2.5(webpack@5.102.1) url-loader: 0.6.2(file-loader@1.1.5(webpack@5.102.1)) - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.102.1) webpack-manifest-plugin: 1.3.2(webpack@5.102.1) whatwg-fetch: 2.0.3 @@ -49208,6 +49339,16 @@ snapshots: react: 18.2.0 refractor: 3.6.0 + react-syntax-highlighter@16.1.0(react@18.2.0): + dependencies: + '@babel/runtime': 7.28.4 + highlight.js: 10.7.3 + highlightjs-vue: 1.0.0 + lowlight: 1.20.0 + prismjs: 1.30.0 + react: 18.2.0 + refractor: 5.0.0 + react-test-renderer@19.1.1(react@18.2.0): dependencies: react: 18.2.0 @@ -49431,6 +49572,13 @@ snapshots: parse-entities: 2.0.0 prismjs: 1.30.0 + refractor@5.0.0: + dependencies: + '@types/hast': 3.0.4 + '@types/prismjs': 1.26.5 + hastscript: 9.0.1 + parse-entities: 4.0.2 + regenerate-unicode-properties@10.2.2: dependencies: regenerate: 1.4.2 @@ -49798,16 +49946,16 @@ snapshots: glob: 11.0.3 package-json-from-dist: 1.0.1 - rollup-plugin-import-css@3.5.8(rollup@4.52.5): + rollup-plugin-import-css@3.5.8(rollup@4.53.1): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) - rollup: 4.52.5 + '@rollup/pluginutils': 5.3.0(rollup@4.53.1) + rollup: 4.53.1 - rollup-plugin-peer-deps-external@2.2.4(rollup@4.52.5): + rollup-plugin-peer-deps-external@2.2.4(rollup@4.53.1): dependencies: - rollup: 4.52.5 + rollup: 4.53.1 - rollup-plugin-postcss@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): + rollup-plugin-postcss@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)): dependencies: chalk: 4.1.2 concat-with-sourcemaps: 1.1.0 @@ -49816,7 +49964,7 @@ snapshots: p-queue: 6.6.2 pify: 5.0.0 postcss: 8.5.6 - postcss-load-config: 3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + postcss-load-config: 3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) postcss-modules: 4.3.1(postcss@8.5.6) promise.series: 0.2.0 resolve: 1.22.11 @@ -49861,12 +50009,12 @@ snapshots: tslib: 2.0.1 typescript: 3.9.10 - rollup-plugin-typescript2@0.36.0(rollup@4.52.5)(typescript@5.8.3): + rollup-plugin-typescript2@0.36.0(rollup@4.53.1)(typescript@5.8.3): dependencies: '@rollup/pluginutils': 4.2.1 find-cache-dir: 3.3.2 fs-extra: 10.1.0 - rollup: 4.52.5 + rollup: 4.53.1 semver: 7.7.3 tslib: 2.8.1 typescript: 5.8.3 @@ -49886,32 +50034,32 @@ snapshots: '@types/node': 22.15.35 acorn: 7.4.1 - rollup@4.52.5: + rollup@4.53.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.5 - '@rollup/rollup-android-arm64': 4.52.5 - '@rollup/rollup-darwin-arm64': 4.52.5 - '@rollup/rollup-darwin-x64': 4.52.5 - '@rollup/rollup-freebsd-arm64': 4.52.5 - '@rollup/rollup-freebsd-x64': 4.52.5 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 - '@rollup/rollup-linux-arm-musleabihf': 4.52.5 - '@rollup/rollup-linux-arm64-gnu': 4.52.5 - '@rollup/rollup-linux-arm64-musl': 4.52.5 - '@rollup/rollup-linux-loong64-gnu': 4.52.5 - '@rollup/rollup-linux-ppc64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-musl': 4.52.5 - '@rollup/rollup-linux-s390x-gnu': 4.52.5 - '@rollup/rollup-linux-x64-gnu': 4.52.5 - '@rollup/rollup-linux-x64-musl': 4.52.5 - '@rollup/rollup-openharmony-arm64': 4.52.5 - '@rollup/rollup-win32-arm64-msvc': 4.52.5 - '@rollup/rollup-win32-ia32-msvc': 4.52.5 - '@rollup/rollup-win32-x64-gnu': 4.52.5 - '@rollup/rollup-win32-x64-msvc': 4.52.5 + '@rollup/rollup-android-arm-eabi': 4.53.1 + '@rollup/rollup-android-arm64': 4.53.1 + '@rollup/rollup-darwin-arm64': 4.53.1 + '@rollup/rollup-darwin-x64': 4.53.1 + '@rollup/rollup-freebsd-arm64': 4.53.1 + '@rollup/rollup-freebsd-x64': 4.53.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.1 + '@rollup/rollup-linux-arm-musleabihf': 4.53.1 + '@rollup/rollup-linux-arm64-gnu': 4.53.1 + '@rollup/rollup-linux-arm64-musl': 4.53.1 + '@rollup/rollup-linux-loong64-gnu': 4.53.1 + '@rollup/rollup-linux-ppc64-gnu': 4.53.1 + '@rollup/rollup-linux-riscv64-gnu': 4.53.1 + '@rollup/rollup-linux-riscv64-musl': 4.53.1 + '@rollup/rollup-linux-s390x-gnu': 4.53.1 + '@rollup/rollup-linux-x64-gnu': 4.53.1 + '@rollup/rollup-linux-x64-musl': 4.53.1 + '@rollup/rollup-openharmony-arm64': 4.53.1 + '@rollup/rollup-win32-arm64-msvc': 4.53.1 + '@rollup/rollup-win32-ia32-msvc': 4.53.1 + '@rollup/rollup-win32-x64-gnu': 4.53.1 + '@rollup/rollup-win32-x64-msvc': 4.53.1 fsevents: 2.3.3 router@2.2.0: @@ -50023,7 +50171,7 @@ snapshots: neo-async: 2.6.2 optionalDependencies: sass: 1.93.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) sass@1.93.3: dependencies: @@ -50035,7 +50183,7 @@ snapshots: sax@1.2.4: {} - sax@1.4.2: {} + sax@1.4.3: {} saxes@3.1.11: dependencies: @@ -50896,11 +51044,11 @@ snapshots: loader-utils: 1.4.2 schema-utils: 0.3.0 - style-loader@1.3.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + style-loader@1.3.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: loader-utils: 2.0.4 schema-utils: 2.7.1 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) style-loader@1.3.0(webpack@5.102.1): dependencies: @@ -50912,11 +51060,11 @@ snapshots: dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) - style-loader@3.3.4(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + style-loader@3.3.4(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) style-loader@3.3.4(webpack@5.102.1): dependencies: @@ -50924,7 +51072,7 @@ snapshots: style-loader@4.0.0(webpack@5.102.1): dependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) style-mod@4.1.3: {} @@ -51065,7 +51213,7 @@ snapshots: svg-url-loader@8.0.0(webpack@5.102.1): dependencies: file-loader: 6.2.0(webpack@5.102.1) - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) svg2ttf@4.3.0: dependencies: @@ -51092,7 +51240,7 @@ snapshots: glob: 7.2.3 neatequal: 1.0.0 readable-stream: 3.6.2 - sax: 1.4.2 + sax: 1.4.3 svg-pathdata: 6.0.3 svgicons2svgfont@5.0.2: @@ -51100,7 +51248,7 @@ snapshots: commander: 2.20.3 neatequal: 1.0.0 readable-stream: 2.3.8 - sax: 1.4.2 + sax: 1.4.3 string.fromcodepoint: 0.2.1 string.prototype.codepointat: 0.2.1 svg-pathdata: 1.0.4 @@ -51132,7 +51280,7 @@ snapshots: del: 2.2.2 sw-precache: 5.2.1 uglify-js: 3.19.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) sw-precache@5.2.1: dependencies: @@ -51156,11 +51304,11 @@ snapshots: dependencies: '@babel/runtime-corejs3': 7.28.4 '@scarf/scarf': 1.4.0 - '@swagger-api/apidom-core': 1.0.0-rc.1 - '@swagger-api/apidom-error': 1.0.0-rc.1 - '@swagger-api/apidom-json-pointer': 1.0.0-rc.1 - '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.1 - '@swagger-api/apidom-reference': 1.0.0-rc.1 + '@swagger-api/apidom-core': 1.0.0-rc.3 + '@swagger-api/apidom-error': 1.0.0-rc.3 + '@swagger-api/apidom-json-pointer': 1.0.0-rc.3 + '@swagger-api/apidom-ns-openapi-3-1': 1.0.0-rc.3 + '@swagger-api/apidom-reference': 1.0.0-rc.3 '@swaggerexpert/cookie': 2.0.2 deepmerge: 4.3.1 fast-json-patch: 3.1.1 @@ -51216,7 +51364,7 @@ snapshots: - '@types/react' - debug - swagger-ui-react@5.30.1(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + swagger-ui-react@5.30.2(@types/react@18.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime-corejs3': 7.28.4 '@scarf/scarf': 1.4.0 @@ -51242,7 +51390,7 @@ snapshots: react-immutable-pure-component: 2.2.2(immutable@3.8.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-inspector: 6.0.2(react@18.2.0) react-redux: 9.2.0(@types/react@18.2.0)(react@18.2.0)(redux@5.0.1) - react-syntax-highlighter: 15.6.6(react@18.2.0) + react-syntax-highlighter: 16.1.0(react@18.2.0) redux: 5.0.1 redux-immutable: 4.0.0(immutable@3.8.2) remarkable: 2.0.1 @@ -51258,15 +51406,15 @@ snapshots: - '@types/react' - debug - swc-loader@0.2.6(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + swc-loader@0.2.6(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: - '@swc/core': 1.14.0(@swc/helpers@0.5.17) + '@swc/core': 1.15.0(@swc/helpers@0.5.17) '@swc/counter': 0.1.3 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) - swc-loader@0.2.6(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1): + swc-loader@0.2.6(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1): dependencies: - '@swc/core': 1.14.0(@swc/helpers@0.5.17) + '@swc/core': 1.15.0(@swc/helpers@0.5.17) '@swc/counter': 0.1.3 webpack: 5.102.1(webpack-cli@5.1.4) @@ -51335,7 +51483,7 @@ snapshots: - tsx - yaml - tailwindcss@4.1.16: {} + tailwindcss@4.1.17: {} tapable@0.2.9: {} @@ -51432,7 +51580,7 @@ snapshots: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - terser-webpack-plugin@4.2.3(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + terser-webpack-plugin@4.2.3(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: cacache: 15.3.0 find-cache-dir: 3.3.2 @@ -51441,8 +51589,8 @@ snapshots: schema-utils: 3.3.0 serialize-javascript: 5.0.1 source-map: 0.6.1 - terser: 5.44.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + terser: 5.44.1 + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) webpack-sources: 1.4.3 terser-webpack-plugin@4.2.3(webpack@5.102.1): @@ -51454,31 +51602,31 @@ snapshots: schema-utils: 3.3.0 serialize-javascript: 5.0.1 source-map: 0.6.1 - terser: 5.44.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + terser: 5.44.1 + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-sources: 1.4.3 - terser-webpack-plugin@5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + terser-webpack-plugin@5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 - terser: 5.44.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + terser: 5.44.1 + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optionalDependencies: - '@swc/core': 1.14.0(@swc/helpers@0.5.17) + '@swc/core': 1.15.0(@swc/helpers@0.5.17) - terser-webpack-plugin@5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1): + terser-webpack-plugin@5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 - terser: 5.44.0 + terser: 5.44.1 webpack: 5.102.1(webpack-cli@5.1.4) optionalDependencies: - '@swc/core': 1.14.0(@swc/helpers@0.5.17) + '@swc/core': 1.15.0(@swc/helpers@0.5.17) terser@4.8.1: dependencies: @@ -51486,7 +51634,7 @@ snapshots: source-map: 0.6.1 source-map-support: 0.5.21 - terser@5.44.0: + terser@5.44.1: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.15.0 @@ -51764,12 +51912,12 @@ snapshots: typescript: 3.9.10 yargs-parser: 18.1.3 - ts-jest@29.3.4(@babel/core@7.27.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)))(typescript@5.8.3): + ts-jest@29.3.4(@babel/core@7.27.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(jest@29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) + jest: 29.7.0(@types/node@22.15.35)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -51813,7 +51961,7 @@ snapshots: '@ts-morph/common': 0.27.0 code-block-writer: 13.0.3 - ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3): + ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -51831,9 +51979,9 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.14.0(@swc/helpers@0.5.17) + '@swc/core': 1.15.0(@swc/helpers@0.5.17) - ts-node@10.9.2(@swc/core@1.14.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3): + ts-node@10.9.2(@swc/core@1.15.0(@swc/helpers@0.5.17))(@types/node@22.15.35)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -51851,7 +51999,7 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.14.0(@swc/helpers@0.5.17) + '@swc/core': 1.15.0(@swc/helpers@0.5.17) optional: true ts-pnp@1.2.0(typescript@4.9.5): @@ -52075,14 +52223,14 @@ snapshots: dependencies: bindings: 1.5.0 bufferstreams: 1.1.3 - nan: 2.23.0 + nan: 2.23.1 node-gyp: 3.8.0 ttf2woff2@4.0.5: dependencies: bindings: 1.5.0 bufferstreams: 3.0.0 - nan: 2.23.0 + nan: 2.23.1 node-gyp: 9.4.1 transitivePeerDependencies: - supports-color @@ -52232,7 +52380,7 @@ snapshots: serialize-javascript: 1.9.1 source-map: 0.6.1 uglify-es: 3.3.9 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-sources: 1.4.3 worker-farm: 1.7.0 @@ -52518,21 +52666,21 @@ snapshots: mime: 1.6.0 schema-utils: 0.3.0 - url-loader@4.1.1(file-loader@6.2.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))))(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))))(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optionalDependencies: - file-loader: 6.2.0(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + file-loader: 6.2.0(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) url-loader@4.1.1(file-loader@6.2.0(webpack@5.102.1))(webpack@5.102.1): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) optionalDependencies: file-loader: 6.2.0(webpack@5.102.1) @@ -52809,7 +52957,7 @@ snapshots: - supports-color - utf-8-validate - vscode-extension-tester@8.14.1(mocha@11.7.4)(typescript@5.8.3): + vscode-extension-tester@8.14.1(mocha@11.7.5)(typescript@5.8.3): dependencies: '@redhat-developer/locators': 1.17.0(@redhat-developer/page-objects@1.17.0(selenium-webdriver@4.38.0)(typescript@5.8.3))(selenium-webdriver@4.38.0) '@redhat-developer/page-objects': 1.17.0(selenium-webdriver@4.38.0)(typescript@5.8.3) @@ -52824,7 +52972,7 @@ snapshots: got: 14.4.7 hpagent: 1.2.0 js-yaml: 4.1.0 - mocha: 11.7.4 + mocha: 11.7.5 sanitize-filename: 1.6.3 selenium-webdriver: 4.38.0 targz: 1.0.1 @@ -53074,7 +53222,7 @@ snapshots: import-local: 3.2.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-merge: 6.0.1 optionalDependencies: webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.102.1) @@ -53096,13 +53244,13 @@ snapshots: webpack: 5.102.1(webpack-cli@6.0.1) webpack-merge: 6.0.1 - webpack-dev-middleware@3.7.3(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + webpack-dev-middleware@3.7.3(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: memory-fs: 0.4.1 mime: 2.6.0 mkdirp: 0.5.6 range-parser: 1.2.1 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) webpack-log: 2.0.0 webpack-dev-middleware@3.7.3(webpack@5.102.1): @@ -53111,7 +53259,7 @@ snapshots: mime: 2.6.0 mkdirp: 0.5.6 range-parser: 1.2.1 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-log: 2.0.0 webpack-dev-middleware@4.3.0(webpack@5.102.1): @@ -53122,9 +53270,9 @@ snapshots: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 3.3.0 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) - webpack-dev-middleware@6.1.3(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + webpack-dev-middleware@6.1.3(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: colorette: 2.0.20 memfs: 3.5.3 @@ -53132,7 +53280,7 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) webpack-dev-middleware@6.1.3(webpack@5.102.1): dependencies: @@ -53144,7 +53292,7 @@ snapshots: optionalDependencies: webpack: 5.102.1(webpack-cli@5.1.4) - webpack-dev-middleware@7.4.5(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + webpack-dev-middleware@7.4.5(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: colorette: 2.0.20 memfs: 4.50.0 @@ -53153,7 +53301,7 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) optional: true webpack-dev-middleware@7.4.5(webpack@5.102.1): @@ -53277,7 +53425,7 @@ snapshots: webpack-dev-middleware: 7.4.5(webpack@5.102.1) ws: 8.18.3 optionalDependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.102.1) transitivePeerDependencies: - bufferutil @@ -53285,7 +53433,7 @@ snapshots: - supports-color - utf-8-validate - webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + webpack-dev-server@5.2.2(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -53313,10 +53461,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + webpack-dev-middleware: 7.4.5(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) ws: 8.18.3 optionalDependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) transitivePeerDependencies: - bufferutil - debug @@ -53362,13 +53510,13 @@ snapshots: - supports-color - utf-8-validate - webpack-filter-warnings-plugin@1.2.1(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))): + webpack-filter-warnings-plugin@1.2.1(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))): dependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)) webpack-filter-warnings-plugin@1.2.1(webpack@5.102.1): dependencies: - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-hot-middleware@2.26.1: dependencies: @@ -53385,7 +53533,7 @@ snapshots: dependencies: fs-extra: 0.30.0 lodash: 4.17.21 - webpack: 5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) + webpack: 5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1) webpack-merge-and-include-globally@2.3.4(webpack@5.102.1): dependencies: @@ -53427,7 +53575,7 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17)): + webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17)): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -53451,7 +53599,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -53459,7 +53607,7 @@ snapshots: - esbuild - uglify-js - webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4): + webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@5.1.4): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -53483,7 +53631,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) watchpack: 2.4.4 webpack-sources: 3.3.3 optionalDependencies: @@ -53493,7 +53641,7 @@ snapshots: - esbuild - uglify-js - webpack@5.102.1(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1): + webpack@5.102.1(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack-cli@6.0.1): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -53517,7 +53665,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) watchpack: 2.4.4 webpack-sources: 3.3.3 optionalDependencies: @@ -53551,7 +53699,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) watchpack: 2.4.4 webpack-sources: 3.3.3 optionalDependencies: @@ -53585,7 +53733,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) watchpack: 2.4.4 webpack-sources: 3.3.3 optionalDependencies: @@ -53619,7 +53767,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(@swc/core@1.14.0(@swc/helpers@0.5.17))(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.0(@swc/helpers@0.5.17))(webpack@5.102.1) watchpack: 2.4.4 webpack-sources: 3.3.3 optionalDependencies: @@ -53884,7 +54032,7 @@ snapshots: xml-js@1.6.11: dependencies: - sax: 1.4.2 + sax: 1.4.3 xml-name-validator@2.0.1: {} @@ -53894,17 +54042,17 @@ snapshots: xml2js@0.4.23: dependencies: - sax: 1.4.2 + sax: 1.4.3 xmlbuilder: 11.0.1 xml2js@0.5.0: dependencies: - sax: 1.4.2 + sax: 1.4.3 xmlbuilder: 11.0.1 xml2js@0.6.2: dependencies: - sax: 1.4.2 + sax: 1.4.3 xmlbuilder: 11.0.1 xml@1.0.1: {} diff --git a/rush.json b/rush.json index 52bbf9ff1b5..33a7790dbd0 100644 --- a/rush.json +++ b/rush.json @@ -237,9 +237,7 @@ * The list of shell commands to run before the Rush build command starts */ "preRushBuild": [ - "npm run init-submodules", - "cd workspaces/mi/mi-extension && pnpm run download-ls", - "cd workspaces/ballerina/ballerina-extension && pnpm run download-ls" + "npm run init-submodules" ], /** * The list of shell commands to run after the Rush build command finishes diff --git a/workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts b/workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts index 79eec4e0eeb..c755aee3da2 100644 --- a/workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts +++ b/workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts @@ -568,7 +568,7 @@ export interface ExecutorPositions { // Test Manager related interfaces export interface TestsDiscoveryRequest { - filePath: string; + projectPath: string; } export interface TestsDiscoveryResponse { diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/index.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/index.ts index 38a70d19043..e81ed5535d3 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/index.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/index.ts @@ -118,7 +118,8 @@ import { RecordsInWorkspaceMentions, BuildMode, DevantMetadata, - GeneratedClientSaveResponse + GeneratedClientSaveResponse, + AddProjectToWorkspaceRequest } from "./interfaces"; export interface BIDiagramAPI { @@ -137,6 +138,7 @@ export interface BIDiagramAPI { getNodeTemplate: (params: BINodeTemplateRequest) => Promise; getAiSuggestions: (params: BIAiSuggestionsRequest) => Promise; createProject: (params: ProjectRequest) => void; + addProjectToWorkspace: (params: AddProjectToWorkspaceRequest) => void; getWorkspaces: () => Promise; getProjectStructure: () => Promise; getProjectComponents: () => Promise; diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/interfaces.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/interfaces.ts index 9a50b133d63..e0abdaed138 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/interfaces.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/interfaces.ts @@ -27,6 +27,18 @@ export interface ProjectRequest { packageName: string; projectPath: string; createDirectory: boolean; + createAsWorkspace?: boolean; + workspaceName?: string; + orgName?: string; + version?: string; +} + +export interface AddProjectToWorkspaceRequest { + projectName: string; + packageName: string; + path: string; + convertToWorkspace?: boolean; + workspaceName?: string; orgName?: string; version?: string; } diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/rpc-type.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/rpc-type.ts index 0b3632859f0..44caa41be76 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/rpc-type.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/rpc-type.ts @@ -120,7 +120,8 @@ import { RecordsInWorkspaceMentions, BuildMode, DevantMetadata, - GeneratedClientSaveResponse + GeneratedClientSaveResponse, + AddProjectToWorkspaceRequest } from "./interfaces"; import { RequestType, NotificationType } from "vscode-messenger-common"; @@ -140,6 +141,7 @@ export const getEnclosedFunction: RequestType = { method: `${_preFix}/getNodeTemplate` }; export const getAiSuggestions: RequestType = { method: `${_preFix}/getAiSuggestions` }; export const createProject: NotificationType = { method: `${_preFix}/createProject` }; +export const addProjectToWorkspace: NotificationType = { method: `${_preFix}/addProjectToWorkspace` }; export const getWorkspaces: RequestType = { method: `${_preFix}/getWorkspaces` }; export const getProjectStructure: RequestType = { method: `${_preFix}/getProjectStructure` }; export const getProjectComponents: RequestType = { method: `${_preFix}/getProjectComponents` }; diff --git a/workspaces/ballerina/ballerina-core/src/state-machine-types.ts b/workspaces/ballerina/ballerina-core/src/state-machine-types.ts index b1545666b76..2b44f15d986 100644 --- a/workspaces/ballerina/ballerina-core/src/state-machine-types.ts +++ b/workspaces/ballerina/ballerina-core/src/state-machine-types.ts @@ -77,6 +77,7 @@ export enum MACHINE_VIEW { BIWelcome = "BI Welcome", BIProjectForm = "BI Project SKIP", BIImportIntegration = "BI Import Integration SKIP", + BIAddProjectForm = "BI Add Project SKIP", BIComponentView = "BI Component View", AddConnectionWizard = "Add Connection Wizard", AddCustomConnector = "Add Custom Connector", @@ -120,7 +121,8 @@ export type FocusFlowDiagramView = typeof FOCUS_FLOW_DIAGRAM_VIEW[keyof typeof F export interface VisualizerLocation { view?: MACHINE_VIEW | null; documentUri?: string; - projectUri?: string; + projectPath?: string; + workspacePath?: string; identifier?: string; parentIdentifier?: string; artifactType?: DIRECTORY_MAP; diff --git a/workspaces/ballerina/ballerina-extension/CHANGELOG.md b/workspaces/ballerina/ballerina-extension/CHANGELOG.md index 56be18b1e93..8d3ceb71062 100644 --- a/workspaces/ballerina/ballerina-extension/CHANGELOG.md +++ b/workspaces/ballerina/ballerina-extension/CHANGELOG.md @@ -4,11 +4,11 @@ All notable changes to the **Ballerina** extension will be documented in this fi The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/). -## [Unreleased] +## [5.6.0](https://github.com/wso2/vscode-extensions/compare/ballerina-integrator-1.4.0...ballerina-integrator-1.5.0) - 2025-11-11 ### Added -- **Editor** — Added visual editing for mono-repositories with multiple Ballerina projects, along with support for "Natural expressions" in Ballerina 2201.13.0. +- **Editor** — Added support for [Ballerina workspaces](https://ballerina.io/learn/workspaces/). This allows you to seamlessly manage, navigate, and build multiple related Ballerina projects within a single VS Code window, greatly improving the development workflow for complex systems. ## [5.5.0](https://github.com/wso2/vscode-extensions/compare/ballerina-integrator-1.3.2...ballerina-integrator-1.4.0) - 2025-11-05 diff --git a/workspaces/ballerina/ballerina-extension/package.json b/workspaces/ballerina/ballerina-extension/package.json index f36cfccdd8a..36347194d47 100644 --- a/workspaces/ballerina/ballerina-extension/package.json +++ b/workspaces/ballerina/ballerina-extension/package.json @@ -2,7 +2,7 @@ "name": "ballerina", "displayName": "Ballerina", "description": "Ballerina Language support, debugging, graphical visualization, AI-based data-mapping and many more.", - "version": "5.5.0", + "version": "5.6.0", "publisher": "wso2", "icon": "resources/images/ballerina.png", "homepage": "https://wso2.com/ballerina/vscode/docs", @@ -689,7 +689,7 @@ }, { "command": "BI.project-explorer.add", - "title": "Add Construct", + "title": "Add Project", "icon": "$(add)", "group": "navigation", "category": "BI" diff --git a/workspaces/ballerina/ballerina-extension/scripts/download-ls.js b/workspaces/ballerina/ballerina-extension/scripts/download-ls.js index d4c2a9d4f59..aff1cd55daa 100644 --- a/workspaces/ballerina/ballerina-extension/scripts/download-ls.js +++ b/workspaces/ballerina/ballerina-extension/scripts/download-ls.js @@ -100,7 +100,6 @@ function downloadFile(url, outputPath, maxRedirects = 5) { return; } - console.log(`Following redirect to: ${res.headers.location}`); makeRequest(res.headers.location, redirectCount + 1); return; } diff --git a/workspaces/ballerina/ballerina-extension/src/RPCLayer.ts b/workspaces/ballerina/ballerina-extension/src/RPCLayer.ts index 9b3407c6091..47fccac94c3 100644 --- a/workspaces/ballerina/ballerina-extension/src/RPCLayer.ts +++ b/workspaces/ballerina/ballerina-extension/src/RPCLayer.ts @@ -130,7 +130,7 @@ async function getContext(): Promise { position: context.position, syntaxTree: context.syntaxTree, isBI: context.isBI, - projectUri: context.projectUri, + projectPath: context.projectPath, serviceType: context.serviceType, type: context.type, isGraphql: context.isGraphql, @@ -140,7 +140,7 @@ async function getContext(): Promise { metadata: { isBISupported: context.isBISupported, haveLS: StateMachine.langClient() && true, - recordFilePath: path.join(context.projectUri, "types.bal"), + recordFilePath: path.join(context.projectPath, "types.bal"), enableSequenceDiagram: extension.ballerinaExtInstance.enableSequenceDiagramView(), target: context.metadata?.target }, diff --git a/workspaces/ballerina/ballerina-extension/src/core/extension.ts b/workspaces/ballerina/ballerina-extension/src/core/extension.ts index e8de14a22a0..c27824327eb 100644 --- a/workspaces/ballerina/ballerina-extension/src/core/extension.ts +++ b/workspaces/ballerina/ballerina-extension/src/core/extension.ts @@ -2346,13 +2346,11 @@ export class BallerinaExtension { public setPersistStatusContext(textEditor: TextEditor) { if (textEditor?.document) { const fileUri: Uri = textEditor.document.uri; - if (checkIsPersistModelFile(fileUri)) { - this.isPersist = true; - commands.executeCommand('setContext', 'isPersistModelActive', true); - return; - } else { - this.isPersist = false; - } + checkIsPersistModelFile(fileUri).then(isPersistModelFile => { + this.isPersist = isPersistModelFile; + commands.executeCommand('setContext', 'isPersistModelActive', isPersistModelFile); + }); + return; } commands.executeCommand('setContext', 'isPersistModelActive', false); } diff --git a/workspaces/ballerina/ballerina-extension/src/extension.ts b/workspaces/ballerina/ballerina-extension/src/extension.ts index ed3451a87a4..491b256b5e8 100644 --- a/workspaces/ballerina/ballerina-extension/src/extension.ts +++ b/workspaces/ballerina/ballerina-extension/src/extension.ts @@ -115,7 +115,7 @@ export async function activate(context: ExtensionContext) { await StateMachine.initialize(); // Then return the ballerina extension context - return { ballerinaExtInstance: extension.ballerinaExtInstance, projectPath: StateMachine.context().projectUri }; + return { ballerinaExtInstance: extension.ballerinaExtInstance, projectPath: StateMachine.context().projectPath }; } export async function activateBallerina(): Promise { diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/activator.ts index 4a52e74099f..50269512877 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/activator.ts @@ -86,7 +86,7 @@ export function activateAIFeatures(ballerinaExternalInstance: BallerinaExtension }); } - const projectPath = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; commands.registerCommand(CONFIGURE_DEFAULT_MODEL_COMMAND, async (...args: any[]) => { const configPath = await getConfigFilePath(ballerinaExternalInstance, projectPath); diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/dataMapping.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/dataMapping.ts index ab3aa1e1876..207687f30c9 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/dataMapping.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/dataMapping.ts @@ -210,7 +210,7 @@ async function createTempBallerinaFile( } export async function createTempBallerinaDir(): Promise { - const projectRoot = StateMachine.context().projectUri; + const projectRoot = StateMachine.context().projectPath; const randomNum = Math.floor(Math.random() * 90000) + 10000; const tempDir = fs.mkdtempSync( path.join(os.tmpdir(), `ballerina-data-mapping-${randomNum}-`) diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/datamapper.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/datamapper.ts index 3ca22f012bc..f52c144b3f0 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/datamapper.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/datamapper.ts @@ -260,7 +260,7 @@ export async function generateMappingCodeCore(mappingRequest: ProcessMappingPara const biDiagramRpcManager = new BiDiagramRpcManager(); const langClient = StateMachine.langClient(); const context = StateMachine.context(); - const projectRoot = context.projectUri; + const projectRoot = context.projectPath; const targetFunctionName = mappingRequest.parameters.functionName; @@ -443,9 +443,9 @@ export async function generateMappingCode(mappingRequest: ProcessMappingParamete } async function collectAllImportsFromProject(): Promise { - const projectUri = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; - const ballerinaSourceFiles = await getBallerinaFiles(Uri.file(projectUri).fsPath); + const ballerinaSourceFiles = await getBallerinaFiles(Uri.file(projectPath).fsPath); const importStatements: ImportStatements[] = []; @@ -457,7 +457,7 @@ async function collectAllImportsFromProject(): Promise { } return { - projectPath: projectUri, + projectPath: projectPath, imports: importStatements, }; } @@ -640,7 +640,7 @@ export async function generateInlineMappingCodeCore(inlineMappingRequest: Metada const langClient = StateMachine.langClient(); const context = StateMachine.context(); - const projectRoot = context.projectUri; + const projectRoot = context.projectPath; const inlineMappingsResult: InlineMappingsSourceResult = await generateInlineMappingsSource(inlineMappingRequest, langClient, context); diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/service/documentation/doc_generator.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/service/documentation/doc_generator.ts index 6e6baf68760..6e4c132b016 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/service/documentation/doc_generator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/service/documentation/doc_generator.ts @@ -20,22 +20,22 @@ import { DocGenerationRequest } from '@wso2/ballerina-core'; import { getServiceDeclaration } from '../../testGenerator'; import { generateDocumentation, DocumentationGenerationRequest } from './documentation'; import { getProjectSource, getOpenAPISpecification } from '../../utils'; -import { StateMachine } from '../../../../stateMachine'; +import { getCurrentProjectRoot } from '../../../../utils/project-utils'; // Main documentation generator function that handles all the logic export async function generateDocumentationForService(params: DocGenerationRequest): Promise { try { // Get the project root - const projectRoot = StateMachine.context().projectUri; + const projectPath = await getCurrentProjectRoot(); // Get the project source files - const projectSource = await getProjectSource(projectRoot); + const projectSource = await getProjectSource(projectPath); if (!projectSource) { throw new Error("The current project is not recognized as a valid Ballerina project. Please ensure you have opened a Ballerina project."); } // Find the service declaration and get OpenAPI spec - const { serviceDocFilePath } = await getServiceDeclaration(projectRoot, params.serviceName); + const { serviceDocFilePath } = await getServiceDeclaration(projectPath, params.serviceName); const openApiSpec = await getOpenAPISpecification(serviceDocFilePath); // Create the documentation generation request diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/service/test/function_tests.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/service/test/function_tests.ts index 609dbba01b5..f4d8c173f66 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/service/test/function_tests.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/service/test/function_tests.ts @@ -20,7 +20,7 @@ import { generateTest, getDiagnostics } from "../../testGenerator"; import { URI } from "vscode-uri"; import * as fs from "fs"; import { CopilotEventHandler, createWebviewEventHandler } from "../event"; -import { StateMachine } from "../../../../stateMachine"; +import { getCurrentProjectRoot } from "../../../../utils/project-utils"; // Core function test generation that emits events export async function generateFunctionTestsCore( @@ -39,8 +39,16 @@ export async function generateFunctionTestsCore( content: `\n\nGenerating tests for the function ${functionIdentifier}. This may take a moment.`, }); - const projectRoot = StateMachine.context().projectUri; - const response = await generateTest(projectRoot, { + let projectPath: string; + try { + projectPath = await getCurrentProjectRoot(); + } catch (error) { + console.error("Error getting current project root:", error); + eventHandler({ type: "error", content: getErrorMessage(error) }); + return; + } + + const response = await generateTest(projectPath, { targetType: TestGenerationTarget.Function, targetIdentifier: functionIdentifier, testPlan: params.testPlan, @@ -63,7 +71,7 @@ export async function generateFunctionTestsCore( ? existingSource + "\n\n// >>>>>>>>>>>>>>TEST CASES NEED TO BE FIXED <<<<<<<<<<<<<<<\n\n" + response.testSource : response.testSource; - const diagnostics = await getDiagnostics(projectRoot, { + const diagnostics = await getDiagnostics(projectPath, { testSource: generatedFullSource, }); @@ -78,7 +86,7 @@ export async function generateFunctionTestsCore( content: `\nRefining tests based on feedback to ensure accuracy and reliability.`, }); - const fixedCode = await generateTest(projectRoot, { + const fixedCode = await generateTest(projectPath, { targetType: TestGenerationTarget.Function, targetIdentifier: functionIdentifier, testPlan: params.testPlan, diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/service/test/test_plan.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/service/test/test_plan.ts index 9643341e7cb..41af2b865b8 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/service/test/test_plan.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/service/test/test_plan.ts @@ -21,7 +21,7 @@ import { TestGenerationTarget, TestPlanGenerationRequest, Command } from "@wso2/ import { generateTest, getDiagnostics } from "../../testGenerator"; import { CopilotEventHandler, createWebviewEventHandler } from "../event"; import { AIPanelAbortController } from "../../../../../src/rpc-managers/ai-panel/utils"; -import { StateMachine } from "../../../../stateMachine"; +import { getCurrentProjectRoot } from "../../../../utils/project-utils"; export interface TestPlanResponse { testPlan: string; @@ -168,8 +168,15 @@ export async function generateTestPlanCore( type: "content_block", content: `\n\nGenerating tests for the ${target} service. This may take a moment.`, }); - const projectRoot = StateMachine.context().projectUri; - const testResp = await generateTest(projectRoot, { + let projectPath: string; + try { + projectPath = await getCurrentProjectRoot(); + } catch (error) { + console.error("Error getting current project root:", error); + eventHandler({ type: "error", content: getErrorMessage(error) }); + return; + } + const testResp = await generateTest(projectPath, { targetType: TestGenerationTarget.Service, targetIdentifier: target, testPlan: assistantResponse, @@ -178,7 +185,7 @@ export async function generateTestPlanCore( type: "content_block", content: `\nAnalyzing generated tests for potential issues.`, }); - const diagnostics = await getDiagnostics(projectRoot, testResp); + const diagnostics = await getDiagnostics(projectPath, testResp); let testCode = testResp.testSource; const testConfig = testResp.testConfig; if (diagnostics.diagnostics.length > 0) { @@ -186,7 +193,7 @@ export async function generateTestPlanCore( type: "content_block", content: `\nRefining tests based on feedback to ensure accuracy and reliability.`, }); - const fixedCode = await generateTest(projectRoot, { + const fixedCode = await generateTest(projectPath, { targetType: TestGenerationTarget.Service, targetIdentifier: target, testPlan: assistantResponse, diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts index 059ee5c5a12..48331a10cf1 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts @@ -19,7 +19,7 @@ import { DiagnosticEntry, Diagnostics, OpenAPISpec, ProjectDiagnostics, ProjectModule, ProjectSource, SyntaxTree, TestGenerationRequest, TestGenerationResponse, TestGenerationTarget } from '@wso2/ballerina-core'; import { ErrorCode } from "@wso2/ballerina-core"; import { DotToken, IdentifierToken, ModulePart, ResourceAccessorDefinition, ResourcePathRestParam, ResourcePathSegmentParam, ServiceDeclaration, SlashToken, STKindChecker } from "@wso2/syntax-tree"; -import { Uri, workspace } from "vscode"; +import { Uri } from "vscode"; import { PARSING_ERROR, UNKNOWN_ERROR, ENDPOINT_REMOVED } from '../../views/ai-panel/errorCodes'; import { langClient } from './activator'; import * as fs from 'fs'; @@ -219,10 +219,9 @@ export async function getResourceAccessorDef(projectRoot: string, resourceMethod } export async function getDiagnostics( - projectRoot: string, + ballerinaProjectRoot: string, generatedTestSource: TestGenerationResponse ): Promise { - const ballerinaProjectRoot = await findBallerinaProjectRoot(projectRoot); const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'temp-bal-test-gen-')); fs.cpSync(ballerinaProjectRoot, tempDir, { recursive: true }); const tempTestFolderPath = path.join(tempDir, 'tests'); @@ -244,12 +243,7 @@ export async function getDiagnostics( }; } -async function getProjectSource(dirPath: string): Promise { - const projectRoot = await findBallerinaProjectRoot(dirPath); - - if (!projectRoot) { - return null; - } +async function getProjectSource(projectRoot: string): Promise { const projectSource: ProjectSource = { sourceFiles: [], @@ -297,14 +291,9 @@ async function getProjectSource(dirPath: string): Promise return projectSource; } -async function getProjectSourceWithTests(dirPath: string): Promise { - const projectRoot = await findBallerinaProjectRoot(dirPath); - - if (!projectRoot) { - return null; - } +async function getProjectSourceWithTests(projectRoot: string): Promise { - const projectSourceWithTests: ProjectSource = await getProjectSource(dirPath); + const projectSourceWithTests: ProjectSource = await getProjectSource(projectRoot); // Read tests const testsDir = path.join(projectRoot, 'tests'); @@ -445,36 +434,6 @@ async function filterTestGenResponse(resp: Response): Promise { - if (dirPath === null) { - return null; - } - - const workspaceFolders = workspace.workspaceFolders; - if (!workspaceFolders) { - return null; - } - - // Check if the directory is within any of the workspace folders - const workspaceFolder = workspaceFolders.find(folder => dirPath.startsWith(folder.uri.fsPath)); - if (!workspaceFolder) { - return null; - } - - let currentDir = dirPath; - - while (currentDir.startsWith(workspaceFolder.uri.fsPath)) { - const ballerinaTomlPath = path.join(currentDir, 'Ballerina.toml'); - if (fs.existsSync(ballerinaTomlPath)) { - return currentDir; - } - currentDir = path.dirname(currentDir); - } - - return null; -} - function isErrorCode(error: any): boolean { return error.hasOwnProperty("code") && error.hasOwnProperty("message"); } diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts index 60ba64e058c..dbe39c57e62 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts @@ -279,47 +279,10 @@ async function showNoBallerinaSourceWarningMessage() { import { ProjectSource, ProjectModule, OpenAPISpec } from '@wso2/ballerina-core'; import { langClient } from './activator'; -/** - * Finds the root directory of a Ballerina project by searching for Ballerina.toml - */ -export async function findBallerinaProjectRoot(dirPath: string): Promise { - if (dirPath === null) { - return null; - } - - const workspaceFolders = workspace.workspaceFolders; - if (!workspaceFolders) { - return null; - } - - // Check if the directory is within any of the workspace folders - const workspaceFolder = workspaceFolders.find(folder => dirPath.startsWith(folder.uri.fsPath)); - if (!workspaceFolder) { - return null; - } - - let currentDir = dirPath; - - while (currentDir.startsWith(workspaceFolder.uri.fsPath)) { - const ballerinaTomlPath = path.join(currentDir, 'Ballerina.toml'); - if (fs.existsSync(ballerinaTomlPath)) { - return currentDir; - } - currentDir = path.dirname(currentDir); - } - - return null; -} - /** * Gets the project source including all .bal files and modules */ -export async function getProjectSource(dirPath: string): Promise { - const projectRoot = await findBallerinaProjectRoot(dirPath); - - if (!projectRoot) { - return null; - } +export async function getProjectSource(projectRoot: string): Promise { const projectSource: ProjectSource = { sourceFiles: [], @@ -370,14 +333,9 @@ export async function getProjectSource(dirPath: string): Promise { - const projectRoot = await findBallerinaProjectRoot(dirPath); - - if (!projectRoot) { - return null; - } +export async function getProjectSourceWithTests(projectRoot: string): Promise { - const projectSourceWithTests: ProjectSource = await getProjectSource(dirPath); + const projectSourceWithTests: ProjectSource = await getProjectSource(projectRoot); // Read tests const testsDir = path.join(projectRoot, 'tests'); diff --git a/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts index cc2561641cf..33f4afddde6 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts @@ -43,12 +43,12 @@ const TRACE_SERVER_VERBOSE = "verbose"; export function activate(context: BallerinaExtension) { commands.registerCommand(BI_COMMANDS.BI_RUN_PROJECT, () => { - prepareAndGenerateConfig(context, StateMachine.context().projectUri, false, true); + prepareAndGenerateConfig(context, StateMachine.context().projectPath, false, true); }); commands.registerCommand(BI_COMMANDS.BI_DEBUG_PROJECT, () => { commands.executeCommand(FOCUS_DEBUG_CONSOLE_COMMAND); - startDebugging(Uri.file(StateMachine.context().projectUri), false, true); + startDebugging(Uri.file(StateMachine.context().projectPath), false, true); }); commands.registerCommand(BI_COMMANDS.ADD_CONNECTIONS, () => { @@ -87,8 +87,9 @@ export function activate(context: BallerinaExtension) { openView(EVENT_TYPE.OPEN_VIEW, { view: MACHINE_VIEW.Overview }); }); - commands.registerCommand(BI_COMMANDS.ADD_PROJECT, () => { - openView(EVENT_TYPE.OPEN_VIEW, { view: MACHINE_VIEW.BIComponentView }); + commands.registerCommand(BI_COMMANDS.ADD_PROJECT, async () => { + const workspacePath = StateMachine.context().workspacePath; + openView(EVENT_TYPE.OPEN_VIEW, { view: MACHINE_VIEW.BIAddProjectForm, workspacePath: workspacePath }); }); commands.registerCommand(BI_COMMANDS.ADD_DATA_MAPPER, () => { @@ -102,6 +103,7 @@ export function activate(context: BallerinaExtension) { commands.registerCommand(BI_COMMANDS.SWITCH_PROJECT, async () => { // Hack to switch the project. This will reload the window and prompt the user to select the project. // This is a temporary solution until we provide the support for multi root workspaces. + // Ref: https://github.com/wso2/product-ballerina-integrator/issues/1465 StateMachine.sendEvent("SWITCH_PROJECT" as any); }); @@ -125,8 +127,8 @@ export function activate(context: BallerinaExtension) { function openBallerinaTomlFile(context: BallerinaExtension) { - const projectRoot = StateMachine.context().projectUri; - const ballerinaTomlFile = path.join(projectRoot, "Ballerina.toml"); + const projectPath = StateMachine.context().projectPath; + const ballerinaTomlFile = path.join(projectPath, "Ballerina.toml"); try { const content = readFileSync(ballerinaTomlFile, "utf8"); if (content) { @@ -148,12 +150,12 @@ function openBallerinaTomlFile(context: BallerinaExtension) { } function openAllBallerinaFiles(context: BallerinaExtension) { - const projectRoot = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; - if (context.langClient && projectRoot) { + if (context.langClient && projectPath) { try { // Find all Ballerina files in the project - const ballerinaFiles = findBallerinaFiles(projectRoot); + const ballerinaFiles = findBallerinaFiles(projectPath); console.log(`>>> Found ${ballerinaFiles.length} Ballerina files in the project`); // Open each Ballerina file @@ -315,7 +317,7 @@ function isFlowNodeOpenInDiagram(connector: FlowNode) { endLine: connector.codedata.lineRange.endLine.line, endColumn: connector.codedata.lineRange.endLine.offset }; - const flowNodeFilePath = path.join(StateMachine.context().projectUri, connector.codedata.lineRange.fileName); + const flowNodeFilePath = path.join(StateMachine.context().projectPath, connector.codedata.lineRange.fileName); return isFilePathsEqual(openedComponentFilePath, flowNodeFilePath) && isPositionEqual(openedCompoentPosition, flowNodePosition); diff --git a/workspaces/ballerina/ballerina-extension/src/features/config-generator/configGenerator.ts b/workspaces/ballerina/ballerina-extension/src/features/config-generator/configGenerator.ts index 9cc7c1c4b5f..c6c52bc6d69 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/config-generator/configGenerator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/config-generator/configGenerator.ts @@ -35,20 +35,27 @@ import { TracerMachine } from "../tracing"; const UNUSED_IMPORT_ERR_CODE = "BCE2002"; -export async function prepareAndGenerateConfig(ballerinaExtInstance: BallerinaExtension, filePath: string, isCommand?: boolean, isBi?: boolean, executeRun: boolean = true, includeOptional: boolean = false): Promise { - const currentProject: BallerinaProject | undefined = await getCurrentBIProject(filePath); +export async function prepareAndGenerateConfig( + ballerinaExtInstance: BallerinaExtension, + projectPath: string, + isCommand?: boolean, + isBi?: boolean, + executeRun: boolean = true, + includeOptional: boolean = false +): Promise { + const currentProject: BallerinaProject | undefined = await getCurrentBIProject(projectPath); const ignoreFile = path.join(currentProject.path, ".gitignore"); const configFile = path.join(currentProject.path, BAL_CONFIG_FILE); const hasWarnings = ( await checkConfigUpdateRequired( ballerinaExtInstance, - filePath + projectPath )).hasWarnings; if (!hasWarnings) { if (!isCommand && executeRun) { - executeRunCommand(ballerinaExtInstance, filePath, isBi); + executeRunCommand(ballerinaExtInstance, projectPath, isBi); } return; } @@ -56,12 +63,15 @@ export async function prepareAndGenerateConfig(ballerinaExtInstance: BallerinaEx await handleOnUnSetValues(currentProject.packageName, configFile, ignoreFile, ballerinaExtInstance, isCommand, isBi); } -export async function checkConfigUpdateRequired(ballerinaExtInstance: BallerinaExtension, filePath: string): Promise<{ hasWarnings: boolean }> { +export async function checkConfigUpdateRequired( + ballerinaExtInstance: BallerinaExtension, + projectPath: string +): Promise<{ hasWarnings: boolean }> { try { const showLibraryConfigVariables = ballerinaExtInstance.showLibraryConfigVariables(); const response = await ballerinaExtInstance.langClient?.getConfigVariablesV2({ - projectPath: filePath, + projectPath, includeLibraries: showLibraryConfigVariables !== false }) as ConfigVariableResponse; @@ -141,8 +151,8 @@ export async function getCurrentBallerinaProjectFromContext(ballerinaExtInstance } export async function getCurrentBIProject(projectPath: string): Promise { - if (StateMachine.context().projectUri) { - projectPath = StateMachine.context().projectUri; + if (StateMachine.context().projectPath) { + projectPath = StateMachine.context().projectPath; } return await getCurrentBallerinaProject(projectPath); diff --git a/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts b/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts index 965fad9fd5b..ce7e26801af 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts @@ -40,7 +40,7 @@ import { TM_EVENT_START_DEBUG_SESSION, CMP_DEBUGGER, sendTelemetryEvent, sendTelemetryException, CMP_NOTEBOOK, TM_EVENT_START_NOTEBOOK_DEBUG } from '../telemetry'; -import { log, debug as debugLog, isSupportedSLVersion, isWindows } from "../../utils"; +import { log, debug as debugLog, isSupportedSLVersion, isWindows, checkIsBallerinaPackage } from "../../utils"; import { getProjectWorkingDirectory } from "../../utils/file-utils"; import { decimal, ExecutableOptions } from 'vscode-languageclient/node'; import { BAL_NOTEBOOK, getTempFile, NOTEBOOK_CELL_SCHEME } from '../../views/notebook'; @@ -49,9 +49,9 @@ import { existsSync } from 'fs'; import { join } from 'path'; import { LoggingDebugSession, OutputEvent, TerminatedEvent } from 'vscode-debugadapter'; import { DebugProtocol } from 'vscode-debugprotocol'; -import { PALETTE_COMMANDS, PROJECT_TYPE } from '../project/cmds/cmd-runner'; +import { PALETTE_COMMANDS } from '../project/cmds/cmd-runner'; import { Disposable } from 'monaco-languageclient'; -import { getCurrentBallerinaFile, getCurrentBallerinaProject, selectBallerinaProjectForDebugging } from '../../utils/project-utils'; +import { getCurrentProjectRoot, selectBallerinaProjectForDebugging } from '../../utils/project-utils'; import { BallerinaProjectComponents, BIGetEnclosedFunctionRequest, EVENT_TYPE, MainFunctionParamsResponse } from '@wso2/ballerina-core'; import { openView, StateMachine } from '../../stateMachine'; import { waitForBallerinaService } from '../tryit/utils'; @@ -66,7 +66,6 @@ import { findHighestVersionJdk } from '../../utils/server/server'; const BALLERINA_COMMAND = "ballerina.command"; const EXTENDED_CLIENT_CAPABILITIES = "capabilities"; -const BALLERINA_TOML = `Ballerina.toml`; export enum DEBUG_REQUEST { LAUNCH = 'launch' @@ -100,7 +99,7 @@ function getValueFromProgramArgs(programArgs: string[], idx: number) { async function handleMainFunctionParams(config: DebugConfiguration) { const res = await extension.ballerinaExtInstance.langClient?.getMainFunctionParams({ projectRootIdentifier: { - uri: Uri.file(StateMachine.context().projectUri).toString() + uri: Uri.file(StateMachine.context().projectPath).toString() } }) as MainFunctionParamsResponse; if (res.hasMain) { @@ -260,8 +259,8 @@ async function getModifiedConfigs(workspaceFolder: WorkspaceFolder, config: Debu if (!config.script) { // If webview is present and in BI mode, use the project path from the state machine (focused project in BI) - if (StateMachine.context().isBI && isWebviewPresent && StateMachine.context().projectUri) { - config.script = StateMachine.context().projectUri; + if (StateMachine.context().isBI && isWebviewPresent && StateMachine.context().projectPath) { + config.script = StateMachine.context().projectPath; } else { config.script = await selectBallerinaProjectForDebugging(workspaceFolder); } @@ -470,7 +469,7 @@ async function handleDebugHitVisualization(uri: Uri, clientBreakpoint: DebugProt const newContext = StateMachine.context(); // Check if breakpoint is in a different package - if (!uri.fsPath.startsWith(newContext.projectUri)) { + if (!uri.fsPath.startsWith(newContext.projectPath)) { console.log("Debug hit in a different package"); window.showInformationMessage("Cannot visualize debug hit since it belongs to a different integration"); openView(EVENT_TYPE.OPEN_VIEW, newContext); @@ -662,7 +661,7 @@ class BIRunAdapter extends LoggingDebugSession { // Use the current process environment which should have the updated PATH const env = process.env; debugLog(`[BIRunAdapter] Creating shell execution with env. PATH length: ${env.PATH?.length || 0}`); - + // Determine the correct working directory for the task let cwd: string; try { @@ -674,8 +673,8 @@ class BIRunAdapter extends LoggingDebugSession { this.sendResponse(response); throw error; } - - const execution = new ShellExecution(runCommand, { + + const execution = new ShellExecution(runCommand, { env: env as { [key: string]: string }, cwd: cwd }); @@ -765,42 +764,6 @@ async function stopRunFast(root: string): Promise { }); } -export async function getCurrentProjectRoot(): Promise { - // 1. Check if the project path is already set in the state machine context - let currentProjectRoot = StateMachine.context().projectUri; - if (currentProjectRoot) { - return currentProjectRoot; - } - - // 2. Try to get any open Ballerina files in the editor and determine the project root from there - let file: string | undefined; - try { - file = getCurrentBallerinaFile(); - } catch (error) { - // ignore - } - - if (file) { - const currentProject = await getCurrentBallerinaProject(file); - if (currentProject.kind === PROJECT_TYPE.SINGLE_FILE) { - return file; - } else if (currentProject.path) { - return currentProject.path; - } - } - - // 3. Fallback to workspace root - const workspaceRoot = getWorkspaceRoot(); - if (!workspaceRoot) { - throw new Error("Unable to determine the current workspace root."); - } - if (isBallerinaProject(workspaceRoot)) { - return workspaceRoot; - } - - throw new Error(`No valid Ballerina project found`); -} - function getJavaCommand(): string { const ballerinaHome = isWindows() ? fs.realpathSync.native(extension.ballerinaExtInstance.getBallerinaHome()) : extension.ballerinaExtInstance.getBallerinaHome(); // Get the base ballerina home by removing the distribution part @@ -827,10 +790,6 @@ function getJavaCommand(): string { return cmd; } -function getWorkspaceRoot(): string | undefined { - return workspace.workspaceFolders?.[0]?.uri.fsPath; -} - function findFreePort(): Promise { return getPortPromise({ port: 5010, stopPort: 20000 }); } @@ -839,8 +798,3 @@ function isFastRunEnabled(): boolean { const config = workspace.getConfiguration('ballerina'); return config.get('enableRunFast'); } - -function isBallerinaProject(projectPath: string): boolean { - const ballerinaToml = path.join(projectPath, BALLERINA_TOML); - return existsSync(ballerinaToml); -} \ No newline at end of file diff --git a/workspaces/ballerina/ballerina-extension/src/features/natural-programming/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/natural-programming/activator.ts index a1aecbde09f..f27851bbb6b 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/natural-programming/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/natural-programming/activator.ts @@ -41,7 +41,7 @@ export function activate(ballerinaExtInstance: BallerinaExtension) { diagnosticCollection = vscode.languages.createDiagnosticCollection('ballerina'); ballerinaExtInstance.context.subscriptions.push(diagnosticCollection); - const projectPath = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; if (backgroundDriftCheckConfig) { if (!ballerinaExtInstance.context || projectPath == null || projectPath == "") { return; diff --git a/workspaces/ballerina/ballerina-extension/src/features/natural-programming/utils.ts b/workspaces/ballerina/ballerina-extension/src/features/natural-programming/utils.ts index 6739245acfb..f5e731c6063 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/natural-programming/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/natural-programming/utils.ts @@ -39,7 +39,7 @@ import { } from "./constants"; import { isError, isNumber } from 'lodash'; import { HttpStatusCode } from 'axios'; -import { OLD_BACKEND_URL } from '../ai/utils'; +import { isBallerinaProjectAsync, OLD_BACKEND_URL } from '../ai/utils'; import { AIMachineEventType, BallerinaProject, LoginMethod } from '@wso2/ballerina-core'; import { getCurrentBallerinaProjectFromContext } from '../config-generator/configGenerator'; import { BallerinaExtension } from 'src/core'; @@ -49,11 +49,11 @@ import { fetchWithAuth } from '../ai/service/connection'; let controller = new AbortController(); -export async function getLLMDiagnostics(projectUri: string, diagnosticCollection +export async function getLLMDiagnostics(projectPath: string, diagnosticCollection : vscode.DiagnosticCollection): Promise { - const ballerinaProjectSource: BallerinaSource = await getBallerinaProjectSourceFiles(projectUri); + const ballerinaProjectSource: BallerinaSource = await getBallerinaProjectSourceFiles(projectPath); const sourcesOfNonDefaultModulesWithReadme: BallerinaSource[] - = getSourcesOfNonDefaultModulesWithReadme(path.join(projectUri, "modules")); + = getSourcesOfNonDefaultModulesWithReadme(path.join(projectPath, "modules")); const sources: BallerinaSource[] = [ballerinaProjectSource, ...sourcesOfNonDefaultModulesWithReadme]; const backendurl = await getBackendURL(); @@ -69,7 +69,7 @@ export async function getLLMDiagnostics(projectUri: string, diagnosticCollection return responses; } - await createDiagnosticCollection(responses, projectUri, diagnosticCollection); + await createDiagnosticCollection(responses, projectPath, diagnosticCollection); } async function getLLMResponses(sources: BallerinaSource[], token: string, backendurl: string) @@ -143,12 +143,15 @@ async function getLLMResponses(sources: BallerinaSource[], token: string, backen return extractedResponses; } -async function createDiagnosticCollection(responses: any[], projectUri: string, - diagnosticCollection: vscode.DiagnosticCollection) { +async function createDiagnosticCollection( + responses: any[], + projectPath: string, + diagnosticCollection: vscode.DiagnosticCollection +) { let diagnosticsMap = new Map(); for (const response of responses) { - diagnosticsMap = await createDiagnosticsResponse(response, projectUri, diagnosticsMap); + diagnosticsMap = await createDiagnosticsResponse(response, projectPath, diagnosticsMap); } // Set diagnostics in VS Code @@ -261,10 +264,10 @@ async function createDiagnostic(result: ResultItem, uri: Uri): Promise { - const ballerinaProjectSource: BallerinaSource = await getBallerinaProjectSourceFiles(projectUri); +export async function getLLMDiagnosticArrayAsString(projectPath: string): Promise { + const ballerinaProjectSource: BallerinaSource = await getBallerinaProjectSourceFiles(projectPath); const sourcesOfNonDefaultModulesWithReadme: BallerinaSource[] - = getSourcesOfNonDefaultModulesWithReadme(path.join(projectUri, "modules")); + = getSourcesOfNonDefaultModulesWithReadme(path.join(projectPath, "modules")); const sources: BallerinaSource[] = [ballerinaProjectSource, ...sourcesOfNonDefaultModulesWithReadme]; const backendurl = await getBackendURL(); @@ -280,7 +283,7 @@ export async function getLLMDiagnosticArrayAsString(projectUri: string): Promise return responses; } - let diagnosticArray = (await createDiagnosticArray(responses, projectUri)).map(diagnostic => { + let diagnosticArray = (await createDiagnosticArray(responses, projectPath)).map(diagnostic => { return `${diagnostic.message}`; }) .join("\n\n"); @@ -288,11 +291,11 @@ export async function getLLMDiagnosticArrayAsString(projectUri: string): Promise return diagnosticArray; } -async function createDiagnosticArray(responses: any[], projectUri: string): Promise { +async function createDiagnosticArray(responses: any[], projectPath: string): Promise { const diagnostics = []; for (const response of responses) { - await createDiagnosticList(response, projectUri, diagnostics); + await createDiagnosticList(response, projectPath, diagnostics); } function filterUniqueDiagnostics(diagnostics: vscode.Diagnostic[]): vscode.Diagnostic[] { @@ -720,20 +723,3 @@ export async function addConfigFile(configPath: string, isNaturalFunctionsAvaila } ); } - -async function isBallerinaProjectAsync(rootPath: string): Promise { - try { - if (!fs.existsSync(rootPath)) { - return false; - } - - const files = fs.readdirSync(rootPath); - return files.some(file => - file.toLowerCase() === 'ballerina.toml' || - file.toLowerCase().endsWith('.bal') - ); - } catch (error) { - console.error(`Error checking Ballerina project: ${error}`); - return false; - } -} diff --git a/workspaces/ballerina/ballerina-extension/src/features/project/cmds/cmd-runner.ts b/workspaces/ballerina/ballerina-extension/src/features/project/cmds/cmd-runner.ts index 4192d63dcc6..74b52546772 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/project/cmds/cmd-runner.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/project/cmds/cmd-runner.ts @@ -91,7 +91,6 @@ export enum MESSAGES { } export const BAL_CONFIG_FILE = 'Config.toml'; -export const BAL_TOML = "Ballerina.toml"; const TERMINAL_NAME = 'Terminal'; const BAL_CONFIG_FILES = 'BAL_CONFIG_FILES'; diff --git a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/commands.ts b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/commands.ts index 756d1d23afe..81a1cf1eefa 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/commands.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/commands.ts @@ -32,7 +32,7 @@ export function activateEditBiTest() { } const fileName = entry.id.split(":")[1]; - const fileUri = path.resolve(StateMachine.context().projectUri, `tests`, fileName); + const fileUri = path.resolve(StateMachine.context().projectPath, `tests`, fileName); if (fileUri) { const range = entry.range; openView(EVENT_TYPE.OPEN_VIEW, { documentUri: fileUri, @@ -43,7 +43,7 @@ export function activateEditBiTest() { }); commands.registerCommand(BI_COMMANDS.BI_ADD_TEST_FUNCTION, () => { - const fileUri = path.resolve(StateMachine.context().projectUri, `tests`, `tests.bal`); + const fileUri = path.resolve(StateMachine.context().projectPath, `tests`, `tests.bal`); ensureFileExists(fileUri); openView(EVENT_TYPE.OPEN_VIEW, { view: MACHINE_VIEW.BITestFunctionForm, documentUri: fileUri, identifier: '', serviceType: 'ADD_NEW_TEST' }); @@ -55,7 +55,7 @@ export function activateEditBiTest() { } const fileName = entry.id.split(":")[1]; - const fileUri = path.resolve(StateMachine.context().projectUri, `tests`, fileName); + const fileUri = path.resolve(StateMachine.context().projectPath, `tests`, fileName); if (fileUri) { openView(EVENT_TYPE.OPEN_VIEW, { view: MACHINE_VIEW.BITestFunctionForm, documentUri: fileUri, identifier: entry.label, serviceType: 'UPDATE_TEST' }); diff --git a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts index cc31b0a2d61..c9881166c05 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts @@ -22,16 +22,21 @@ import { StateMachine } from "../../stateMachine"; import { TestsDiscoveryRequest, TestsDiscoveryResponse, FunctionTreeNode } from "@wso2/ballerina-core"; import { BallerinaExtension } from "../../core"; import { Position, Range, TestController, Uri, TestItem, commands } from "vscode"; +import { getCurrentProjectRoot } from "../../utils/project-utils"; let groups: string[] = []; export async function discoverTestFunctionsInProject(ballerinaExtInstance: BallerinaExtension, testController: TestController) { groups.push(testController.id); - const filePath: string = path.join(StateMachine.context().projectUri); - const request: TestsDiscoveryRequest = { - filePath - }; + + const projectPath = await getCurrentProjectRoot(); + + if (!projectPath) { + return; + } + + const request: TestsDiscoveryRequest = { projectPath }; const response: TestsDiscoveryResponse = await ballerinaExtInstance.langClient?.getProjectTestFunctions(request); if (response) { createTests(response, testController); @@ -41,8 +46,6 @@ export async function discoverTestFunctionsInProject(ballerinaExtInstance: Balle function createTests(response: TestsDiscoveryResponse, testController: TestController) { if (response.result) { - const projectDir = path.join(StateMachine.context().projectUri); - // Check if the result is a Map or a plain object const isMap = response.result instanceof Map; @@ -74,7 +77,7 @@ function createTests(response: TestsDiscoveryResponse, testController: TestContr const testFunc: FunctionTreeNode = tf as FunctionTreeNode; // Generate a unique ID for the test item using the function name const fileName: string = testFunc.lineRange.fileName; - const fileUri = Uri.file(path.join(projectDir, fileName)); + const fileUri = Uri.file(path.join(StateMachine.context().projectPath, fileName)); const testId = `test:${path.basename(fileUri.path)}:${testFunc.functionName}`; // Create a test item for the test function @@ -102,7 +105,7 @@ function createTests(response: TestsDiscoveryResponse, testController: TestContr export async function handleFileChange(ballerinaExtInstance: BallerinaExtension, uri: Uri, testController: TestController) { const request: TestsDiscoveryRequest = { - filePath: uri.path + projectPath: uri.path }; const response: TestsDiscoveryResponse = await ballerinaExtInstance.langClient?.getFileTestFunctions(request); if (!response || !response.result) { diff --git a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/runner.ts b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/runner.ts index e6c03cd9b4e..576d1e197c8 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/runner.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/runner.ts @@ -138,7 +138,7 @@ enum TEST_STATUS { } async function reportTestResults(run: TestRun, testItems: TestItem[], timeElapsed: number, individualTest: boolean = false) { - const projectRoot = StateMachine.context().projectUri; + const projectRoot = StateMachine.context().projectPath; // reading test results let testsJson: JSON | undefined = undefined; @@ -213,7 +213,7 @@ function endGroup(test: TestItem, allPassed: boolean, run: TestRun) { async function runCommand(command: string): Promise { return new Promise((resolve, reject) => { - exec(command, { cwd: StateMachine.context().projectUri }, (error, stdout, stderr) => { + exec(command, { cwd: StateMachine.context().projectPath }, (error, stdout, stderr) => { if (error) { // Report test failure reject(new Error(stderr || 'Test failed!')); @@ -229,7 +229,7 @@ async function runCommand(command: string): Promise { */ export async function startDebugging(testDebug: boolean, args: any[]) : Promise { - const uri: Uri = Uri.parse(StateMachine.context().projectUri); + const uri: Uri = Uri.parse(StateMachine.context().projectPath); const workspaceFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(uri); const debugConfig: DebugConfiguration = await constructDebugConfig(uri, testDebug, args); diff --git a/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts index a5ae789b001..0f740e5a862 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts @@ -30,7 +30,7 @@ import { startDebugging } from "../editor-support/activator"; import { v4 as uuidv4 } from "uuid"; import { createGraphqlView } from "../../views/graphql"; import { StateMachine } from "../../stateMachine"; -import { getCurrentProjectRoot } from "../debugger"; +import { getCurrentProjectRoot } from "../../utils/project-utils"; // File constants const FILE_NAMES = { @@ -68,19 +68,18 @@ async function openTryItView(withNotice: boolean = false, resourceMetadata?: Res throw new Error('Ballerina Language Server is not connected'); } - let projectPath = StateMachine.context().projectUri; - if (!projectPath) { - const currentProjectRoot = await getCurrentProjectRoot(); - if (!currentProjectRoot) { - throw new Error('Please open a workspace first'); - } - // If currentProjectRoot is a file (single file project), use its directory - // Otherwise, use the current project root - try { - projectPath = getProjectWorkingDirectory(currentProjectRoot); - } catch (error) { - throw new Error(`Failed to determine working directory`); - } + const currentProjectRoot = await getCurrentProjectRoot(); + if (!currentProjectRoot) { + throw new Error('Please open a workspace first'); + } + + // If currentProjectRoot is a file (single file project), use its directory + // Otherwise, use the current project root + let projectPath: string; + try { + projectPath = getProjectWorkingDirectory(currentProjectRoot); + } catch (error) { + throw new Error(`Failed to determine working directory`); } let services: ServiceInfo[] | null = await getAvailableServices(projectPath); @@ -473,6 +472,8 @@ async function getOpenAPIDefinition(service: ServiceInfo): Promise { if (openapiDefinitions === 'NOT_SUPPORTED_TYPE') { throw new Error(`OpenAPI spec generation failed for the service with base path: '${service.basePath}'`); + } else if (openapiDefinitions.error) { + throw new Error(openapiDefinitions.error); } const matchingDefinition = (openapiDefinitions as OpenAPISpec).content?.filter(content => diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-agent/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-agent/rpc-manager.ts index e64989ea722..0ca219219f4 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-agent/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-agent/rpc-manager.ts @@ -150,8 +150,8 @@ export class AiAgentRpcManager implements AIAgentAPI { const context = StateMachine.context(); try { - const projectUri = context.projectUri; - const filePath = Utils.joinPath(URI.file(projectUri), "agents.bal").fsPath; + const projectPath = context.projectPath; + const filePath = Utils.joinPath(URI.file(projectPath), "agents.bal").fsPath; let selectedModel = ""; // Create the tools first if (params.newTools.length > 0) { @@ -161,7 +161,7 @@ export class AiAgentRpcManager implements AIAgentAPI { } // Create the model Second - const aiModuleOrg = await StateMachine.langClient().getAiModuleOrg({ projectPath: projectUri }); + const aiModuleOrg = await StateMachine.langClient().getAiModuleOrg({ projectPath: projectPath }); const allAgents = (await StateMachine.langClient().getAllAgents({ filePath, orgName: aiModuleOrg.orgName })); console.log("All Agents: ", allAgents); @@ -230,8 +230,8 @@ export class AiAgentRpcManager implements AIAgentAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectUri = context.projectUri; - const filePath = Utils.joinPath(URI.file(projectUri), "agents.bal").fsPath; + const projectPath = context.projectPath; + const filePath = Utils.joinPath(URI.file(projectPath), "agents.bal").fsPath; // Create the tools if there are any if (params.newTools.length > 0) { for (const tool of params.newTools) { @@ -269,14 +269,14 @@ export class AiAgentRpcManager implements AIAgentAPI { async createTool(tool: AgentTool): Promise { try { - const projectUri = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; const toolName = tool.toolName; const connectionName = tool.connectionName; - const toolsPath = Utils.joinPath(URI.file(projectUri), "agents.bal").fsPath; + const toolsPath = Utils.joinPath(URI.file(projectPath), "agents.bal").fsPath; let flowNode: FlowNode; // REMOTE_ACTION_CALL| FUNCTION_DEFINITION if (tool.toolType === "Connector") { - const filePath = Utils.joinPath(URI.file(projectUri), "connections.bal").fsPath; + const filePath = Utils.joinPath(URI.file(projectPath), "connections.bal").fsPath; const connectorFlowNode = tool.connectorFlowNode; const connectorActionCodeData = tool.connectorActionCodeData; @@ -301,7 +301,7 @@ export class AiAgentRpcManager implements AIAgentAPI { this.updateFlowNodeProperties(flowNode); } if (tool.toolType === "Function") { - const filePath = Utils.joinPath(URI.file(projectUri), "functions.bal").fsPath; + const filePath = Utils.joinPath(URI.file(projectPath), "functions.bal").fsPath; if (tool.functionState === 1) { // 1 = Create the function first // Get new function flow node @@ -328,7 +328,7 @@ export class AiAgentRpcManager implements AIAgentAPI { .getFunctionNode({ functionName: tool.functionName, fileName: "functions.bal", - projectPath: projectUri + projectPath }); flowNode = existingFunctionFlowNode.functionDefinition as FlowNode; } @@ -351,14 +351,14 @@ export class AiAgentRpcManager implements AIAgentAPI { async createAgentTool(tool: AgentToolRequest): Promise { try { - const projectUri = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; const toolName = tool.toolName; - const toolsPath = Utils.joinPath(URI.file(projectUri), "agents.bal").fsPath; + const toolsPath = Utils.joinPath(URI.file(projectPath), "agents.bal").fsPath; let flowNode: FlowNode; // REMOTE_ACTION_CALL| FUNCTION_DEFINITION const selectedCodeData = tool.selectedCodeData; if (selectedCodeData.node === "REMOTE_ACTION_CALL") { - const filePath = Utils.joinPath(URI.file(projectUri), "connections.bal").fsPath; + const filePath = Utils.joinPath(URI.file(projectPath), "connections.bal").fsPath; // Get the flowNode for connector action const connectorActionFlowNode = await StateMachine.langClient() .getNodeTemplate({ @@ -370,7 +370,7 @@ export class AiAgentRpcManager implements AIAgentAPI { this.updateFlowNodeProperties(flowNode); } if (selectedCodeData.node === "FUNCTION_CALL") { - const filePath = Utils.joinPath(URI.file(projectUri), "functions.bal").fsPath; + const filePath = Utils.joinPath(URI.file(projectPath), "functions.bal").fsPath; // Get the flowNode for existing function action const existingFunctionFlowNode = await StateMachine.langClient() .getNodeTemplate({ @@ -410,9 +410,9 @@ export class AiAgentRpcManager implements AIAgentAPI { } async updateMCPToolKit(params: McpToolUpdateRequest): Promise { - const projectUri = StateMachine.context().projectUri; - const agentsFilePath = Utils.joinPath(URI.file(projectUri), params.agentFlowNode.codedata.lineRange.fileName).fsPath; - const connectionsFilePath = Utils.joinPath(URI.file(projectUri), "connections.bal").fsPath; + const projectPath = StateMachine.context().projectPath; + const agentsFilePath = Utils.joinPath(URI.file(projectPath), params.agentFlowNode.codedata.lineRange.fileName).fsPath; + const connectionsFilePath = Utils.joinPath(URI.file(projectPath), "connections.bal").fsPath; const mcpToolKitVarName = params.updatedNode.properties["variable"].value; // 1. Use the updatedNode from params for the MCP ToolKit edits diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts index a4293462a47..0cd49fbb2dc 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts @@ -101,6 +101,7 @@ import { attemptRepairProject, checkProjectDiagnostics } from "./repair-utils"; import { AIPanelAbortController, addToIntegration, cleanDiagnosticMessages, isErrorCode, requirementsSpecification, searchDocumentation } from "./utils"; import { fetchData } from "./utils/fetch-data-utils"; import { checkToken } from "../../../src/views/ai-panel/utils"; +import { getCurrentProjectRoot } from "../../utils/project-utils"; export class AiPanelRpcManager implements AIPanelAPI { @@ -185,7 +186,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } async addToProject(req: AddToProjectRequest): Promise { - const projectPath = StateMachine.context().projectUri; + const projectPath = await getCurrentProjectRoot(); // Check if workspaceFolderPath is a Ballerina project // Assuming a Ballerina project must contain a 'Ballerina.toml' file const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); @@ -207,9 +208,8 @@ export class AiPanelRpcManager implements AIPanelAPI { return true; } - async getFromFile(req: GetFromFileRequest): Promise { - const projectPath = StateMachine.context().projectUri; + const projectPath = await getCurrentProjectRoot(); const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); @@ -225,7 +225,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } async deleteFromProject(req: DeleteFromProjectRequest): Promise { - const projectPath = StateMachine.context().projectUri; + const projectPath = await getCurrentProjectRoot(); const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); @@ -247,7 +247,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } async getFileExists(req: GetFromFileRequest): Promise { - const projectPath = StateMachine.context().projectUri; + const projectPath = await getCurrentProjectRoot(); const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); @@ -310,9 +310,9 @@ export class AiPanelRpcManager implements AIPanelAPI { async getGeneratedTests(params: TestGenerationRequest): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = StateMachine.context().projectUri; + const projectPath = await getCurrentProjectRoot(); - const generatedTests = await generateTest(projectRoot, params, AIPanelAbortController.getInstance()); + const generatedTests = await generateTest(projectPath, params, AIPanelAbortController.getInstance()); resolve(generatedTests); } catch (error) { reject(error); @@ -323,8 +323,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getTestDiagnostics(params: TestGenerationResponse): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = StateMachine.context().projectUri; - const diagnostics = await getDiagnostics(projectRoot, params); + const projectPath = await getCurrentProjectRoot(); + const diagnostics = await getDiagnostics(projectPath, params); resolve(diagnostics); } catch (error) { reject(error); @@ -335,8 +335,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getServiceSourceForName(params: string): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = StateMachine.context().projectUri; - const { serviceDeclaration, serviceDocFilePath } = await getServiceDeclaration(projectRoot, params); + const projectPath = await getCurrentProjectRoot(); + const { serviceDeclaration } = await getServiceDeclaration(projectPath, params); resolve(serviceDeclaration.source); } catch (error) { reject(error); @@ -347,8 +347,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getResourceSourceForMethodAndPath(params: string): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = StateMachine.context().projectUri; - const { serviceDeclaration, resourceAccessorDef, serviceDocFilePath } = await getResourceAccessorDef(projectRoot, params); + const projectPath = await getCurrentProjectRoot(); + const { resourceAccessorDef } = await getResourceAccessorDef(projectPath, params); resolve(resourceAccessorDef.source); } catch (error) { reject(error); @@ -359,8 +359,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getServiceNames(): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = StateMachine.context().projectUri; - const serviceDeclNames = await getServiceDeclarationNames(projectRoot); + const projectPath = await getCurrentProjectRoot(); + const serviceDeclNames = await getServiceDeclarationNames(projectPath); resolve({ mentions: serviceDeclNames }); @@ -373,8 +373,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getResourceMethodAndPaths(): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = StateMachine.context().projectUri; - const resourceAccessorNames = await getResourceAccessorNames(projectRoot); + const projectPath = await getCurrentProjectRoot(); + const resourceAccessorNames = await getResourceAccessorNames(projectPath); resolve({ mentions: resourceAccessorNames }); @@ -393,9 +393,9 @@ export class AiPanelRpcManager implements AIPanelAPI { } async applyDoOnFailBlocks(): Promise { - const projectRoot = StateMachine.context().projectUri; + const projectPath = await getCurrentProjectRoot(); - if (!projectRoot) { + if (!projectPath) { return null; } @@ -414,7 +414,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } }; - findBalFiles(projectRoot); + findBalFiles(projectPath); for (const balFile of balFiles) { const req: BIModuleNodesRequest = { @@ -655,7 +655,7 @@ export class AiPanelRpcManager implements AIPanelAPI { async addFilesToProject(params: AddFilesToProjectRequest): Promise { try { - const projectPath = StateMachine.context().projectUri; + const projectPath = await getCurrentProjectRoot(); const ballerinaProjectFile = path.join(projectPath, "Ballerina.toml"); if (!fs.existsSync(ballerinaProjectFile)) { @@ -741,14 +741,14 @@ interface BalModification { async function setupProjectEnvironment(project: ProjectSource): Promise<{ langClient: ExtendedLangClient, tempDir: string } | null> { //TODO: Move this to LS - const projectRoot = StateMachine.context().projectUri; - if (!projectRoot) { + const projectPath = await getCurrentProjectRoot(); + if (!projectPath) { return null; } const randomNum = Math.floor(Math.random() * 90000) + 10000; const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `bal-proj-${randomNum}-`)); - fs.cpSync(projectRoot, tempDir, { recursive: true }); + fs.cpSync(projectPath, tempDir, { recursive: true }); //Copy project const langClient = StateMachine.langClient(); //Apply edits @@ -815,14 +815,14 @@ enum CodeGenerationType { } async function getCurrentProjectSource(requestType: OperationType): Promise { - const projectRoot = StateMachine.context().projectUri; + const projectPath = await getCurrentProjectRoot(); - if (!projectRoot) { + if (!projectPath) { return null; } // Read the Ballerina.toml file to get package name - const ballerinaTomlPath = path.join(projectRoot, 'Ballerina.toml'); + const ballerinaTomlPath = path.join(projectPath, 'Ballerina.toml'); let packageName; if (fs.existsSync(ballerinaTomlPath)) { const tomlContent = await fs.promises.readFile(ballerinaTomlPath, 'utf-8'); @@ -842,20 +842,20 @@ async function getCurrentProjectSource(requestType: OperationType): Promise rpcManger.getNodeTemplate(args)); messenger.onRequest(getAiSuggestions, (args: BIAiSuggestionsRequest) => rpcManger.getAiSuggestions(args)); messenger.onNotification(createProject, (args: ProjectRequest) => rpcManger.createProject(args)); + messenger.onNotification(addProjectToWorkspace, (args: AddProjectToWorkspaceRequest) => rpcManger.addProjectToWorkspace(args)); messenger.onRequest(getWorkspaces, () => rpcManger.getWorkspaces()); messenger.onRequest(getProjectStructure, () => rpcManger.getProjectStructure()); messenger.onRequest(getProjectComponents, () => rpcManger.getProjectComponents()); diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-manager.ts index a55c1a09997..ed92e9295c7 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-manager.ts @@ -144,6 +144,7 @@ import { FormDiagnosticsResponse, ExpressionTokensRequest, ExpressionTokensResponse, + AddProjectToWorkspaceRequest, } from "@wso2/ballerina-core"; import * as fs from "fs"; import * as path from 'path'; @@ -166,14 +167,15 @@ import { notifyBreakpointChange } from "../../RPCLayer"; import { OLD_BACKEND_URL } from "../../features/ai/utils"; import { cleanAndValidateProject, getCurrentBIProject } from "../../features/config-generator/configGenerator"; import { BreakpointManager } from "../../features/debugger/breakpoint-manager"; -import { StateMachine, updateView } from "../../stateMachine"; +import { openView, StateMachine, updateView } from "../../stateMachine"; import { getAccessToken, getLoginMethod } from "../../utils/ai/auth"; import { getCompleteSuggestions } from '../../utils/ai/completions'; -import { README_FILE, createBIAutomation, createBIFunction, createBIProjectPure } from "../../utils/bi"; +import { README_FILE, addProjectToExistingWorkspace, convertProjectToWorkspace, createBIAutomation, createBIFunction, createBIProjectPure, createBIWorkspace, openInVSCode } from "../../utils/bi"; import { writeBallerinaFileDidOpen } from "../../utils/modification"; import { updateSourceCode } from "../../utils/source-utils"; import { getView } from "../../utils/state-machine-utils"; import { checkProjectDiagnostics, removeUnusedImports } from "../ai-panel/repair-utils"; + export class BiDiagramRpcManager implements BIDiagramAPI { OpenConfigTomlRequest: (params: OpenConfigTomlRequest) => Promise; @@ -425,7 +427,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { const fileNameOrPath = params.filePath; let filePath = fileNameOrPath; if (path.basename(fileNameOrPath) === fileNameOrPath) { - filePath = path.join(StateMachine.context().projectUri, fileNameOrPath); + filePath = path.join(StateMachine.context().projectPath, fileNameOrPath); } StateMachine.langClient() .getAvailableNodes({ @@ -585,7 +587,29 @@ export class BiDiagramRpcManager implements BIDiagramAPI { } async createProject(params: ProjectRequest): Promise { - createBIProjectPure(params); + if (params.createAsWorkspace) { + const workspaceRoot = createBIWorkspace(params); + openInVSCode(workspaceRoot); + } else { + const projectRoot = createBIProjectPure(params); + openInVSCode(projectRoot); + } + } + + async addProjectToWorkspace(params: AddProjectToWorkspaceRequest): Promise { + if (params.convertToWorkspace) { + try { + await convertProjectToWorkspace(params); + } catch (error) { + window.showErrorMessage("Error converting project to workspace"); + } + } else { + try { + await addProjectToExistingWorkspace(params); + } catch (error) { + window.showErrorMessage("Error adding project to existing workspace"); + } + } } async getWorkspaces(): Promise { @@ -627,7 +651,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async getProjectComponents(): Promise { return new Promise(async (resolve) => { const components = await StateMachine.langClient().getBallerinaProjectComponents({ - documentIdentifiers: [{ uri: Uri.file(StateMachine.context().projectUri).toString() }], + documentIdentifiers: [{ uri: Uri.file(StateMachine.context().projectPath).toString() }], }); resolve({ components }); }); @@ -769,7 +793,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async deleteFlowNode(params: BISourceCodeRequest): Promise { console.log(">>> requesting bi delete node from ls", params); // Clean project diagnostics before deleting flow node - await cleanAndValidateProject(StateMachine.langClient(), StateMachine.context().projectUri); + await cleanAndValidateProject(StateMachine.langClient(), StateMachine.context().projectPath); return new Promise((resolve) => { StateMachine.langClient() @@ -791,8 +815,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async handleReadmeContent(params: ReadmeContentRequest): Promise { // console.log(">>> Savineadme.md", params); return new Promise((resolve) => { - const projectUri = StateMachine.context().projectUri; - const readmePath = path.join(projectUri, README_FILE); + const projectPath = StateMachine.context().projectPath; + const readmePath = path.join(projectPath, README_FILE); if (params.read) { if (!fs.existsSync(readmePath)) { resolve({ content: "" }); @@ -831,7 +855,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async getConfigVariables(): Promise { return new Promise(async (resolve) => { - const projectPath = path.join(StateMachine.context().projectUri); + const projectPath = StateMachine.context().projectPath; const variables = await StateMachine.langClient().getConfigVariables({ projectPath: projectPath }) as ConfigVariableResponse; resolve(variables); }); @@ -855,7 +879,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async getConfigVariablesV2(params: ConfigVariableRequest): Promise { return new Promise(async (resolve) => { - const projectPath = path.join(StateMachine.context().projectUri); + const projectPath = StateMachine.context().projectPath; const showLibraryConfigVariables = extension.ballerinaExtInstance.showLibraryConfigVariables(); // if params includeLibraries is not set, then use settings @@ -920,8 +944,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { return new Promise(async (resolve) => { const currentProject: BallerinaProject | undefined = await getCurrentBIProject(params.filePath); - const configFilePath = path.join(StateMachine.context().projectUri, "Config.toml"); - const ignoreFile = path.join(StateMachine.context().projectUri, ".gitignore"); + const configFilePath = path.join(StateMachine.context().projectPath, "Config.toml"); + const ignoreFile = path.join(StateMachine.context().projectPath, ".gitignore"); const docLink = "https://ballerina.io/learn/provide-values-to-configurable-variables/#provide-via-toml-syntax"; const uri = Uri.file(configFilePath); @@ -1041,8 +1065,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { const deployementParams: ICreateComponentCmdParams = { integrationType: integrationType as any, buildPackLang: "ballerina", // Example language - name: path.basename(StateMachine.context().projectUri), - componentDir: StateMachine.context().projectUri, + name: path.basename(StateMachine.context().projectPath), + componentDir: StateMachine.context().projectPath, extName: "Devant" }; commands.executeCommand(PlatformExtCommandIds.CreateNewComponent, deployementParams); @@ -1066,15 +1090,15 @@ export class BiDiagramRpcManager implements BIDiagramAPI { console.log(">>> requesting bi module nodes from ls"); return new Promise((resolve) => { const context = StateMachine.context(); - if (!context.projectUri) { - console.log(">>> projectUri not found in the context"); + if (!context.projectPath) { + console.log(">>> projectPath not found in the context"); return new Promise((resolve) => { resolve(undefined); }); } const params: BIModuleNodesRequest = { - filePath: context.projectUri, + filePath: context.projectPath, }; StateMachine.langClient() @@ -1202,7 +1226,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async deleteByComponentInfo(params: BIDeleteByComponentInfoRequest): Promise { console.log(">>> requesting bi delete node from ls by componentInfo", params); - const projectDiags: Diagnostics[] = await checkProjectDiagnostics(StateMachine.langClient(), StateMachine.context().projectUri); + const projectDiags: Diagnostics[] = await checkProjectDiagnostics(StateMachine.langClient(), StateMachine.context().projectPath); const position: NodePosition = { startLine: params.component?.startLine, @@ -1212,10 +1236,10 @@ export class BiDiagramRpcManager implements BIDiagramAPI { }; // Check if the filepath is only the filename or the full path if not concatenate the project uri let filePath = params.component?.filePath; - if (!filePath.includes(StateMachine.context().projectUri)) { - filePath = path.join(StateMachine.context().projectUri, filePath); + if (!filePath.includes(StateMachine.context().projectPath)) { + filePath = path.join(StateMachine.context().projectPath, filePath); } - const componentView = await getView(filePath, position, StateMachine.context().projectUri); + const componentView = await getView(filePath, position, StateMachine.context().projectPath); // Helper function to perform the actual delete operation const performDelete = async (): Promise => { return new Promise((resolve, reject) => { @@ -1429,10 +1453,10 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async getDesignModel(): Promise { console.log(">>> requesting design model from ls"); return new Promise((resolve) => { - const projectUri = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; StateMachine.langClient() - .getDesignModel({ projectPath: projectUri }) + .getDesignModel({ projectPath }) .then((model) => { console.log(">>> design model from ls", model); resolve(model); @@ -1449,8 +1473,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async getTypes(params: GetTypesRequest): Promise { - const projectUri = StateMachine.context().projectUri; - const ballerinaFiles = await getBallerinaFiles(Uri.file(projectUri).fsPath); + const projectPath = StateMachine.context().projectPath; + const ballerinaFiles = await getBallerinaFiles(Uri.file(projectPath).fsPath); return new Promise((resolve, reject) => { StateMachine.langClient() @@ -1465,8 +1489,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { } async updateType(params: UpdateTypeRequest): Promise { - const projectUri = StateMachine.context().projectUri; - const filePath = path.join(projectUri, params.filePath); + const projectPath = StateMachine.context().projectPath; + const filePath = path.join(projectPath, params.filePath); return new Promise((resolve, reject) => { console.log(">>> updating type request", params.type); StateMachine.langClient() @@ -1591,8 +1615,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { } async createGraphqlClassType(params: UpdateTypeRequest): Promise { - const projectUri = StateMachine.context().projectUri; - const filePath = path.join(projectUri, params.filePath); + const projectPath = StateMachine.context().projectPath; + const filePath = path.join(projectPath, params.filePath); return new Promise((resolve, reject) => { StateMachine.langClient() .createGraphqlClassType({ filePath, type: params.type, description: "" }) @@ -1655,8 +1679,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { } async renameIdentifier(params: RenameIdentifierRequest): Promise { - const projectUri = StateMachine.context().projectUri; - const filePath = path.join(projectUri, params.fileName); + const projectPath = StateMachine.context().projectPath; + const filePath = path.join(projectPath, params.fileName); const fileUri = Uri.file(filePath).toString(); const request: RenameRequest = { textDocument: { @@ -1730,8 +1754,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { let hasComponent = false; let hasLocalChanges = false; try { - const projectRoot = StateMachine.context().projectUri; - const repoRoot = getRepoRoot(projectRoot); + const projectPath = StateMachine.context().projectPath; + const repoRoot = getRepoRoot(projectPath); if (repoRoot) { const contextYamlPath = path.join(repoRoot, ".choreo", "context.yaml"); if (fs.existsSync(contextYamlPath)) { @@ -1744,10 +1768,10 @@ export class BiDiagramRpcManager implements BIDiagramAPI { return { hasComponent: hasContextYaml, isLoggedIn: false }; } const platformExtAPI: IWso2PlatformExtensionAPI = await platformExt.activate(); - hasLocalChanges = await platformExtAPI.localRepoHasChanges(projectRoot); + hasLocalChanges = await platformExtAPI.localRepoHasChanges(projectPath); isLoggedIn = platformExtAPI.isLoggedIn(); if (isLoggedIn) { - const components = platformExtAPI.getDirectoryComponents(projectRoot); + const components = platformExtAPI.getDirectoryComponents(projectPath); hasComponent = components.length > 0; return { isLoggedIn, hasComponent, hasLocalChanges }; } @@ -1805,8 +1829,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async updateTypes(params: UpdateTypesRequest): Promise { return new Promise((resolve, reject) => { - const projectUri = StateMachine.context().projectUri; - const completeFilePath = path.join(projectUri, params.filePath); + const projectPath = StateMachine.context().projectPath; + const completeFilePath = path.join(projectPath, params.filePath); StateMachine.langClient().updateTypes( { filePath: completeFilePath, types: params.types } @@ -1849,7 +1873,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async generateOpenApiClient(params: OpenAPIClientGenerationRequest): Promise { return new Promise((resolve, reject) => { - const projectPath = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; const request: OpenAPIClientGenerationRequest = { openApiContractPath: params.openApiContractPath, projectPath: projectPath, @@ -1883,7 +1907,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async getOpenApiGeneratedModules(params: OpenAPIGeneratedModulesRequest): Promise { return new Promise((resolve, reject) => { - const projectPath = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; const request: OpenAPIGeneratedModulesRequest = { projectPath: projectPath }; @@ -1898,7 +1922,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async deleteOpenApiGeneratedModules(params: OpenAPIClientDeleteRequest): Promise { return new Promise((resolve, reject) => { - const projectPath = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; const request: OpenAPIClientDeleteRequest = { projectPath: projectPath, module: params.module @@ -1933,8 +1957,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async getTypeFromJson(params: JsonToTypeRequest): Promise { return new Promise((resolve, reject) => { - const projectUri = StateMachine.context().projectUri; - const filePath = path.join(projectUri, 'types.bal'); + const projectPath = StateMachine.context().projectPath; + const filePath = path.join(projectPath, 'types.bal'); StateMachine.langClient().getTypeFromJson({ ...params, filePath }) .then((response) => { console.log(">>> type from json response", response); @@ -1949,8 +1973,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async deleteType(params: DeleteTypeRequest): Promise { return new Promise((resolve, reject) => { - const projectUri = StateMachine.context().projectUri; - const filePath = path.join(projectUri, params.filePath); + const projectPath = StateMachine.context().projectPath; + const filePath = path.join(projectPath, params.filePath); StateMachine.langClient().deleteType({ filePath: filePath, lineRange: params.lineRange }) .then(async (deleteTypeResponse: DeleteTypeResponse) => { if (deleteTypeResponse.textEdits) { @@ -1966,8 +1990,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { } async verifyTypeDelete(params: VerifyTypeDeleteRequest): Promise { - const projectUri = StateMachine.context().projectUri; - const filePath = path.join(projectUri, params.filePath); + const projectPath = StateMachine.context().projectPath; + const filePath = path.join(projectPath, params.filePath); const request: VerifyTypeDeleteRequest = { filePath: filePath, diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/rpc-manager.ts index 6c96823599b..4c49e256156 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/rpc-manager.ts @@ -90,8 +90,8 @@ export class CommonRpcManager implements CommonRPCAPI { async goToSource(params: GoToSourceRequest): Promise { const context = StateMachine.context(); let filePath = params?.filePath || context.documentUri!; - if (params?.fileName && context?.projectUri) { - filePath = path.join(context.projectUri, params.fileName); + if (params?.fileName && context?.projectPath) { + filePath = path.join(context.projectPath, params.fileName); } goToSource(params.position, filePath); } @@ -259,7 +259,7 @@ export class CommonRpcManager implements CommonRPCAPI { } async getCurrentProjectTomlValues(): Promise { - return getProjectTomlValues(StateMachine.context().projectUri); + return getProjectTomlValues(StateMachine.context().projectPath); } async getWorkspaceType(): Promise { diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts index 3dc74928b47..872a3843427 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts @@ -16,9 +16,9 @@ * under the License. */ +import * as os from 'os'; import { NodePosition } from "@wso2/syntax-tree"; import { Position, Range, Uri, window, workspace, WorkspaceEdit } from "vscode"; -import * as os from 'os'; import { TextEdit } from "@wso2/ballerina-core"; export const BALLERINA_INTEGRATOR_ISSUES_URL = "https://github.com/wso2/product-ballerina-integrator/issues"; diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/icp-service/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/icp-service/rpc-manager.ts index acdbb2f75c2..4f73cf6b2c8 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/icp-service/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/icp-service/rpc-manager.ts @@ -36,7 +36,7 @@ export class ICPServiceRpcManager implements ICPServiceAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectPath: string = context.projectUri; + const projectPath: string = context.projectPath; const param = { projectPath }; const res: TestSourceEditResponse = await context.langClient.addICP(param); await updateSourceCode({ textEdits: res.textEdits, description: 'ICP Creation' }); @@ -52,7 +52,7 @@ export class ICPServiceRpcManager implements ICPServiceAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectPath: string = context.projectUri; + const projectPath: string = context.projectPath; const param = { projectPath }; const res: TestSourceEditResponse = await context.langClient.disableICP(param); await updateSourceCode({ textEdits: res.textEdits, description: 'ICP Disable' }); @@ -69,7 +69,7 @@ export class ICPServiceRpcManager implements ICPServiceAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectPath: string = context.projectUri; + const projectPath: string = context.projectPath; const param = { projectPath }; const res: ICPEnabledResponse = await context.langClient.isIcpEnabled(param); resolve(res); diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/record-creator/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/record-creator/rpc-manager.ts index bfdc3950e64..16561f73fc1 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/record-creator/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/record-creator/rpc-manager.ts @@ -44,8 +44,8 @@ export class RecordCreatorRpcManager implements RecordCreatorAPI { } async convertJsonToRecordType(params: JsonToRecordParams): Promise { - const projectUri = StateMachine.context().projectUri; - const filePathUri = path.join(projectUri, 'types.bal'); + const projectPath = StateMachine.context().projectPath; + const filePathUri = path.join(projectPath, 'types.bal'); return new Promise(async (resolve) => { const response = await StateMachine.langClient().convertJsonToRecordType({ ...params, @@ -56,8 +56,8 @@ export class RecordCreatorRpcManager implements RecordCreatorAPI { } async convertXmlToRecordType(params: XMLToRecordParams): Promise { - const projectUri = StateMachine.context().projectUri; - const filePath = path.join(projectUri, 'types.bal'); + const projectPath = StateMachine.context().projectPath; + const filePath = path.join(projectPath, 'types.bal'); return new Promise(async (resolve) => { const response = await StateMachine.langClient().convertXmlToRecordType({ ...params, diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/service-designer/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/service-designer/rpc-manager.ts index aa6192916e8..040e97d28cb 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/service-designer/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/service-designer/rpc-manager.ts @@ -101,8 +101,8 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectDir = path.join(StateMachine.context().projectUri); - const targetFile = path.join(projectDir, `main.bal`); + const projectPath = path.join(StateMachine.context().projectPath); + const targetFile = path.join(projectPath, `main.bal`); this.ensureFileExists(targetFile); params.filePath = targetFile; const res: ListenersResponse = await context.langClient.getListeners(params); @@ -129,8 +129,8 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectDir = path.join(StateMachine.context().projectUri); - const targetFile = path.join(projectDir, `main.bal`); + const projectPath = path.join(StateMachine.context().projectPath); + const targetFile = path.join(projectPath, `main.bal`); this.ensureFileExists(targetFile); params.filePath = targetFile; const res: ListenerSourceCodeResponse = await context.langClient.addListenerSourceCode(params); @@ -166,8 +166,8 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectDir = path.join(StateMachine.context().projectUri); - const targetFile = path.join(projectDir, `main.bal`); + const projectPath = path.join(StateMachine.context().projectPath); + const targetFile = path.join(projectPath, `main.bal`); this.ensureFileExists(targetFile); params.filePath = targetFile; const res: ListenerSourceCodeResponse = await context.langClient.updateListenerSourceCode(params); @@ -186,8 +186,8 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectDir = path.join(StateMachine.context().projectUri); - const targetFile = path.join(projectDir, `main.bal`); + const projectPath = path.join(StateMachine.context().projectPath); + const targetFile = path.join(projectPath, `main.bal`); this.ensureFileExists(targetFile); params.filePath = targetFile; const res: ServiceModelResponse = await context.langClient.getServiceModel(params); @@ -202,8 +202,8 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectDir = path.join(StateMachine.context().projectUri); - const targetFile = path.join(projectDir, `main.bal`); + const projectPath = path.join(StateMachine.context().projectPath); + const targetFile = path.join(projectPath, `main.bal`); this.ensureFileExists(targetFile); params.filePath = targetFile; const identifiers = []; @@ -241,8 +241,8 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectDir = path.join(StateMachine.context().projectUri); - const targetFile = path.join(projectDir, `main.bal`); + const projectPath = path.join(StateMachine.context().projectPath); + const targetFile = path.join(projectPath, `main.bal`); this.ensureFileExists(targetFile); params.filePath = targetFile; const identifiers = []; @@ -298,9 +298,9 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectDir = path.join(StateMachine.context().projectUri); + const projectPath = path.join(StateMachine.context().projectPath); if (!params.filePath) { - const targetFile = path.join(projectDir, `main.bal`); + const targetFile = path.join(projectPath, `main.bal`); this.ensureFileExists(targetFile); params.filePath = targetFile; } @@ -396,7 +396,7 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { async getResourceReturnTypes(params: ResourceReturnTypesRequest): Promise { return new Promise(async (resolve) => { const context = StateMachine.context(); - params.filePath = params.filePath || context.projectUri; + params.filePath = params.filePath || context.projectPath; try { const res: VisibleTypesResponse = await context.langClient.getResourceReturnTypes(params); resolve(res); @@ -422,7 +422,7 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectDir = path.join(StateMachine.context().projectUri); + const projectDir = path.join(StateMachine.context().projectPath); const targetFile = path.join(projectDir, `main.bal`); this.ensureFileExists(targetFile); params.filePath = targetFile; @@ -438,7 +438,7 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { return new Promise(async (resolve) => { const context = StateMachine.context(); try { - const projectDir = path.join(StateMachine.context().projectUri); + const projectDir = path.join(StateMachine.context().projectPath); const targetFile = path.join(projectDir, `main.bal`); this.ensureFileExists(targetFile); params.filePath = targetFile; diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/visualizer/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/visualizer/rpc-manager.ts index f4da55ae255..0226d4cfc8a 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/visualizer/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/visualizer/rpc-manager.ts @@ -200,7 +200,7 @@ export class VisualizerRpcManager implements VisualizerAPI { async joinProjectPath(segments: string | string[]): Promise { return new Promise((resolve) => { - const projectPath = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; const filePath = Array.isArray(segments) ? Utils.joinPath(URI.file(projectPath), ...segments) : Utils.joinPath(URI.file(projectPath), segments); resolve(filePath.fsPath); }); diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index 0e647e6a217..340671dfac4 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -12,9 +12,28 @@ import * as path from 'path'; import { extension } from './BalExtensionContext'; import { AIStateMachine } from './views/ai-panel/aiMachine'; import { StateMachinePopup } from './stateMachinePopup'; -import { checkIsBallerinaPackage, checkIsBI, fetchScope, getOrgPackageName, UndoRedoManager, getProjectTomlValues } from './utils'; +import { + checkIsBallerinaPackage, + checkIsBallerinaWorkspace, + checkIsBI, + fetchScope, + filterPackagePaths, + getOrgPackageName, + UndoRedoManager, + getProjectTomlValues, + getWorkspaceTomlValues +} from './utils'; import { buildProjectArtifactsStructure } from './utils/project-artifacts'; +export interface ProjectMetadata { + readonly isBI: boolean; + readonly projectPath: string; + readonly workspacePath?: string; + readonly scope?: SCOPE; + readonly orgName?: string; + readonly packageName?: string; +} + interface MachineContext extends VisualizerLocation { langClient: ExtendedLangClient | null; isBISupported: boolean; @@ -59,7 +78,7 @@ const stateMachine = createMachine( UPDATE_PROJECT_ROOT: { actions: [ assign({ - projectUri: (context, event) => event.projectPath + projectPath: (context, event) => event.projectPath }), async (context, event) => { await buildProjectArtifactsStructure(event.projectPath, StateMachine.langClient(), true); @@ -86,20 +105,22 @@ const stateMachine = createMachine( states: { switch_project: { invoke: { - src: checkForProjects, + src: (context, event) => checkForProjects(true), onDone: [ { target: "viewActive.viewReady", actions: [ assign({ isBI: (context, event) => event.data.isBI, - projectUri: (context, event) => event.data.projectPath, + projectPath: (context, event) => event.data.projectPath, + workspacePath: (context, event) => event.data.workspacePath, scope: (context, event) => event.data.scope, org: (context, event) => event.data.orgName, package: (context, event) => event.data.packageName, }), async (context, event) => { await buildProjectArtifactsStructure(event.data.projectPath, StateMachine.langClient(), true); + openView(EVENT_TYPE.OPEN_VIEW, { view: MACHINE_VIEW.Overview }); notifyCurrentWebview(); } ] @@ -109,14 +130,15 @@ const stateMachine = createMachine( }, initialize: { invoke: { - src: checkForProjects, + src: (context, event) => checkForProjects(false), onDone: [ { target: "renderInitialView", cond: (context, event) => event.data && event.data.isBI, actions: assign({ isBI: (context, event) => event.data.isBI, - projectUri: (context, event) => event.data.projectPath, + projectPath: (context, event) => event.data.projectPath, + workspacePath: (context, event) => event.data.workspacePath, scope: (context, event) => event.data.scope, org: (context, event) => event.data.orgName, package: (context, event) => event.data.packageName, @@ -127,7 +149,8 @@ const stateMachine = createMachine( cond: (context, event) => event.data && event.data.isBI === false, actions: assign({ isBI: (context, event) => event.data.isBI, - projectUri: (context, event) => event.data.projectPath, + projectPath: (context, event) => event.data.projectPath, + workspacePath: (context, event) => event.data.workspacePath, scope: (context, event) => event.data.scope, org: (context, event) => event.data.orgName, package: (context, event) => event.data.packageName, @@ -274,7 +297,7 @@ const stateMachine = createMachine( position: (context, event) => event.viewLocation.position, identifier: (context, event) => event.viewLocation.identifier, serviceType: (context, event) => event.viewLocation.serviceType, - projectUri: (context, event) => event.viewLocation?.projectUri || context?.projectUri, + projectPath: (context, event) => event.viewLocation?.projectPath || context?.projectPath, package: (context, event) => event.viewLocation?.package || context?.package, type: (context, event) => event.viewLocation?.type, isGraphql: (context, event) => event.viewLocation?.isGraphql, @@ -339,13 +362,13 @@ const stateMachine = createMachine( try { // Register the event driven listener to get the artifact changes context.langClient.registerPublishArtifacts(); - // If the project uri is not set, we don't need to build the project structure - if (context.projectUri) { + // If the project uri or workspace path is not set, we don't need to build the project structure + if (context.projectPath || context.workspacePath) { // Add a 2 second delay before registering artifacts await new Promise(resolve => setTimeout(resolve, 1000)); // Initial Project Structure - const projectStructure = await buildProjectArtifactsStructure(context.projectUri, context.langClient); + const projectStructure = await buildProjectArtifactsStructure(context.projectPath, context.langClient); resolve({ projectStructure }); } else { resolve({ projectStructure: undefined }); @@ -383,10 +406,10 @@ const stateMachine = createMachine( }, resolveMissingDependencies: (context, event) => { return new Promise(async (resolve, reject) => { - if (context?.projectUri) { + if (context?.projectPath) { const diagnostics: ProjectDiagnosticsResponse = await StateMachine.langClient().getProjectDiagnostics({ projectRootIdentifier: { - uri: Uri.file(context.projectUri).toString(), + uri: Uri.file(context.projectPath).toString(), } }); @@ -463,7 +486,7 @@ const stateMachine = createMachine( }, findView(context, event): Promise { return new Promise(async (resolve, reject) => { - const projectTomlValues = await getProjectTomlValues(context.projectUri); + const projectTomlValues = await getProjectTomlValues(context.projectPath); const packageName = projectTomlValues?.package?.name; if (!context.view && context.langClient) { if (!context.position || ("groupId" in context.position)) { @@ -476,7 +499,8 @@ const stateMachine = createMachine( }); return resolve(); } - const view = await getView(context.documentUri, context.position, context?.projectUri); + const view = await getView(context.documentUri, context.position, context?.projectPath); + view.location.package = packageName || context.package; view.location.package = packageName || context.package; history.push(view); return resolve(); @@ -661,7 +685,7 @@ export function openView(type: EVENT_TYPE, viewLocation: VisualizerLocation, res stateService.send({ type: type, viewLocation: viewLocation }); } -export function updateView(refreshTreeView?: boolean, projectUri?: string) { +export function updateView(refreshTreeView?: boolean) { let lastView = getLastHistory(); // Step over to the next location if the last view is skippable if (!refreshTreeView && lastView?.location.view.includes("SKIP")) { @@ -738,7 +762,7 @@ export function updateView(refreshTreeView?: boolean, projectUri?: string) { stateService.send({ type: "VIEW_UPDATE", viewLocation: lastView ? newLocation : { view: "Overview" } }); if (refreshTreeView) { - buildProjectArtifactsStructure(projectUri || StateMachine.context().projectUri, StateMachine.langClient(), true); + buildProjectArtifactsStructure(StateMachine.context().projectPath, StateMachine.langClient(), true); } notifyCurrentWebview(); } @@ -773,7 +797,7 @@ function getLastHistory() { return historyStack?.[historyStack?.length - 1]; } -async function checkForProjects(): Promise<{ isBI: boolean, projectPath: string, scope?: SCOPE }> { +async function checkForProjects(isSwitching: boolean = false): Promise { const workspaceFolders = workspace.workspaceFolders; if (!workspaceFolders) { @@ -781,13 +805,13 @@ async function checkForProjects(): Promise<{ isBI: boolean, projectPath: string, } if (workspaceFolders.length > 1) { - return await handleMultipleWorkspaces(workspaceFolders); + return await handleMultipleWorkspaceFolders(workspaceFolders); } - return await handleSingleWorkspace(workspaceFolders[0].uri); + return await handleSingleWorkspaceFolder(workspaceFolders[0].uri, isSwitching); } -async function handleMultipleWorkspaces(workspaceFolders: readonly WorkspaceFolder[]) { +async function handleMultipleWorkspaceFolders(workspaceFolders: readonly WorkspaceFolder[]): Promise { const balProjectChecks = await Promise.all( workspaceFolders.map(async folder => ({ folder, @@ -799,21 +823,20 @@ async function handleMultipleWorkspaces(workspaceFolders: readonly WorkspaceFold .map(result => result.folder); if (balProjects.length > 1 && workspace.workspaceFile?.scheme === "file") { - const projectPaths = balProjects.map(folder => folder.uri.fsPath); - let selectedProject = await window.showQuickPick(projectPaths, { - placeHolder: 'Select a project to load the WSO2 Integrator' + // Show notification to guide users to use Ballerina workspaces instead of VSCode workspaces + window.showInformationMessage( + 'Multiple Ballerina projects detected in VSCode workspace. Please use Ballerina workspaces for better project management and native support.', + 'Learn More' + ).then(selection => { + if (selection === 'Learn More') { + // TODO: Add a guide on how to use Ballerina workspaces + // Open documentation or guide about Ballerina workspaces + commands.executeCommand('vscode.open', Uri.parse('https://ballerina.io/learn/workspaces')); + } }); - if (!selectedProject) { - // Pick the first project if the user cancels the selection - selectedProject = projectPaths[0]; - } - - const isBI = checkIsBI(Uri.file(selectedProject)); - const scope = isBI && fetchScope(Uri.file(selectedProject)); - const { orgName, packageName } = getOrgPackageName(selectedProject); - setBIContext(isBI); - return { isBI, projectPath: selectedProject, scope, orgName, packageName }; + // Return empty result to indicate no project should be loaded + return { isBI: false, projectPath: '' }; } else if (balProjects.length === 1) { const isBI = checkIsBI(balProjects[0].uri); const scope = isBI && fetchScope(balProjects[0].uri); @@ -825,21 +848,80 @@ async function handleMultipleWorkspaces(workspaceFolders: readonly WorkspaceFold return { isBI: false, projectPath: '' }; } -async function handleSingleWorkspace(workspaceURI: any) { - const isBallerina = await checkIsBallerinaPackage(workspaceURI); - const isBI = isBallerina && checkIsBI(workspaceURI); - const scope = fetchScope(workspaceURI); - const projectPath = isBallerina ? workspaceURI.fsPath : ""; - const { orgName, packageName } = getOrgPackageName(projectPath); +async function handleSingleWorkspaceFolder(workspaceURI: Uri, isSwitching: boolean = false): Promise { + const isBallerinaWorkspace = await checkIsBallerinaWorkspace(workspaceURI); - setBIContext(isBI); - if (!isBI) { - console.error("No BI enabled workspace found"); - } + if (isBallerinaWorkspace) { + // A workaround for supporting multiple packages in a workspace + // TODO: Once the artifacts API is updated to support multiple packages and the new API for detecting the + // most appropriate package to load the WSO2 Integrator is implemented, this workaround can be removed + // Ref: https://github.com/wso2/product-ballerina-integrator/issues/1465 + const workspaceTomlValues = await getWorkspaceTomlValues(workspaceURI.fsPath); - return { isBI, projectPath, scope, orgName, packageName }; -} + if (!workspaceTomlValues) { + return { isBI: false, projectPath: '' }; + } + + const biExtension = extensions.getExtension('wso2.ballerina-integrator'); + const shouldShowBIQuickPick = isSwitching || (biExtension && checkIsBI(workspaceURI)); + const packages = await filterPackagePaths(workspaceTomlValues.workspace.packages, workspaceURI.fsPath); + let targetPackage; + + if (packages.length === 0) { + return { isBI: false, projectPath: '' }; + } else if (shouldShowBIQuickPick && packages.length > 1) { + targetPackage = await window.showQuickPick(packages, { + title: 'Select Package for WSO2 Integrator: BI', + placeHolder: 'Choose a package from your workspace to load in BI mode', + ignoreFocusOut: true + }); + } else if (!shouldShowBIQuickPick || packages.length === 1) { + targetPackage = packages[0]; + } + + if (!targetPackage && packages.length > 1) { + // If the user has not selected a package, select the first package + // This is a temporary solution until we provide the support for multi root workspaces + // Ref: https://github.com/wso2/product-ballerina-integrator/issues/1465 + targetPackage = packages[0]; + } + if (targetPackage) { + const packagePath = path.isAbsolute(targetPackage) + ? targetPackage + : path.join(workspaceURI.fsPath, targetPackage); + const packageUri = Uri.file(packagePath); + + const isBallerinaPackage = await checkIsBallerinaPackage(packageUri); + const isBI = isBallerinaPackage && checkIsBI(packageUri); + const scope = fetchScope(packageUri); + const projectPath = isBallerinaPackage ? packagePath : ""; + const { orgName, packageName } = getOrgPackageName(projectPath); + + setBIContext(isBI); + if (!isBI) { + console.error("No BI enabled workspace found"); + } + + return { isBI, projectPath, workspacePath: workspaceURI.fsPath, scope, orgName, packageName }; + } else { + return { isBI: false, projectPath: '' }; + } + } else { + const isBallerinaPackage = await checkIsBallerinaPackage(workspaceURI); + const isBI = isBallerinaPackage && checkIsBI(workspaceURI); + const scope = fetchScope(workspaceURI); + const projectPath = isBallerinaPackage ? workspaceURI.fsPath : ""; + const { orgName, packageName } = getOrgPackageName(projectPath); + + setBIContext(isBI); + if (!isBI) { + console.error("No BI enabled workspace found"); + } + + return { isBI, projectPath, scope, orgName, packageName }; + } +} function setBIContext(isBI: boolean) { commands.executeCommand('setContext', 'isBIProject', isBI); } diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachinePopup.ts b/workspaces/ballerina/ballerina-extension/src/stateMachinePopup.ts index a9e3022be13..b0506e90673 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachinePopup.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachinePopup.ts @@ -146,7 +146,7 @@ const stateMachinePopup = createMachine({ initializeData: (context, event) => { // Get context values from the project storage so that we can restore the earlier state when user reopens vscode return new Promise((resolve, reject) => { - const documentUri = StateMachine.context().projectUri; + const documentUri = StateMachine.context().projectPath; resolve({ documentUri }); }); }, diff --git a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts index 07e0521f36c..cd1de8524b8 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts @@ -20,17 +20,32 @@ import { exec } from "child_process"; import { window, commands, workspace, Uri } from "vscode"; import * as fs from 'fs'; import path from "path"; -import { BallerinaProjectComponents, ComponentRequest, CreateComponentResponse, createFunctionSignature, EVENT_TYPE, MigrateRequest, NodePosition, ProjectRequest, STModification, SyntaxTreeResponse } from "@wso2/ballerina-core"; +import { AddProjectToWorkspaceRequest, BallerinaProjectComponents, ComponentRequest, CreateComponentResponse, createFunctionSignature, EVENT_TYPE, MACHINE_VIEW, MigrateRequest, NodePosition, ProjectRequest, STModification, SyntaxTreeResponse, VisualizerLocation, WorkspaceTomlValues } from "@wso2/ballerina-core"; import { StateMachine, history, openView } from "../stateMachine"; import { applyModifications, modifyFileContent, writeBallerinaFileDidOpen } from "./modification"; import { ModulePart, STKindChecker } from "@wso2/syntax-tree"; import { URI } from "vscode-uri"; import { debug } from "./logger"; +import { parse } from "toml"; +import { buildProjectArtifactsStructure } from "./project-artifacts"; +import { getProjectTomlValues } from "./config"; export const README_FILE = "readme.md"; export const FUNCTIONS_FILE = "functions.bal"; export const DATA_MAPPING_FILE = "data_mappings.bal"; +/** + * Interface for the processed project information + */ +interface ProcessedProjectInfo { + sanitizedPackageName: string; + projectRoot: string; + finalOrgName: string; + finalVersion: string; + packageName: string; + integrationName: string; +} + const settingsJsonContent = ` { "ballerina.isBI": true @@ -86,54 +101,6 @@ generated/ Config.toml `; -export function openBIProject() { - window.showOpenDialog({ canSelectFolders: true, canSelectFiles: false, openLabel: 'Open Integration' }) - .then(uri => { - if (uri && uri[0]) { - commands.executeCommand('vscode.openFolder', uri[0]); - } - }); -} - -export function createBIProject(name: string, isService: boolean) { - window.showOpenDialog({ canSelectFolders: true, canSelectFiles: false, openLabel: 'Select Project Location' }) - .then(uri => { - if (uri && uri[0]) { - const projectLocation = uri[0].fsPath; - - const command = isService ? `bal new -t service ${name}` : `bal new ${name}`; - const options = { cwd: projectLocation }; - - exec(command, options, (error, stdout, stderr) => { - if (error) { - console.error(`Error creating BI project: ${error.message}`); - return; - } - - console.log(`BI project created successfully at ${projectLocation}`); - console.log(`stdout: ${stdout}`); - console.error(`stderr: ${stderr}`); - - // Update Ballerina.toml file in the created project folder - const tomlFilePath = `${projectLocation}/${name}/Ballerina.toml`; - hackToUpdateBallerinaToml(tomlFilePath); - - if (isService) { - const filePath = `${projectLocation}/${name}/service.bal`; - hackToUpdateService(filePath); - } else { - const filePath = `${projectLocation}/${name}/main.bal`; - hackToUpdateMain(filePath); - } - - const newProjectUri = Uri.joinPath(uri[0], name); - commands.executeCommand('vscode.openFolder', newProjectUri); - - }); - } - }); -} - export function getUsername(): string { // Get current username from the system across different OS platforms let username: string; @@ -147,23 +114,64 @@ export function getUsername(): string { return username; } -function setupProjectInfo(projectRequest: ProjectRequest) { - const sanitizedPackageName = sanitizeName(projectRequest.packageName); - - const projectRoot = projectRequest.createDirectory - ? path.join(projectRequest.projectPath, sanitizedPackageName) - : projectRequest.projectPath; +/** + * Generic function to resolve directory paths and create directories if needed + * Can be used for both project and workspace directory creation + * @param basePath - Base directory path + * @param directoryName - Name of the directory to create (optional) + * @param shouldCreateDirectory - Whether to create a new directory + * @returns The resolved directory path + */ +function resolveDirectoryPath(basePath: string, directoryName?: string, shouldCreateDirectory: boolean = true): string { + const resolvedPath = directoryName + ? path.join(basePath, directoryName) + : basePath; - // Create project root directory if needed - if (projectRequest.createDirectory && !fs.existsSync(projectRoot)) { - fs.mkdirSync(projectRoot, { recursive: true }); + if (shouldCreateDirectory && !fs.existsSync(resolvedPath)) { + fs.mkdirSync(resolvedPath, { recursive: true }); } - let finalOrgName = projectRequest.orgName; - if (!finalOrgName) { - finalOrgName = getUsername(); - } + return resolvedPath; +} + +/** + * Resolves the project root path and creates the directory if needed + * @param projectPath - Base project path + * @param sanitizedPackageName - Sanitized package name for directory creation + * @param createDirectory - Whether to create a new directory + * @returns The resolved project root path + */ +function resolveProjectPath(projectPath: string, sanitizedPackageName: string, createDirectory: boolean): string { + return resolveDirectoryPath( + projectPath, + createDirectory ? sanitizedPackageName : undefined, + createDirectory + ); +} +/** + * Resolves the workspace root path and creates the directory + * @param basePath - Base path where workspace should be created + * @param workspaceName - Name of the workspace directory + * @returns The resolved workspace root path + */ +function resolveWorkspacePath(basePath: string, workspaceName: string): string { + return resolveDirectoryPath(basePath, workspaceName, true); +} + +/** + * Orchestrates the setup of project information + * @param projectRequest - The project request containing all necessary information + * @returns Processed project information ready for use + */ +function setupProjectInfo(projectRequest: ProjectRequest): ProcessedProjectInfo { + const sanitizedPackageName = sanitizeName(projectRequest.packageName); + const projectRoot = resolveProjectPath( + projectRequest.projectPath, + sanitizedPackageName, + projectRequest.createDirectory + ); + const finalOrgName = projectRequest.orgName || getUsername(); const finalVersion = projectRequest.version || "0.1.0"; return { @@ -176,7 +184,28 @@ function setupProjectInfo(projectRequest: ProjectRequest) { }; } -export function createBIProjectPure(projectRequest: ProjectRequest) { +export function createBIWorkspace(projectRequest: ProjectRequest): string { + const ballerinaTomlContent = ` +[workspace] +packages = ["${projectRequest.packageName}"] + +`; + + // Use the workspace-specific directory resolver + const workspaceRoot = resolveWorkspacePath(projectRequest.projectPath, projectRequest.workspaceName); + + // Create Ballerina.toml file + const ballerinaTomlPath = path.join(workspaceRoot, 'Ballerina.toml'); + writeBallerinaFileDidOpen(ballerinaTomlPath, ballerinaTomlContent); + + // Create Ballerina Package + createBIProjectPure({...projectRequest, projectPath: workspaceRoot, createDirectory: true}); + + console.log(`BI workspace created successfully at ${workspaceRoot}`); + return workspaceRoot; +} + +export function createBIProjectPure(projectRequest: ProjectRequest): string { const projectInfo = setupProjectInfo(projectRequest); const { projectRoot, finalOrgName, finalVersion, packageName: finalPackageName, integrationName } = projectInfo; @@ -194,8 +223,6 @@ sticky = true `; - - // Create Ballerina.toml file const ballerinaTomlPath = path.join(projectRoot, 'Ballerina.toml'); writeBallerinaFileDidOpen(ballerinaTomlPath, ballerinaTomlContent); @@ -251,6 +278,119 @@ sticky = true fs.writeFileSync(gitignorePath, gitignoreContent.trim()); console.log(`BI project created successfully at ${projectRoot}`); + return projectRoot; +} + +export async function convertProjectToWorkspace(params: AddProjectToWorkspaceRequest) { + const currentProjectPath = StateMachine.context().projectPath; + const tomlValues = await getProjectTomlValues(currentProjectPath); + const currentPackageName = tomlValues?.package?.name; + if (!currentPackageName) { + throw new Error('No package name found in Ballerina.toml'); + } + + const newDirectory = path.join(path.dirname(currentProjectPath), params.workspaceName); + + if (!fs.existsSync(newDirectory)) { + fs.mkdirSync(newDirectory, { recursive: true }); + } + + const updatedProjectPath = path.join(newDirectory, path.basename(currentProjectPath)); + fs.renameSync(currentProjectPath, updatedProjectPath); + + createWorkspaceToml(newDirectory, currentPackageName); + updateWorkspaceToml(newDirectory, params.packageName); + + createProjectInWorkspace(params, newDirectory); + + openInVSCode(newDirectory); +} + +export async function addProjectToExistingWorkspace(params: AddProjectToWorkspaceRequest): Promise { + const workspacePath = StateMachine.context().workspacePath; + + updateWorkspaceToml(workspacePath, params.packageName); + + const projectPath = createProjectInWorkspace(params, workspacePath); + + await openNewlyCreatedProject(params, workspacePath, projectPath); +} + +function createWorkspaceToml(workspacePath: string, packageName: string) { + const ballerinaTomlContent = ` +[workspace] +packages = ["${packageName}"] +`; + const ballerinaTomlPath = path.join(workspacePath, 'Ballerina.toml'); + writeBallerinaFileDidOpen(ballerinaTomlPath, ballerinaTomlContent); +} + +function updateWorkspaceToml(workspacePath: string, packageName: string) { + const ballerinaTomlPath = path.join(workspacePath, 'Ballerina.toml'); + + if (!fs.existsSync(ballerinaTomlPath)) { + return; + } + + try { + const ballerinaTomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); + const tomlData: WorkspaceTomlValues = parse(ballerinaTomlContent); + const existingPackages: string[] = tomlData.workspace?.packages || []; + + if (existingPackages.includes(packageName)) { + return; // Package already exists + } + + const updatedContent = addPackageToToml(ballerinaTomlContent, packageName); + fs.writeFileSync(ballerinaTomlPath, updatedContent); + } catch (error) { + console.error('Failed to update workspace Ballerina.toml:', error); + } +} + +function addPackageToToml(tomlContent: string, packageName: string): string { + const packagesRegex = /packages\s*=\s*\[([\s\S]*?)\]/; + const match = tomlContent.match(packagesRegex); + + if (match) { + const currentArrayContent = match[1].trim(); + const newArrayContent = currentArrayContent === '' + ? `"${packageName}"` + : `${currentArrayContent}, "${packageName}"`; + + return tomlContent.replace(packagesRegex, `packages = [${newArrayContent}]`); + } else { + return tomlContent + `\npackages = ["${packageName}"]\n`; + } +} + +function createProjectInWorkspace(params: AddProjectToWorkspaceRequest, workspacePath: string): string { + const projectRequest: ProjectRequest = { + projectName: params.projectName, + packageName: params.packageName, + projectPath: workspacePath, + createDirectory: true, + orgName: params.orgName, + version: params.version + }; + + return createBIProjectPure(projectRequest); +} + +async function openNewlyCreatedProject(params: AddProjectToWorkspaceRequest, workspacePath: string, projectPath: string) { + const viewLocation: VisualizerLocation = { + view: MACHINE_VIEW.Overview, + workspacePath: workspacePath, + projectPath: projectPath, + package: params.packageName, + org: params.orgName + }; + + await buildProjectArtifactsStructure(projectPath, StateMachine.langClient(), true); + openView(EVENT_TYPE.OPEN_VIEW, viewLocation); +} + +export function openInVSCode(projectRoot: string) { commands.executeCommand('vscode.openFolder', Uri.file(path.resolve(projectRoot))); } @@ -299,7 +439,7 @@ export async function createBIAutomation(params: ComponentRequest): Promise { const functionFile = await handleAutomationCreation(params); const components = await StateMachine.langClient().getBallerinaProjectComponents({ - documentIdentifiers: [{ uri: URI.file(StateMachine.context().projectUri).toString() }] + documentIdentifiers: [{ uri: URI.file(StateMachine.context().projectPath).toString() }] }) as BallerinaProjectComponents; const position: NodePosition = {}; for (const pkg of components.packages) { @@ -321,9 +461,9 @@ export async function createBIAutomation(params: ComponentRequest): Promise { return new Promise(async (resolve) => { const isExpressionBodied = params.functionType.isExpressionBodied; - const projectDir = path.join(StateMachine.context().projectUri); + const projectPath = StateMachine.context().projectPath; // Hack to create trasformation function (Use LS API to create the function when available) - const targetFile = path.join(projectDir, isExpressionBodied ? DATA_MAPPING_FILE : FUNCTIONS_FILE); + const targetFile = path.join(projectPath, isExpressionBodied ? DATA_MAPPING_FILE : FUNCTIONS_FILE); if (!fs.existsSync(targetFile)) { writeBallerinaFileDidOpen(targetFile, ''); } @@ -367,9 +507,9 @@ ${funcSignature} } } `; - const projectDir = path.join(StateMachine.context().projectUri); + const projectPath = StateMachine.context().projectPath; // Create foo.bal file within services directory - const taskFile = path.join(projectDir, `automation.bal`); + const taskFile = path.join(projectPath, `automation.bal`); writeBallerinaFileDidOpen(taskFile, balContent); console.log('Task Created.', `automation.bal`); return taskFile; @@ -424,77 +564,3 @@ export async function handleFunctionCreation(targetFile: string, params: Compone export function sanitizeName(name: string): string { return name.replace(/[^a-z0-9]_./gi, '_').toLowerCase(); // Replace invalid characters with underscores } - -// ------------------- HACKS TO MANIPULATE PROJECT FILES ----------------> -function hackToUpdateBallerinaToml(filePath: string) { - fs.readFile(filePath, 'utf8', (err, data) => { - if (err) { - console.error(`Error reading Ballerina.toml file: ${err.message}`); - return; - } - - // Append "bi=true" to the Ballerina.toml content - const updatedContent = `${data.trim()}\nbi = true\n`; - - // Write the updated content back to the Ballerina.toml file - fs.writeFile(filePath, updatedContent, 'utf8', (err) => { - if (err) { - console.error(`Error updating Ballerina.toml file: ${err.message}`); - } else { - console.log('Ballerina.toml file updated successfully'); - } - }); - }); -} - -function hackToUpdateService(filePath: string) { - fs.readFile(filePath, 'utf8', (err, data) => { - if (err) { - console.error(`Error reading Ballerina.toml file: ${err.message}`); - return; - } - - // Append "bi=true" to the Ballerina.toml content - const newContent = `import ballerina/http; - - service /hello on new http:Listener(9090) { - resource function get greeting(string name) returns string|error { - - } - } - `; - - // Write the updated content back to the Ballerina.toml file - fs.writeFile(filePath, newContent, 'utf8', (err) => { - if (err) { - console.error(`Error updating Ballerina.toml file: ${err.message}`); - } else { - console.log('Ballerina.toml file updated successfully'); - } - }); - }); -} - -function hackToUpdateMain(filePath: string) { - fs.readFile(filePath, 'utf8', (err, data) => { - if (err) { - console.error(`Error reading Ballerina.toml file: ${err.message}`); - return; - } - - // Append "bi=true" to the Ballerina.toml content - const newContent = `public function main() { - - } - `; - - // Write the updated content back to the Ballerina.toml file - fs.writeFile(filePath, newContent, 'utf8', (err) => { - if (err) { - console.error(`Error updating Ballerina.toml file: ${err.message}`); - } else { - console.log('Ballerina.toml file updated successfully'); - } - }); - }); -} diff --git a/workspaces/ballerina/ballerina-extension/src/utils/config.ts b/workspaces/ballerina/ballerina-extension/src/utils/config.ts index 9329f3591fa..b5f4f8cfebf 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -16,7 +16,7 @@ * under the License. */ -import { SCOPE, PackageTomlValues, WorkspaceTomlValues } from '@wso2/ballerina-core'; +import { PackageTomlValues, SCOPE, WorkspaceTomlValues } from '@wso2/ballerina-core'; import { BallerinaExtension } from '../core'; import { WorkspaceConfiguration, workspace, Uri, RelativePattern } from 'vscode'; import * as fs from 'fs'; @@ -204,6 +204,45 @@ export async function getBallerinaPackages(uri: Uri): Promise { } } +/** + * Filters package paths to only include valid Ballerina packages within the workspace. + * + * For each path, this function: + * - Resolves the path (handling relative paths like `.` and `..`) + * - Verifies the path exists on the filesystem + * - Ensures the path is within the workspace boundaries (prevents path traversal) + * - Validates it's a valid Ballerina package + * + * @param packagePaths Array of package paths (relative or absolute) + * @param workspacePath Absolute path to the workspace root + * @returns Filtered array of valid Ballerina package paths that exist within the workspace + */ +export async function filterPackagePaths(packagePaths: string[], workspacePath: string): Promise { + const results = await Promise.all( + packagePaths.map(async pkgPath => { + if (path.isAbsolute(pkgPath)) { + const resolvedPath = path.resolve(pkgPath); + const resolvedWorkspacePath = path.resolve(workspacePath); + if (fs.existsSync(resolvedPath) && isPathInside(resolvedPath, resolvedWorkspacePath)) { + return await checkIsBallerinaPackage(Uri.file(resolvedPath)); + } + } + const resolvedPath = path.resolve(workspacePath, pkgPath); + const resolvedWorkspacePath = path.resolve(workspacePath); + if (fs.existsSync(resolvedPath) && isPathInside(resolvedPath, resolvedWorkspacePath)) { + return await checkIsBallerinaPackage(Uri.file(resolvedPath)); + } + return false; + }) + ); + return packagePaths.filter((_, index) => results[index]); +} + +function isPathInside(childPath: string, parentPath: string): boolean { + const relative = path.relative(parentPath, childPath); + return !relative.startsWith('..') && !path.isAbsolute(relative); +} + export function getOrgPackageName(projectPath: string): { orgName: string, packageName: string } { const ballerinaTomlPath = path.join(projectPath, 'Ballerina.toml'); @@ -229,6 +268,32 @@ export function getOrgPackageName(projectPath: string): { orgName: string, packa } } +export async function getProjectTomlValues(projectPath: string): Promise { + const ballerinaTomlPath = path.join(projectPath, 'Ballerina.toml'); + if (fs.existsSync(ballerinaTomlPath)) { + const tomlContent = await fs.promises.readFile(ballerinaTomlPath, 'utf-8'); + try { + return parse(tomlContent); + } catch (error) { + console.error("Failed to load Ballerina.toml content for project at path: ", projectPath, error); + return; + } + } +} + +export async function getWorkspaceTomlValues(workspacePath: string): Promise { + const ballerinaTomlPath = path.join(workspacePath, 'Ballerina.toml'); + if (fs.existsSync(ballerinaTomlPath)) { + const tomlContent = await fs.promises.readFile(ballerinaTomlPath, 'utf-8'); + try { + return parse(tomlContent); + } catch (error) { + console.error("Failed to load Ballerina.toml content for workspace at path: ", workspacePath, error); + return; + } + } +} + export function fetchScope(uri: Uri): SCOPE { const config = workspace.getConfiguration('ballerina', uri); const inspected = config.inspect('scope'); @@ -256,29 +321,3 @@ export function setupBIFiles(projectDir: string): void { } }); } - -export async function getProjectTomlValues(projectPath: string): Promise { - const ballerinaTomlPath = path.join(projectPath, 'Ballerina.toml'); - if (fs.existsSync(ballerinaTomlPath)) { - const tomlContent = await fs.promises.readFile(ballerinaTomlPath, 'utf-8'); - try { - return parse(tomlContent); - } catch (error) { - console.error("Failed to load Ballerina.toml content for project at path: ", projectPath, error); - return; - } - } -} - -export async function getWorkspaceTomlValues(workspacePath: string): Promise { - const ballerinaTomlPath = path.join(workspacePath, 'Ballerina.toml'); - if (fs.existsSync(ballerinaTomlPath)) { - const tomlContent = await fs.promises.readFile(ballerinaTomlPath, 'utf-8'); - try { - return parse(tomlContent); - } catch (error) { - console.error("Failed to load Ballerina.toml content for workspace at path: ", workspacePath, error); - return; - } - } -} diff --git a/workspaces/ballerina/ballerina-extension/src/utils/file-utils.ts b/workspaces/ballerina/ballerina-extension/src/utils/file-utils.ts index 2831400ca3a..97bc039886a 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/file-utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/file-utils.ts @@ -38,6 +38,7 @@ import { } from "../features/telemetry"; import { NodePosition } from "@wso2/syntax-tree"; import { existsSync } from "fs"; +import { checkIsBallerinaPackage } from "./config"; interface ProgressMessage { message: string; increment?: number; @@ -47,7 +48,6 @@ const ALLOWED_ORG_LIST = ['ballerina-platform', 'ballerina-guides', 'ballerinax' const GIT_DOMAIN = "github.com"; const GIST_OWNER = "ballerina-github-bot"; const NEXT_STARTING_UP_FILE = "next-starting-up-file"; -const BALLERINA_TOML = "Ballerina.toml"; const REPO_LOCATIONS = "repository-locations"; const buildStatusItem = window.createStatusBarItem(StatusBarAlignment.Left, 100); @@ -384,8 +384,8 @@ function getGitHubRawFileUrl(githubFileUrl) { } async function resolveModules(langClient: ExtendedLangClient, pathValue) { - const isBallerinProject = findBallerinaTomlFile(pathValue); - if (isBallerinProject) { + const ballerinProjectPath = await findBallerinaPackageRoot(pathValue); + if (ballerinProjectPath) { // Create a status bar item for the build notification buildStatusItem.text = "$(sync~spin) Pulling modules..."; buildStatusItem.tooltip = "Pulling the missing ballerina modules."; @@ -427,19 +427,28 @@ async function resolveModules(langClient: ExtendedLangClient, pathValue) { } } -function findBallerinaTomlFile(filePath) { +export async function findBallerinaPackageRoot(filePath: string) { + if (!filePath) { + return null; + } + let currentFolderPath = path.dirname(filePath); while (currentFolderPath !== path.sep) { - const tomlFilePath = path.join(currentFolderPath, BALLERINA_TOML); - if (fs.existsSync(tomlFilePath)) { + const isBallerinaPackage = await checkIsBallerinaPackage(Uri.parse(currentFolderPath)); + if (isBallerinaPackage) { return currentFolderPath; } - currentFolderPath = path.dirname(currentFolderPath); + const parentPath = path.dirname(currentFolderPath); + // Prevent infinite loop + if (parentPath === currentFolderPath) { + break; + } + currentFolderPath = parentPath; } - return null; // Ballerina.toml not found in any parent folder + return null; } export async function handleResolveMissingDependencies(ballerinaExtInstance: BallerinaExtension) { diff --git a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts index a7e0a1e7513..7602bf9bfd8 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts @@ -16,6 +16,7 @@ * under the License. */ import * as vscode from "vscode"; +import * as path from 'path'; import { URI, Utils } from "vscode-uri"; import { ARTIFACT_TYPE, Artifacts, ArtifactsNotification, BaseArtifact, DIRECTORY_MAP, NodePosition, ProjectStructureArtifactResponse, ProjectStructureResponse } from "@wso2/ballerina-core"; import { StateMachine } from "../stateMachine"; @@ -23,7 +24,11 @@ import { ExtendedLangClient } from "../core/extended-language-client"; import { ArtifactsUpdated, ArtifactNotificationHandler } from "./project-artifacts-handler"; import { CommonRpcManager } from "../rpc-managers/common/rpc-manager"; -export async function buildProjectArtifactsStructure(projectDir: string, langClient: ExtendedLangClient, isUpdate: boolean = false): Promise { +export async function buildProjectArtifactsStructure( + projectPath: string, + langClient: ExtendedLangClient, + isUpdate: boolean = false +): Promise { const result: ProjectStructureResponse = { projectName: "", directoryMap: { @@ -40,21 +45,29 @@ export async function buildProjectArtifactsStructure(projectDir: string, langCli [DIRECTORY_MAP.LOCAL_CONNECTORS]: [], } }; - const designArtifacts = await langClient.getProjectArtifacts({ projectPath: projectDir }); + const designArtifacts = await langClient.getProjectArtifacts({ projectPath }); console.log("designArtifacts", designArtifacts); if (designArtifacts?.artifacts) { traverseComponents(designArtifacts.artifacts, result); - await populateLocalConnectors(projectDir, result); + await populateLocalConnectors(projectPath, result); } // Attempt to get the project name from the workspace folder as a fallback if not found in Ballerina.toml - const workspace = vscode.workspace.workspaceFolders?.find(folder => folder.uri.fsPath === projectDir); - let projectName = workspace?.name; + const workspace = vscode.workspace.workspaceFolders?.find(folder => folder.uri.fsPath === projectPath); + + let projectName = ""; + if (workspace) { + projectName = workspace.name; + } else { + // Project defined within a Ballerina workspace + projectName = path.basename(projectPath); + } // Get the project name from the ballerina.toml file const commonRpcManager = new CommonRpcManager(); const tomlValues = await commonRpcManager.getCurrentProjectTomlValues(); if (tomlValues) { projectName = tomlValues.package?.title || tomlValues.package?.name; } + result.projectName = projectName; if (isUpdate) { @@ -66,8 +79,10 @@ export async function buildProjectArtifactsStructure(projectDir: string, langCli export async function updateProjectArtifacts(publishedArtifacts: ArtifactsNotification): Promise { // Current project structure const currentProjectStructure: ProjectStructureResponse = StateMachine.context().projectStructure; - const projectUri = URI.file(StateMachine.context().projectUri); - const isWithinProject = URI.parse(publishedArtifacts.uri).fsPath.toLowerCase().includes(projectUri.fsPath.toLowerCase()); + const projectUri = URI.file(StateMachine.context().projectPath); + const isWithinProject = URI + .parse(publishedArtifacts.uri).fsPath.toLowerCase() + .includes(projectUri.fsPath.toLowerCase()); if (currentProjectStructure && isWithinProject) { const entryLocations = await traverseUpdatedComponents(publishedArtifacts.artifacts, currentProjectStructure); const notificationHandler = ArtifactNotificationHandler.getInstance(); @@ -117,7 +132,7 @@ async function getComponents(artifacts: Record, artifactTy } async function getEntryValue(artifact: BaseArtifact, icon: string, moduleName?: string) { - const targetFile = Utils.joinPath(URI.file(StateMachine.context().projectUri), artifact.location.fileName).fsPath; + const targetFile = Utils.joinPath(URI.file(StateMachine.context().projectPath), artifact.location.fileName).fsPath; const entryValue: ProjectStructureArtifactResponse = { id: artifact.id, name: artifact.name, @@ -376,7 +391,7 @@ async function traverseUpdatedComponents(publishedArtifacts: Artifacts, currentP async function populateLocalConnectors(projectDir: string, response: ProjectStructureResponse) { const filePath = `${projectDir}/Ballerina.toml`; - const localConnectors = (await StateMachine.langClient().getOpenApiGeneratedModules({ projectPath: projectDir })).modules; + const localConnectors = (await StateMachine.langClient().getOpenApiGeneratedModules({ projectPath: projectDir })).modules || []; const mappedEntries: ProjectStructureArtifactResponse[] = localConnectors.map(moduleName => ({ id: moduleName, name: moduleName, diff --git a/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts b/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts index 9d97e793900..622ce5fe70b 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts @@ -19,11 +19,13 @@ import { extension } from "../BalExtensionContext"; import { Uri, window, workspace, RelativePattern, WorkspaceFolder } from "vscode"; import * as path from 'path'; -import { isSupportedVersion, VERSION } from "./config"; +import { checkIsBallerinaPackage, isSupportedVersion, VERSION } from "./config"; import { BallerinaProject } from "@wso2/ballerina-core"; import { readFileSync } from 'fs'; import { dirname, sep } from 'path'; import { parseTomlToConfig } from '../features/config-generator/utils'; +import { PROJECT_TYPE } from "../features/project"; +import { StateMachine } from "../stateMachine"; const BALLERINA_TOML_REGEX = `**${sep}Ballerina.toml`; const BALLERINA_FILE_REGEX = `**${sep}*.bal`; @@ -40,11 +42,11 @@ export interface PACKAGE { distribution: string; } -function getCurrentBallerinaProject(file?: string): Promise { +function getCurrentBallerinaProject(projectPath?: string): Promise { return new Promise((resolve, reject) => { const activeEditor = window.activeTextEditor; // get path of the current bal file - const uri = file ? Uri.file(file) : activeEditor.document.uri; + const uri = projectPath ? Uri.file(projectPath) : activeEditor.document.uri; // if currently opened file is a bal file if (extension.ballerinaExtInstance.langClient && isSupportedVersion(extension.ballerinaExtInstance, VERSION.BETA, 1)) { // get Ballerina Project path for current Ballerina file @@ -137,4 +139,93 @@ async function selectBallerinaProjectForDebugging(workspaceFolder?: WorkspaceFol } } -export { addToWorkspace, getCurrentBallerinaProject, getCurrentBallerinaFile, getCurrenDirectoryPath, selectBallerinaProjectForDebugging }; + +/** + * Determines and returns the current project root directory. + * + * Resolution order: + * 1. State machine context (when working within a webview) + * 2. Open Ballerina file's project root + * 3. Workspace root (if it's a valid Ballerina package) + * + * @returns The current project root path + * @throws Error if unable to determine a valid Ballerina project root + */ +async function getCurrentProjectRoot(): Promise { + const currentFilePath = tryGetCurrentBallerinaFile(); + const contextProjectRoot = StateMachine.context()?.projectPath; + + // Use state machine context only when not in a regular text editor (e.g., within a webview) + if (contextProjectRoot && !currentFilePath) { + return contextProjectRoot; + } + + // Resolve project root from the currently open Ballerina file + if (currentFilePath) { + const projectRoot = await resolveProjectRootFromFile(currentFilePath); + if (projectRoot) { + return projectRoot; + } + } + + // Fallback to workspace root if it's a valid Ballerina package + const workspaceRoot = getWorkspaceRoot(); + if (!workspaceRoot) { + throw new Error("Unable to determine the current workspace root."); + } + + if (await checkIsBallerinaPackage(Uri.file(workspaceRoot))) { + return workspaceRoot; + } + + throw new Error(`No valid Ballerina project found`); +} + +/** + * Safely attempts to get the current Ballerina file without throwing errors. + * @returns The current Ballerina file path or undefined if not available + */ +function tryGetCurrentBallerinaFile(): string | undefined { + try { + return getCurrentBallerinaFile(); + } catch { + return undefined; + } +} + +/** + * Resolves the project root from the given Ballerina file. + * @param filePath The Ballerina file path + * @returns The project root path or undefined if unable to resolve + */ +async function resolveProjectRootFromFile(filePath: string): Promise { + try { + const project = await getCurrentBallerinaProject(filePath); + + if (project.kind === PROJECT_TYPE.SINGLE_FILE) { + return filePath; + } + + return project.path; + } catch { + return undefined; + } +} + +/** + * Gets the workspace root directory. + * @returns The workspace root path or undefined if not available + */ +function getWorkspaceRoot(): string | undefined { + return workspace.workspaceFolders?.[0]?.uri.fsPath; +} + +export { + addToWorkspace, + getCurrentBallerinaProject, + getCurrentBallerinaFile, + getCurrenDirectoryPath, + selectBallerinaProjectForDebugging, + getCurrentProjectRoot, + getWorkspaceRoot +}; diff --git a/workspaces/ballerina/ballerina-extension/src/utils/state-machine-utils.ts b/workspaces/ballerina/ballerina-extension/src/utils/state-machine-utils.ts index a519d4da8a2..cd112d139fa 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/state-machine-utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/state-machine-utils.ts @@ -28,10 +28,10 @@ import { getConstructBodyString } from "./history/util"; import { extension } from "../BalExtensionContext"; import path from "path"; -export async function getView(documentUri: string, position: NodePosition, projectUri?: string): Promise { +export async function getView(documentUri: string, position: NodePosition, projectPath?: string): Promise { const haveTreeData = !!StateMachine.context().projectStructure; const isServiceClassFunction = await checkForServiceClassFunctions(documentUri, position); - if (isServiceClassFunction || path.relative(projectUri || '', documentUri).startsWith("tests")) { + if (isServiceClassFunction || path.relative(projectPath || '', documentUri).startsWith("tests")) { return { location: { view: MACHINE_VIEW.BIDiagram, @@ -42,10 +42,10 @@ export async function getView(documentUri: string, position: NodePosition, proje dataMapperDepth: 0 }; } else if (haveTreeData) { - return getViewByArtifacts(documentUri, position, projectUri); + return getViewByArtifacts(documentUri, position, projectPath); } else { - return await getViewBySTRange(documentUri, position, projectUri); + return await getViewBySTRange(documentUri, position, projectPath); } } @@ -66,7 +66,7 @@ async function checkForServiceClassFunctions(documentUri: string, position: Node } // TODO: This is not used anymore. Remove it. -async function getViewBySTRange(documentUri: string, position: NodePosition, projectUri?: string) { +async function getViewBySTRange(documentUri: string, position: NodePosition, projectPath?: string): Promise { const req = getSTByRangeReq(documentUri, position); const node = await StateMachine.langClient().getSTByRange(req) as SyntaxTreeResponse; if (node.parseSuccess) { @@ -85,7 +85,7 @@ async function getViewBySTRange(documentUri: string, position: NodePosition, pro documentUri: documentUri, position: position, identifier: name, - projectUri: projectUri + projectPath } }; } @@ -104,7 +104,7 @@ async function getViewBySTRange(documentUri: string, position: NodePosition, pro documentUri: documentUri, position: position, identifier: name, - projectUri: projectUri + projectPath } }; } @@ -123,7 +123,7 @@ async function getViewBySTRange(documentUri: string, position: NodePosition, pro documentUri: documentUri, position: position, identifier: name, - projectUri: projectUri + projectPath } }; } @@ -174,7 +174,7 @@ async function getViewBySTRange(documentUri: string, position: NodePosition, pro identifier: node.syntaxTree.absoluteResourcePath.map((path) => path.value).join(''), documentUri: documentUri, position: position, - projectUri: projectUri + projectPath } }; } else { @@ -266,7 +266,7 @@ async function getViewBySTRange(documentUri: string, position: NodePosition, pro } -function getViewByArtifacts(documentUri: string, position: NodePosition, projectUri?: string) { +function getViewByArtifacts(documentUri: string, position: NodePosition, projectPath?: string) { const currentProjectArtifacts = StateMachine.context().projectStructure; if (currentProjectArtifacts) { // Iterate through each category in the directory map @@ -276,7 +276,7 @@ function getViewByArtifacts(documentUri: string, position: NodePosition, project // Go through the resources array if it exists if (dir.resources && dir.resources.length > 0) { for (const resource of dir.resources) { - const view = findViewByArtifact(resource, position, documentUri, projectUri); + const view = findViewByArtifact(resource, position, documentUri, projectPath); if (view) { view.location.parentIdentifier = dir.name; return view; @@ -284,7 +284,7 @@ function getViewByArtifacts(documentUri: string, position: NodePosition, project } } // Check the current directory - const view = findViewByArtifact(dir, position, documentUri, projectUri); + const view = findViewByArtifact(dir, position, documentUri, projectPath); if (view) { return view; } @@ -295,7 +295,12 @@ function getViewByArtifacts(documentUri: string, position: NodePosition, project } } -function findViewByArtifact(dir: ProjectStructureArtifactResponse, position: NodePosition, documentUri: string, projectUri?: string): HistoryEntry { +function findViewByArtifact( + dir: ProjectStructureArtifactResponse, + position: NodePosition, + documentUri: string, + projectPath?: string +): HistoryEntry { const currentDocumentUri = documentUri; const artifactUri = dir.path; if (artifactUri === currentDocumentUri && isPositionWithinRange(position, dir.position)) { @@ -308,7 +313,7 @@ function findViewByArtifact(dir: ProjectStructureArtifactResponse, position: Nod identifier: dir.name, documentUri: currentDocumentUri, position: position, - projectUri: projectUri, + projectPath: projectPath, artifactType: DIRECTORY_MAP.SERVICE } }; @@ -319,7 +324,7 @@ function findViewByArtifact(dir: ProjectStructureArtifactResponse, position: Nod identifier: dir.name, documentUri: currentDocumentUri, position: position, - projectUri: projectUri, + projectPath: projectPath, artifactType: DIRECTORY_MAP.SERVICE, } }; @@ -398,7 +403,7 @@ function findViewByArtifact(dir: ProjectStructureArtifactResponse, position: Nod documentUri: currentDocumentUri, position: position, identifier: dir.name, - projectUri: projectUri, + projectPath: projectPath, artifactType: DIRECTORY_MAP.TYPE } }; diff --git a/workspaces/ballerina/ballerina-extension/src/views/persist-layer-diagram/activator.ts b/workspaces/ballerina/ballerina-extension/src/views/persist-layer-diagram/activator.ts index ff24490d7fa..76eb85b55a9 100644 --- a/workspaces/ballerina/ballerina-extension/src/views/persist-layer-diagram/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/views/persist-layer-diagram/activator.ts @@ -16,19 +16,10 @@ * under the License. */ -import { TextEditor, Uri, ViewColumn, WebviewPanel, commands, window, workspace } from "vscode"; -import { debounce } from "lodash"; -import { basename, dirname, join } from "path"; -import { existsSync } from "fs"; -import { PALETTE_COMMANDS } from "../../features/project/cmds/cmd-runner"; +import { TextEditor, Uri, window } from "vscode"; +import { basename, dirname } from "path"; import { BallerinaExtension, ExtendedLangClient } from "../../core"; -import { getCommonWebViewOptions } from "../../utils"; -import { render } from "./renderer"; - -const COMPATIBILITY_MESSAGE = "An incompatible Ballerina version was detected. Update Ballerina to 2201.6.0 or higher to use the feature."; - -let diagramWebview: WebviewPanel | undefined; -let filePath: string | undefined; +import { checkIsBallerinaPackage } from "../../utils"; export function activate(ballerinaExtInstance: BallerinaExtension) { const langClient = ballerinaExtInstance.langClient; @@ -119,6 +110,10 @@ export function activate(ballerinaExtInstance: BallerinaExtension) { // return parseFloat(ballerinaVersion) >= 2201.6; // } -export function checkIsPersistModelFile(fileUri: Uri): boolean { - return basename(dirname(fileUri.fsPath)) === 'persist' && existsSync(join(dirname(dirname(fileUri.fsPath)), 'Ballerina.toml')); +export async function checkIsPersistModelFile(fileUri: Uri): Promise { + const directoryPath = dirname(fileUri.fsPath); + const parentDirectoryPath = dirname(directoryPath); + const directoryName = basename(directoryPath); + const isBallerinaPackage = await checkIsBallerinaPackage(Uri.parse(parentDirectoryPath)); + return directoryName === 'persist' && isBallerinaPackage; } diff --git a/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts b/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts index 1d8ce9af830..3e1c4b28e77 100644 --- a/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts +++ b/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts @@ -21,8 +21,8 @@ import { PALETTE_COMMANDS } from '../../features/project/cmds/cmd-runner'; import { StateMachine, openView } from '../../stateMachine'; import { extension } from '../../BalExtensionContext'; import { BI_COMMANDS, EVENT_TYPE, MACHINE_VIEW, NodePosition, SHARED_COMMANDS } from '@wso2/ballerina-core'; -import { findBallerinaProjectRoot } from '../../features/ai/utils'; import { buildProjectArtifactsStructure } from '../../utils/project-artifacts'; +import { findBallerinaPackageRoot } from '../../utils'; export function activateSubscriptions() { const context = extension.context; @@ -70,8 +70,8 @@ export function activateSubscriptions() { } } - const projectPath = StateMachine.context()?.projectUri; - const projectRoot = await findBallerinaProjectRoot(documentPath); + const projectPath = StateMachine.context().projectPath; + const projectRoot = await findBallerinaPackageRoot(documentPath); if (!projectPath || projectPath !== projectRoot) { // Initialize project structure if not already set by finding and loading the Ballerina project root @@ -98,7 +98,7 @@ export function activateSubscriptions() { context.subscriptions.push( vscode.commands.registerCommand(SHARED_COMMANDS.FORCE_UPDATE_PROJECT_ARTIFACTS, () => { - return buildProjectArtifactsStructure(StateMachine.context().projectUri, StateMachine.langClient(), true); + return buildProjectArtifactsStructure(StateMachine.context().projectPath, StateMachine.langClient(), true); }) ); diff --git a/workspaces/ballerina/ballerina-extension/src/views/visualizer/webview.ts b/workspaces/ballerina/ballerina-extension/src/views/visualizer/webview.ts index 4cb902a33f8..a7ed431ac61 100644 --- a/workspaces/ballerina/ballerina-extension/src/views/visualizer/webview.ts +++ b/workspaces/ballerina/ballerina-extension/src/views/visualizer/webview.ts @@ -64,9 +64,9 @@ export class VisualizerWebview { } // Check the file is changed in the project. - const projectUri = StateMachine.context().projectUri; + const projectPath = StateMachine.context().projectPath; const documentUri = document.document.uri.toString(); - const isDocumentUnderProject = documentUri.includes(projectUri); + const isDocumentUnderProject = documentUri.includes(projectPath); // Reset visualizer the undo-redo stack if user did changes in the editor if (isOpened && isDocumentUnderProject && !this._panel?.active) { undoRedoManager.reset(); @@ -146,13 +146,14 @@ export class VisualizerWebview { } private getWebviewContent(webView: Webview) { + const biExtension = vscode.extensions.getExtension('wso2.ballerina-integrator'); const body = `
-

WSO2 Integrator: BI

+

${biExtension ? 'WSO2 Integrator: BI' : 'Ballerina Visualizer'}

Setting up your workspace and tools

Loading diff --git a/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/bi-diagram/rpc-client.ts b/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/bi-diagram/rpc-client.ts index 47455b7a01c..1c206541478 100644 --- a/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/bi-diagram/rpc-client.ts +++ b/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/bi-diagram/rpc-client.ts @@ -125,6 +125,7 @@ import { createComponent, createGraphqlClassType, createProject, + addProjectToWorkspace, deleteByComponentInfo, deleteConfigVariableV2, deleteFlowNode, @@ -197,6 +198,7 @@ import { getFormDiagnostics, getExpressionTokens, ExpressionTokensRequest, + AddProjectToWorkspaceRequest, } from "@wso2/ballerina-core"; import { HOST_EXTENSION } from "vscode-messenger-common"; import { Messenger } from "vscode-messenger-webview"; @@ -268,6 +270,10 @@ export class BiDiagramRpcClient implements BIDiagramAPI { return this._messenger.sendNotification(createProject, HOST_EXTENSION, params); } + addProjectToWorkspace(params: AddProjectToWorkspaceRequest): void { + return this._messenger.sendNotification(addProjectToWorkspace, HOST_EXTENSION, params); + } + getWorkspaces(): Promise { return this._messenger.sendRequest(getWorkspaces, HOST_EXTENSION); } diff --git a/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/common/rpc-client.ts b/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/common/rpc-client.ts index 54b1724faeb..21bb01bcc7c 100644 --- a/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/common/rpc-client.ts +++ b/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/common/rpc-client.ts @@ -113,7 +113,7 @@ export class CommonRpcClient implements CommonRPCAPI { showErrorMessage(params: ShowErrorMessageRequest): void { return this._messenger.sendNotification(showErrorMessage, HOST_EXTENSION, params); } - + getCurrentProjectTomlValues(): Promise { return this._messenger.sendRequest(getCurrentProjectTomlValues, HOST_EXTENSION); } diff --git a/workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx b/workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx index ff6e22e907d..177823c1147 100644 --- a/workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx +++ b/workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx @@ -159,6 +159,7 @@ export const ExpressionField: React.FC = ({ targetLineRange={targetLineRange} extractArgsFromFunction={extractArgsFromFunction} onOpenExpandedMode={onOpenExpandedMode} + onRemove={onRemove} isInExpandedMode={isInExpandedMode} /> ); diff --git a/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionBaseComponent.tsx b/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionBaseComponent.tsx index 49b6f5a53b6..18d0ebb93e1 100644 --- a/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionBaseComponent.tsx +++ b/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionBaseComponent.tsx @@ -37,7 +37,7 @@ import { setCursorPositionToExpressionModel, updateTokens, } from "./utils"; -import { CompletionItem, FnSignatureDocumentation, HelperPaneHeight } from "@wso2/ui-toolkit"; +import { Button, Codicon, CompletionItem, FnSignatureDocumentation, HelperPaneHeight, ThemeColors } from "@wso2/ui-toolkit"; import { useFormContext } from "../../../../context"; import { DATA_ELEMENT_ID_ATTRIBUTE, FOCUS_MARKER, ARROW_LEFT_MARKER, ARROW_RIGHT_MARKER, BACKSPACE_MARKER, COMPLETIONS_MARKER, HELPER_MARKER, DELETE_MARKER } from "./constants"; import { LineRange } from "@wso2/ballerina-core/lib/interfaces/common"; @@ -63,6 +63,7 @@ export type ChipExpressionBaseComponentProps = { }>; targetLineRange?: LineRange; onOpenExpandedMode?: () => void; + onRemove?: () => void; isInExpandedMode?: boolean; } @@ -553,6 +554,11 @@ export const ChipExpressionBaseComponent = (props: ChipExpressionBaseComponentPr />
+ {props.onRemove && ( + + )} ) } diff --git a/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx b/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx index 6ea30f3e309..cb0703bc9cc 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx @@ -41,6 +41,7 @@ import { ServiceDesigner } from "./views/BI/ServiceDesigner"; import { WelcomeView, ProjectForm, + AddProjectForm, ComponentListView, PopupMessage, FunctionForm, @@ -282,7 +283,7 @@ const MainPanel = () => { case MACHINE_VIEW.Overview: setViewComponent( ); break; @@ -322,7 +323,7 @@ const MainPanel = () => { { setViewComponent( { setViewComponent( ); } else { // To support rerendering when user click on view all btn from left side panel setViewComponent( - ); } @@ -383,7 +385,7 @@ const MainPanel = () => { filePath={value.documentUri} codedata={value?.dataMapperMetadata?.codeData} name={value?.dataMapperMetadata?.name} - projectUri={value.projectUri} + projectPath={value.projectPath} position={position} reusable /> @@ -401,7 +403,7 @@ const MainPanel = () => { case MACHINE_VIEW.BIDataMapperForm: setViewComponent( { case MACHINE_VIEW.BINPFunctionForm: setViewComponent( { break; case MACHINE_VIEW.GraphQLDiagram: const getProjectStructure = await rpcClient.getBIDiagramRpcClient().getProjectStructure(); - const entryPoint = getProjectStructure.directoryMap[DIRECTORY_MAP.SERVICE].find((service: ProjectStructureArtifactResponse) => service.name === value?.identifier); - setViewComponent(); + const entryPoint = getProjectStructure + .directoryMap[DIRECTORY_MAP.SERVICE] + .find((service: ProjectStructureArtifactResponse) => service.name === value?.identifier); + setViewComponent( + ); break; case MACHINE_VIEW.BallerinaUpdateView: setNavActive(false); @@ -444,7 +453,10 @@ const MainPanel = () => { setShowHome(false); setViewComponent(); break; - + case MACHINE_VIEW.BIAddProjectForm: + setShowHome(false); + setViewComponent(); + break; case MACHINE_VIEW.BIComponentView: setViewComponent(); break; @@ -465,7 +477,11 @@ const MainPanel = () => { ); break; case MACHINE_VIEW.BIServiceConfigView: - setViewComponent(); + setViewComponent( + ); break; case MACHINE_VIEW.BIServiceClassConfigView: setViewComponent( @@ -473,7 +489,7 @@ const MainPanel = () => { type={value?.type} fileName={value.documentUri} position={value?.position} - projectUri={value?.projectUri} /> + /> ); break; case MACHINE_VIEW.BIListenerConfigView: @@ -482,14 +498,13 @@ const MainPanel = () => { case MACHINE_VIEW.AddConnectionWizard: setViewComponent( ); break; case MACHINE_VIEW.EditConnectionWizard: setViewComponent( ); @@ -497,16 +512,28 @@ const MainPanel = () => { case MACHINE_VIEW.AddCustomConnector: setViewComponent( ); break; case MACHINE_VIEW.BIMainFunctionForm: - setViewComponent(); + setViewComponent( + + ); break; case MACHINE_VIEW.BIFunctionForm: - setViewComponent(); + setViewComponent( + ); break; case MACHINE_VIEW.BITestFunctionForm: setViewComponent( { ); break; diff --git a/workspaces/ballerina/ballerina-visualizer/src/PopupPanel.tsx b/workspaces/ballerina/ballerina-visualizer/src/PopupPanel.tsx index c9941e51abc..a44b9b0a3af 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/PopupPanel.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/PopupPanel.tsx @@ -71,7 +71,7 @@ const PopupPanel = (props: PopupPanelProps) => { rpcClient.getVisualizerLocation().then((location) => { setViewComponent( { setViewComponent( <> @@ -98,7 +97,7 @@ const PopupPanel = (props: PopupPanelProps) => { rpcClient.getVisualizerLocation().then(async (location) => { const defaultFunctionsFile = await rpcClient.getVisualizerRpcClient().joinProjectPath('functions.bal'); setViewComponent( @@ -111,7 +110,7 @@ const PopupPanel = (props: PopupPanelProps) => { const defaultFunctionsFile = await rpcClient.getVisualizerRpcClient().joinProjectPath('data_mappings.bal'); setViewComponent( { const defaultFunctionsFile = await rpcClient.getVisualizerRpcClient().joinProjectPath('functions.bal'); setViewComponent( { setLoading(true); - projectPath.current = await rpcClient.getVisualizerLocation().then((location) => location.projectUri); + projectPath.current = await rpcClient.getVisualizerLocation().then((location) => location.projectPath); currentFilePath.current = fileName; + const endPosition = await rpcClient.getBIDiagramRpcClient().getEndOfFile({ filePath: currentFilePath.current }); diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionCreator.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionCreator.tsx index b2a48023f8b..1684b021a42 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionCreator.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionCreator.tsx @@ -54,7 +54,7 @@ export function ConnectionCreator(props: ConnectionCreatorProps): JSX.Element { const initPanel = async () => { setLoading(true); - projectPath.current = await rpcClient.getVisualizerLocation().then((location) => location.projectUri); + projectPath.current = await rpcClient.getVisualizerLocation().then((location) => location.projectPath); connectionsFilePath.current = Utils.joinPath(URI.file(projectPath.current), CONNECTIONS_FILE).fsPath; const endPosition = await rpcClient.getBIDiagramRpcClient().getEndOfFile({ filePath: connectionsFilePath.current diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionSelectionList.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionSelectionList.tsx index 78a50a02136..3b065af2205 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionSelectionList.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionSelectionList.tsx @@ -51,7 +51,7 @@ export function ConnectionSelectionList(props: ConnectionSelectionListProps): JS const initPanel = async () => { setLoading(true); - projectPath.current = await rpcClient.getVisualizerLocation().then((location) => location.projectUri); + projectPath.current = await rpcClient.getVisualizerLocation().then((location) => location.projectPath); aiModuleOrg.current = await getAiModuleOrg(rpcClient, selectedNode?.codedata?.node); searchConfig.current = getSearchConfig(connectionKind, aiModuleOrg.current); progressTimeoutRef.current = setTimeout(() => { diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/ContextBasedFormTypeEditor/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/ContextBasedFormTypeEditor/index.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx index 4dedc557717..f7b603568fd 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx @@ -163,13 +163,13 @@ export function TopNavigationBar(props: TopNavigationBarProps) { return ( {index > 0 && ( - )} @@ -182,10 +182,10 @@ export function TopNavigationBar(props: TopNavigationBarProps) { {hasMultiplePackages && crumb.location.package && ( - {crumb.location.package} diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx index 8c99fe25f0e..8b82122c168 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx @@ -196,7 +196,7 @@ const AIChat: React.FC = () => { async function fetchBackendUrl() { try { backendRootUri = await rpcClient.getAiPanelRpcClient().getBackendUrl(); - chatLocation = (await rpcClient.getVisualizerLocation()).projectUri; + chatLocation = (await rpcClient.getVisualizerLocation()).projectPath ; setIsReqFileExists( chatLocation != null && chatLocation != undefined && diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx index d9455765eca..2a6a41b49d1 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx @@ -136,7 +136,7 @@ export function AIChatAgentWizard(props: AIChatAgentWizardProps) { aiModuleOrg.current = await getAiModuleOrg(rpcClient); const visualizerLocation = await rpcClient.getVisualizerLocation(); - projectPath.current = visualizerLocation.projectUri; + projectPath.current = visualizerLocation.projectPath; // hack: fetching from Central to build module dependency map in LS may take time progressTimeoutRef.current = setTimeout(() => { @@ -171,6 +171,68 @@ export function AIChatAgentWizard(props: AIChatAgentWizardProps) { modelNodeTemplate.properties.variable.value = modelVarName; await rpcClient.getBIDiagramRpcClient().getSourceCode({ filePath: projectPath.current, flowNode: modelNodeTemplate }); + // hack: Generate agent at module level for Ballerina versions under 2201.13.0 + let ballerinaVersion: string | undefined; + try { + const versionResponse = await rpcClient.getLangClientRpcClient().getBallerinaVersion(); + ballerinaVersion = versionResponse?.version; + } catch (error) { + console.warn("Unable to resolve Ballerina version; falling back to legacy agent generation.", error); + } + + // Execute for versions under 2201.13.0, or if version cannot be determined (safety fallback) + const executeForLegacyVersion = !ballerinaVersion || (() => { + const parts = ballerinaVersion.split('.'); + if (parts.length < 2) { + return true; // Can't parse properly, execute for safety + } + const majorVersion = parseInt(parts[0], 10); + const minorVersion = parseInt(parts[1], 10); + if (isNaN(majorVersion) || isNaN(minorVersion)) { + return true; // Can't parse version numbers, execute for safety + } + // Only versions < 2201.13 are legacy + if (majorVersion < 2201) { + return true; + } + if (majorVersion > 2201) { + return false; + } + return minorVersion < 13; + })(); + + if (executeForLegacyVersion) { + // Search for agent node in the current file + const agentSearchResponse = await rpcClient.getBIDiagramRpcClient().search({ + filePath: projectPath.current, + queryMap: { orgName: aiModuleOrg.current }, + searchKind: "AGENT" + }); + + // Validate search response structure + if (!agentSearchResponse?.categories?.[0]?.items?.[0]) { + throw new Error('No agent node found in search response'); + } + + const agentNode = agentSearchResponse.categories[0].items[0] as AvailableNode; + console.log(">>> agentNode", agentNode); + + // Generate template from agent node + const agentNodeTemplate = await getNodeTemplate(rpcClient, agentNode.codedata, projectPath.current); + + // save the agent node + const systemPromptValue = `{role: string \`\`, instructions: string \`\`}`; + const agentVarName = `${agentName}Agent`; + agentNodeTemplate.properties.systemPrompt.value = systemPromptValue; + agentNodeTemplate.properties.model.value = modelVarName; + agentNodeTemplate.properties.tools.value = []; + agentNodeTemplate.properties.variable.value = agentVarName; + + await rpcClient + .getBIDiagramRpcClient() + .getSourceCode({ filePath: projectPath.current, flowNode: agentNodeTemplate }); + } + setCurrentStep(3); const listenerVariableName = agentName + LISTENER; diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AddMcpServer.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AddMcpServer.tsx index 3957b96f77e..51fb1774a2f 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AddMcpServer.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AddMcpServer.tsx @@ -98,7 +98,7 @@ export function AddMcpServer(props: AddMcpServerProps): JSX.Element { // Get project path URI const visualizerLocation = await rpcClient.getVisualizerLocation(); - projectPathUriRef.current = visualizerLocation.projectUri; + projectPathUriRef.current = visualizerLocation.projectPath; const moduleNodes = await fetchModuleNodes(); // Store module variables for later use diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewAgent.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewAgent.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewTool.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewTool.tsx index cfd273fe4f6..25021961af4 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewTool.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewTool.tsx @@ -53,7 +53,7 @@ export function NewTool(props: NewToolProps): JSX.Element { const [savingForm, setSavingForm] = useState(false); const agentFilePath = useRef(""); - const projectUri = useRef(""); + const projectPath = useRef(""); useEffect(() => { initPanel(); @@ -61,9 +61,9 @@ export function NewTool(props: NewToolProps): JSX.Element { const initPanel = async () => { // get agent file path - const filePath = await rpcClient.getVisualizerLocation(); + const visualizerContext = await rpcClient.getVisualizerLocation(); agentFilePath.current = await rpcClient.getVisualizerRpcClient().joinProjectPath("agents.bal"); - projectUri.current = filePath.projectUri; + projectPath.current = visualizerContext.projectPath; // fetch tools and agent node await fetchAgentNode(); }; @@ -154,7 +154,7 @@ export function NewTool(props: NewToolProps): JSX.Element { return ( <> {agentFilePath.current && !savingForm && ( - + )} {(!agentFilePath.current || savingForm) && ( diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/utils.ts b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/utils.ts index 5ee298d9ee3..6d1666a9371 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/utils.ts +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/utils.ts @@ -39,10 +39,10 @@ export const getNodeTemplate = async ( export const getAiModuleOrg = async (rpcClient: BallerinaRpcClient, nodeKind?: NodeKind) => { if (nodeKind && (nodeKind === "NP_FUNCTION" || nodeKind === "NP_FUNCTION_DEFINITION")) return BALLERINA; - const filePath = await rpcClient.getVisualizerLocation(); + const visualizerContext = await rpcClient.getVisualizerLocation(); const aiModuleOrgResponse = await rpcClient .getAIAgentRpcClient() - .getAiModuleOrg({ projectPath: filePath.projectUri }); + .getAiModuleOrg({ projectPath: visualizerContext.projectPath }); console.log(">>> agent org", aiModuleOrgResponse.orgName); return aiModuleOrgResponse.orgName; } @@ -54,17 +54,17 @@ export const getAgentFilePath = async (rpcClient: BallerinaRpcClient) => { }; export const getNPFilePath = async (rpcClient: BallerinaRpcClient) => { - const filePath = await rpcClient.getVisualizerLocation(); + const visualizerContext = await rpcClient.getVisualizerLocation(); // Create the NP file path - const agentFilePath = Utils.joinPath(URI.file(filePath.projectUri), "functions.bal").fsPath; + const agentFilePath = Utils.joinPath(URI.file(visualizerContext.projectPath), "functions.bal").fsPath; return agentFilePath; }; export const getMainFilePath = async (rpcClient: BallerinaRpcClient) => { // Get the main file path and update the node - const filePath = await rpcClient.getVisualizerLocation(); + const visualizerContext = await rpcClient.getVisualizerLocation(); // Create the main file path - const mainFilePath = Utils.joinPath(URI.file(filePath.projectUri), "main.bal").fsPath; + const mainFilePath = Utils.joinPath(URI.file(visualizerContext.projectPath), "main.bal").fsPath; return mainFilePath; }; diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/AddConnectionWizard/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/AddConnectionWizard/index.tsx index b65f5e71d95..0d39a7d3909 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/AddConnectionWizard/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/AddConnectionWizard/index.tsx @@ -20,6 +20,7 @@ import { useEffect, useRef, useState } from "react"; import styled from "@emotion/styled"; import { AvailableNode, + DataMapperDisplayMode, DIRECTORY_MAP, EVENT_TYPE, FlowNode, @@ -42,6 +43,7 @@ import { HelperView } from "../../HelperView"; import { BodyText } from "../../../styles"; import { DownloadIcon } from "../../../../components/DownloadIcon"; import FormGeneratorNew from "../../Forms/FormGeneratorNew"; +import { FormSubmitOptions } from "../../FlowDiagram"; const Container = styled.div` width: 100%; @@ -232,12 +234,12 @@ export function AddConnectionWizard(props: AddConnectionWizardProps) { setCurrentStep(WizardStep.GENERATE_CONNECTOR); }; - const handleOnFormSubmit = async (node: FlowNode) => { + const handleOnFormSubmit = async (node: FlowNode, _dataMapperMode?: DataMapperDisplayMode, options?: FormSubmitOptions) => { console.log(">>> on form submit", node); if (selectedNodeRef.current) { setSavingFormStatus(SavingFormStatus.SAVING); const visualizerLocation = await rpcClient.getVisualizerLocation(); - let connectionsFilePath = visualizerLocation.documentUri || visualizerLocation.projectUri; + let connectionsFilePath = visualizerLocation.documentUri || visualizerLocation.projectPath; if (node.codedata.isGenerated && !connectionsFilePath.endsWith(".bal")) { connectionsFilePath += "/main.bal"; @@ -258,15 +260,28 @@ export function AddConnectionWizard(props: AddConnectionWizardProps) { }; } + // Check if the node is a connector + // otherwise, this is a new variable creation or something else + // triggered by the helper pane + const isConnector = node.codedata.node === "NEW_CONNECTION"; + rpcClient .getBIDiagramRpcClient() .getSourceCode({ filePath: connectionsFilePath, flowNode: node, - isConnector: true, + isConnector: isConnector, }) .then((response) => { console.log(">>> Updated source code", response); + if (!isConnector) { + setSavingFormStatus(SavingFormStatus.SUCCESS); + selectedNodeRef.current = undefined; + if (options?.postUpdateCallBack) { + options.postUpdateCallBack(); + } + return; + }; if (response.artifacts.length > 0) { // clear memory selectedNodeRef.current = undefined; @@ -295,12 +310,12 @@ export function AddConnectionWizard(props: AddConnectionWizardProps) { prevFields.map((field) => field.key === "module" ? { - ...field, - diagnostics: [ - ...field.diagnostics, - { message: response.errorMessage, severity: "ERROR" }, - ], - } + ...field, + diagnostics: [ + ...field.diagnostics, + { message: response.errorMessage, severity: "ERROR" }, + ], + } : field ) ); diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/ConnectionConfigView/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/ConnectionConfigView/index.tsx index 9b68166967b..046628e7094 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/ConnectionConfigView/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/ConnectionConfigView/index.tsx @@ -19,11 +19,12 @@ import React, { ReactNode, useEffect, useState } from "react"; import styled from "@emotion/styled"; import { ExpressionFormField } from "@wso2/ballerina-side-panel"; -import { FlowNode, LineRange, SubPanel } from "@wso2/ballerina-core"; +import { DataMapperDisplayMode, FlowNode, LineRange, SubPanel } from "@wso2/ballerina-core"; import FormGenerator from "../../Forms/FormGenerator"; import { useRpcContext } from "@wso2/ballerina-rpc-client"; import { SidePanelView } from "../../FlowDiagram/PanelManager"; import { ConnectionKind } from "../../../../components/ConnectionSelector"; +import { FormSubmitOptions } from "../../FlowDiagram"; const Container = styled.div` max-width: 600px; @@ -50,7 +51,7 @@ interface ConnectionConfigViewProps { submitText?: string; isSaving?: boolean; selectedNode: FlowNode; - onSubmit: (node?: FlowNode) => void; + onSubmit: (updatedNode?: FlowNode, dataMapperMode?: DataMapperDisplayMode, options?: FormSubmitOptions) => void; openSubPanel?: (subPanel: SubPanel) => void; updatedExpressionField?: ExpressionFormField; resetUpdatedExpressionField?: () => void; @@ -114,6 +115,7 @@ export function ConnectionConfigView(props: ConnectionConfigViewProps) { resetUpdatedExpressionField={resetUpdatedExpressionField} disableSaveButton={isPullingConnector} navigateToPanel={navigateToPanel} + handleOnFormSubmit={onSubmit} /> )} diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/EditConnectionWizard/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/EditConnectionWizard/index.tsx index 4eb8afe443c..076559ebf36 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/EditConnectionWizard/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/Connection/EditConnectionWizard/index.tsx @@ -49,7 +49,6 @@ export enum WizardView { } interface EditConnectionWizardProps { - projectUri: string; connectionName: string; onClose?: () => void; } diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx index eb87d3da873..db229a3cd64 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx @@ -107,8 +107,8 @@ export const Configurables = (props: ConfigurablesPageProps) => { }, []) const getProjectInfo = async () => { - const projectPath = await rpcClient.getVisualizerLocation(); - setProjectPathUri(URI.file(projectPath.projectUri).fsPath); + const visualizerContext = await rpcClient.getVisualizerLocation(); + setProjectPathUri(URI.file(visualizerContext.projectPath).fsPath); } const getConfigVariables = async () => { diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Functions.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Functions.tsx index 22207013355..9e1661f9de7 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Functions.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Functions.tsx @@ -66,12 +66,12 @@ export const FunctionsPage = ({ const [showContent, setShowContent] = useState(false); const [functionInfo, setFunctionInfo] = useState(undefined); const [libraryBrowserInfo, setLibraryBrowserInfo] = useState(undefined); - const [projectUri, setProjectUri] = useState(''); + const [projectPath, setProjectPath] = useState(''); const { addModal, closeModal } = useModalStack(); //TODO: get the correct filepath - let defaultFunctionsFile = Utils.joinPath(URI.file(projectUri), 'functions.bal').fsPath; + let defaultFunctionsFile = Utils.joinPath(URI.file(projectPath), 'functions.bal').fsPath; const debounceFetchFunctionInfo = useCallback( debounce((searchText: string, includeAvailableFunctions?: string) => { @@ -158,7 +158,7 @@ export const FunctionsPage = ({ const setDefaultFunctionsPath = () => { rpcClient.getVisualizerLocation().then((location) => { - setProjectUri(location?.projectUri || '') + setProjectPath(location?.projectPath || '') }) } @@ -182,7 +182,7 @@ export const FunctionsPage = ({ const handleNewFunctionClick = () => { addModal( { }, [targetLineRange]) const getProjectInfo = async () => { - const projectPath = await rpcClient.getVisualizerLocation(); - setProjectPathUri(URI.file(projectPath.projectUri).fsPath); + const visualizerContext = await rpcClient.getVisualizerLocation(); + setProjectPathUri(URI.file(visualizerContext.projectPath).fsPath); } const handleSubmit = (updatedNode?: FlowNode, dataMapperMode?: DataMapperDisplayMode) => { diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ImportIntegration/ConfigureProjectForm.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ImportIntegration/ConfigureProjectForm.tsx index 3e55736eb3c..cb0c791bd48 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ImportIntegration/ConfigureProjectForm.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ImportIntegration/ConfigureProjectForm.tsx @@ -30,6 +30,8 @@ export function ConfigureProjectForm({ onNext, onBack }: ConfigureProjectFormPro packageName: "", path: "", createDirectory: true, + createAsWorkspace: false, + workspaceName: "", orgName: "", version: "", }); @@ -44,6 +46,8 @@ export function ConfigureProjectForm({ onNext, onBack }: ConfigureProjectFormPro packageName: formData.packageName, projectPath: formData.path, createDirectory: formData.createDirectory, + createAsWorkspace: formData.createAsWorkspace, + workspaceName: formData.workspaceName, orgName: formData.orgName || undefined, version: formData.version || undefined, }); diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx new file mode 100644 index 00000000000..ce9b2ba2ee5 --- /dev/null +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { useEffect, useMemo, useState } from "react"; +import { + Button, + Icon, + Typography, +} from "@wso2/ui-toolkit"; +import styled from "@emotion/styled"; +import { useRpcContext } from "@wso2/ballerina-rpc-client"; +import { AddProjectFormFields, AddProjectFormData } from "./AddProjectFormFields"; +import { isFormValidAddProject } from "./utils"; + +const FormContainer = styled.div` + display: flex; + flex-direction: column; + margin: 80px 120px; + max-width: 600px; +`; + +const TitleContainer = styled.div` + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 32px; +`; + +const ButtonWrapper = styled.div` + margin-top: 20px; + display: flex; + justify-content: flex-end; +`; + +const IconButton = styled.div` + cursor: pointer; + border-radius: 4px; + width: 20px; + height: 20px; + font-size: 20px; + &:hover { + background-color: var(--vscode-toolbar-hoverBackground); + } +`; + +export function AddProjectForm() { + const { rpcClient } = useRpcContext(); + const [formData, setFormData] = useState({ + integrationName: "", + packageName: "", + workspaceName: "", + orgName: "", + version: "", + }); + const [isInWorkspace, setIsInWorkspace] = useState(false); + const [path, setPath] = useState(""); + const [isLoading, setIsLoading] = useState(false); + + const handleFormDataChange = (data: Partial) => { + setFormData(prev => ({ ...prev, ...data })); + }; + + useEffect(() => { + Promise.all([ + rpcClient.getCommonRpcClient().getWorkspaceRoot(), + rpcClient.getCommonRpcClient().getWorkspaceType() + ]).then(([path, workspaceType]) => { + setPath(path.path); + setIsInWorkspace(workspaceType.type === "BALLERINA_WORKSPACE"); + }); + }, []); + + const handleAddProject = () => { + setIsLoading(true); + rpcClient.getBIDiagramRpcClient().addProjectToWorkspace({ + projectName: formData.integrationName, + packageName: formData.packageName, + convertToWorkspace: !isInWorkspace, + path: path, + workspaceName: formData.workspaceName, + orgName: formData.orgName || undefined, + version: formData.version || undefined, + }); + }; + + const goBack = () => { + rpcClient.getVisualizerRpcClient().goBack(); + }; + + return ( + + + + + + + {!isInWorkspace + ? "Convert to Workspace & Add Integration" + : "Add New Integration"} + + + + + + + + + + ); +} + diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx new file mode 100644 index 00000000000..4193218b369 --- /dev/null +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx @@ -0,0 +1,202 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { useEffect, useState } from "react"; +import { TextField, CheckBox, LinkButton, ThemeColors, Codicon } from "@wso2/ui-toolkit"; +import styled from "@emotion/styled"; +import { sanitizePackageName, validatePackageName } from "./utils"; + +const FieldGroup = styled.div` + margin-bottom: 20px; +`; + +const CheckboxContainer = styled.div` + margin: 16px 0; +`; + +const OptionalConfigRow = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + width: 100%; + margin-bottom: 8px; +`; + +const OptionalConfigButtonContainer = styled.div` + display: flex; + flex-direction: row; + flex-grow: 1; + justify-content: flex-end; +`; + +const OptionalConfigContent = styled.div` + margin-top: 16px; +`; + +const Description = styled.div` + color: var(--vscode-list-deemphasizedForeground); + margin-top: 4px; + text-align: left; +`; + +const WorkspaceSection = styled.div` + margin-bottom: 24px; + padding-bottom: 24px; + border-bottom: 1px solid var(--vscode-panel-border); +`; + +export interface AddProjectFormData { + integrationName: string; + packageName: string; + workspaceName?: string; + orgName: string; + version: string; +} + +export interface AddProjectFormFieldsProps { + formData: AddProjectFormData; + onFormDataChange: (data: Partial) => void; + isInWorkspace: boolean; // true if already in a workspace, false if in a package +} + +export function AddProjectFormFields({ + formData, + onFormDataChange, + isInWorkspace +}: AddProjectFormFieldsProps) { + const [packageNameTouched, setPackageNameTouched] = useState(false); + const [showOptionalConfigurations, setShowOptionalConfigurations] = useState(false); + const [packageNameError, setPackageNameError] = useState(null); + + const handleIntegrationName = (value: string) => { + onFormDataChange({ integrationName: value }); + // Auto-populate package name if user hasn't manually edited it + if (!packageNameTouched) { + onFormDataChange({ packageName: sanitizePackageName(value) }); + } + }; + + const handlePackageName = (value: string) => { + const sanitized = sanitizePackageName(value); + onFormDataChange({ packageName: sanitized }); + setPackageNameTouched(value.length > 0); + // Clear error while typing + if (packageNameError) { + setPackageNameError(null); + } + }; + + const handleShowOptionalConfigurations = () => { + setShowOptionalConfigurations(true); + }; + + const handleHideOptionalConfigurations = () => { + setShowOptionalConfigurations(false); + }; + + // Effect to trigger validation when requested by parent + useEffect(() => { + const error = validatePackageName(formData.packageName, formData.integrationName); + setPackageNameError(error); + }, [formData.packageName]); + + return ( + <> + {!isInWorkspace && ( + + onFormDataChange({ workspaceName: value })} + value={formData.workspaceName} + label="Workspace Name" + placeholder="Enter workspace name" + autoFocus={true} + required={true} + /> + + )} + + + + + + + + + + + Optional Configurations + + {!showOptionalConfigurations && ( + + + Expand + + )} + {showOptionalConfigurations && ( + + + Collapse + + )} + + + + {showOptionalConfigurations && ( + + + onFormDataChange({ orgName: value })} + value={formData.orgName} + label="Organization Name" + description="The organization that owns this Ballerina package." + /> + + + onFormDataChange({ version: value })} + value={formData.version} + label="Package Version" + placeholder="0.1.0" + description="Version of the Ballerina package." + /> + + + )} + + ); +} diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/ProjectFormFields.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/ProjectFormFields.tsx index 43a1d230c63..198d96afd59 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/ProjectFormFields.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/ProjectFormFields.tsx @@ -17,7 +17,7 @@ */ import { useEffect, useState } from "react"; -import { LocationSelector, TextField, CheckBox, LinkButton, ThemeColors, Codicon } from "@wso2/ui-toolkit"; +import { LocationSelector, TextField, CheckBox, LinkButton, ThemeColors, Codicon, FormCheckBox } from "@wso2/ui-toolkit"; import styled from "@emotion/styled"; import { useRpcContext } from "@wso2/ballerina-rpc-client"; import { sanitizePackageName, validatePackageName } from "./utils"; @@ -50,11 +50,19 @@ const OptionalConfigContent = styled.div` margin-top: 16px; `; +const Description = styled.div` + color: var(--vscode-list-deemphasizedForeground); + margin-top: 4px; + text-align: left; +`; + export interface ProjectFormData { integrationName: string; packageName: string; path: string; createDirectory: boolean; + createAsWorkspace: boolean; + workspaceName: string; orgName: string; version: string; } @@ -144,7 +152,7 @@ export function ProjectFormFields({ formData, onFormDataChange, onValidationChan onFormDataChange({ createDirectory: checked })} /> @@ -185,6 +193,27 @@ export function ProjectFormFields({ formData, onFormDataChange, onValidationChan {showOptionalConfigurations && ( + + + onFormDataChange({ createAsWorkspace: checked })} + /> + + Include this integration in a new workspace for multi-project management. + + + {formData.createAsWorkspace && ( + onFormDataChange({ workspaceName: value })} + value={formData.workspaceName} + label="Workspace Name" + placeholder="Enter workspace name" + required={true} + /> + )} + onFormDataChange({ orgName: value })} diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/index.tsx index 3303ceef8e5..f32e948b1c3 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/index.tsx @@ -66,6 +66,8 @@ export function ProjectForm() { packageName: "", path: "", createDirectory: true, + createAsWorkspace: false, + workspaceName: "", orgName: "", version: "", }); @@ -80,6 +82,8 @@ export function ProjectForm() { packageName: formData.packageName, projectPath: formData.path, createDirectory: formData.createDirectory, + createAsWorkspace: formData.createAsWorkspace, + workspaceName: formData.workspaceName, orgName: formData.orgName || undefined, version: formData.version || undefined, }); @@ -114,7 +118,7 @@ export function ProjectForm() { onClick={handleCreateProject} appearance="primary" > - Create Integration + {formData.createAsWorkspace ? "Create Workspace" : "Create Integration"} diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/utils.ts b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/utils.ts index 8c0ab104b43..3d210daa161 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/utils.ts +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/utils.ts @@ -17,6 +17,7 @@ */ // Import from the component file since types.ts was removed +import { AddProjectFormData } from "./AddProjectFormFields"; import { ProjectFormData } from "./ProjectFormFields"; export const isValidPackageName = (name: string): boolean => { @@ -64,6 +65,15 @@ export const isFormValid = (formData: ProjectFormData): boolean => { ); }; +export const isFormValidAddProject = (formData: AddProjectFormData, isInWorkspace: boolean): boolean => { + return ( + formData.integrationName.length >= 2 && + formData.packageName.length >= 2 && + (isInWorkspace || (!isInWorkspace && formData.workspaceName?.length >= 1)) && + validatePackageName(formData.packageName, formData.integrationName) === null + ); +}; + export const sanitizePackageName = (name: string): string => { // Allow dots but sanitize other characters, then convert consecutive dots to single dot return name diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceClassEditor/ServiceClassConfig.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceClassEditor/ServiceClassConfig.tsx index d6d8094115b..952f9db7269 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceClassEditor/ServiceClassConfig.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceClassEditor/ServiceClassConfig.tsx @@ -61,7 +61,6 @@ const LoadingContainer = styled.div` interface ServiceClassConfigProps { fileName: string; position: NodePosition; - projectUri: string; type: Type; } diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/index.tsx index cc0d267d5c0..602e603c82a 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/index.tsx @@ -18,6 +18,7 @@ export { WelcomeView } from "./WelcomeView"; export { ProjectForm } from "./ProjectForm"; +export { AddProjectForm } from "./ProjectForm/AddProjectForm"; export { Overview as BIOverview } from "./Overview"; export { ComponentListView } from "./ComponentListView"; export { ComponentDiagram } from "./ComponentDiagram"; diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/DataMapper/DataMapperView.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/DataMapper/DataMapperView.tsx index 916d8e0d90d..3998a0e69a8 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/DataMapper/DataMapperView.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/DataMapper/DataMapperView.tsx @@ -67,7 +67,7 @@ interface ModelSignature { } export function DataMapperView(props: DataMapperProps) { - const { filePath, codedata, name, projectUri, position, reusable, onClose } = props; + const { filePath, codedata, name, projectPath, position, reusable, onClose } = props; const [isFileUpdateError, setIsFileUpdateError] = useState(false); const [modelState, setModelState] = useState({ @@ -667,7 +667,7 @@ export function DataMapperView(props: DataMapperProps) { <> {reusable && (!hasInputs || !hasOutputs) ? ( void; diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/ObjectViewer/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/ObjectViewer/index.tsx index 85ba5b8dcd9..615836f8af5 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/ObjectViewer/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/ObjectViewer/index.tsx @@ -179,12 +179,11 @@ interface GraphqlObjectViewerProps { type: Type; onClose: () => void; onImplementation: (type: Type) => void; - projectUri: string; serviceIdentifier: string; } export function GraphqlObjectViewer(props: GraphqlObjectViewerProps) { - const { onClose, type, projectUri, onImplementation, serviceIdentifier } = props; + const { onClose, type, onImplementation, serviceIdentifier } = props; const { rpcClient } = useRpcContext(); const [serviceClassModel, setServiceClassModel] = useState(); const [editingFunction, setEditingFunction] = useState(undefined); diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/index.tsx index b7cf126bc5f..f0f7541ad3a 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/index.tsx @@ -81,12 +81,11 @@ const Path = styled.span` interface GraphQLDiagramProps { filePath: string; position: NodePosition; - projectUri?: string; serviceIdentifier: string; } export function GraphQLDiagram(props: GraphQLDiagramProps) { - const { filePath, position, projectUri, serviceIdentifier } = props; + const { filePath, position, serviceIdentifier } = props; const { rpcClient } = useRpcContext(); const queryClient = useQueryClient(); const [isServiceEditorOpen, setIsServiceEditorOpen] = useState(false); @@ -527,7 +526,6 @@ export function GraphQLDiagram(props: GraphQLDiagramProps) { serviceIdentifier={serviceIdentifier} onClose={onTypeEditorClosed} type={editingType} - projectUri={projectUri} onImplementation={handleOnImplementation} /> )} diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/TypeDiagram/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/TypeDiagram/index.tsx index 6c1ab388c12..329fcf7d54d 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/TypeDiagram/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/TypeDiagram/index.tsx @@ -37,8 +37,8 @@ export const Title: React.FC = styled.div` interface TypeDiagramProps { selectedTypeId?: string; - projectUri?: string; addType?: boolean; + projectPath?: string; } interface TypeEditorState { @@ -51,7 +51,7 @@ interface TypeEditorState { const MAX_TYPES_FOR_FULL_VIEW = 80; export function TypeDiagram(props: TypeDiagramProps) { - const { selectedTypeId, projectUri, addType } = props; + const { selectedTypeId, addType, projectPath } = props; const { rpcClient } = useRpcContext(); const commonRpcClient = rpcClient.getCommonRpcClient(); const [visualizerLocation, setVisualizerLocation] = React.useState(); @@ -152,7 +152,7 @@ export function TypeDiagram(props: TypeDiagramProps) { setVisualizerLocation(value); }); } - }, [rpcClient, projectUri]); + }, [rpcClient, projectPath]); useEffect(() => { setIsModelLoaded(false); diff --git a/workspaces/ballerina/type-editor/src/RecordFromJson/RecordFromJson.tsx b/workspaces/ballerina/type-editor/src/RecordFromJson/RecordFromJson.tsx index 5365b4a58be..a512214c245 100644 --- a/workspaces/ballerina/type-editor/src/RecordFromJson/RecordFromJson.tsx +++ b/workspaces/ballerina/type-editor/src/RecordFromJson/RecordFromJson.tsx @@ -92,22 +92,19 @@ export const RecordFromJson = (props: RecordFromJsonProps) => { typeName: name }); - // find the record with the name - const record = typesFromJson.types.find((t) => t.type.name === name); - // if there are other records than the matching name, get the types + const record = typesFromJson.types[typesFromJson.types.length - 1]; const otherRecords = typesFromJson.types - .filter((t) => t.type.name !== name) + .slice(0, -1) .map((t) => t.type); - if (otherRecords.length > 0) { - const response: UpdateTypesResponse = await rpcClient.getBIDiagramRpcClient().updateTypes({ + await rpcClient.getBIDiagramRpcClient().updateTypes({ filePath: 'types.bal', types: otherRecords }); if (!isPopupTypeForm) { - await props.rpcClient.getVisualizerRpcClient().openView( + await rpcClient.getVisualizerRpcClient().openView( { type: EVENT_TYPE.UPDATE_PROJECT_LOCATION, location: { addType: false } } ); } @@ -115,6 +112,9 @@ export const RecordFromJson = (props: RecordFromJsonProps) => { if (record) { onImport([record.type]); + } else { + setIsSaving(false); + setError("Could not import JSON as type."); } } catch (err) { setError("Could not import JSON as type."); diff --git a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx index 4966fbde8fd..668d44568d9 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx @@ -335,10 +335,10 @@ export function ContextTypeCreatorTab(props: ContextTypeCreatorProps) { return { isValid: isTypeNameValid, error: nameError }; } - const projectUri = await rpcClient.getVisualizerLocation().then((res) => res.projectUri); + const projectPath = await rpcClient.getVisualizerLocation().then((res) => res.projectPath); const endPosition = await rpcClient.getBIDiagramRpcClient().getEndOfFile({ - filePath: Utils.joinPath(URI.file(projectUri), 'types.bal').fsPath + filePath: Utils.joinPath(URI.file(projectPath), 'types.bal').fsPath }); const response = await rpcClient.getBIDiagramRpcClient().getExpressionDiagnostics({ diff --git a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/EditTypeView.tsx b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/EditTypeView.tsx index 71781ac5cb2..0fd31c80691 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/EditTypeView.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/EditTypeView.tsx @@ -344,10 +344,10 @@ export function EditTypeView(props: EditTypeViewProps) { return; } - const projectUri = await rpcClient.getVisualizerLocation().then((res) => res.projectUri); + const projectPath = await rpcClient.getVisualizerLocation().then((res) => res.projectPath); const endPosition = await rpcClient.getBIDiagramRpcClient().getEndOfFile({ - filePath: Utils.joinPath(URI.file(projectUri), 'types.bal').fsPath + filePath: Utils.joinPath(URI.file(projectPath), 'types.bal').fsPath }); const response = await rpcClient.getBIDiagramRpcClient().getExpressionDiagnostics({ diff --git a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx index 0e2ca1f2ae0..505e8ad46af 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx @@ -201,10 +201,10 @@ export function GenericImportTab(props: GenericImportTabProps) { }; const validateTypeName = useCallback(debounce(async (value: string) => { - const projectUri = await rpcClient.getVisualizerLocation().then((res) => res.projectUri); + const projectPath = await rpcClient.getVisualizerLocation().then((res) => res.projectPath); const endPosition = await rpcClient.getBIDiagramRpcClient().getEndOfFile({ - filePath: Utils.joinPath(URI.file(projectUri), 'types.bal').fsPath + filePath: Utils.joinPath(URI.file(projectPath), 'types.bal').fsPath }); const response = await rpcClient.getBIDiagramRpcClient().getExpressionDiagnostics({ diff --git a/workspaces/ballerina/type-editor/src/TypeEditor/IdentifierField.tsx b/workspaces/ballerina/type-editor/src/TypeEditor/IdentifierField.tsx index 87d7565ea88..bae86fd2072 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/IdentifierField.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/IdentifierField.tsx @@ -53,10 +53,10 @@ export const IdentifierField = forwardRef { - const projectUri = await rpcClient.getVisualizerLocation().then((res) => res.projectUri); + const projectPath = await rpcClient.getVisualizerLocation().then((res) => res.projectPath); const endPosition = await rpcClient.getBIDiagramRpcClient().getEndOfFile({ - filePath: Utils.joinPath(URI.file(projectUri), 'types.bal').fsPath + filePath: Utils.joinPath(URI.file(projectPath), 'types.bal').fsPath }); const response = await rpcClient.getBIDiagramRpcClient().getExpressionDiagnostics({ diff --git a/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/ImportTab.tsx b/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/ImportTab.tsx index d549288612a..23909b37463 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/ImportTab.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/ImportTab.tsx @@ -88,10 +88,10 @@ export function ImportTab(props: ImportTabProps) { } const validateTypeName = useCallback(debounce(async (value: string) => { - const projectUri = await rpcClient.getVisualizerLocation().then((res) => res.projectUri); + const projectPath = await rpcClient.getVisualizerLocation().then((res) => res.projectPath); const endPosition = await rpcClient.getBIDiagramRpcClient().getEndOfFile({ - filePath: Utils.joinPath(URI.file(projectUri), 'types.bal').fsPath + filePath: Utils.joinPath(URI.file(projectPath), 'types.bal').fsPath }); const response = await rpcClient.getBIDiagramRpcClient().getExpressionDiagnostics({ diff --git a/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/TypeCreatorTab.tsx b/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/TypeCreatorTab.tsx index c18132123d3..fc8bf0c6f60 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/TypeCreatorTab.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/TypeCreatorTab.tsx @@ -347,10 +347,10 @@ export function TypeCreatorTab(props: TypeCreatorTabProps) { return { isValid: isTypeNameValid, error: nameError }; } - const projectUri = await rpcClient.getVisualizerLocation().then((res) => res.projectUri); + const projectPath = await rpcClient.getVisualizerLocation().then((res) => res.projectPath); const endPosition = await rpcClient.getBIDiagramRpcClient().getEndOfFile({ - filePath: Utils.joinPath(URI.file(projectUri), 'types.bal').fsPath + filePath: Utils.joinPath(URI.file(projectPath), 'types.bal').fsPath }); const response = await rpcClient.getBIDiagramRpcClient().getExpressionDiagnostics({ diff --git a/workspaces/ballerina/type-editor/src/TypeEditor/TypeField.tsx b/workspaces/ballerina/type-editor/src/TypeEditor/TypeField.tsx index 719a303a745..8a7e57ed833 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/TypeField.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/TypeField.tsx @@ -125,10 +125,10 @@ export const TypeField = forwardRef((props, re return; } } - const projectUri = await rpcClient.getVisualizerLocation().then((res) => res.projectUri); + const projectPath = await rpcClient.getVisualizerLocation().then((res) => res.projectPath); const endPosition = await rpcClient.getBIDiagramRpcClient().getEndOfFile({ - filePath: Utils.joinPath(URI.file(projectUri), 'types.bal').fsPath + filePath: Utils.joinPath(URI.file(projectPath), 'types.bal').fsPath }); const response = await rpcClient.getBIDiagramRpcClient().getExpressionDiagnostics({ diff --git a/workspaces/bi/bi-extension/CHANGELOG.md b/workspaces/bi/bi-extension/CHANGELOG.md index e3d7a48fe11..d3b9f6558df 100644 --- a/workspaces/bi/bi-extension/CHANGELOG.md +++ b/workspaces/bi/bi-extension/CHANGELOG.md @@ -4,11 +4,11 @@ All notable changes to the **WSO2 Integrator: BI** extension will be documented The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/). -## [Unreleased] +## [1.5.0](https://github.com/wso2/vscode-extensions/compare/ballerina-integrator-1.4.0...ballerina-integrator-1.5.0) - 2025-11-05 ### Added -- **Editor** — Added visual editing for mono-repositories with multiple Ballerina projects, along with support for "Natural expressions" in Ballerina 2201.13.0. +- **Editor** — Added support for [Ballerina workspaces](https://ballerina.io/learn/workspaces/). This allows you to seamlessly manage, navigate, and build multiple related Ballerina projects within a single VS Code window, greatly improving the development workflow for complex systems. ## [1.4.0](https://github.com/wso2/vscode-extensions/compare/ballerina-integrator-1.3.2...ballerina-integrator-1.4.0) - 2025-11-05 diff --git a/workspaces/bi/bi-extension/package.json b/workspaces/bi/bi-extension/package.json index 468bb6dcf3e..97acac33da8 100644 --- a/workspaces/bi/bi-extension/package.json +++ b/workspaces/bi/bi-extension/package.json @@ -2,7 +2,7 @@ "name": "ballerina-integrator", "displayName": "WSO2 Integrator: BI", "description": "An extension which gives a development environment for designing, developing, debugging, and testing integration solutions.", - "version": "1.4.0", + "version": "1.5.0", "publisher": "wso2", "icon": "resources/images/wso2-ballerina-integrator-logo.png", "repository": { @@ -137,7 +137,7 @@ "view/title": [ { "command": "BI.project-explorer.switch-project", - "when": "view == BI.project-explorer && BI.isMultiRoot == true", + "when": "view == BI.project-explorer && BI.isBalWorkspace == true", "group": "navigation" }, { @@ -175,7 +175,8 @@ "dependencies": { "@wso2/ballerina-core": "workspace:*", "@wso2/font-wso2-vscode": "workspace:*", - "xstate": "^4.38.3" + "xstate": "^4.38.3", + "toml": "^3.0.0" }, "devDependencies": { "@vscode/vsce": "^3.4.0", diff --git a/workspaces/bi/bi-extension/src/project-explorer/activate.ts b/workspaces/bi/bi-extension/src/project-explorer/activate.ts index 7820d33e9da..67abc97fe9c 100644 --- a/workspaces/bi/bi-extension/src/project-explorer/activate.ts +++ b/workspaces/bi/bi-extension/src/project-explorer/activate.ts @@ -25,11 +25,11 @@ interface ExplorerActivationConfig { context: ExtensionContext; isBI: boolean; isBallerina?: boolean; - isMultiRoot?: boolean; + isBalWorkspace?: boolean; } export function activateProjectExplorer(config: ExplorerActivationConfig) { - const { context, isBI, isBallerina, isMultiRoot } = config; + const { context, isBI, isBallerina, isBalWorkspace } = config; if (extension.langClient && extension.biSupported) { setLoadingStatus(); @@ -39,7 +39,7 @@ export function activateProjectExplorer(config: ExplorerActivationConfig) { const projectTree = createProjectTree(projectExplorerDataProvider); if (isBallerina) { - registerBallerinaCommands(projectExplorerDataProvider, isBI, isMultiRoot); + registerBallerinaCommands(projectExplorerDataProvider, isBI, isBalWorkspace); } handleVisibilityChangeEvents(projectTree, projectExplorerDataProvider, isBallerina); @@ -54,11 +54,11 @@ function createProjectTree(dataProvider: ProjectExplorerEntryProvider) { return window.createTreeView(BI_COMMANDS.PROJECT_EXPLORER, { treeDataProvider: dataProvider }); } -function registerBallerinaCommands(dataProvider: ProjectExplorerEntryProvider, isBI: boolean, isMultiRoot?: boolean) { +function registerBallerinaCommands(dataProvider: ProjectExplorerEntryProvider, isBI: boolean, isBalWorkspace?: boolean) { commands.registerCommand(BI_COMMANDS.REFRESH_COMMAND, () => dataProvider.refresh()); - if (isMultiRoot) { - commands.executeCommand('setContext', 'BI.isMultiRoot', true); + if (isBalWorkspace) { + commands.executeCommand('setContext', 'BI.isBalWorkspace', true); } if (isBI) { registerBICommands(); diff --git a/workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts b/workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts index 354115c107f..711242f300d 100644 --- a/workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts +++ b/workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts @@ -134,20 +134,36 @@ async function getProjectStructureData(): Promise { const data: ProjectExplorerEntry[] = []; if (extension.langClient) { const stateContext: VisualizerLocation = await commands.executeCommand(SHARED_COMMANDS.GET_STATE_CONTEXT); - const workspace = vscode + if (!stateContext) { + return []; + } + + const ballerinaWorkspace = stateContext.workspacePath; + const workspaceFolderOfPackage = vscode .workspace .workspaceFolders - .find(folder => folder.uri.fsPath === stateContext.projectUri); + .find(folder => folder.uri.fsPath === stateContext.projectPath); - if (!workspace) { - return []; + let packageName: string; + let packagePath: string; + + if (!workspaceFolderOfPackage) { + if (ballerinaWorkspace) { + packageName = path.basename(Uri.parse(stateContext.projectPath).path); + packagePath = stateContext.projectPath; + } else { + return []; + } + } else { + packageName = workspaceFolderOfPackage.name; + packagePath = workspaceFolderOfPackage.uri.fsPath; } // Get the state context from ballerina extension as it maintain the event driven tree data let projectStructure; if (typeof stateContext === 'object' && stateContext !== null && 'projectStructure' in stateContext && stateContext.projectStructure !== null) { projectStructure = stateContext.projectStructure; - const projectTree = generateTreeData(workspace, projectStructure); + const projectTree = generateTreeData(packageName, packagePath, projectStructure); if (projectTree) { data.push(projectTree); } @@ -159,12 +175,15 @@ async function getProjectStructureData(): Promise { return []; } -function generateTreeData(project: vscode.WorkspaceFolder, components: ProjectStructureResponse): ProjectExplorerEntry | undefined { - const projectRootPath = project.uri.fsPath; +function generateTreeData( + packageName: string, + packagePath: string, + components: ProjectStructureResponse +): ProjectExplorerEntry | undefined { const projectRootEntry = new ProjectExplorerEntry( - `${project.name}`, + `${packageName}`, vscode.TreeItemCollapsibleState.Expanded, - projectRootPath, + packagePath, 'project', true ); diff --git a/workspaces/bi/bi-extension/src/stateMachine.ts b/workspaces/bi/bi-extension/src/stateMachine.ts index 68aced9d28c..2e6439a2867 100644 --- a/workspaces/bi/bi-extension/src/stateMachine.ts +++ b/workspaces/bi/bi-extension/src/stateMachine.ts @@ -24,7 +24,7 @@ import { fetchProjectInfo, ProjectInfo } from './utils'; interface MachineContext { isBI: boolean; isBallerina?: boolean; - isMultiRoot?: boolean; + isBalWorkspace?: boolean; } const stateMachine = createMachine({ @@ -45,7 +45,7 @@ const stateMachine = createMachine({ actions: assign({ isBI: (context, event) => event.data.isBI, isBallerina: (context, event) => event.data.isBallerina, - isMultiRoot: (context, event) => event.data.isMultiRoot + isBalWorkspace: (context, event) => event.data.isBalWorkspace }) }, ], @@ -68,7 +68,7 @@ const stateMachine = createMachine({ context: extension.context, isBI: context.isBI, isBallerina: context.isBallerina, - isMultiRoot: context.isMultiRoot + isBalWorkspace: context.isBalWorkspace }); } }, @@ -84,5 +84,5 @@ export const StateMachine = { }; async function findProjectInfo(): Promise { - return fetchProjectInfo(); + return await fetchProjectInfo(); }; diff --git a/workspaces/bi/bi-extension/src/utils.ts b/workspaces/bi/bi-extension/src/utils.ts index 3a5d920514a..47ae99e5da4 100644 --- a/workspaces/bi/bi-extension/src/utils.ts +++ b/workspaces/bi/bi-extension/src/utils.ts @@ -20,11 +20,13 @@ import { Uri, Webview, workspace } from "vscode"; import * as fs from 'fs'; import * as path from 'path'; import { extension } from "./biExtentionContext"; +import { PackageTomlValues, WorkspaceTomlValues } from "@wso2/ballerina-core"; +import { parse } from "toml"; export interface ProjectInfo { isBI: boolean; isBallerina: boolean; - isMultiRoot: boolean; + isBalWorkspace: boolean; }; export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) { @@ -34,39 +36,67 @@ export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList)); } -export function fetchProjectInfo(): ProjectInfo { +/** + * Fetches project information for the current workspace. + * Analyzes the workspace to determine if it's a Ballerina Integrator (BI) project, + * a Ballerina project, and whether it's a multi-root workspace. + * + * @returns A Promise that resolves to ProjectInfo containing: + * - isBI: true if the workspace is a Ballerina Integrator project + * - isBallerina: true if the workspace contains a valid Ballerina project/workspace + * - isBalWorkspace: true if the workspace is a Ballerina workspace with multiple packages + * + * @remarks + * - Returns all false values if no workspace folders exist or multiple workspace folders are present + * - For Ballerina workspaces, filters package paths to ensure they exist within the workspace + */ +export async function fetchProjectInfo(): Promise { const workspaceFolders = workspace.workspaceFolders; - const isWorkspaceFile = workspace.workspaceFile?.scheme === "file"; - if (workspaceFolders?.length > 1 && !isWorkspaceFile) { - return { - isBI: false, - isBallerina: false, - isMultiRoot: false - }; + if (!workspaceFolders || workspaceFolders.length > 1) { + return { isBI: false, isBallerina: false, isBalWorkspace: false }; } - const workspaceUris = workspaceFolders - ? workspaceFolders.map(folder => folder.uri) - : []; - let isBICount = 0; // Counter for workspaces with isBI set to true - let isBalCount = 0; // Counter for workspaces with Ballerina project - - // Check each workspace folder's configuration for 'isBI' - for (const uri of workspaceUris) { - const isBallerina = checkIsBallerina(uri); - if (isBallerina) { - isBalCount++; - if (checkIsBI(uri)) { - isBICount++; + const workspaceUri = workspaceFolders[0].uri; + const isBallerinaWorkspace = await checkIsBallerinaWorkspace(workspaceUri); + + if (isBallerinaWorkspace) { + const workspaceTomlValues = await getWorkspaceTomlValues(workspaceUri.fsPath); + if (!workspaceTomlValues?.workspace?.packages) { + return { isBI: false, isBallerina: false, isBalWorkspace: false }; + } + const packagePaths = workspaceTomlValues.workspace.packages; + + const filteredPackagePaths = await filterPackagePaths(packagePaths, workspaceUri.fsPath); + + let isBICount = 0; // Counter for workspaces with isBI set to true + let isBalCount = 0; // Counter for workspaces with Ballerina project + + for (const pkgPath of filteredPackagePaths) { + let packagePath = path.join(workspaceUri.fsPath, pkgPath); + if (path.isAbsolute(pkgPath)) { + packagePath = path.resolve(pkgPath); + } + const isBallerina = await checkIsBallerinaPackage(Uri.file(packagePath)); + if (isBallerina) { + isBalCount++; + if (checkIsBI(Uri.file(packagePath))) { + isBICount++; + } } } + + return { + isBI: isBICount > 0, + isBallerina: isBalCount > 0, + isBalWorkspace: true + }; } return { - isBI: isBICount > 0, - isBallerina: isBalCount > 0, - isMultiRoot: isBalCount > 1 // Set to true only if more than one workspace has a Ballerina project + isBI: checkIsBI(workspaceUri), + isBallerina: await checkIsBallerinaPackage(workspaceUri), + isBalWorkspace: false }; } @@ -86,7 +116,133 @@ export function checkIsBI(uri: Uri): boolean { return false; // Return false if isBI is not set } -export function checkIsBallerina(uri: Uri): boolean { +/** + * Checks if the given URI represents a Ballerina package directory. + * A directory is considered a Ballerina package if it contains a Ballerina.toml file + * with a [package] section. + * + * @param uri - The URI of the directory to check + * @returns true if the directory is a valid Ballerina package, false otherwise + */ +export async function checkIsBallerinaPackage(uri: Uri): Promise { + const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); + + // First check if the file exists + if (!fs.existsSync(ballerinaTomlPath)) { + return false; + } + + try { + const tomlValues = await getProjectTomlValues(uri.fsPath); + return tomlValues?.package !== undefined; + } catch (error) { + // If there's an error reading the file, it's not a valid Ballerina project + console.error(`Error reading package Ballerina.toml: ${error}`); + return false; + } +} + +/** + * Checks if the given URI represents a Ballerina workspace directory. + * A directory is considered a Ballerina workspace if it contains a Ballerina.toml file + * with a [workspace] section. + * + * @param uri - The URI of the directory to check + * @returns true if the directory is a valid Ballerina workspace, false otherwise + */ +export async function checkIsBallerinaWorkspace(uri: Uri): Promise { const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); - return fs.existsSync(ballerinaTomlPath); + + // First check if the file exists + if (!fs.existsSync(ballerinaTomlPath)) { + return false; + } + + try { + const tomlValues = await getWorkspaceTomlValues(uri.fsPath); + return tomlValues?.workspace !== undefined; + } catch (error) { + // If there's an error reading the file, it's not a valid Ballerina workspace + console.error(`Error reading workspace Ballerina.toml: ${error}`); + return false; + } +} + +/** + * Reads and parses the Ballerina.toml file from the given project path. + * + * @param projectPath - The file system path to the project directory + * @returns A Promise that resolves to the parsed TOML values if successful, + * or undefined if the file doesn't exist or parsing fails + */ +async function getProjectTomlValues(projectPath: string): Promise { + const ballerinaTomlPath = path.join(projectPath, 'Ballerina.toml'); + if (fs.existsSync(ballerinaTomlPath)) { + const tomlContent = await fs.promises.readFile(ballerinaTomlPath, 'utf-8'); + try { + return parse(tomlContent); + } catch (error) { + console.error("Failed to load Ballerina.toml content for project at path: ", projectPath, error); + return; + } + } +} + +/** + * Reads and parses the Ballerina.toml file from the given workspace path. + * + * @param workspacePath - The file system path to the workspace directory + * @returns A Promise that resolves to the parsed TOML values if successful, + * or undefined if the file doesn't exist or parsing fails + */ +async function getWorkspaceTomlValues(workspacePath: string): Promise { + const ballerinaTomlPath = path.join(workspacePath, 'Ballerina.toml'); + if (fs.existsSync(ballerinaTomlPath)) { + const tomlContent = await fs.promises.readFile(ballerinaTomlPath, 'utf-8'); + try { + return parse(tomlContent); + } catch (error) { + console.error("Failed to load Ballerina.toml content for workspace at path: ", workspacePath, error); + return; + } + } +} + +/** + * Filters package paths to only include valid Ballerina packages within the workspace. + * + * For each path, this function: + * - Resolves the path (handling relative paths like `.` and `..`) + * - Verifies the path exists on the filesystem + * - Ensures the path is within the workspace boundaries (prevents path traversal) + * - Validates it's a valid Ballerina package + * + * @param packagePaths Array of package paths (relative or absolute) + * @param workspacePath Absolute path to the workspace root + * @returns Filtered array of valid Ballerina package paths that exist within the workspace + */ +export async function filterPackagePaths(packagePaths: string[], workspacePath: string): Promise { + const results = await Promise.all( + packagePaths.map(async pkgPath => { + if (path.isAbsolute(pkgPath)) { + const resolvedPath = path.resolve(pkgPath); + const resolvedWorkspacePath = path.resolve(workspacePath); + if (fs.existsSync(resolvedPath) && isPathInside(resolvedPath, resolvedWorkspacePath)) { + return await checkIsBallerinaPackage(Uri.file(resolvedPath)); + } + } + const resolvedPath = path.resolve(workspacePath, pkgPath); + const resolvedWorkspacePath = path.resolve(workspacePath); + if (fs.existsSync(resolvedPath) && isPathInside(resolvedPath, resolvedWorkspacePath)) { + return await checkIsBallerinaPackage(Uri.file(resolvedPath)); + } + return false; + }) + ); + return packagePaths.filter((_, index) => results[index]); +} + +function isPathInside(childPath: string, parentPath: string): boolean { + const relative = path.relative(parentPath, childPath); + return !relative.startsWith('..') && !path.isAbsolute(relative); } diff --git a/workspaces/common-libs/rpc-generator/package-lock.json b/workspaces/common-libs/rpc-generator/package-lock.json deleted file mode 100644 index 743bdc933fb..00000000000 --- a/workspaces/common-libs/rpc-generator/package-lock.json +++ /dev/null @@ -1,331 +0,0 @@ -{ - "name": "rpc-generator", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "rpc-generator", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@ts-morph/common": "^0.27.0", - "ts-morph": "^26.0.0" - }, - "devDependencies": { - "@types/node": "^22.15.24" - } - }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ts-morph/common": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz", - "integrity": "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==", - "license": "MIT", - "dependencies": { - "fast-glob": "^3.3.3", - "minimatch": "^10.0.1", - "path-browserify": "^1.0.1" - } - }, - "node_modules/@types/node": { - "version": "22.18.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.0.tgz", - "integrity": "sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/code-block-writer": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", - "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "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.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.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==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.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==", - "license": "MIT", - "engines": { - "node": ">=0.12.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==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "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==", - "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/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.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==", - "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/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==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-morph": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-26.0.0.tgz", - "integrity": "sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==", - "license": "MIT", - "dependencies": { - "@ts-morph/common": "~0.27.0", - "code-block-writer": "^13.0.3" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/workspaces/mi/mi-core/src/rpc-types/ai-panel/index.ts b/workspaces/mi/mi-core/src/rpc-types/ai-panel/index.ts index d22519e7897..8f971233928 100644 --- a/workspaces/mi/mi-core/src/rpc-types/ai-panel/index.ts +++ b/workspaces/mi/mi-core/src/rpc-types/ai-panel/index.ts @@ -18,9 +18,14 @@ import { GenerateSuggestionsRequest, GenerateSuggestionsResponse, - GetBackendRootUrlResponse, GenerateCodeRequest, GenerateCodeResponse, - AbortCodeGenerationResponse + AbortCodeGenerationResponse, + GenerateUnitTestRequest, GenerateUnitTestResponse, + GenerateUnitTestCaseRequest, GenerateUnitTestCaseResponse, + ProcessIdpRequest, ProcessIdpResponse, + FillIdpSchemaRequest, FillIdpSchemaResponse, + DmcToTsRequest, DmcToTsResponse, + AutoFillFormRequest, AutoFillFormResponse } from "./types"; // Export types for external use @@ -32,32 +37,74 @@ export type { AbortCodeGenerationResponse, CodeGenerationEvent, XmlCodeEntry, - CorrectedCodeItem + CorrectedCodeItem, + GenerateUnitTestRequest, + GenerateUnitTestResponse, + GenerateUnitTestCaseRequest, + GenerateUnitTestCaseResponse, + ProcessIdpRequest, + ProcessIdpResponse, + FillIdpSchemaRequest, + FillIdpSchemaResponse, + DmcToTsRequest, + DmcToTsResponse, + AutoFillFormRequest, + AutoFillFormResponse } from './types'; export interface MIAIPanelAPI { // ================================== // General Functions // ================================== - getBackendRootUrl: () => Promise generateSuggestions: (request: GenerateSuggestionsRequest) => Promise generateCode: (request: GenerateCodeRequest) => Promise abortCodeGeneration: () => Promise - + // ================================== // API Key Management // ================================== - setAnthropicApiKey: () => Promise hasAnthropicApiKey: () => Promise + + // ================================== + // Usage Management + // ================================== + fetchUsage: () => Promise<{ max_usage: number; remaining_tokens: number; time_to_reset: number } | undefined> + + // ================================== + // Unit Test Generation + // ================================== + generateUnitTest: (request: GenerateUnitTestRequest) => Promise + generateUnitTestCase: (request: GenerateUnitTestCaseRequest) => Promise + + // ================================== + // IDP (Intelligent Document Processor) + // ================================== + processIdp: (request: ProcessIdpRequest) => Promise + fillIdpSchema: (request: FillIdpSchemaRequest) => Promise + + // ================================== + // DMC to TypeScript Conversion + // ================================== + dmcToTs: (request: DmcToTsRequest) => Promise + + // ================================== + // Auto-Fill Form + // ================================== + autoFillForm: (request: AutoFillFormRequest) => Promise } // Export RPC type definitions export { - getBackendRootUrl, generateSuggestions, generateCode, abortCodeGeneration, codeGenerationEvent, - setAnthropicApiKey, - hasAnthropicApiKey + hasAnthropicApiKey, + fetchUsage, + generateUnitTest, + generateUnitTestCase, + processIdp, + fillIdpSchema, + dmcToTs, + autoFillForm } from './rpc-type'; diff --git a/workspaces/mi/mi-core/src/rpc-types/ai-panel/rpc-type.ts b/workspaces/mi/mi-core/src/rpc-types/ai-panel/rpc-type.ts index 75219ee2c59..3f21cc75467 100644 --- a/workspaces/mi/mi-core/src/rpc-types/ai-panel/rpc-type.ts +++ b/workspaces/mi/mi-core/src/rpc-types/ai-panel/rpc-type.ts @@ -19,20 +19,40 @@ import { RequestType, NotificationType } from "vscode-messenger-common"; import { GenerateSuggestionsRequest, GenerateSuggestionsResponse, - GetBackendRootUrlResponse, GenerateCodeRequest, GenerateCodeResponse, AbortCodeGenerationResponse, - CodeGenerationEvent + CodeGenerationEvent, + GenerateUnitTestRequest, GenerateUnitTestResponse, + GenerateUnitTestCaseRequest, GenerateUnitTestCaseResponse, + ProcessIdpRequest, ProcessIdpResponse, + FillIdpSchemaRequest, FillIdpSchemaResponse, + DmcToTsRequest, DmcToTsResponse, + AutoFillFormRequest, AutoFillFormResponse } from "./types"; const _prefix = "mi-ai-panel"; -export const getBackendRootUrl: RequestType = { method: `${_prefix}/getBackendRootUrl` }; export const generateSuggestions: RequestType = { method: `${_prefix}/generateSuggestions` }; export const generateCode: RequestType = { method: `${_prefix}/generateCode` }; export const abortCodeGeneration: RequestType = { method: `${_prefix}/abortCodeGeneration` }; -export const setAnthropicApiKey: RequestType = { method: `${_prefix}/setAnthropicApiKey` }; export const hasAnthropicApiKey: RequestType = { method: `${_prefix}/hasAnthropicApiKey` }; +export const fetchUsage: RequestType = { method: `${_prefix}/fetchUsage` }; + +// Unit test generation methods +export const generateUnitTest: RequestType = { method: `${_prefix}/generateUnitTest` }; +export const generateUnitTestCase: RequestType = { method: `${_prefix}/generateUnitTestCase` }; + +// IDP (Intelligent Document Processor) method +export const processIdp: RequestType = { method: `${_prefix}/processIdp` }; + +// IDP schema filling method +export const fillIdpSchema: RequestType = { method: `${_prefix}/fillIdpSchema` }; + +// DMC to TypeScript conversion method +export const dmcToTs: RequestType = { method: `${_prefix}/dmcToTs` }; + +// Auto-fill form method +export const autoFillForm: RequestType = { method: `${_prefix}/autoFillForm` }; // Notification for streaming events export const codeGenerationEvent: NotificationType = { method: `${_prefix}/codeGenerationEvent` }; diff --git a/workspaces/mi/mi-core/src/rpc-types/ai-panel/types.ts b/workspaces/mi/mi-core/src/rpc-types/ai-panel/types.ts index 9d5cf677f37..70578afdf47 100644 --- a/workspaces/mi/mi-core/src/rpc-types/ai-panel/types.ts +++ b/workspaces/mi/mi-core/src/rpc-types/ai-panel/types.ts @@ -57,7 +57,7 @@ export interface AbortCodeGenerationResponse { } // Event types for streaming -export type CodeGenerationEventType = +export type CodeGenerationEventType = | "code_generation_start" | "content_block" | "code_generation_end" @@ -65,7 +65,8 @@ export type CodeGenerationEventType = | "code_diagnostic_end" | "messages" | "error" - | "stop"; + | "stop" + | "aborted"; export interface CodeGenerationEvent { type: CodeGenerationEventType; @@ -76,6 +77,7 @@ export interface CodeGenerationEvent { command?: string; xmlCodes?: XmlCodeEntry[]; correctedCodes?: CorrectedCodeItem[]; + willRunDiagnostics?: boolean; } // Diagnostics types @@ -98,3 +100,84 @@ export interface CorrectedCodeItem { configuration?: string; code?: string; } + +// Unit Test Generation Types +export interface GenerateUnitTestRequest { + context: string[]; + testFileName: string; + fullContext?: string[]; + pomFile?: string; + externalConnectors?: string[]; +} + +export interface GenerateUnitTestResponse { + response: string; // Markdown response containing unit test and mock services +} + +// Unit Test Case Addition Types +export interface GenerateUnitTestCaseRequest { + context: string[]; + testFileName: string; + testSuiteFile: string; + testCaseDescription: string; + existingMockServices?: string[]; + existingMockServiceNames?: string[]; + fullContext?: string[]; + pomFile?: string; + externalConnectors?: string[]; +} + +export interface GenerateUnitTestCaseResponse { + response: string; // Markdown response containing updated test suite and new mock services +} + +// IDP (Intelligent Document Processor) Types +export interface ProcessIdpRequest { + operation: 'generate' | 'finetune'; + userInput?: string; + jsonSchema?: string; + images?: string[]; // Base64-encoded images +} + +export interface ProcessIdpResponse { + schema: string; // Generated or modified JSON schema +} + +// IDP Schema Filling Types (populate schema with data from images) +export interface FillIdpSchemaRequest { + jsonSchema: string; // Schema to populate + images: string[]; // Base64-encoded images +} + +export interface FillIdpSchemaResponse { + filledData: string; // JSON data matching schema +} + +// DMC to TypeScript Conversion Types +export interface DmcToTsRequest { + dmcContent: string; // DMC (Data Mapping Configuration) file content + tsFile: string; // TypeScript file with interfaces and empty mapFunction +} + +export interface DmcToTsResponse { + mapping: string; // Complete TypeScript file with implemented mapFunction +} + +// Auto-Fill Form Types +export interface AutoFillFormRequest { + payloads?: string[]; // Pre-defined user payloads (JSON structures) + variables?: string[]; // Pre-defined variables + params?: string[]; // Pre-defined parameters + properties?: string[]; // Pre-defined properties + headers?: string[]; // Pre-defined headers + configs?: string[]; // Pre-defined configurations + connection_names?: string[]; // Available connection names (for config_key fields) + form_details?: string; // Schema/structure of the form + current_values: Record; // Current form field values + field_descriptions?: Record; // Field descriptions for schema + question?: string; // Optional user query/instructions (User Prompt Mode) +} + +export interface AutoFillFormResponse { + filled_values: Record; // Filled form values matching input structure +} diff --git a/workspaces/mi/mi-core/src/rpc-types/mi-data-mapper/index.ts b/workspaces/mi/mi-core/src/rpc-types/mi-data-mapper/index.ts index 1cf1076c63f..0e7b4cbcaa6 100644 --- a/workspaces/mi/mi-core/src/rpc-types/mi-data-mapper/index.ts +++ b/workspaces/mi/mi-core/src/rpc-types/mi-data-mapper/index.ts @@ -51,5 +51,4 @@ export interface MIDataMapperAPI { getMappingFromAI: () => void; writeDataMapping: (params: DataMapWriteRequest)=> void; confirmMappingAction: ()=> Promise; - authenticateUser(): Promise; } diff --git a/workspaces/mi/mi-core/src/rpc-types/mi-data-mapper/rpc-type.ts b/workspaces/mi/mi-core/src/rpc-types/mi-data-mapper/rpc-type.ts index 5270c7b0a4f..e872863f644 100644 --- a/workspaces/mi/mi-core/src/rpc-types/mi-data-mapper/rpc-type.ts +++ b/workspaces/mi/mi-core/src/rpc-types/mi-data-mapper/rpc-type.ts @@ -57,4 +57,3 @@ export const getDMDiagnostics: RequestType = { method: `${_preFix}/getMappingFromAI` }; export const writeDataMapping: NotificationType = { method: `${_preFix}/writeDataMapping` }; export const confirmMappingAction: RequestType = { method: `${_preFix}/confirmMappingAction` }; -export const authenticateUser: RequestType = { method: `${_preFix}/authenticateUser` }; diff --git a/workspaces/mi/mi-core/src/rpc-types/mi-diagram/index.ts b/workspaces/mi/mi-core/src/rpc-types/mi-diagram/index.ts index 77a214e84e1..a2a22477709 100644 --- a/workspaces/mi/mi-core/src/rpc-types/mi-diagram/index.ts +++ b/workspaces/mi/mi-core/src/rpc-types/mi-diagram/index.ts @@ -46,7 +46,6 @@ import { ImportProjectResponse, ESBConfigsResponse, HighlightCodeRequest, - AIUserInput, WriteContentToFileRequest, WriteContentToFileResponse, HandleFileRequest, @@ -333,7 +332,6 @@ export interface MiDiagramAPI { createProject: (params: CreateProjectRequest) => Promise; importProject: (params: ImportProjectRequest) => Promise; migrateProject: (params: MigrateProjectRequest) => Promise; - getAIResponse: (params: AIUserInput) => Promise; writeContentToFile: (params: WriteContentToFileRequest) => Promise; handleFileWithFS: (params: HandleFileRequest) => Promise; writeIdpSchemaFileToRegistry: (params: WriteIdpSchemaFileToRegistryRequest) => Promise; diff --git a/workspaces/mi/mi-core/src/rpc-types/mi-diagram/rpc-type.ts b/workspaces/mi/mi-core/src/rpc-types/mi-diagram/rpc-type.ts index acb1076cb21..98e27fe7331 100644 --- a/workspaces/mi/mi-core/src/rpc-types/mi-diagram/rpc-type.ts +++ b/workspaces/mi/mi-core/src/rpc-types/mi-diagram/rpc-type.ts @@ -47,7 +47,6 @@ import { ImportProjectResponse, ESBConfigsResponse, HighlightCodeRequest, - AIUserInput, WriteContentToFileRequest, WriteContentToFileResponse, WriteIdpSchemaFileToRegistryRequest, @@ -337,7 +336,6 @@ export const askOpenAPIDirPath: RequestType = { method: ` export const createProject: RequestType = { method: `${_preFix}/createProject` }; export const importProject: RequestType = { method: `${_preFix}/importProject` }; export const migrateProject: RequestType = { method: `${_preFix}/migrateProject` }; -export const getAIResponse: RequestType = { method: `${_preFix}/getAIResponse` }; export const writeContentToFile: RequestType = { method: `${_preFix}/writeContentToFile` }; export const handleFileWithFS: RequestType = { method: `${_preFix}/handleFileWithFS` }; export const writeIdpSchemaFileToRegistry: RequestType = { method: `${_preFix}/writeIdpSchemaFileToRegistry` }; diff --git a/workspaces/mi/mi-core/src/state-machine-types.ts b/workspaces/mi/mi-core/src/state-machine-types.ts index b08086bc72c..4873e81c436 100644 --- a/workspaces/mi/mi-core/src/state-machine-types.ts +++ b/workspaces/mi/mi-core/src/state-machine-types.ts @@ -109,22 +109,100 @@ export type MachineStateValue = | { ready: 'viewReady' } | { ready: 'viewEditing' } | { ready: 'resolveMissingDependencies' } | { newProject: 'viewReady' }| { environmentSetup: 'viewReady' }; -export type AIMachineStateValue = 'Initialize' | 'loggedOut' | 'Ready' | 'WaitingForLogin' | 'Executing' | 'updateExtension' | 'disabled' | 'notSupported'; +export type AIMachineStateValue = + | 'Initialize' // (checking auth, first load) + | 'Unauthenticated' // (show login window) + | { Authenticating: 'determineFlow' | 'ssoFlow' | 'apiKeyFlow' | 'validatingApiKey' } // hierarchical substates + | 'Authenticated' // (ready, main view) + | 'UsageExceeded' // (free usage quota exceeded, prompt user to set API key) + | 'Disabled' // (optional: if AI Chat is globally unavailable) + | 'NotSupported'; // (workspace not supported) export type PopupMachineStateValue = 'initialize' | 'ready' | { open: 'active' } | { ready: 'reopen' } | { ready: 'notify' } | 'disabled'; export type MiServerRunStatus = 'Running' | 'Stopped'; export enum AI_EVENT_TYPE { + CHECK_AUTH = 'CHECK_AUTH', LOGIN = "LOGIN", + AUTH_WITH_API_KEY = 'AUTH_WITH_API_KEY', + SUBMIT_API_KEY = 'SUBMIT_API_KEY', SIGN_IN_SUCCESS = "SIGN_IN_SUCCESS", LOGOUT = "LOGOUT", + SILENT_LOGOUT = "SILENT_LOGOUT", EXECUTE = "EXECUTE", CLEAR = "CLEAR", CLEAR_PROMPT = "CLEAR_PROMPT", DISPOSE = "DISPOSE", + COMPLETE_AUTH = 'COMPLETE_AUTH', CANCEL = "CANCEL", + CANCEL_LOGIN = 'CANCEL_LOGIN', RETRY = "RETRY", + USAGE_EXCEEDED = "USAGE_EXCEEDED", + USAGE_RESET = "USAGE_RESET", + UPDATE_USAGE = "UPDATE_USAGE", +} + +export type AIMachineEventMap = { + [AI_EVENT_TYPE.CHECK_AUTH]: undefined; + [AI_EVENT_TYPE.LOGIN]: undefined; + [AI_EVENT_TYPE.AUTH_WITH_API_KEY]: undefined; + [AI_EVENT_TYPE.SUBMIT_API_KEY]: { apiKey: string }; + [AI_EVENT_TYPE.SIGN_IN_SUCCESS]: undefined; + [AI_EVENT_TYPE.LOGOUT]: undefined; + [AI_EVENT_TYPE.SILENT_LOGOUT]: undefined; + [AI_EVENT_TYPE.EXECUTE]: undefined; + [AI_EVENT_TYPE.CLEAR]: undefined; + [AI_EVENT_TYPE.CLEAR_PROMPT]: undefined; + [AI_EVENT_TYPE.DISPOSE]: undefined; + [AI_EVENT_TYPE.COMPLETE_AUTH]: undefined; + [AI_EVENT_TYPE.CANCEL]: undefined; + [AI_EVENT_TYPE.CANCEL_LOGIN]: undefined; + [AI_EVENT_TYPE.RETRY]: undefined; + [AI_EVENT_TYPE.USAGE_EXCEEDED]: undefined; + [AI_EVENT_TYPE.USAGE_RESET]: undefined; + [AI_EVENT_TYPE.UPDATE_USAGE]: { usage: any }; +}; + +export type AIMachineSendableEvent = + | { [K in keyof AIMachineEventMap]: AIMachineEventMap[K] extends undefined + ? { type: K } + : { type: K; payload: AIMachineEventMap[K] } + }[keyof AIMachineEventMap]; + +export enum LoginMethod { + MI_INTEL = 'miIntel', + ANTHROPIC_KEY = 'anthropic_key' +} + +interface MIIntelSecrets { + accessToken: string; + refreshToken: string; +} + +interface AnthropicKeySecrets { + apiKey: string; +} + +export type AuthCredentials = + | { + loginMethod: LoginMethod.MI_INTEL; + secrets: MIIntelSecrets; + } + | { + loginMethod: LoginMethod.ANTHROPIC_KEY; + secrets: AnthropicKeySecrets; + }; + +export interface AIUserToken { + token: string; // For MI Intel, this is the access token and for Anthropic, this is the API key +} + +export interface AIMachineContext { + loginMethod?: LoginMethod; + userToken?: AIUserToken; + usage?: AIUserTokens; + errorMessage?: string; } export enum EVENT_TYPE { @@ -201,7 +279,10 @@ export interface AIVisualizerLocation { view?: AI_MACHINE_VIEW | null; initialPrompt?: PromptObject; state?: AIMachineStateValue; - userTokens?: AIUserTokens; + loginMethod?: LoginMethod; + userToken?: AIUserToken; + usage?: AIUserTokens; + errorMessage?: string; } export interface AIUserTokens { diff --git a/workspaces/mi/mi-data-mapper/src/components/DataMapper/Header/AIMapButton.tsx b/workspaces/mi/mi-data-mapper/src/components/DataMapper/Header/AIMapButton.tsx index c7ab6b1199d..5ca1db43e3a 100644 --- a/workspaces/mi/mi-data-mapper/src/components/DataMapper/Header/AIMapButton.tsx +++ b/workspaces/mi/mi-data-mapper/src/components/DataMapper/Header/AIMapButton.tsx @@ -24,6 +24,7 @@ import { useVisualizerContext } from '@wso2/mi-rpc-client'; interface AIMapButtonProps { onClick: () => void; isLoading: boolean; + disabled?: boolean; } const ButtonContainer = styled.div` @@ -47,8 +48,8 @@ const StyledButton = styled(Button) <{ isLoading: boolean }>` min-width: 80px; `; -const AIMapButton: React.FC = ({ onClick, isLoading }) => { - var [remaingTokenLessThanOne, setRemainingTokenLessThanOne] = useState(false); +const AIMapButton: React.FC = ({ onClick, isLoading, disabled = false }) => { + var [remainingTokenLessThanOne, setRemainingTokenLessThanOne] = useState(false); var [remainingTokenPercentage, setRemainingTokenPercentage] = useState(""); const { rpcClient } = useVisualizerContext(); @@ -82,7 +83,7 @@ const AIMapButton: React.FC = ({ onClick, isLoading }) => { }); }, []); - var tokenUsageText = remainingTokenPercentage === 'Unlimited' ? remainingTokenPercentage : (remaingTokenLessThanOne ? '<1%' : `${remainingTokenPercentage}%`); + var tokenUsageText = remainingTokenPercentage === 'Unlimited' ? remainingTokenPercentage : (remainingTokenLessThanOne ? '<1%' : `${remainingTokenPercentage}%`); return ( @@ -90,11 +91,11 @@ const AIMapButton: React.FC = ({ onClick, isLoading }) => { appearance="secondary" tooltip={`Generate Mapping using AI.\nRemaining Free Usage: ${tokenUsageText}`} onClick={async () => { - if (!isLoading) { + if (!isLoading && !disabled) { await onClick(); } }} - disabled={isLoading} + disabled={isLoading || disabled} isLoading={isLoading} >
diff --git a/workspaces/mi/mi-data-mapper/src/components/DataMapper/Header/DataMapperHeader.tsx b/workspaces/mi/mi-data-mapper/src/components/DataMapper/Header/DataMapperHeader.tsx index 9a23fae8852..27ad7caa148 100644 --- a/workspaces/mi/mi-data-mapper/src/components/DataMapper/Header/DataMapperHeader.tsx +++ b/workspaces/mi/mi-data-mapper/src/components/DataMapper/Header/DataMapperHeader.tsx @@ -30,6 +30,7 @@ import { DataMapWriteRequest } from "@wso2/mi-core"; import { FunctionDeclaration } from "ts-morph"; import { DMType } from "@wso2/mi-core"; import { doesMappingExist } from "../../../index"; +import { hasFields } from "../../Diagram/utils/node-utils"; export interface DataMapperHeaderProps { fnST: FunctionDeclaration; @@ -53,18 +54,19 @@ export function DataMapperHeader(props: DataMapperHeaderProps) { const { filePath, views, switchView, hasEditDisabled, onClose, applyModifications, onDataMapButtonClick: onDataMapClick, onDataMapClearClick: onClear, setIsLoading, isLoading, setIsMapping, isMapping, fnST, inputTrees, outputTree } = props; const { rpcClient } = useVisualizerContext(); + // Check if both input and output schemas have valid fields + // Note: [].every() returns true, so we must check length > 0 first + const hasValidSchemas = inputTrees.length > 0 && inputTrees.every((tree) => hasFields(tree)) && hasFields(outputTree); + const handleDataMapButtonClick = async () => { try { let mappingExist = doesMappingExist(fnST, inputTrees, outputTree), choice; if (mappingExist) { choice = await rpcClient.getMiDataMapperRpcClient().confirmMappingAction(); } - if (!mappingExist || choice) { + console.log("valid schemas:", hasValidSchemas); + if ((!mappingExist || choice) && hasValidSchemas) { props.setIsLoading(true); - let authstatus = await rpcClient.getMiDataMapperRpcClient().authenticateUser(); - if (authstatus === false) { - return; - } props.setIsMapping(true); await rpcClient.getMiDataMapperRpcClient().getMappingFromAI(); } @@ -102,6 +104,7 @@ export function DataMapperHeader(props: DataMapperHeaderProps) { { + const nextKey = + key === "configKey" ? "config_key" : + key === "isExpression" ? "is_expression" : + key === "insertText" ? "insert_text" : + key; + return [nextKey, remapKeys(val)]; + }) + ); + } + return value; +} + +function remapKeysReverse(value: any): any { + if (Array.isArray(value)) { + return value.map(remapKeysReverse); + } + if (value && typeof value === "object") { + return Object.fromEntries( + Object.entries(value).map(([key, val]) => { + const nextKey = + key === "config_key" ? "configKey" : + key === "is_expression" ? "isExpression" : + key === "insert_text" ? "insertText" : + key; + return [nextKey, remapKeysReverse(val)]; + }) + ); + } + return value; +} + export function FormGenerator(props: FormGeneratorProps) { const { rpcClient } = useVisualizerContext(); const sidePanelContext = React.useContext(SidePanelContext); @@ -404,13 +447,6 @@ export function FormGenerator(props: FormGeneratorProps) { }; const handleGenerateAi = async () => { - let token: any; - try { - token = await rpcClient.getMiDiagramRpcClient().getUserAccessToken(); - } catch (error) { - rpcClient.getMiDiagramRpcClient().executeCommand({ commands: ["MI.openAiPanel"] }).catch(console.error); - throw new Error("User not authenticated"); - } try { setGeneratedFormDetails(null); setIsAutoFillBtnClicked(false); @@ -469,37 +505,45 @@ export function FormGenerator(props: FormGeneratorProps) { form_description: formData.doc || "", }; const values = getValues(); - const base_url = (await rpcClient.getMiDiagramRpcClient().getBackendRootUrl()).url; - const response = await fetch(`${base_url}/form/auto-fill`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${token.token}` - }, - body: JSON.stringify({ - payloads, - variables, - params, - properties, - headers, - configs, - current_values: values, - form_details: formDetails, - connection_names: connectionNames, - question: currentInput, - feild_descriptions: fieldDescriptions, - }).replaceAll("insertText", "insert_text").replaceAll("configKey", "config_key").replaceAll("isExpression", "is_expression"), + + // Convert helper pane data to JSON strings with structural key remapping + // Convert payload format: insertText → insert_text (structurally, not via string replacement) + // Only serialize if the source data exists (avoid sending [null]) + const convertedPayloadsStr = payloads ? JSON.stringify(remapKeys(payloads)) : undefined; + const convertedVariablesStr = variables ? JSON.stringify(variables) : undefined; + const convertedParamsStr = params ? JSON.stringify(params) : undefined; + const convertedPropertiesStr = properties ? JSON.stringify(properties) : undefined; + const convertedHeadersStr = headers ? JSON.stringify(headers) : undefined; + const convertedConfigsStr = configs ? JSON.stringify(configs) : undefined; + + // Convert current values format: configKey → config_key, isExpression → is_expression + const convertedValues = remapKeys(values); + + // Flatten connectionNames object to array of all connection names + const allConnectionNames = Object.values(connectionNames).flat(); + + // Call RPC method instead of backend API + // Only include arrays when the corresponding JSON string exists + const response = await rpcClient.getMiAiPanelRpcClient().autoFillForm({ + payloads: convertedPayloadsStr ? [convertedPayloadsStr] : undefined, + variables: convertedVariablesStr ? [convertedVariablesStr] : undefined, + params: convertedParamsStr ? [convertedParamsStr] : undefined, + properties: convertedPropertiesStr ? [convertedPropertiesStr] : undefined, + headers: convertedHeadersStr ? [convertedHeadersStr] : undefined, + configs: convertedConfigsStr ? [convertedConfigsStr] : undefined, + current_values: convertedValues, + form_details: JSON.stringify(formDetails), + connection_names: allConnectionNames, + question: currentInput, + field_descriptions: fieldDescriptions, }); - if (!response.ok) { - throw new Error("Failed to fetch suggestion"); - } - const responseData = await response.json(); - if (!responseData.suggestion) { - throw new Error("No valid suggestion found"); - } - if (generatedFormDetails !== responseData.suggestion) { - const result = JSON.stringify(responseData.suggestion).replaceAll("is_expression", "isExpression"); - setGeneratedFormDetails(JSON.parse(result)); + + // Convert response back: is_expression → isExpression, config_key → configKey + if (response.filled_values) { + const result = remapKeysReverse(response.filled_values); + setGeneratedFormDetails(result); + } else { + throw new Error("No filled values returned from auto-fill"); } } catch (error) { console.error("Error in handleGenerateAi:", error); diff --git a/workspaces/mi/mi-extension/.env.example b/workspaces/mi/mi-extension/.env.example index c6cf4955535..419d87991dd 100644 --- a/workspaces/mi/mi-extension/.env.example +++ b/workspaces/mi/mi-extension/.env.example @@ -1,11 +1,8 @@ MI_AUTH_ORG= MI_AUTH_CLIENT_ID= -MI_COPILOT_BACKEND=https://e95488c8-8511-4882-967f-ec3ae2a0f86f-dev.e1-us-east-azure.choreoapis.dev/miaideployments/micopilot/mi-copilot-backend-be2/v1.0 -MI_COPILOT_BACKEND_V2=https://e95488c8-8511-4882-967f-ec3ae2a0f86f-prod.e1-us-east-azure.choreoapis.dev/miaideployments/micopilot/mi-copilot-backend-be2/v2.0 -MI_COPILOT_BACKEND_V3=https://e95488c8-8511-4882-967f-ec3ae2a0f86f-prod.e1-us-east-azure.choreoapis.dev/miaideployments/micopilot/mi-copilot-backend-be2/v3 MI_COPILOT_FEEDBACK=https://e95488c8-8511-4882-967f-ec3ae2a0f86f-prod.e1-us-east-azure.choreoapis.dev/miaideployments/copilot-analytics/v1.0 MI_COPILOT_OPENAI_PROXY_URL=https://e95488c8-8511-4882-967f-ec3ae2a0f86f-prod.e1-us-east-azure.choreoapis.dev/miaideployments/ai-proxy/v1.0 -MI_COPILOT_ANTHROPIC_PROXY_URL=https://e95488c8-8511-4882-967f-ec3ae2a0f86f-prod.e1-us-east-azure.choreoapis.dev/miaideployments/ai-proxy/mi-copilot-anthropic-proxy-v1/v1.0 +MI_COPILOT_ANTHROPIC_PROXY_URL=https://e95488c8-8511-4882-967f-ec3ae2a0f86f-prod.e1-us-east-azure.choreoapis.dev/miaideployments/ai-proxy/v1.0 MI_AUTH_REDIRECT_URL=https://mi-copilot.wso2.com/vscode-auth MI_UPDATE_VERSION_CHECK_URL=https://mi-distribution.wso2.com/versions.json MI_SAMPLE_ICONS_GITHUB_URL=https://raw.githubusercontent.com/wso2/integration-studio/main/SamplesForVSCode/icons/ diff --git a/workspaces/mi/mi-extension/package.json b/workspaces/mi/mi-extension/package.json index c211615f305..b75e950fbd1 100644 --- a/workspaces/mi/mi-extension/package.json +++ b/workspaces/mi/mi-extension/package.json @@ -967,80 +967,86 @@ "postbuild": "pnpm run copyVSIX" }, "devDependencies": { - "@types/vscode": "^1.100.0", + "@playwright/test": "~1.55.1", "@types/mocha": "^10.0.1", "@types/node": "22.15.21", + "@types/vscode": "^1.100.0", "@typescript-eslint/eslint-plugin": "^8.32.1", "@typescript-eslint/parser": "^8.32.1", + "@vscode/test-electron": "^2.5.2", + "@wso2/mi-data-mapper-utils": "workspace:*", + "@wso2/mi-diagram": "workspace:*", + "@wso2/mi-rpc-client": "workspace:*", + "@wso2/mi-visualizer": "workspace:*", + "@wso2/wso2-platform-core": "workspace:*", + "await-notify": "^1.0.1", "eslint": "^9.27.0", "glob": "^11.0.2", "mocha": "^11.4.0", - "typescript": "5.8.3", + "playwright-core": "~1.55.1", + "rimraf": "~6.0.1", "ts-loader": "^9.5.2", + "ts-morph": "^26.0.0", + "typescript": "5.8.3", "webpack": "^5.89.0", "webpack-cli": "^4.10.0", - "@vscode/test-electron": "^2.5.2", - "rimraf": "~6.0.1", - "@wso2/mi-visualizer": "workspace:*", - "@wso2/mi-rpc-client": "workspace:*", - "@wso2/mi-data-mapper-utils": "workspace:*", - "@wso2/mi-diagram": "workspace:*", - "@wso2/wso2-platform-core": "workspace:*", - "ts-morph": "^26.0.0", - "await-notify": "^1.0.1", - "yaml": "~2.8.0", - "@playwright/test": "~1.55.1", - "playwright-core": "~1.55.1" + "yaml": "~2.8.0" }, "dependencies": { - "copyfiles": "^2.4.1", + "@ai-sdk/anthropic": "^2.0.35", + "@apidevtools/json-schema-ref-parser": "12.0.2", + "@babel/core": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1", + "@iarna/toml": "^2.2.5", + "ai": "^5.0.76", + "@types/fs-extra": "~11.0.4", + "@types/handlebars": "^4.1.0", + "@types/json-schema": "7.0.15", + "@types/lodash": "~4.17.17", + "@types/mustache": "~4.2.6", + "@types/tmp": "~0.2.6", + "@types/xml2js": "~0.4.12", "@vscode/vsce": "~3.4.2", - "vscode-messenger": "^0.5.1", - "vscode-messenger-common": "^0.5.1", + "@wso2/font-wso2-vscode": "workspace:*", "@wso2/mi-core": "workspace:*", + "@wso2/mi-diagram": "workspace:*", "@wso2/mi-rpc-client": "workspace:*", + "@wso2/mi-syntax-tree": "workspace:*", "@wso2/playwright-vscode-tester": "workspace:*", - "vscode-extension-tester": "~8.14.1", - "vscode-languageclient": "^9.0.1", - "@types/xml2js": "~0.4.12", - "jsonix": "~3.0.0", - "lodash": "~4.17.21", + "adm-zip": "~0.5.16", "axios": "~1.12.0", - "@types/lodash": "~4.17.17", - "vscode-languageserver-protocol": "^3.17.5", + "copyfiles": "^2.4.1", + "cors-anywhere": "^0.4.4", + "dotenv": "~16.5.0", "fast-xml-parser": "~5.2.3", - "@wso2/mi-syntax-tree": "workspace:*", - "@wso2/font-wso2-vscode": "workspace:*", - "xstate": "^4.38.3", - "node-fetch": "~3.3.2", + "find-process": "~1.4.10", + "fs-extra": "~11.3.0", + "handlebars": "^4.7.8", + "json-schema": "0.4.0", + "json-schema-to-ts": "3.1.1", + "jsonix": "~3.0.0", + "jwt-decode": "^4.0.0", + "lodash": "~4.17.21", "mustache": "~4.2.0", - "@types/mustache": "~4.2.6", - "uuid": "~11.1.0", + "node-fetch": "~3.3.2", + "node-loader": "~2.1.0", + "portfinder": "^1.0.37", + "recast": "^0.23.11", + "tmp": "~0.2.4", + "to-json-schema": "0.2.5", + "tree-kill": "~1.2.2", "unzipper": "~0.12.3", + "upath": "~2.0.1", + "uuid": "~11.1.0", "vscode-debugadapter": "^1.51.0", "vscode-debugprotocol": "^1.51.0", - "tree-kill": "~1.2.2", - "node-loader": "~2.1.0", - "to-json-schema": "0.2.5", + "vscode-extension-tester": "~8.14.1", + "vscode-languageclient": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-messenger": "^0.5.1", + "vscode-messenger-common": "^0.5.1", "xml2js": "0.6.2", - "tmp": "~0.2.4", - "fs-extra": "~11.3.0", - "@iarna/toml": "^2.2.5", - "@types/tmp": "~0.2.6", - "@types/fs-extra": "~11.0.4", - "json-schema-to-ts": "3.1.1", - "@babel/plugin-transform-typescript": "^7.27.1", - "recast": "^0.23.11", - "@babel/core": "^7.27.1", - "@types/json-schema": "7.0.15", - "json-schema": "0.4.0", - "portfinder": "^1.0.37", - "cors-anywhere": "^0.4.4", - "@apidevtools/json-schema-ref-parser": "12.0.2", - "adm-zip": "~0.5.16", - "@wso2/mi-diagram": "workspace:*", - "upath": "~2.0.1", - "find-process": "~1.4.10", - "dotenv": "~16.5.0" + "xstate": "^4.38.3", + "zod": "^3.24.1" } } diff --git a/workspaces/mi/mi-extension/src/RPCLayer.ts b/workspaces/mi/mi-extension/src/RPCLayer.ts index e154bf23d31..10727240ead 100644 --- a/workspaces/mi/mi-extension/src/RPCLayer.ts +++ b/workspaces/mi/mi-extension/src/RPCLayer.ts @@ -55,7 +55,7 @@ export class RPCLayer { registerMIAiPanelRpcHandlers(messenger, projectUri); // ----- AI Webview RPC Methods messenger.onRequest(getAIVisualizerState, () => getAIContext()); - messenger.onRequest(sendAIStateEvent, (event: AI_EVENT_TYPE) => StateMachineAI.sendEvent(event)); + messenger.onRequest(sendAIStateEvent, (event: any) => StateMachineAI.sendEvent(event)); // ----- Form Views RPC Methods messenger.onRequest(getPopupVisualizerState, () => getFormContext(projectUri)); @@ -102,11 +102,8 @@ async function getContext(projectUri: string): Promise { env: { MI_AUTH_ORG: process.env.MI_AUTH_ORG || '', MI_AUTH_CLIENT_ID: process.env.MI_AUTH_CLIENT_ID || '', - MI_COPILOT_BACKEND_V2: process.env.MI_COPILOT_BACKEND_V2 || '', - MI_COPILOT_BACKEND_V3: process.env.MI_COPILOT_BACKEND_V3 || '', MI_AUTH_REDIRECT_URL: process.env.MI_AUTH_REDIRECT_URL || '', MI_UPDATE_VERSION_CHECK_URL: process.env.MI_UPDATE_VERSION_CHECK_URL || '', - MI_COPILOT_BACKEND: process.env.MI_COPILOT_BACKEND || '', MI_SAMPLE_ICONS_GITHUB_URL: process.env.MI_SAMPLE_ICONS_GITHUB_URL || '', MI_CONNECTOR_STORE: process.env.MI_CONNECTOR_STORE || '', MI_CONNECTOR_STORE_BACKEND: process.env.MI_CONNECTOR_STORE_BACKEND || '', @@ -122,7 +119,14 @@ async function getContext(projectUri: string): Promise { async function getAIContext(): Promise { const context = StateMachineAI.context(); return new Promise((resolve) => { - resolve({ view: context.view, initialPrompt: extension.initialPrompt, state: StateMachineAI.state(), userTokens: context.userTokens }); + resolve({ + initialPrompt: extension.initialPrompt, + state: StateMachineAI.state(), + loginMethod: context.loginMethod, + userToken: context.userToken, + usage: context.usage, + errorMessage: context.errorMessage + }); }); } diff --git a/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts b/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts index ef75dd02ef8..800340758b0 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts @@ -19,305 +19,436 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { createMachine, assign, interpret } from 'xstate'; import * as vscode from 'vscode'; -import { EVENT_TYPE, AIVisualizerLocation, AIMachineStateValue, AI_EVENT_TYPE, AIUserTokens } from '@wso2/mi-core'; +import { AIMachineStateValue, AIMachineContext, AI_EVENT_TYPE, AIMachineSendableEvent, LoginMethod } from '@wso2/mi-core'; import { AiPanelWebview } from './webview'; -import { getAuthUrl, refreshAuthCode } from './auth'; import { extension } from '../MIExtensionContext'; -import fetch from 'node-fetch'; -import { log } from '../util/logger'; +import { getAccessToken, getLoginMethod, checkToken, initiateInbuiltAuth, logout, validateApiKey } from './auth'; import { PromptObject } from '@wso2/mi-core'; -interface ChatEntry { - role: string; - content: string; - errorCode?: string; -} - -interface UserToken { - token?: string; - userToken?: AIUserTokens; -} +export const openAIWebview = (initialPrompt?: PromptObject) => { + extension.initialPrompt = initialPrompt; + if (!AiPanelWebview.currentPanel) { + AiPanelWebview.currentPanel = new AiPanelWebview(); + } else { + AiPanelWebview.currentPanel!.getWebview()?.reveal(); + } +}; -interface AiMachineContext extends AIVisualizerLocation { - token: string | undefined; - errorMessage?: string; - errorCode?: string; - chatLog: ChatEntry[]; -} +export const closeAIWebview = () => { + if (AiPanelWebview.currentPanel) { + AiPanelWebview.currentPanel.dispose(); + AiPanelWebview.currentPanel = undefined; + } +}; -const aiStateMachine = createMachine({ - /** @xstate-layout N4IgpgJg5mDOIC5QFsCWBaAhqgdASQDtUAXVTAG1QC8wBiCAewLB1QIDcGBrFtLXQiTKUaCNpwDGmUkwDaABgC6CxYlAAHBrCFM1IAB6IATEYAsOAJwBmAOwBWeaZs2rFm-Lt2ANCACeiGwA2QJw7KysjeXkLAA4Y0wt5AEYAXxSfPmx8IlIKajpGZlYObl4MLMFckTAxEqkZAhVZJNUkEE1tBr1DBCMLEJsY+3lA0zshmLsbH38ECz6cd3k+1xikvs80jPKBHOF82jAAJyOGI5x1cmkAMzPkHEzdoTzRcQZ61DklFT0OnQJuohAjFFjYkuEostAvIYvJpn5EEk4fJLKZbEYhs4nEYtiBHjhyAwoDAIAB5ACuxFoABlSQBxPAAOR+bT+XTaPQsphRySSozsJmSgQs3gRCCSSXioJFkqspjMphiOPSeJ2OAASmBMBBfDT6aSAKoAFRZGi0-0BCDsiRwRkCfLWgRcSRcMRmASsdhw0SmNjMGLlcVx+M12t1AFEABrhgDCxvDpva5vZoB663coKMdmhdklNic7qtGO9SXlTmdGPBwbVoZ1tBj1PDAEF1Ym2Z8ARzEeEQaYkhZYn35BEYoFC36QtFEnCjEkwn3ldt+DgAOrYUgEKAAMTO1KJbHoTBYbx4DzVa6Em53Rz3UDYtUk0g7TSUv2THctSO53qcMUSVmhNFwkLIYBnGRIkSmcsbGrZcLw3bdd33AhDhOM4LiuYhbiOe58XgthEJvZCH3eJ8vmUV9WXfXQuwQSYLBwJFoTtFwjHCF0QLCSwe36J1hQ8WCsnwq8kLvFCYybRkY3Dak22oztUwCGwGL6Cx+z-F0ISMQsJVnb0ljtQIIjtOxBNwYTCNvA8tybPBqTwcNW0os1Og-WinRU4JImFfohjsUwdKiAZTBzdwrElKYzJwcN9DACRKQI+tSQAWQABUbI0E2cpNXJoxTxSMFxQmUl0EmBEUjMLPsQRiKx4n7cZuRdKKYrihLN1oRz1VJJzWhci1aPWP8cD-ZTPSdMw4kLADzGiExnDUtx+k9FrYvihDaAAZSNUlUrk3KFIMRE+nMYF-L6YVfTGQs+hsYr7FiQratGQJVrajbaTpcMABFDRNbL2zyo7xTlIxFn7FwHCzDx1h0qxHBwe1lLWf0-0cNIVQIBgIDgPRHjfA7LXQeHGLqv0-X8sI1MCbSxVnFFCvsP9mOCBqosqfYaAJgb8vlEDauK3MlmSELFSiwliUgCliG5lNgetBjJTcOcEkxJUdJdFT4cGfMB1U16VRDLUdVltz8sgsGhkVNYpmSKYLELAUwezWEIWhcLlKiiBUFgTAACNyEgU2gbTO7R3TQqxhCmE6o1yZGNqiIRjR0coos68rMOwHDp6NYrEsMw1MmDF+hcQtHtRHMFy5JG3vWgjg5zxEwnzyYYXmEZXBMx2TFCYFh3h93NYxlIgA */ +const aiMachine = createMachine({ id: 'mi-ai', - initial: "checkWorkspace", + initial: 'Initialize', predictableActionArguments: true, context: { - token: undefined, - chatLog: [], - errorCode: undefined, + loginMethod: undefined, + userToken: undefined, + usage: undefined, errorMessage: undefined, }, on: { DISPOSE: { - target: "checkWorkspace", + target: 'Initialize', + actions: assign({ + loginMethod: (_ctx) => undefined, + userToken: (_ctx) => undefined, + usage: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, + }) } }, states: { - checkWorkspace: { + Initialize: { invoke: { - src: "checkWorkspaceSupport", + id: 'checkWorkspaceAndToken', + src: 'checkWorkspaceAndToken', onDone: [ { - cond: (context, event) => event.data === true, - target: "initialize", - } - ], - onError: [ - { - target: 'notSupported', + cond: (_ctx, event) => event.data.workspaceSupported && !!event.data.tokenData, + target: 'Authenticated', + actions: assign({ + loginMethod: (_ctx, event) => event.data.tokenData.loginMethod, + userToken: (_ctx, event) => ({ token: event.data.tokenData.token }), + errorMessage: (_ctx) => undefined, + }) }, - ] - } - }, - notSupported: { - on: { - RETRY: { - target: "checkWorkspace", - }, - LOGOUT: "loggedOut", - } - }, - initialize: { - invoke: { - src: "checkToken", - onDone: [ { - cond: (context, event) => event.data.token !== undefined, // Token is valid - target: "Ready", + cond: (_ctx, event) => event.data.workspaceSupported && !event.data.tokenData, + target: 'Unauthenticated', actions: assign({ - token: (context, event) => event.data.token, - userTokens: (context, event) => event.data.userToken + loginMethod: (_ctx) => undefined, + userToken: (_ctx) => undefined, + usage: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, }) }, { - cond: (context, event) => event.data.token === undefined, // No token found - target: 'loggedOut' + cond: (_ctx, event) => !event.data.workspaceSupported, + target: 'NotSupported', + actions: assign({ + loginMethod: (_ctx) => undefined, + userToken: (_ctx) => undefined, + usage: (_ctx) => undefined, + errorMessage: (_ctx) => 'Multi-root workspace is not supported', + }) } ], onError: [ { - cond: (context, event) => event.data.status === 404, - target: 'updateExtension', + cond: (_ctx, event) => event.data?.message === 'TOKEN_EXPIRED', + target: 'Unauthenticated', + actions: [ + 'silentLogout', + assign({ + loginMethod: (_ctx) => undefined, + userToken: (_ctx) => undefined, + usage: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, + }) + ] }, { - target: 'disabled', - actions: - assign({ - errorCode: (context, event) => event.data + target: 'Disabled', + actions: assign({ + loginMethod: (_ctx) => undefined, + userToken: (_ctx) => undefined, + usage: (_ctx) => undefined, + errorMessage: (_ctx, event) => event.data?.message || 'Unknown error' }) } ] } }, - - loggedOut: { + Unauthenticated: { on: { - LOGIN: { - target: "WaitingForLogin", + [AI_EVENT_TYPE.LOGIN]: { + target: 'Authenticating', + actions: assign({ + loginMethod: (_ctx) => LoginMethod.MI_INTEL + }) + }, + [AI_EVENT_TYPE.AUTH_WITH_API_KEY]: { + target: 'Authenticating', + actions: assign({ + loginMethod: (_ctx) => LoginMethod.ANTHROPIC_KEY + }) } } }, - - Ready: { + Authenticating: { + initial: 'determineFlow', + states: { + determineFlow: { + always: [ + { + cond: (context) => context.loginMethod === LoginMethod.MI_INTEL, + target: 'ssoFlow' + }, + { + cond: (context) => context.loginMethod === LoginMethod.ANTHROPIC_KEY, + target: 'apiKeyFlow' + }, + { + target: 'ssoFlow' // default + } + ] + }, + ssoFlow: { + invoke: { + id: 'openLogin', + src: 'openLogin', + onError: { + target: '#mi-ai.Unauthenticated', + actions: assign({ + loginMethod: (_ctx) => undefined, + errorMessage: (_ctx, event) => event.data?.message || 'SSO authentication failed' + }) + } + }, + on: { + [AI_EVENT_TYPE.COMPLETE_AUTH]: { + target: '#mi-ai.Authenticated', + actions: assign({ + errorMessage: (_ctx) => undefined, + }) + }, + [AI_EVENT_TYPE.SIGN_IN_SUCCESS]: { + target: '#mi-ai.Authenticated', + actions: assign({ + errorMessage: (_ctx) => undefined, + }) + }, + [AI_EVENT_TYPE.CANCEL_LOGIN]: { + target: '#mi-ai.Unauthenticated', + actions: assign({ + loginMethod: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, + }) + }, + [AI_EVENT_TYPE.CANCEL]: { + target: '#mi-ai.Unauthenticated', + actions: assign({ + loginMethod: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, + }) + } + } + }, + apiKeyFlow: { + on: { + [AI_EVENT_TYPE.SUBMIT_API_KEY]: { + target: 'validatingApiKey', + actions: assign({ + errorMessage: (_ctx) => undefined + }) + }, + [AI_EVENT_TYPE.CANCEL_LOGIN]: { + target: '#mi-ai.Unauthenticated', + actions: assign({ + loginMethod: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, + }) + }, + [AI_EVENT_TYPE.CANCEL]: { + target: '#mi-ai.Unauthenticated', + actions: assign({ + loginMethod: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, + }) + } + } + }, + validatingApiKey: { + invoke: { + id: 'validateApiKey', + src: 'validateApiKey', + onDone: { + target: '#mi-ai.Authenticated', + actions: assign({ + errorMessage: (_ctx) => undefined, + }) + }, + onError: { + target: 'apiKeyFlow', + actions: assign({ + errorMessage: (_ctx, event) => event.data?.message || 'API key validation failed' + }) + } + } + } + } + }, + Authenticated: { invoke: { - src: 'getSuggestions', + id: 'getTokenAndLoginMethod', + src: 'getTokenAndLoginMethod', onDone: { - target: "Ready" + actions: assign({ + userToken: (_ctx, event) => ({ token: event.data.token }), + loginMethod: (_ctx, event) => event.data.loginMethod, + errorMessage: (_ctx) => undefined, + }) }, onError: { - target: "Ready", + target: 'Unauthenticated', actions: assign({ - errorCode: (context, event) => event.data + userToken: (_ctx) => undefined, + loginMethod: (_ctx) => undefined, + usage: (_ctx) => undefined, + errorMessage: (_ctx, event) => event.data?.message || 'Failed to retrieve authentication credentials', }) } }, on: { - LOGOUT: "loggedOut", - EXECUTE: "Executing", - CLEAR: { - target: "Ready", + [AI_EVENT_TYPE.LOGOUT]: { + target: 'Unauthenticated', + actions: [ + 'logout', + assign({ + loginMethod: (_) => undefined, + userToken: (_) => undefined, + usage: (_) => undefined, + errorMessage: (_) => undefined, + }) + ] + }, + [AI_EVENT_TYPE.SILENT_LOGOUT]: { + target: 'Unauthenticated', + actions: [ + 'silentLogout', + assign({ + loginMethod: (_) => undefined, + userToken: (_) => undefined, + usage: (_) => undefined, + errorMessage: (_) => undefined, + }) + ] + }, + [AI_EVENT_TYPE.USAGE_EXCEEDED]: { + target: 'UsageExceeded', + actions: assign({ + errorMessage: (_ctx) => 'Your free usage quota has been exceeded. Set your own Anthropic API key to continue.', + }) + }, + [AI_EVENT_TYPE.UPDATE_USAGE]: { + actions: assign({ + usage: (_ctx, event) => event.payload?.usage, + }) } } }, - - disabled: { - invoke: { - src: 'disableExtension' - }, + UsageExceeded: { on: { - RETRY: { - target: "initialize", + [AI_EVENT_TYPE.AUTH_WITH_API_KEY]: { + target: 'Authenticating', + actions: assign({ + loginMethod: (_ctx) => LoginMethod.ANTHROPIC_KEY, + errorMessage: (_ctx) => undefined, + }) }, - LOGOUT: "loggedOut", - } - }, - - WaitingForLogin: { - invoke: { - src: 'openLogin', - onError: { - target: "loggedOut", + [AI_EVENT_TYPE.USAGE_RESET]: { + target: 'Authenticated', + actions: assign({ + errorMessage: (_ctx) => undefined, + }) + }, + [AI_EVENT_TYPE.LOGOUT]: { + target: 'Unauthenticated', + actions: [ + 'logout', + assign({ + loginMethod: (_) => undefined, + userToken: (_) => undefined, + usage: (_) => undefined, + errorMessage: (_) => undefined, + }) + ] + }, + [AI_EVENT_TYPE.UPDATE_USAGE]: { actions: assign({ - errorCode: (context, event) => event.data + usage: (_ctx, event) => event.payload?.usage, }) } - }, - on: { - SIGN_IN_SUCCESS: "Ready", - CANCEL: "loggedOut", - FAILIER: "loggedOut" } }, - - Executing: { + Disabled: { on: { - COMPLETE: "Ready", - ERROR: "Ready", - STOP: "Ready", - LOGEDOUT: "loggedOut" + [AI_EVENT_TYPE.RETRY]: { + target: 'Initialize', + actions: assign({ + userToken: (_ctx) => undefined, + usage: (_ctx) => undefined, + loginMethod: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, + }) + } } }, - - updateExtension: { + NotSupported: { on: { - RETRY: { - target: "initialize", + [AI_EVENT_TYPE.RETRY]: { + target: 'Initialize', + actions: assign({ + userToken: (_ctx) => undefined, + usage: (_ctx) => undefined, + loginMethod: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, + }) + }, + [AI_EVENT_TYPE.LOGOUT]: { + target: 'Unauthenticated', + actions: [ + 'logout', + assign({ + loginMethod: (_) => undefined, + userToken: (_) => undefined, + usage: (_) => undefined, + errorMessage: (_) => undefined, + }) + ] } } } } -}, { - services: { - checkToken: checkToken, - openLogin: openLogin, - checkWorkspaceSupport: checkWorkspaceSupport, - } }); -async function checkWorkspaceSupport(context, event): Promise { - return new Promise((resolve, reject) => { - if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) { - reject(); - } else { - resolve(true); - } - }); -} +const checkWorkspaceAndToken = async (): Promise<{ workspaceSupported: boolean; tokenData?: { token: string; loginMethod: LoginMethod } }> => { + // Check workspace support first + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) { + return { workspaceSupported: false }; + } -async function checkToken(context, event): Promise { - return new Promise(async (resolve, reject) => { - try { - const token = await extension.context.secrets.get('MIAIUser'); - if (token) { - const MI_COPILOT_BACKEND_V2 = process.env.MI_COPILOT_BACKEND_V2 as string; - const url = MI_COPILOT_BACKEND_V2 + '/user/usage'; - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}`, - }, - }); - if (response.ok) { - const responseBody = await response.json() as AIUserTokens | undefined; - resolve({token, userToken: responseBody}); - } else { - if (response.status === 401 || response.status === 403) { - const newToken = await refreshAuthCode(); - if (newToken !=""){ - const tokenFetchResponse = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${newToken}`, - }, - }); - if(tokenFetchResponse.ok){ - const responseBody = await tokenFetchResponse.json() as AIUserTokens | undefined; - resolve({token: newToken, userToken: responseBody}); - }else{ - console.log("Error: " + tokenFetchResponse.statusText); - console.log("Error Code: " + tokenFetchResponse.status); - throw new Error(`Error while checking token: ${tokenFetchResponse.statusText}`); - } - }else{ - resolve({token: undefined, userToken: undefined}); - } - }else if (response.status === 404){ - throw { status: 404, message: 'Resource not found' }; - }else{ - console.log("Error: " + response.statusText); - console.log("Error Code: " + response.status); - throw new Error(`Error while checking token: ${response.statusText}`); - } - } - } else { - resolve({ token: undefined, userToken: undefined }); - } - } catch (error: any) { - log(error.toString()); - reject(error); - } - }); -} + // Then check token + const tokenData = await checkToken(); + return { workspaceSupported: true, tokenData }; +}; -async function openLogin(context, event) { - return new Promise(async (resolve, reject) => { - try { - initiateInbuiltAuth(); - } catch (error) { - reject(error); - } - }); -} +const openLogin = async () => { + const status = await initiateInbuiltAuth(); + if (!status) { + aiStateService.send({ type: AI_EVENT_TYPE.CANCEL_LOGIN }); + } + return status; +}; -async function initiateInbuiltAuth() { - const callbackUri = await vscode.env.asExternalUri( - vscode.Uri.parse(`${vscode.env.uriScheme}://wso2.micro-integrator/signin`) - ); - const oauthURL = await getAuthUrl(callbackUri.toString()); - return vscode.env.openExternal(vscode.Uri.parse(oauthURL)); -} +const validateApiKeyService = async (_context: AIMachineContext, event: any) => { + const apiKey = event.payload?.apiKey; + if (!apiKey) { + throw new Error('API key is required'); + } + return await validateApiKey(apiKey, LoginMethod.ANTHROPIC_KEY); +}; -// Create a service to interpret the machine -export const aiStateService = interpret(aiStateMachine); +const getTokenAndLoginMethod = async () => { + const result = await getAccessToken(); + const loginMethod = await getLoginMethod(); + if (!result || !loginMethod) { + throw new Error('No authentication credentials found'); + } + + return { token: result, loginMethod: loginMethod }; +}; + +const aiStateService = interpret(aiMachine.withConfig({ + services: { + checkWorkspaceAndToken: checkWorkspaceAndToken, + openLogin: openLogin, + validateApiKey: validateApiKeyService, + getTokenAndLoginMethod: getTokenAndLoginMethod, + }, + actions: { + logout: () => { + logout(); + }, + silentLogout: () => { + logout(false); + }, + } +})); + +const isExtendedEvent = ( + arg: K | AIMachineSendableEvent +): arg is Extract => { + return typeof arg !== "string"; +}; -// Define your API as functions export const StateMachineAI = { initialize: () => aiStateService.start(), service: () => { return aiStateService; }, context: () => { return aiStateService.getSnapshot().context; }, state: () => { return aiStateService.getSnapshot().value as AIMachineStateValue; }, - sendEvent: (eventType: AI_EVENT_TYPE) => { aiStateService.send({ type: eventType }); }, -}; - -export function openAIWebview(initialPrompt?: PromptObject) { - extension.initialPrompt = initialPrompt; - if (!AiPanelWebview.currentPanel) { - AiPanelWebview.currentPanel = new AiPanelWebview(); + sendEvent: ( + event: K | Extract + ) => { + if (isExtendedEvent(event)) { + aiStateService.send(event as AIMachineSendableEvent); } else { - AiPanelWebview.currentPanel!.getWebview()?.reveal(); + aiStateService.send({ type: event } as AIMachineSendableEvent); + } } -} - -export function navigateAIView(type: EVENT_TYPE, viewLocation?: AIVisualizerLocation) { - aiStateService.send({ type: type, viewLocation: viewLocation }); -} - -async function checkAiStatus() { - return new Promise((resolve, reject) => { - // Check for AI API status - resolve(true); - }); -} - - +}; diff --git a/workspaces/mi/mi-extension/src/ai-panel/auth.ts b/workspaces/mi/mi-extension/src/ai-panel/auth.ts index c9d8231131f..00134e86f1a 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/auth.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/auth.ts @@ -16,130 +16,395 @@ * under the License. */ +/** + * Unified Authentication Module + * + * This file contains ALL authentication logic for MI Copilot: + * - Credential storage and retrieval + * - OAuth/SSO flow (MI_INTEL login method) + * - API key validation (ANTHROPIC_KEY login method) + * - Token refresh + * - Login/Logout operations + */ + import axios from 'axios'; import { StateMachineAI } from './aiMachine'; -import { AI_EVENT_TYPE, AIUserTokens } from '@wso2/mi-core'; +import { AI_EVENT_TYPE, AIUserToken, AuthCredentials, LoginMethod } from '@wso2/mi-core'; import { extension } from '../MIExtensionContext'; import * as vscode from 'vscode'; -import fetch from 'node-fetch'; -import { MiDiagramRpcManager } from '../rpc-managers/mi-diagram/rpc-manager'; - -export interface AccessToken { - accessToken: string; - expirationTime?: number; - loginTime: string; - refreshToken?: string; -} +import { jwtDecode, JwtPayload } from 'jwt-decode'; +import { createAnthropic } from '@ai-sdk/anthropic'; +import { generateText } from 'ai'; +import { logInfo, logWarn, logError } from './copilot/logger'; + +// ================================== +// Configuration +// ================================== +const config = vscode.workspace.getConfiguration('MI'); +const AUTH_ORG = process.env.MI_AUTH_ORG || config.get('authOrg') as string; +const AUTH_CLIENT_ID = process.env.MI_AUTH_CLIENT_ID || config.get('authClientID') as string; +const MI_AUTH_REDIRECT_URL = process.env.MI_AUTH_REDIRECT_URL || config.get('authRedirectURL') as string; const CommonReqHeaders = { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf8', 'Accept': 'application/json' }; -const config = vscode.workspace.getConfiguration('MI'); -const AUTH_ORG = process.env.MI_AUTH_ORG || config.get('authOrg') as string; -const AUTH_CLIENT_ID = process.env.MI_AUTH_CLIENT_ID || config.get('authClientID') as string; -const MI_AUTH_REDIRECT_URL = process.env.MI_AUTH_REDIRECT_URL || config.get('authRedirectURL') as string; +// Credential storage key +const AUTH_CREDENTIALS_SECRET_KEY = 'MIAuthCredentials'; -export async function getAuthUrl(callbackUri: string): Promise { +// Legacy keys (for migration) +const LEGACY_ACCESS_TOKEN_SECRET_KEY = 'MIAIUser'; +const LEGACY_REFRESH_TOKEN_SECRET_KEY = 'MIAIRefreshToken'; - // return `${this._config.loginUrl}?profile=vs-code&client_id=${this._config.clientId}` - // + `&state=${stateBase64}&code_challenge=${this._challenge.code_challenge}`; +// ================================== +// Credential Storage (Core) +// ================================== - const state = encodeURIComponent(btoa(JSON.stringify({ callbackUri: 'vscode://wso2.micro-integrator/signin' }))); - return `https://api.asgardeo.io/t/${AUTH_ORG}/oauth2/authorize?response_type=code&redirect_uri=${MI_AUTH_REDIRECT_URL}&client_id=${AUTH_CLIENT_ID}&scope=openid%20email&state=${state}`; -} +/** + * Store authentication credentials in VSCode secrets + */ +export const storeAuthCredentials = async (credentials: AuthCredentials): Promise => { + const credentialsJson = JSON.stringify(credentials); + await extension.context.secrets.store(AUTH_CREDENTIALS_SECRET_KEY, credentialsJson); +}; + +/** + * Retrieve authentication credentials from VSCode secrets + */ +export const getAuthCredentials = async (): Promise => { + const credentialsJson = await extension.context.secrets.get(AUTH_CREDENTIALS_SECRET_KEY); + if (!credentialsJson) { + return undefined; + } + + try { + return JSON.parse(credentialsJson) as AuthCredentials; + } catch (error) { + logError('Error parsing auth credentials', error); + return undefined; + } +}; + +/** + * Clear all authentication credentials + */ +export const clearAuthCredentials = async (): Promise => { + await extension.context.secrets.delete(AUTH_CREDENTIALS_SECRET_KEY); +}; + +/** + * Get the current login method + */ +export const getLoginMethod = async (): Promise => { + const credentials = await getAuthCredentials(); + return credentials?.loginMethod; +}; + +/** + * Get access token/API key based on login method + * Automatically refreshes MI_INTEL tokens if expired + */ +export const getAccessToken = async (): Promise => { + const credentials = await getAuthCredentials(); + if (!credentials) { + return undefined; + } + + switch (credentials.loginMethod) { + case LoginMethod.MI_INTEL: { + const { accessToken } = credentials.secrets; + let finalToken = accessToken; + + try { + const decoded = jwtDecode(accessToken); + const now = Math.floor(Date.now() / 1000); + + if (decoded.exp && decoded.exp < now) { + finalToken = await getRefreshedAccessToken(); + } + + return finalToken; + } catch (err) { + if (axios.isAxiosError(err) && err.response?.status === 400) { + throw new Error("TOKEN_EXPIRED"); + } + throw err; + } + } + case LoginMethod.ANTHROPIC_KEY: + return credentials.secrets.apiKey; + } + + return undefined; +}; + +/** + * Refresh MI_INTEL access token using refresh token + */ +export const getRefreshedAccessToken = async (): Promise => { + const credentials = await getAuthCredentials(); + if (!credentials || credentials.loginMethod !== LoginMethod.MI_INTEL) { + throw new Error('Token refresh is only supported for MI Intelligence authentication'); + } + + const { refreshToken } = credentials.secrets; + if (!refreshToken) { + throw new Error('Refresh token is not available'); + } -export async function exchangeAuthCodeNew(authCode: string): Promise { const params = new URLSearchParams({ client_id: AUTH_CLIENT_ID, - code: authCode, - grant_type: 'authorization_code', - // redirect_uri: 'vscode://wso2.micro-integrator/signin', - redirect_uri: MI_AUTH_REDIRECT_URL, + refresh_token: refreshToken, + grant_type: 'refresh_token', scope: 'openid email' }); + + const response = await axios.post( + `https://api.asgardeo.io/t/${AUTH_ORG}/oauth2/token`, + params.toString(), + { headers: CommonReqHeaders } + ); + + const newAccessToken = response.data.access_token; + const newRefreshToken = response.data.refresh_token; + + const updatedCredentials: AuthCredentials = { + ...credentials, + secrets: { + accessToken: newAccessToken, + refreshToken: newRefreshToken + } + }; + await storeAuthCredentials(updatedCredentials); + + return newAccessToken; +}; + +/** + * Cleanup legacy tokens from old authentication system + */ +export const cleanupLegacyTokens = async (): Promise => { try { - const response = await axios.post(`https://api.asgardeo.io/t/${AUTH_ORG}/oauth2/token`, params.toString(), { headers: CommonReqHeaders }); - return { - accessToken: response.data.access_token, - refreshToken: response.data.refresh_token, - loginTime: new Date().toISOString(), - expirationTime: response.data.expires_in - }; - } catch (err) { - throw new Error(`Error while exchanging auth code to token: ${err}`); + const legacyToken = await extension.context.secrets.get(LEGACY_ACCESS_TOKEN_SECRET_KEY); + const legacyRefreshToken = await extension.context.secrets.get(LEGACY_REFRESH_TOKEN_SECRET_KEY); + + if (legacyToken || legacyRefreshToken) { + await extension.context.secrets.delete(LEGACY_ACCESS_TOKEN_SECRET_KEY); + await extension.context.secrets.delete(LEGACY_REFRESH_TOKEN_SECRET_KEY); + logInfo('Legacy tokens cleaned up successfully.'); + } + } catch (error) { + logError('Error cleaning up legacy tokens', error); } +}; + +// ================================== +// OAuth/SSO Functions (MI_INTEL) +// ================================== + +/** + * Generate OAuth authorization URL + */ +export async function getAuthUrl(callbackUri: string): Promise { + const statePayload = Buffer.from(JSON.stringify({ callbackUri }), 'utf8').toString('base64'); + const state = encodeURIComponent(statePayload); + return `https://api.asgardeo.io/t/${AUTH_ORG}/oauth2/authorize?response_type=code&redirect_uri=${MI_AUTH_REDIRECT_URL}&client_id=${AUTH_CLIENT_ID}&scope=openid%20email&state=${state}`; } +/** + * Get logout URL for Asgardeo + */ +export function getLogoutUrl(): string { + return `https://api.asgardeo.io/t/${AUTH_ORG}/oidc/logout`; +} -export async function exchangeAuthCode(authCode: string) { +/** + * Exchange OAuth authorization code for access and refresh tokens + */ +export async function exchangeAuthCode(authCode: string): Promise { if (!authCode) { throw new Error("Auth code is not provided."); - } else { - try { - console.log("Exchanging auth code to token..."); - const response = await exchangeAuthCodeNew(authCode); - console.log("Access token: " + response.accessToken); - console.log("Refresh token: " + response.refreshToken); - console.log("Login time: " + response.loginTime); - console.log("Expiration time: " + response.expirationTime); - await extension.context.secrets.store('MIAIUser', response.accessToken); - await extension.context.secrets.store('MIAIRefreshToken', response.refreshToken ?? ''); - - // TODO: This is a temporary fix to get the backend root url. Once multiple workspace support is added, this should be removed. - const rpcManager = new MiDiagramRpcManager(vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? ""); - const backendRootUrlResponse = await rpcManager.getBackendRootUrl(); - const url = backendRootUrlResponse.url + '/user/usage'; - - const fetch_response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${response.accessToken}`, - }, - }); - - if(fetch_response.ok) { - const responseBody = await fetch_response.json() as AIUserTokens | undefined; - const context = StateMachineAI.context(); - context.userTokens = responseBody; - }else{ - throw new Error(`Error while checking token usage: ${fetch_response.statusText}`); + } + + try { + logInfo("Exchanging auth code for tokens..."); + + const params = new URLSearchParams({ + client_id: AUTH_CLIENT_ID, + code: authCode, + grant_type: 'authorization_code', + redirect_uri: MI_AUTH_REDIRECT_URL, + scope: 'openid email' + }); + + const response = await axios.post( + `https://api.asgardeo.io/t/${AUTH_ORG}/oauth2/token`, + params.toString(), + { headers: CommonReqHeaders } + ); + + const accessToken = response.data.access_token; + const refreshToken = response.data.refresh_token; + + logInfo("Tokens obtained successfully"); + + // Store credentials + const credentials: AuthCredentials = { + loginMethod: LoginMethod.MI_INTEL, + secrets: { + accessToken: accessToken, + refreshToken: refreshToken } + }; + await storeAuthCredentials(credentials); - StateMachineAI.sendEvent(AI_EVENT_TYPE.SIGN_IN_SUCCESS); - } catch (error: any) { - const errMsg = "Error while signing in to MI AI! " + error?.message; - throw new Error(errMsg); - } + // Notify state machine + StateMachineAI.sendEvent(AI_EVENT_TYPE.SIGN_IN_SUCCESS); + } catch (error: any) { + const errMsg = "Error while signing in to MI AI! " + error?.message; + throw new Error(errMsg); } } -export async function refreshAuthCode(): Promise{ - const refresh_token = await extension.context.secrets.get('MIAIRefreshToken'); - if (!refresh_token) { - throw new Error("Refresh token is not available."); - } else { +// ================================== +// High-Level Auth Operations +// ================================== + +/** + * Check if valid authentication credentials exist + */ +export const checkToken = async (): Promise<{ token: string; loginMethod: LoginMethod } | undefined> => { + await cleanupLegacyTokens(); + + const [token, loginMethod] = await Promise.all([ + getAccessToken(), + getLoginMethod() + ]); + + if (!token || !loginMethod) { + return undefined; + } + + return { token, loginMethod }; +}; + +/** + * Initiate OAuth/SSO login flow (MI_INTEL) + */ +export async function initiateInbuiltAuth(): Promise { + const callbackUri = await vscode.env.asExternalUri( + vscode.Uri.parse(`${vscode.env.uriScheme}://wso2.micro-integrator/signin`) + ); + const oauthURL = await getAuthUrl(callbackUri.toString()); + return vscode.env.openExternal(vscode.Uri.parse(oauthURL)); +} + +/** + * Validate Anthropic API key + */ +export const validateApiKey = async (apiKey: string, loginMethod: LoginMethod): Promise => { + if (loginMethod !== LoginMethod.ANTHROPIC_KEY) { + throw new Error('This login method is not supported. Please use SSO login instead.'); + } + + // Validate format + if (!apiKey || !apiKey.startsWith('sk-ant-') || apiKey.length < 20) { + throw new Error('Please enter a valid Anthropic API key (format: sk-ant-...)'); + } + + try { + logInfo('Validating Anthropic API key...'); + + // Test the API key by making a minimal request + const directAnthropic = createAnthropic({ + apiKey: apiKey, + baseURL: 'https://api.anthropic.com/v1' + }); + + await generateText({ + model: directAnthropic('claude-3-5-haiku-20241022'), + maxOutputTokens: 1, + messages: [{ role: 'user', content: 'Hi' }], + maxRetries: 0, // Disable retries to prevent retry loops on quota errors (429) + }); + + logInfo('API key validated successfully'); + + // Store credentials + const credentials: AuthCredentials = { + loginMethod: LoginMethod.ANTHROPIC_KEY, + secrets: { + apiKey: apiKey + } + }; + await storeAuthCredentials(credentials); + + return { token: apiKey }; + + } catch (error) { + logError('API key validation failed', error); + + if (error instanceof Error) { + if (error.message.includes('401') || error.message.includes('authentication')) { + throw new Error('Invalid API key. Please check your key and try again.'); + } else if (error.message.includes('403')) { + throw new Error('Your API key does not have access to Claude. Please check your Anthropic account.'); + } else if (error.message.includes('rate_limit')) { + throw new Error('Too many requests. Please wait a moment and try again.'); + } else if (error.message.includes('network') || error.message.includes('ENOTFOUND')) { + throw new Error('Connection failed. Please check your internet connection.'); + } + } + + throw new Error('API key validation failed. Please ensure your key is valid and has access to Claude models.'); + } +}; + +/** + * Logout and clear authentication credentials + */ +export const logout = async (isUserLogout: boolean = true): Promise => { + // For user-initiated logout, invalidate the session on the server (MI_INTEL only) + if (isUserLogout) { try { - console.log("Refreshing token..."); - const params = new URLSearchParams({ - client_id: AUTH_CLIENT_ID, - refresh_token: refresh_token, - grant_type: 'refresh_token', - scope: 'openid email' - }); - const response = await axios.post(`https://api.asgardeo.io/t/${AUTH_ORG}/oauth2/token`, params.toString(), { headers: CommonReqHeaders }); - const newAccessToken = response.data.access_token; - const newRefreshToken = response.data.refresh_token; - await extension.context.secrets.store('MIAIUser', newAccessToken); - await extension.context.secrets.store('MIAIRefreshToken', newRefreshToken); - console.log("Token refreshed successfully!"); - return newAccessToken; - } catch (error: any) { - const errMsg = "Error while refreshing token! " + error?.message; - console.error(errMsg); - return ""; + const tokenData = await checkToken(); + if (tokenData?.token && tokenData.loginMethod === LoginMethod.MI_INTEL) { + // Send logout request to Asgardeo to invalidate the session + const logoutURL = getLogoutUrl(); + await fetch(logoutURL, { + method: 'GET', + headers: { + 'Authorization': `Bearer ${tokenData.token}` + } + }).catch(err => { + // Ignore errors - we'll clear local credentials anyway + logInfo('Logout request to server failed (non-critical): ' + String(err)); + }); + } + } catch (error) { + // Ignore errors during token check + logInfo('Error during logout token check (non-critical): ' + String(error)); } } + + // Always clear stored credentials locally + await clearAuthCredentials(); +}; + +// ================================== +// Deprecated/Legacy Functions +// ================================== + +/** + * @deprecated Use getRefreshedAccessToken() instead + */ +export async function refreshAuthCode(): Promise { + logWarn('refreshAuthCode() is deprecated. Use getRefreshedAccessToken() instead.'); + try { + return await getRefreshedAccessToken(); + } catch (error) { + logError('Token refresh failed', error); + return ""; + } } diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/fill.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/fill.ts new file mode 100644 index 00000000000..41dd355f0aa --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/fill.ts @@ -0,0 +1,240 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateObject } from "ai"; +import { z } from "zod"; +import * as Handlebars from "handlebars"; +import { getAnthropicClient, ANTHROPIC_HAIKU_4_5 } from "../connection"; +import { SYSTEM } from "./system"; +import { PROMPT } from "./prompt"; +import { SYNAPSE_GUIDE } from "../context/synapse_guide"; +import { SYNAPSE_EXPRESSION_GUIDE } from "../context/synapse_expression_guide"; +import { logInfo, logError } from "../logger"; + +// Register Handlebars partials +Handlebars.registerPartial("synapse_guide", SYNAPSE_GUIDE); +Handlebars.registerPartial("synapse_expression_guide", SYNAPSE_EXPRESSION_GUIDE); + +/** + * Helper function to render Handlebars templates + */ +function renderTemplate(template: string, context: any): string { + const compiledTemplate = Handlebars.compile(template); + return compiledTemplate(context); +} + +/** + * Request parameters for auto-fill + */ +export interface AutoFillFormParams { + payloads?: string[]; + variables?: string[]; + params?: string[]; + properties?: string[]; + headers?: string[]; + configs?: string[]; + connection_names?: string[]; + form_details?: string; + current_values: Record; + field_descriptions?: Record; + question?: string; +} + +/** + * Response from auto-fill + */ +export interface AutoFillFormResult { + filled_values: Record; +} + +/** + * Creates a dynamic Zod schema based on the current form values structure + * This mimics Python's dynamic Pydantic model creation + */ +function createDynamicZodSchema( + currentValues: Record, + fieldDescriptions?: Record +): z.ZodObject { + const schemaFields: Record = {}; + + for (const [key, value] of Object.entries(currentValues)) { + let fieldSchema: z.ZodTypeAny; + + // Determine field type based on current value + if (typeof value === 'string') { + fieldSchema = z.string(); + } else if (typeof value === 'boolean') { + fieldSchema = z.boolean(); + } else if (typeof value === 'number') { + fieldSchema = z.number(); + } else if (Array.isArray(value)) { + // Handle array fields - infer element type from first element if available + if (value.length > 0) { + const firstElement = value[0]; + if (Array.isArray(firstElement)) { + // Array of arrays + fieldSchema = z.array(z.array(z.any())); + } else if (typeof firstElement === 'object' && firstElement !== null) { + // Array of objects + if ('is_expression' in firstElement && 'value' in firstElement) { + // Array of PropertyData objects + fieldSchema = z.array(z.object({ + is_expression: z.boolean().describe("Whether the value is a Synapse expression"), + value: z.string().describe("The actual value or expression"), + })); + } else { + // Array of generic objects + fieldSchema = z.array(z.record(z.any())); + } + } else if (typeof firstElement === 'string') { + fieldSchema = z.array(z.string()); + } else if (typeof firstElement === 'number') { + fieldSchema = z.array(z.number()); + } else if (typeof firstElement === 'boolean') { + fieldSchema = z.array(z.boolean()); + } else { + // Fallback for unknown element types + fieldSchema = z.array(z.any()); + } + } else { + // Empty array - default to array of any + fieldSchema = z.array(z.any()); + } + } else if (typeof value === 'object' && value !== null) { + // Check if it's a PropertyData object (has is_expression and value) + if ('is_expression' in value && 'value' in value) { + fieldSchema = z.object({ + is_expression: z.boolean().describe("Whether the value is a Synapse expression"), + value: z.string().describe("The actual value or expression"), + }); + } else { + // Generic object - try to infer structure + fieldSchema = z.record(z.any()); + } + } else { + // Default to any for unknown types + fieldSchema = z.any(); + } + + // Add description if available + if (fieldDescriptions && fieldDescriptions[key]) { + fieldSchema = fieldSchema.describe(fieldDescriptions[key]); + } + + // Handle special case: config_key should be renamed to configKey in the response + const schemaKey = key === 'config_key' ? 'configKey' : key; + schemaFields[schemaKey] = fieldSchema; + } + + return z.object(schemaFields); +} + +/** + * Maps the AI response back to match the original field names + * Handles the configKey → config_key conversion + */ +function mapResponseToOriginalKeys( + aiResponse: Record, + originalKeys: string[] +): Record { + const result: Record = {}; + + for (const originalKey of originalKeys) { + // Handle configKey → config_key mapping + if (originalKey === 'config_key' && 'configKey' in aiResponse) { + result['config_key'] = aiResponse['configKey']; + } else if (originalKey in aiResponse) { + result[originalKey] = aiResponse[originalKey]; + } + } + + return result; +} + +/** + * Auto-fills form fields using AI based on context and user input + * + * This function: + * 1. Builds a dynamic Zod schema from the current form structure + * 2. Renders prompts with available context (payloads, variables, etc.) + * 3. Calls the AI model with structured output + * 4. Returns filled form values matching the original structure + * + * @param params - Auto-fill parameters including form context and current values + * @returns Filled form values with AI-suggested content + */ +export async function autoFillForm( + params: AutoFillFormParams +): Promise { + try { + // Create dynamic Zod schema based on current form structure + const dynamicSchema = createDynamicZodSchema( + params.current_values, + params.field_descriptions + ); + + // Render system prompt (includes synapse_guide and expression_guide) + const systemPrompt = renderTemplate(SYSTEM, {}); + + // Render user prompt with all available context + const userPrompt = renderTemplate(PROMPT, { + payloads: params.payloads || [], + variables: params.variables || [], + params: params.params || [], + properties: params.properties || [], + headers: params.headers || [], + configs: params.configs || [], + connection_names: params.connection_names || [], + form_details: params.form_details || '', + current_values: params.current_values, + question: params.question || '', // Empty means No-Prompt Mode (auto-fill) + }); + + logInfo('Generating AI suggestions for form...'); + + // Get AI model + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); + + // Generate structured output using the dynamic schema + // Type assertion to avoid TypeScript deep instantiation issues with Zod + const result = await (generateObject as any)({ + model: model, + system: systemPrompt, + prompt: userPrompt, + schema: dynamicSchema, + maxTokens: 8000, + temperature: 0.2, // Lower temperature for deterministic form filling + }); + + // Extract the filled values from the result + const aiResponse = result.object as Record; + + // Map response back to original field names (handle configKey → config_key) + const originalKeys = Object.keys(params.current_values); + const filledValues = mapResponseToOriginalKeys(aiResponse, originalKeys); + + logInfo('Successfully generated form suggestions'); + + return { + filled_values: filledValues, + }; + } catch (error) { + logError('Error generating form suggestions', error); + throw error; + } +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/prompt.ts new file mode 100644 index 00000000000..cdca3712b9d --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/prompt.ts @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const PROMPT = ` +{{#if payloads}} +### Pre-defined Payloads +These are pre-defined user payloads in JSON format for this project. +Use these payloads to populate form fields using the 'insertText' values. + +#### How to use payloads: +1. Access payload values using Synapse expressions with the \${} syntax +2. Use the exact 'insert_text' path provided in the payload structure +3. For nested objects, use dot notation to access child properties + +#### Example: +For a payload structure like: +\`\`\`json +[ + {"children": [], "label": "name", "insert_text": "payload.name"}, + {"children": [], "label": "email", "insertT_text": "payload.email"}, + {"children": [ + {"children": [], "label": "street", "insert_text": "payload.address.street"}, + {"children": [], "label": "city", "insert_text": "payload.address.city"} + ], "label": "address", "insert_text": "payload.address"} +] +\`\`\` + +Access values as: +- Name: \${payload.name} +- Email: \${payload.email} +- Street Address: \${payload.address.street} +- City: \${payload.address.city} + + +{{#each payloads}} +{{{this}}} +{{/each}} + +{{/if}} + +{{#if variables}} +### Pre-defined Variables +These are pre-defined user variables for this project. +Use these variables to populate form fields using the 'insert_text' values. + +#### How to use variables: +1. Access variable values using Synapse expressions with the \${} syntax +2. Use the exact 'insert_text' path provided in the variable structure + + +{{#each variables}} +{{{this}}} +{{/each}} + +{{/if}} + +{{#if params}} +### Pre-defined Parameters +These are pre-defined user parameters for this project. +Use these parameters to populate form fields using the 'insert_text' values. + +#### How to use parameters: +1. Access parameter values using Synapse expressions with the \${} syntax +2. Use the exact 'insert_text' path provided in the parameter structure + + +{{#each params}} +{{{this}}} +{{/each}} + +{{/if}} + +{{#if properties}} +### Pre-defined Properties +These are pre-defined user properties for this project. +Use these properties to populate form fields using the 'insert_text' values. + +#### How to use properties: +1. Access property values using Synapse expressions with the \${} syntax +2. Use the exact 'insert_text' path provided in the property structure + + +{{#each properties}} +{{{this}}} +{{/each}} + +{{/if}} + +{{#if headers}} +### Pre-defined Headers +These are pre-defined user headers for this project. +Use these headers to populate form fields using the 'insert_text' values. +#### How to use headers: +1. Access header values using Synapse expressions with the \${} syntax +2. Use the exact 'insert_text' path provided in the header structure + +{{#each headers}} +{{{this}}} +{{/each}} + +{{/if}} + +{{#if configs}} +### Pre-defined Configurations +These are pre-defined user configurations for this project. +Use these configurations to populate form fields using the 'insert_text' values. +#### How to use configurations: +1. Access configuration values using Synapse expressions with the \${} syntax +2. Use the exact 'insert_text' path provided in the configuration structure + +{{#each configs}} +{{{this}}} +{{/each}} + +{{/if}} + + +{{#if connection_names}} +### Pre-defined Connections +These are pre-defined user connections for this project. +Use these connections specifically when populating 'config_key' fields. + +#### How to use connections: +Always select from the provided connection names + + +{{#each connection_names}} +{{{this}}} +{{/each}} + +{{/if}} + +{{#if form_details}} +### Form Details +This is the current form structure and its requirements. +Use these details to understand the context and required fields. + + +{{{form_details}}} + +{{/if}} + +{{#if current_values}} +### Current Values +These are the current values of the form fields. +{{{current_values}}} +{{/if}} + +### User Query Processing + +{{#if question}} +Now you are operating in User Prompt Mode. You will receive a user query that may contain specific instructions or values. +When processing the user query: +1. Analyze it thoroughly to extract specific instructions and values +2. Analyze user query thoroughly and in great detail. +3. Prioritize user query information over default predictions +4. Use user query context to inform your field completions + +This is the user query. Use it to fill the form fields with the most relevant values using the given information. + +{{{question}}} + +Start creating your response now. +{{else}} +Now you are operating in User Prompt Mode. You will not receive a user query. +Now populate the form fields with the highest-confidence values based on the given information. +{{/if}} +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/system.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/system.ts new file mode 100644 index 00000000000..cbe589aa74f --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/system.ts @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYSTEM = ` +You are an intelligent assistant embedded within the WSO2 Micro Integrator VSCode Extension. Your primary purpose is to streamline the development workflow by automatically filling form fields for mediator configurations, reducing manual effort and potential errors. + +### Your Core Identity and Purpose + +You serve as an AI-powered helper within the Micro Integrator Extension, focused specifically on the auto-fill functionality. You receive JSON objects representing form fields for mediator configurations, analyze them for empty or incomplete values, and return the same JSON structure with intelligently populated fields. + +### Your Capabilities + +1. Form Field Analysis: + - You can analyze form fields and understand their intended purpose + - You recognize patterns in field names and types to suggest appropriate values + - You maintain awareness of relationships between different configuration elements + - Maintain consistency across related fields + +2. Value Prediction: + -You generate intelligent predictions for form fields based on: + - Project context and existing configurations + - Common patterns in integration development + - Best practices for WSO2 Micro Integrator implementations + - Field-specific requirements and constraints + - Make best-effort predictions for all empty fields + +### Context Awareness + +You operate within two distinct modes: +1. User Prompt Mode + -You suggest appropriate values based on context and user query +2. No-Prompt Mode + -You automatically fill values without requiring user query + +When predicting values, consider: + +1. Field Relationships: Values in one field may influence appropriate values in another +2. Mediator Type: Different mediator types have different expected field patterns +3. Integration Context: Existing payloads, properties, variables, parameters, headers and configs defined in the project + + +### Value Prediction Guidelines + +WSO2 has introduced Synapse Expressions, which should be used instead of JsonPath or XPath. Refer to the following documentation. + + + {{> synapse_expression_guide}} + + + {{> synapse_expression_examples}} + + +When predicting values, follow these guidelines: + +1. Messages and Names: Create descriptive, actionable messages relevant to the mediator's purpose. +2. Expressions: Use proper Synapse expression syntax. +3. Descriptions: Provide concise but informative descriptions of the mediator's purpose +4. Boolean Values: Predict sensible defaults based on common integration patterns + +### How You Operate +1. You analyze the form structure and required fields +2. You evaluate the current project context to understand available variables, payloads, parameters, properties, headers and configurations +3. You determine which fields can be auto-filled with high confidence +4. For each field, you generate appropriate value suggestions +5. You prioritize suggestions based on relevance and confidence level +6. You format suggestions according to the expected field type +7. In User Prompt Mode, You present suggestions based on user queries +8. In No-Prompt Mode, You automatically populate fields with highest-confidence values +9. You ensure all inter-related fields maintain consistency + +### Special Field Handling + 1. is_expression: + - description: Boolean flag indicating if a value contains a synapse expression + - logic: Set to true if the value contains synapse expression patterns + + +### Current Values and Output Format + +You receive current values representing form fields with their current values. For example: + +\`\`\`json +{ + "name": {"is_expression": false, "value": ""}, + "category": "INFO", + "message": "", + "appendId": false, + "description": "" +} +\`\`\` +You must return output in the exact same, with appropriate values filled in for empty fields: + +\`\`\`json +{ + "name": {"is_expression": false, "value": "LogMediator"}, + "category": "INFO", + "message": "Request processed successfully", + "appendId": false, + "description": "Logs successful API request processing" +} +\`\`\` +If you want to change the current values, you can do so by providing a new value in the output. + +#### Examples of Form Completion + +##### Example 1: Log Mediator + +Input: +\`\`\`json +{ + "name": {"is_expression": false, "value": ""}, + "category": "INFO", + "message": "", + "appendId": false, + "description": "" +} +\`\`\` +Output: +\`\`\`json +{ + "name": {"is_expression": false, "value": "LogMediatorName"}, + "category": "INFO", + "message": "Request processed successfully", + "appendId": false, + "description": "Logs successful API request processing" +} +\`\`\` +##### Example 2: Property Mediator + +Input: + +\`\`\`json +{ + "name": {"is_expression": false, "value": ""}, + "property": "", + "value": {"is_expression": false, "value": ""}, + "scope": "default", + "description": "" +} +\`\`\` +Output: +\`\`\`json +{ + "name": {"is_expression": false, "value": "PropertyMediatorName"}, + "property": "REQUEST_PAYLOAD", + "value": {"is_expression": true, "value": "\${payload.request}"}, + "scope": "default", + "description": "Stores the request payload in a property for later use" +} +\`\`\` + +### Value Proposition + +By providing this intelligent auto-fill capability, you: +- Save developers significant time on repetitive configuration tasks +- Reduce errors in complex mediator setups +- Create a more fluid and efficient development experience +- Allow developers to focus on integration logic rather than form details + +Always remember: Your ultimate purpose is to make the development process smoother, faster, and more reliable for WSO2 Micro Integrator users by reducing the cognitive load of form completion while maintaining accuracy and consistency. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts new file mode 100644 index 00000000000..1dba6ec04ae --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts @@ -0,0 +1,170 @@ +// Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com/) All Rights Reserved. + +// WSO2 LLC. licenses this file to you 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. + +import { createAnthropic } from "@ai-sdk/anthropic"; +import * as vscode from "vscode"; +import { getAccessToken, getLoginMethod, getRefreshedAccessToken } from "../auth"; +import { StateMachineAI, openAIWebview } from "../aiMachine"; +import { AI_EVENT_TYPE, LoginMethod } from "@wso2/mi-core"; +import { logInfo, logDebug, logError } from "./logger"; + +export const ANTHROPIC_HAIKU_4_5 = "claude-haiku-4-5-20251001"; +export const ANTHROPIC_SONNET_4_5 = "claude-sonnet-4-5-20250929"; + +type AnthropicModel = + | typeof ANTHROPIC_HAIKU_4_5 + | typeof ANTHROPIC_SONNET_4_5; + +let cachedAnthropic: ReturnType | null = null; +let cachedAuthMethod: LoginMethod | null = null; + +/** + * Get the backend URL for MI Copilot + */ +const getAnthropicProxyUrl = (): string => { + const proxyUrl = process.env.MI_COPILOT_ANTHROPIC_PROXY_URL; + if (!proxyUrl) { + throw new Error('MI_COPILOT_ANTHROPIC_PROXY_URL environment variable is not set'); + } + return `${proxyUrl}/proxy/anthropic/v1`; +}; + +/** + * Reusable fetch function that handles authentication with token refresh + * @param input - The URL, Request object, or string to fetch + * @param options - Fetch options + * @returns Promise + */ +export async function fetchWithAuth(input: string | URL | Request, options: RequestInit = {}): Promise { + try { + const accessToken = await getAccessToken(); + + // Ensure headers object exists + options.headers = { + ...options.headers, + 'Authorization': `Bearer ${accessToken}`, + 'User-Agent': 'MI-VSCode-Plugin', + 'Connection': 'keep-alive', + }; + + let response = await fetch(input, options); + + // Handle rate limit/quota errors (429) + if (response.status === 429) { + logInfo("Rate limit/quota exceeded (429)"); + let errorDetail = ""; + try { + const body = await response.json(); + errorDetail = body.detail || ""; + } catch (e) { + logError("Failed to parse 429 response body", e); + } + + // Transition to UsageExceeded state + StateMachineAI.sendEvent(AI_EVENT_TYPE.USAGE_EXCEEDED); + + // Notify user and prompt to use their own API key + vscode.window.showWarningMessage( + "Your free usage quota has been exceeded. Set your own Anthropic API key to continue using MI Copilot with unlimited access.", + "Set API Key", + "Learn More" + ).then(selection => { + if (selection === "Set API Key") { + openAIWebview(); + StateMachineAI.sendEvent(AI_EVENT_TYPE.AUTH_WITH_API_KEY); + } else if (selection === "Learn More") { + vscode.env.openExternal(vscode.Uri.parse("https://console.anthropic.com/")); + } + }); + + // Create a special error that should not be retried + const error: any = new Error(`USAGE_QUOTA_EXCEEDED: ${errorDetail}`.trim()); + error.isUsageQuotaError = true; + error.status = 429; + throw error; + } + + // Handle token expiration + if (response.status === 401) { + logInfo("Token expired. Refreshing token..."); + const newToken = await getRefreshedAccessToken(); + if (newToken) { + options.headers = { + ...options.headers, + 'Authorization': `Bearer ${newToken}`, + }; + response = await fetch(input, options); + } else { + StateMachineAI.sendEvent(AI_EVENT_TYPE.LOGOUT); + throw new Error("Authentication failed: Unable to refresh token"); + } + } + + return response; + } catch (error: any) { + if (error?.message === "TOKEN_EXPIRED") { + StateMachineAI.sendEvent(AI_EVENT_TYPE.LOGOUT); + throw new Error("Authentication failed: Token expired"); + } else { + throw error; + } + } +} + +/** + * Returns a singleton Anthropic client instance. + * Re-initializes the client if the login method has changed. + */ +export const getAnthropicClient = async (model: AnthropicModel): Promise => { + const loginMethod = await getLoginMethod(); + + // Recreate client if login method has changed or no cached instance + if (!cachedAnthropic || cachedAuthMethod !== loginMethod) { + if (loginMethod === LoginMethod.MI_INTEL) { + const backendUrl = getAnthropicProxyUrl(); + cachedAnthropic = createAnthropic({ + baseURL: backendUrl, + apiKey: "xx", // dummy value; real auth is via fetchWithAuth + fetch: fetchWithAuth, + }); + } else if (loginMethod === LoginMethod.ANTHROPIC_KEY) { + const apiKey = await getAccessToken(); + if (!apiKey) { + throw new Error("Authentication failed: Unable to get API key"); + } + cachedAnthropic = createAnthropic({ + baseURL: "https://api.anthropic.com/v1", + apiKey: apiKey, + }); + } else { + throw new Error(`Unsupported login method: ${loginMethod}`); + } + + cachedAuthMethod = loginMethod; + } else { + logDebug('Using cached Anthropic client'); + } + + return cachedAnthropic!(model); +}; + +/** + * Returns cache control options for prompt caching + * @returns Cache control options for Anthropic + */ +export const getProviderCacheControl = async () => { + return { anthropic: { cacheControl: { type: "ephemeral" } } }; +}; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connector_db.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connector_db.ts new file mode 100644 index 00000000000..d77d11fab1e --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connector_db.ts @@ -0,0 +1,5821 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const CONNECTOR_DB = [ + { + "connectorName": "AI", + "repoName": "mi-module-generative-ai", + "description": "The AI module allows you to integrate with AI services and develop AI applications.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-ai", + "version": { + "tagName": "0.1.5", + "releaseId": "220614930", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "chat", + "description": "Invoke a LLM service.", + "isHidden": false + }, + { + "name": "ragChat", + "description": "Invoke RAG.", + "isHidden": false + }, + { + "name": "agent", + "description": "Create an AI agent.", + "isHidden": false + }, + { + "name": "addToKnowledge", + "description": "Add text and its embeddings to a selected vector store.", + "isHidden": false + }, + { + "name": "getFromKnowledge", + "description": "Search for similar texts in a selected vector store.", + "isHidden": false + } + ], + "connections": [ + { + "name": "ANTHROPIC", + "description": "Connection for interacting with the Anthropic AI service.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_ANTHROPIC.svg" + }, + { + "name": "AZURE_OPEN_AI", + "description": "Connection for interacting with the Azure OpenAI service.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_AZURE_OPEN_AI.svg" + }, + { + "name": "MISTRAL_AI", + "description": "Connection for interacting with the MistralAI service.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_MISTRAL_AI.svg" + }, + { + "name": "DEEPSEEK", + "description": "Connection for interacting with the DeepSeek service.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_DEEPSEEK.svg" + }, + { + "name": "OPEN_AI", + "description": "Connection for interacting with the OpenAI service.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_OPEN_AI.svg" + }, + { + "name": "MI_VECTOR_STORE", + "description": "Connection for interacting with the MI In-Registry vector store.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_MI_VECTOR_STORE.svg" + }, + { + "name": "PINECONE", + "description": "Connection for interacting with the Pinecone vector store.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_PINECONE.svg" + }, + { + "name": "POSTGRES_VECTOR", + "description": "Connection for interacting with the PostgreSQL vector database.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_POSTGRES_VECTOR.svg" + }, + { + "name": "CHROMA_DB", + "description": "Connection for interacting with the ChromaDB vector database.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_CHROMA_DB.svg" + }, + { + "name": "POSTGRES_MEMORY", + "description": "Connection for interacting with the PostgreSQL memory database.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_POSTGRES_MEMORY.svg" + }, + { + "name": "FILE_MEMORY", + "description": "Connection for interacting with the file-based memory.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-module-generative-ai_FILE_MEMORY.svg" + } + ] + }, + "otherVersions": {}, + "connectorRank": 5, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-module-generative-ai.gif" + }, + { + "connectorName": "Amazon DynamoDB", + "repoName": "esb-connector-amazondynamodb", + "description": "The Amazon DynamoDB connector allows you to access the Amazon DynamoDB REST API through the WSO2 ESB and perform CRUD operations. Amazon DynamoDB is a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-amazondynamodb", + "version": { + "tagName": "2.0.0", + "releaseId": "226990592", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "putItem", + "description": "Put Item", + "isHidden": false + }, + { + "name": "describeTable", + "description": "Describe Table", + "isHidden": false + }, + { + "name": "describeLimits", + "description": "Describe Limits", + "isHidden": false + }, + { + "name": "listTables", + "description": "List Tables", + "isHidden": false + }, + { + "name": "deleteItem", + "description": "Delete Item", + "isHidden": false + }, + { + "name": "updateTable", + "description": "Update Table", + "isHidden": false + }, + { + "name": "batchGetItem", + "description": "Batch Get Item", + "isHidden": false + }, + { + "name": "batchWriteItem", + "description": "Batch Write Item", + "isHidden": false + }, + { + "name": "getItem", + "description": "Get Item", + "isHidden": false + }, + { + "name": "scan", + "description": "Scan", + "isHidden": false + }, + { + "name": "updateItem", + "description": "Update Item", + "isHidden": false + }, + { + "name": "deleteTable", + "description": "Delete Table", + "isHidden": false + }, + { + "name": "createTable", + "description": "Create Table", + "isHidden": false + }, + { + "name": "query", + "description": "Query", + "isHidden": false + }, + { + "name": "init", + "description": "Config operation with common parameters.", + "isHidden": true + }, + { + "name": "createTable", + "description": "Add a new table.", + "isHidden": false + }, + { + "name": "deleteTable", + "description": "Delete a table and all of its items.", + "isHidden": false + }, + { + "name": "describeLimits", + "description": "Get the current provisioned-capacity limits.", + "isHidden": false + }, + { + "name": "describeTable", + "description": "Get the information about the table.", + "isHidden": false + }, + { + "name": "listTables", + "description": "Get an array of table names.", + "isHidden": false + }, + { + "name": "updateTable", + "description": "Update the table.", + "isHidden": false + }, + { + "name": "batchGetItem", + "description": "Get the attributes of one or more items from one or more tables.", + "isHidden": false + }, + { + "name": "batchWriteItem", + "description": "Put or delete multiple items in one or more tables.", + "isHidden": false + }, + { + "name": "putItem", + "description": "Create a new item, or replace an old item with a new item.", + "isHidden": false + }, + { + "name": "deleteItem", + "description": "Delete a single item in a table by primary key.", + "isHidden": false + }, + { + "name": "getItem", + "description": "Get a set of attributes.", + "isHidden": false + }, + { + "name": "updateItem", + "description": "Update an existing item's attributes.", + "isHidden": false + }, + { + "name": "query", + "description": "Return all of the items from the table or index with that partition key value.", + "isHidden": false + }, + { + "name": "scan", + "description": "Returns one or more items and item attributes by accessing every item in a table or a secondary index.", + "isHidden": false + } + ], + "connections": [ + { + "name": "amazondynamodb", + "description": "Amazon DynamoDB Connection", + "iconUrl": "" + }, + { + "name": "amazondynamodb", + "description": "Connection for an Amazon DynamoDB", + "iconUrl": "" + } + ] + }, + "otherVersions": { + "1.0.2": "196760097" + }, + "connectorRank": 33, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-amazondynamodb.png" + }, + { + "connectorName": "Amazon Lambda", + "repoName": "esb-connector-amazonlambda", + "description": "The AmazonLambda Connector allows you to access the REST API of Amazon Web Service Lambda (AWS Lambda) , which lets you run code without provisioning or managing servers. With Lambda, you can run code for virtually any type of application or backend service - all with zero administration. Just upload your code in one of the languages that AWS Lambda supports (currently Node.js, Java, C#, Go and Python) and Lambda takes care of everything required to run and scale your code with high availability. ", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-amazonlambda", + "version": { + "tagName": "2.0.0", + "releaseId": "226990139", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "getAccountSettings", + "description": "Gets the settings of an account.", + "isHidden": false + }, + { + "name": "createAlias", + "description": "Create an alias for a lambda function version.", + "isHidden": false + }, + { + "name": "deleteAlias", + "description": "Deletes a Lambda function alias.", + "isHidden": false + }, + { + "name": "getAlias", + "description": "Returns details about a Lambda function alias.", + "isHidden": false + }, + { + "name": "updateAlias", + "description": "Updates the configuration of a Lambda function alias.", + "isHidden": false + }, + { + "name": "init", + "description": "Config operations with common parameters.", + "isHidden": true + }, + { + "name": "addPermission", + "description": "Grants an AWS service or another account permission to use a function.", + "isHidden": false + }, + { + "name": "createFunction", + "description": "Creates a lambda function.", + "isHidden": false + }, + { + "name": "deleteFunction", + "description": "Deletes a Lambda function.", + "isHidden": false + }, + { + "name": "getFunction", + "description": "Returns information about function or function version.", + "isHidden": false + }, + { + "name": "getFunctionConfiguration", + "description": "Returns the version-specific settings of a lambda function or version.", + "isHidden": false + }, + { + "name": "invoke", + "description": "Invokes a lambda function.", + "isHidden": false + }, + { + "name": "listFunctions", + "description": "Returns a list of Lambda functions.", + "isHidden": false + }, + { + "name": "removePermission", + "description": "Removes function use permission from an AWS service or another account.", + "isHidden": false + }, + { + "name": "addLayerVersionPermission", + "description": "Adds permission to the resource-based policy of a version of an AWS Lambda layer.", + "isHidden": false + }, + { + "name": "removeLayerVersionPermission", + "description": "Removes a statement from the permissions policy for a version of an AWS Lambda layer.", + "isHidden": false + } + ], + "connections": [ + { + "name": "amazonLambda", + "description": "Connection for accessing Amazon Lambda functions.", + "iconUrl": "" + } + ] + }, + "otherVersions": { + "1.0.1": "191162419" + }, + "connectorRank": 34, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-amazonlambda.gif" + }, + { + "connectorName": "Amazon S3", + "repoName": "esb-connector-amazons3", + "description": "The Amazon S3 connector allows you to access the Amazon Simple Storage Service (Amazon S3), which provides a simple web services interface that can be used to store and retrieve any amount of data, at any time, from anywhere on the web. It gives any developer access to the same highly scalable, reliable, secure, fast, inexpensive infrastructure that Amazon S3 uses to run its own global network of websites. The connector with WSO2 EI enables you to publish and manage your enterprise data at Amazon S3 service. ", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-amazons3", + "version": { + "tagName": "2.0.10", + "releaseId": "207333141", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "createBucket", + "description": "Get details about payments that have not completed", + "isHidden": false + }, + { + "name": "deleteBucket", + "description": "Deletes the bucket named in the URI. All objects (including all object versions and Delete Markers) in the bucket must be deleted before the bucket itself can be deleted", + "isHidden": false + }, + { + "name": "deleteBucketCORS", + "description": "Deletes the CORS configuration information set for the bucket", + "isHidden": false + }, + { + "name": "deleteBucketLifecycle", + "description": "Deletes the lifecycle configuration from the specified bucket", + "isHidden": false + }, + { + "name": "deleteBucketPolicy", + "description": "Delete the policy on a specified bucket", + "isHidden": false + }, + { + "name": "deleteBucketReplication", + "description": "Deletes the replication subresource associated with the specified bucket", + "isHidden": false + }, + { + "name": "deleteBucketTagging", + "description": "Removes a tags set from the specified bucket", + "isHidden": false + }, + { + "name": "deleteBucketWebsiteConfiguration", + "description": "Removes the website configuration for a bucket", + "isHidden": false + }, + { + "name": "getBucketACL", + "description": "Return the access control list (ACL) of a bucket", + "isHidden": false + }, + { + "name": "getBucketCORS", + "description": "Returns the CORS configuration information set for the bucket", + "isHidden": false + }, + { + "name": "getBucketLifecycleConfiguration", + "description": "Returns the lifecycle configuration information set on the bucket", + "isHidden": false + }, + { + "name": "getBucketLocation", + "description": "Returns a bucket's region", + "isHidden": false + }, + { + "name": "getBucketLogging", + "description": "Returns the logging storageClass of a bucket", + "isHidden": false + }, + { + "name": "getBucketNotificationConfiguration", + "description": "Returns the notification configuration of a bucket", + "isHidden": false + }, + { + "name": "getBucketPolicy", + "description": "Return the policy of a specified bucket", + "isHidden": false + }, + { + "name": "getBucketReplication", + "description": "Returns replication configuration information set on the bucket", + "isHidden": false + }, + { + "name": "getBucketRequestPayment", + "description": "Return the request payment configuration of a bucket", + "isHidden": false + }, + { + "name": "getBucketTagging", + "description": "Returns the tags set associated with the bucket", + "isHidden": false + }, + { + "name": "getBucketVersioning", + "description": "Returns the versioning state of a bucket", + "isHidden": false + }, + { + "name": "getBucketWebsite", + "description": "Returns the website configuration associated with a bucket", + "isHidden": false + }, + { + "name": "headBucket", + "description": "To determine if a bucket exists and you have permission to access it", + "isHidden": false + }, + { + "name": "listBuckets", + "description": "Return the request payment configuration of a bucket.", + "isHidden": false + }, + { + "name": "listMultipartUploads", + "description": "Lists in-progress multipart uploads", + "isHidden": false + }, + { + "name": "listObjects", + "description": "Returns some or all (up to 1000) of the objects in a bucket", + "isHidden": false + }, + { + "name": "listObjectVersions", + "description": "List metadata about all of the versions of objects in a bucket", + "isHidden": false + }, + { + "name": "putBucketACL", + "description": "Set the permissions on an existing bucket using access control lists", + "isHidden": false + }, + { + "name": "putBucketCORS", + "description": "Sets the CORS configuration for your bucket", + "isHidden": false + }, + { + "name": "putBucketLifecycleConfiguration", + "description": "Creates a new lifecycle configuration for the bucket or replaces an existing lifecycle configuration", + "isHidden": false + }, + { + "name": "putBucketPolicy", + "description": "Add to or replace a policy on a bucket", + "isHidden": false + }, + { + "name": "putBucketReplication", + "description": "Creates a new replication configuration", + "isHidden": false + }, + { + "name": "putBucketRequestPayment", + "description": "Set the request payment configuration of a bucket", + "isHidden": false + }, + { + "name": "putBucketTagging", + "description": "Adds a set of tags to an existing bucket", + "isHidden": false + }, + { + "name": "putBucketVersioning", + "description": "Set the versioning state of an existing bucket", + "isHidden": false + }, + { + "name": "putBucketWebsite", + "description": "Sets the configuration of the website that is specified in the website subresource", + "isHidden": false + }, + { + "name": "init", + "description": "Configuration files.", + "isHidden": true + }, + { + "name": "abortMultipartUpload", + "description": "Abort a currently active multipart upload", + "isHidden": false + }, + { + "name": "copyBucketObject", + "description": "Creates a copy of an object that is already stored in Amazon S3", + "isHidden": false + }, + { + "name": "deleteObject", + "description": "Removes the null version (if there is one) of an object and inserts a delete marker, which becomes the latest version of the object", + "isHidden": false + }, + { + "name": "deleteObjects", + "description": "Delete multiple objects from a bucket using a single HTTP request", + "isHidden": false + }, + { + "name": "getObject", + "description": "Retrieves objects from Amazon S3", + "isHidden": false + }, + { + "name": "getObjectACL", + "description": "Returns the access control list (ACL) of an object", + "isHidden": false + }, + { + "name": "getObjectTagging", + "description": "Retrieve the list of tags associated with the object", + "isHidden": false + }, + { + "name": "getObjectTorrent", + "description": "Returns torrent files from a bucket", + "isHidden": false + }, + { + "name": "headObject", + "description": "Retrieves metadata from an object without returning the object itself", + "isHidden": false + }, + { + "name": "completeMultipartUpload", + "description": "Complete the multipart upload", + "isHidden": false + }, + { + "name": "createMultipartUpload", + "description": "Create a multipart upload for Part uploads", + "isHidden": false + }, + { + "name": "listParts", + "description": "Retrieve list of uploaded parts", + "isHidden": false + }, + { + "name": "multipartUpload", + "description": "Complete a currently active multipart upload", + "isHidden": false + }, + { + "name": "putObject", + "description": "Create objects from Amazon S3 bucket", + "isHidden": false + }, + { + "name": "putObjectAcl", + "description": "Set the access control list (ACL) permissions for an object that already exists in a bucket", + "isHidden": false + }, + { + "name": "restoreObject", + "description": "Restores a temporary copy of an archived object", + "isHidden": false + }, + { + "name": "uploadPart", + "description": "Upload a tag for a current multipart upload", + "isHidden": false + }, + { + "name": "uploadPartCopy", + "description": "Uploads a tag by copying data from an existing object as data source", + "isHidden": false + }, + { + "name": "generatePutObjectPresignedUrl", + "description": "Generate a presigned URL to upload an object to Amazon S3", + "isHidden": false + }, + { + "name": "generateGetObjectPresignedUrl", + "description": "Generate a presigned URL to download an object from Amazon S3", + "isHidden": false + } + ], + "connections": [ + { + "name": "amazons3", + "description": "Connection for accessing Amazon S3 storage.", + "iconUrl": "" + } + ] + }, + "otherVersions": { + "2.0.9": "201982453" + }, + "connectorRank": 11, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-amazons3.png" + }, + { + "connectorName": "Amazon Simple Queue Service", + "repoName": "esb-connector-amazonsqs", + "description": "Amazon SQS offers reliable and scalable hosted queues for storing messages as they travel between computers. By using Amazon SQS, you can move data between distributed components of your applications that perform different tasks without losing messages or requiring each component to be always available. The Amazon SQS connector for WSO2 EI uses the Amazon SQS API and allows you to send and receive messages, facilitating asynchronous messaging between integrated systems.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-amazonsqs", + "version": { + "tagName": "2.0.3", + "releaseId": "208301548", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Config operation", + "isHidden": true + }, + { + "name": "sendMessage", + "description": "Delivers a message to the specified queue", + "isHidden": false + }, + { + "name": "sendMessageBatch", + "description": "Delivers batch messages to the specified queue", + "isHidden": false + }, + { + "name": "receiveMessage", + "description": "Retrieves one or more messages from the specified queue", + "isHidden": false + }, + { + "name": "changeMessageVisibility", + "description": "Changes the visibility timeout of a specified message in a queue", + "isHidden": false + }, + { + "name": "changeMessageVisibilityBatch", + "description": "Changes the visibility timeout of multiple messages", + "isHidden": false + }, + { + "name": "deleteMessage", + "description": "Deletes the specified message from the specified queue", + "isHidden": false + }, + { + "name": "deleteMessageBatch", + "description": "Deletes multiple messages from the specified queue", + "isHidden": false + }, + { + "name": "addPermission", + "description": "Adds a permission to a queue for a specific principal which allows access sharing to the queue", + "isHidden": false + }, + { + "name": "removePermission", + "description": "Revokes any permissions in the queue policy", + "isHidden": false + }, + { + "name": "createQueue", + "description": "Creates a new queue, or returns the URL of an existing one", + "isHidden": false + }, + { + "name": "getQueueAttributes", + "description": "Gets attributes for the specified queue", + "isHidden": false + }, + { + "name": "setQueueAttributes", + "description": "Sets the value of one or more queue attributes", + "isHidden": false + }, + { + "name": "getQueueUrl", + "description": "Returns the URL of an existing queue", + "isHidden": false + }, + { + "name": "listQueues", + "description": "Returns a list of queues", + "isHidden": false + }, + { + "name": "deleteQueue", + "description": "Deletes the queue specified by the queue URL", + "isHidden": false + }, + { + "name": "purgeQueue", + "description": "Deletes the messages in a queue specified by the queue URL", + "isHidden": false + }, + { + "name": "listDeadLetterSourceQueues", + "description": "Returns a list of your queues that have the RedrivePolicy queue attribute", + "isHidden": false + } + ], + "connections": [ + { + "name": "amazonsqs", + "description": "Connection for interacting with Amazon SQS queues.", + "iconUrl": "" + } + ] + }, + "otherVersions": { + "2.0.2": "191922252" + }, + "connectorRank": 22, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-amazonsqs.png" + }, + { + "connectorName": "AS400 PCML", + "repoName": "esb-connector-pcml", + "description": "The AS400 PCML connector allows you to access RPG programs that are available on AS400 (renamed as IBM iSeries) servers using WSO2 ESB. This is done using Program Call Markup Language (PCML). The AS400 is a mid-range server used by small businesses and departments in large enterprises and has been re-designed to work with web applications in a distributed network.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-pcml", + "version": { + "tagName": "2.0.1", + "releaseId": "193423606", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Initializes AS400 instance. Authenticates if credentials are provided.", + "isHidden": false + }, + { + "name": "call", + "description": "Calls a program in the AS400 system.", + "isHidden": false + }, + { + "name": "trace", + "description": "Modify trace log levels.", + "isHidden": false + }, + { + "name": "returnPool", + "description": "Returns the AS400 connection to the connection pool.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 25, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-pcml.gif" + }, + { + "connectorName": "Azure Data Lake Storage Gen2", + "repoName": "mi-connector-msazuredatalakestorage", + "description": "The Azure Data Lake Storage Gen2 Connector allows you to access the Azure Data Lake Storage Service.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-msazuredatalakestorage", + "version": { + "tagName": "1.0.2", + "releaseId": "218793394", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Config operation", + "isHidden": true + }, + { + "name": "createFileSystem", + "description": "Create File System", + "isHidden": false + }, + { + "name": "deleteFileSystem", + "description": "Delete File System", + "isHidden": false + }, + { + "name": "listFileSystems", + "description": "List File Systems", + "isHidden": false + }, + { + "name": "createDirectory", + "description": "Create Directory in the file system", + "isHidden": false + }, + { + "name": "deleteDirectory", + "description": "Delete Directory in the file system", + "isHidden": false + }, + { + "name": "uploadFile", + "description": "Upload File to the file system", + "isHidden": false + }, + { + "name": "downloadFile", + "description": "Download File from the file system", + "isHidden": false + }, + { + "name": "deleteFile", + "description": "Delete File from the file system", + "isHidden": false + }, + { + "name": "renamePath", + "description": "Rename path of a file or directory in the file system", + "isHidden": false + }, + { + "name": "readFile", + "description": "Read File from the file system", + "isHidden": false + }, + { + "name": "getMetadata", + "description": "Get Metadata of the file system", + "isHidden": false + }, + { + "name": "updateMetadata", + "description": "Update Metadata of the file system", + "isHidden": false + }, + { + "name": "listPaths", + "description": "List Paths of the file system", + "isHidden": false + }, + { + "name": "appendFile", + "description": "Append text to a file in the file system", + "isHidden": false + }, + { + "name": "flushFile", + "description": "Flush the file after appending in the filesystem.", + "isHidden": false + } + ], + "connections": [ + { + "name": "azureDataLake", + "description": "Azure Data Lake Storage Gen2 Connection", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 20, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-connector-msazuredatalakestorage.png" + }, + { + "connectorName": "Ceridian Dayforce", + "repoName": "esb-connector-dayforce", + "description": "Dayforce, provided by Ceridian, is a cloud-based solution for Payroll, Benefits, Workforce Management, Human Resources, Talent Management, Document Management, and Analytics. You can use this connector to connect to perform operations on Dayforce HCM API within WSO2 MI development workflow. ", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-ceridiandayforce", + "version": { + "tagName": "1.0.2", + "releaseId": "191449967", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Init operation", + "isHidden": false + }, + { + "name": "getEmployeeDetails", + "description": "Get employee details REST API", + "isHidden": false + }, + { + "name": "getEmployees", + "description": "Get employees REST API", + "isHidden": false + }, + { + "name": "postEmployee", + "description": "POST employees REST API", + "isHidden": false + }, + { + "name": "patchEmployee", + "description": "PATCH employees REST API", + "isHidden": false + }, + { + "name": "getOrgUnits", + "description": "Get Organization Units REST API", + "isHidden": false + }, + { + "name": "postOrgUnits", + "description": "Post Organization Units REST API", + "isHidden": false + }, + { + "name": "patchOrgUnits", + "description": "Patch Organization Units REST API", + "isHidden": false + }, + { + "name": "getOrgUnitDetails", + "description": "Get Organization Units Details REST API", + "isHidden": false + }, + { + "name": "getReportMetadata", + "description": "Get Organization Units REST API", + "isHidden": false + }, + { + "name": "getReportMetadataDetails", + "description": "Get Organization Units REST API", + "isHidden": false + }, + { + "name": "getReports", + "description": "Get Organization Units REST API", + "isHidden": false + }, + { + "name": "getEmployeeManagers", + "description": "Get employee Managers REST API", + "isHidden": false + }, + { + "name": "getEmployeeWorkAssignmentManagers", + "description": "Get employee Managers REST API", + "isHidden": false + }, + { + "name": "postEmployeeWorkAssignmentManagers", + "description": "Post employee Managers REST API", + "isHidden": false + }, + { + "name": "patchEmployeeWorkAssignmentManagers", + "description": "Patch employee Managers REST API", + "isHidden": false + }, + { + "name": "getDocumentManagementSecurityGroups", + "description": "Retrieve Document Management Security Groups assigned to an employee that control access to documents REST API", + "isHidden": false + }, + { + "name": "getEmployeeLocations", + "description": "Retrieve locations, and their respective authority types, that an employee manages REST API", + "isHidden": false + }, + { + "name": "postEmployeeLocations", + "description": "Assign locations and authority types for an employee to manage REST API", + "isHidden": false + }, + { + "name": "patchEmployeeLocations", + "description": "Update assigned locations and authority types for an employee to manage REST API", + "isHidden": false + }, + { + "name": "getEmployeeRoles", + "description": "Retrieve user roles assigned to an employee REST API", + "isHidden": false + }, + { + "name": "postEmployeeRoles", + "description": "Assign roles to an employee REST API", + "isHidden": false + }, + { + "name": "patchEmployeeRoles", + "description": "Update the assigned roles to an employee REST API", + "isHidden": false + }, + { + "name": "getEmployeeSSOAccounts", + "description": "Retrieve Single Sign-On (SSO) accounts of an employee REST API", + "isHidden": false + }, + { + "name": "postEmployeeSSOAccounts", + "description": "Create Single Sign-On (SSO) accounts of an employee REST API", + "isHidden": false + }, + { + "name": "patchEmployeeSSOAccounts", + "description": "Update Single Sign-On (SSO) accounts of an employee REST API", + "isHidden": false + }, + { + "name": "getUserPayAdjustCodeGroups", + "description": "Retrieve User Pay Adjustment Groups assigned to an employee. These control which pay adjustment codes the employee can assign to timesheets REST API", + "isHidden": false + }, + { + "name": "getEmployeeOrgInfo", + "description": "Get employee Organization Info REST API", + "isHidden": false + }, + { + "name": "getEmployeeWorkAssignments", + "description": "Get employee work assignments REST API", + "isHidden": false + }, + { + "name": "postEmployeeWorkAssignments", + "description": "Post employee work assignments REST API", + "isHidden": false + }, + { + "name": "patchEmployeeWorkAssignments", + "description": "Patch employee work assignments REST API", + "isHidden": false + }, + { + "name": "getEmployeeEmploymentStatuses", + "description": "Retrieve an employee's employment statuses that control how employee's pay, time-off, statutory holidays, etc. are calculated. REST API", + "isHidden": false + }, + { + "name": "postEmployeeEmploymentStatuses", + "description": "Create an employee's employment statuses that control how employee's pay, time-off, statutory holidays, etc. are calculated. REST API", + "isHidden": false + }, + { + "name": "patchEmployeeEmploymentStatuses", + "description": "Update an employee's employment statuses that control how employee's pay, time-off, statutory holidays, etc. are calculated. REST API", + "isHidden": false + }, + { + "name": "getEmployeeClockDeviceGroups", + "description": "Retrieve an employee's clock device groups that control access to the clocks the employee can punch on. REST API", + "isHidden": false + }, + { + "name": "getEmployeeCompensationSummary", + "description": "Retrieve an employee's condensed status information based on compensation changes. REST API", + "isHidden": false + }, + { + "name": "getEmployeeCourses", + "description": "Retrieve courses associated to an employee. REST API", + "isHidden": false + }, + { + "name": "getEmployeeEmploymentAgreements", + "description": "Retrieve the employment agreement information of an employee REST API", + "isHidden": false + }, + { + "name": "postEmployeeEmploymentAgreements", + "description": "Retrieve the employment agreement information of an employee REST API", + "isHidden": false + }, + { + "name": "patchEmployeeEmploymentAgreements", + "description": "Retrieve the employment agreement information of an employee REST API", + "isHidden": false + }, + { + "name": "getEmployeeEmploymentTypes", + "description": "Retrieve employee employment types (i.e: contractor, pensioner, etc.) REST API", + "isHidden": false + }, + { + "name": "getEmployeeHighlyCompensatedEmployees", + "description": "Retrieve highly compensated employee indicators REST API", + "isHidden": false + }, + { + "name": "getEmployeeHRIncidents", + "description": "Retrieve HR incidents attached to an employee REST API", + "isHidden": false + }, + { + "name": "getEmployeeLabourDefaults", + "description": "Retrieve employee labor defaults. Labor defaults specify an employee default postion, project, docket or other timesheet information. REST API", + "isHidden": false + }, + { + "name": "getEmployeeOnboardingPolicies", + "description": "Retrieve onboarding policies assigned to an employee REST API", + "isHidden": false + }, + { + "name": "postEmployeeOnboardingPolicies", + "description": "Assign onboarding policies to an employee REST API", + "isHidden": false + }, + { + "name": "patchEmployeeOnboardingPolicies", + "description": "Update the onboarding policies assigned to an employee REST API", + "isHidden": false + }, + { + "name": "getEmployeePayAdjustmentGroups", + "description": "Retrieve employee pay adjustment groups that control which pay codes can be used in an employee's timesheet REST API", + "isHidden": false + }, + { + "name": "getEmployeePayGradeRates", + "description": "Retrieve employee pay grade rates related to their position rate policies REST API", + "isHidden": false + }, + { + "name": "getEmployeePerformanceRatings", + "description": "Retrieve details on employee performance reviews REST API", + "isHidden": false + }, + { + "name": "getEmployeeProperties", + "description": "Retrieve employee properties that represent custom defined information REST API", + "isHidden": false + }, + { + "name": "postEmployeeProperties", + "description": "Create employee properties that represent custom defined information REST API", + "isHidden": false + }, + { + "name": "patchEmployeeProperties", + "description": "Update employee properties that represent custom defined information REST API", + "isHidden": false + }, + { + "name": "getEmployeeSkills", + "description": "Retrieve skills attached to an employee REST API", + "isHidden": false + }, + { + "name": "getEmployeeTrainingPrograms", + "description": "Retrieve training programs attached to an employee REST API", + "isHidden": false + }, + { + "name": "getEmployeeUnionMemberships", + "description": "Retrieve employee union membership information REST API", + "isHidden": false + }, + { + "name": "getEmployeeWorkContracts", + "description": "Retrieve work contracts used in UK to represent the employee contracted work duration REST API", + "isHidden": false + }, + { + "name": "postEmployeeWorkContracts", + "description": "Create work contracts used in UK to represent the employee contracted work duration REST API", + "isHidden": false + }, + { + "name": "patchEmployeeWorkContracts", + "description": "Update work contracts used in UK to represent the employee contracted work duration REST API", + "isHidden": false + }, + { + "name": "getEmployeeAddresses", + "description": "Retrieve addresses of an employee REST API", + "isHidden": false + }, + { + "name": "postEmployeeAddresses", + "description": "Create addresses of an employee REST API", + "isHidden": false + }, + { + "name": "patchEmployeeAddresses", + "description": "Update addresses of an employee REST API", + "isHidden": false + }, + { + "name": "getEmployeeCANFederalTaxes", + "description": "Retrieve a Canadian employee's total federal claim amount, resident status and authorized tax credits REST API", + "isHidden": false + }, + { + "name": "getEmployeeCANStateTaxes", + "description": "Retrieve a Canadian employee's total provincial claim amount, prescribed deductions and authorized tax credits REST API", + "isHidden": false + }, + { + "name": "getEmployeeCANTaxStatuses", + "description": "Retrieve a Canadian employee's provincial tax filing status (e.g. single, married) REST API", + "isHidden": false + }, + { + "name": "getEmployeeContacts", + "description": "Retrieve contacts of an employee REST API", + "isHidden": false + }, + { + "name": "postEmployeeContacts", + "description": "Create contacts of an employee REST API", + "isHidden": false + }, + { + "name": "patchEmployeeContacts", + "description": "Update contacts of an employee REST API", + "isHidden": false + }, + { + "name": "getEmployeeDirectDeposits", + "description": "Retrieve an employee's direct deposit information REST API", + "isHidden": false + }, + { + "name": "getEmployeeEmergencyContacts", + "description": "Retrieve an employee's emergency contacts REST API", + "isHidden": false + }, + { + "name": "postEmployeeEmergencyContacts", + "description": "Create an employee's emergency contacts REST API", + "isHidden": false + }, + { + "name": "patchEmployeeEmergencyContacts", + "description": "Update an employee's emergency contacts REST API", + "isHidden": false + }, + { + "name": "getEmployeeEthnicities", + "description": "Retrieve an employee's ethnicity information REST API", + "isHidden": false + }, + { + "name": "getEmployeeHealthAndWellness", + "description": "Retrieve an employee's tobacco use status REST API", + "isHidden": false + }, + { + "name": "getEmployeeMaritalStatuses", + "description": "Retrieve an employee's marital status information REST API", + "isHidden": false + }, + { + "name": "postEmployeeMaritalStatuses", + "description": "Create an employee's marital status information REST API", + "isHidden": false + }, + { + "name": "patchEmployeeMaritalStatuses", + "description": "Update an employee's marital status information REST API", + "isHidden": false + }, + { + "name": "getEmployeeUSFederalTaxes", + "description": "Retrieve a US employee's total federal claim amount, resident status and authorized tax credits REST API", + "isHidden": false + }, + { + "name": "getEmployeeUSStateTaxes", + "description": "Retrieve a US employee's total state claim amount, prescribed deductions and authorized tax credits REST API", + "isHidden": false + }, + { + "name": "getEmployeeUSTaxStatuses", + "description": "Retrieve a US employee's state tax filing status (e.g. single, married) REST API", + "isHidden": false + }, + { + "name": "getAListOfDocuments", + "description": "This request allows to retrieve the list of documents attached to an employee. The response includes the document GUID used to retrieve contents with Get Document Details REST API", + "isHidden": false + }, + { + "name": "getDocumentDetails", + "description": "This request allows to retrieve the contents of a particular document. It requires the document GUID that can be obtained with Get a List of Documents REST API", + "isHidden": false + }, + { + "name": "getAvailability", + "description": "Availabilty represents the periods an employee is available to be scheduled for work. This request allows you to retrieve a single employee's daily availability between two dates. In order to use it, an employee XRefCodes is needed. Employee XRefCodes can be retrieved with GET Employees REST API", + "isHidden": false + }, + { + "name": "getSchedules", + "description": "Retrieve the configured schedules for a single employee for every day within a defined period. In order to use this request, an employee XRefCodes is needed. Employee XRefCodes can be retrieved with GET Employees REST API", + "isHidden": false + }, + { + "name": "getTimeAwayFromWork", + "description": "Retrieve the scheduled time away from work (TAFW) periods of a single employee. In order to use this request, an employee XRefCodes is needed. Employee XRefCodes can be retrieved with GET Employees REST API", + "isHidden": false + }, + { + "name": "getEmployeePunches", + "description": "Extract the worked shift data for several employees at a time. Required parameters for the call include FilterTransactionStartTimeUTC and FilterTransactionEndTimeUTC. The system will search for all employee punch records that were modified between these two dates. The two dates must be 7 days apart or less. REST API", + "isHidden": false + }, + { + "name": "getEmployeeRawPunches", + "description": "Retrieve raw punches as they are entered at the clock REST API", + "isHidden": false + }, + { + "name": "postEmployeeRawPunches", + "description": "Insert a raw punch. This raw punch record will be treated as a punch coming from the clock and be validated against configured punch policies REST API", + "isHidden": false + }, + { + "name": "getJobPostings", + "description": "Availabilty represents the periods an employee is available to be scheduled for work. This request allows you to retrieve a single employee's daily availability between two dates. In order to use it, an employee XRefCodes is needed. Employee XRefCodes can be retrieved with GET Employees REST API", + "isHidden": false + }, + { + "name": "patchI9Order", + "description": "Update I-9 employment eligibility verification order status REST API", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 56, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-dayforce.gif" + }, + { + "connectorName": "CSV", + "repoName": "mediation-csv-module", + "description": "CSV Module provides the capability to transform CSV payloads into JSON and XML and provides tools to transform CSV payload format.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.module", + "mavenArtifactId": "mi-module-csv", + "version": { + "tagName": "2.0.0", + "releaseId": "214700833", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "csvToCsv", + "description": "Transform a CSV payload", + "isHidden": false + }, + { + "name": "csvToJson", + "description": "Convert CSV to Json", + "isHidden": false + }, + { + "name": "csvToXml", + "description": "Convert CSV to XML", + "isHidden": false + }, + { + "name": "jsonToCsv", + "description": "Convert Json to CSV", + "isHidden": false + }, + { + "name": "xmlToCsv", + "description": "Transform XML to CSV", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": { + "1.0.6": "191799733", + "1.0.7": "204643750" + }, + "connectorRank": 3, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mediation-csv-module.png" + }, + { + "connectorName": "Email", + "repoName": "esb-connector-email", + "description": "The Email Connector allows you to list, send emails and perform other actions such as mark email as read, mark email as deleted, delete email and expunge folder on different mailboxes using protocols IMAP, POP3 and SMTP.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-email", + "version": { + "tagName": "2.0.0", + "releaseId": "214089850", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Init operation", + "isHidden": true + }, + { + "name": "list", + "description": "List all the emails.", + "isHidden": false + }, + { + "name": "expungeFolder", + "description": "Delete all the messages scheduled for deletion with the DELETED flag set from the mailbox.", + "isHidden": false + }, + { + "name": "markAsDeleted", + "description": "Mark an incoming email as DELETED. Not physically deleted, only a state change.", + "isHidden": false + }, + { + "name": "markAsRead", + "description": "Marks a single email as READ changing its state in the specified mailbox folder.", + "isHidden": false + }, + { + "name": "send", + "description": "Sends an email message.", + "isHidden": false + }, + { + "name": "delete", + "description": "Deletes an email.", + "isHidden": false + }, + { + "name": "getEmailBody", + "description": "Retrieves email body by index.", + "isHidden": false + }, + { + "name": "getEmailAttachment", + "description": "Retrieves email attachment by index.", + "isHidden": false + } + ], + "connections": [ + { + "name": "POP3", + "description": "Connection for retrieving emails via POP3.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-email_POP3.svg" + }, + { + "name": "POP3S", + "description": "Secure connection for retrieving emails via POP3S.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-email_POP3S.svg" + }, + { + "name": "IMAP", + "description": "Connection for accessing emails via IMAP.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-email_IMAP.svg" + }, + { + "name": "IMAPS", + "description": "Secure connection for accessing emails via IMAPS.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-email_IMAPS.svg" + }, + { + "name": "SMTP", + "description": "Connection for sending emails via SMTP.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-email_SMTP.svg" + }, + { + "name": "SMTPS", + "description": "Secure connection for sending emails via SMTPS.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-email_SMTPS.svg" + } + ] + }, + "otherVersions": { + "1.1.4": "198873772" + }, + "connectorRank": 6, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-email.png" + }, + { + "connectorName": "Epic", + "repoName": "esb-connector-epic", + "description": "The Epic connector allows you to access the Epic FHIR APIs through WSO2 Micro Integrator (WSO2 EI). Epic connector currently supports the R4 version of the FHIR standard.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-epic", + "version": { + "tagName": "2.0.1", + "releaseId": "191923006", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "create", + "description": "Create a new resource in the Epic FHIR server.", + "isHidden": false + }, + { + "name": "getCapabilityStatement", + "description": "Retrieve the CapabilityStatement resource from the Epic system.", + "isHidden": false + }, + { + "name": "readById", + "description": "Retrieve a resource by its ID from the Epic system.", + "isHidden": false + }, + { + "name": "searchAccount", + "description": "Search for account resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchAdverseEvent", + "description": "Search for adverse event resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchAllergyIntolerance", + "description": "Search for allergy intolerance resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchAppointment", + "description": "Search for appointment resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchBinary", + "description": "Search for binary resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchBodyStructure", + "description": "Search for body structure resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchCarePlan", + "description": "Search for care plan resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchCareTeam", + "description": "Search for care team resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchCommunication", + "description": "Search for communication resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchCondition", + "description": "Search for condition resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchConsent", + "description": "Search for consent resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchContract", + "description": "Search for contract resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchCoverage", + "description": "Search for coverage resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchDevice", + "description": "Search for device resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchDeviceRequest", + "description": "Search for device request resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchDeviceUseStatement", + "description": "Search for device use statement resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchDiagnosticReport", + "description": "Search for diagnostic report resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchDocumentReference", + "description": "Search for document reference resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchEncounter", + "description": "Search for encounter resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchEpisodeOfCare", + "description": "Search for episode of care resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchExplanationOfBenefit", + "description": "Search for explanation of benefit resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchFamilyMemberHistory", + "description": "Search for family member history resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchFlag", + "description": "Search for flag resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchGoal", + "description": "Search for goal resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchGroup", + "description": "Search for group resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchImagingStudy", + "description": "Search for imaging study resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchImmunization", + "description": "Search for immunization resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchImmunizationRecommendation", + "description": "Search for immunization recommendation resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchList", + "description": "Search for list resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchLocation", + "description": "Search for location resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchMeasure", + "description": "Search for measure resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchMeasureReport", + "description": "Search for measure report resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchMedia", + "description": "Search for media resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchMedication", + "description": "Search for medication resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchMedicationAdministration", + "description": "Search for medication administration resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchMedicationDispense", + "description": "Search for medication dispense resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchMedicationRequest", + "description": "Search for medication request resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchNutritionOrder", + "description": "Search for nutrition order resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchObservation", + "description": "Search for observation resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchOrganization", + "description": "Search for organization resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchPatient", + "description": "Search for patient resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchPractitioner", + "description": "Search for practitioner resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchPractitionerRole", + "description": "Search for practitioner role resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchProcedure", + "description": "Search for procedure resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchQuestionnaire", + "description": "Search for questionnaire resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchQuestionnaireResponse", + "description": "Search for questionnaire response resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchRelatedPerson", + "description": "Search for related person resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchRequestGroup", + "description": "Search for request group resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchRequestStudy", + "description": "Search for research study resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchResearchSubject", + "description": "Search for research subject resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchServiceRequest", + "description": "Search for service request resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchSpecimen", + "description": "Search for specimen resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchSubstance", + "description": "Search for substance resources in the Epic system.", + "isHidden": false + }, + { + "name": "searchTask", + "description": "Search for task resources in the Epic system.", + "isHidden": false + }, + { + "name": "update", + "description": "Update resources in the Epic system.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 78, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-epic.png" + }, + { + "connectorName": "Facebook Ads", + "repoName": "esb-connector-facebookads", + "description": "The Facebook Ads Connector allows you to access the Facebook Marketing API. This lets you create, update, and delete ad campaigns and ads.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-facebookads", + "version": { + "tagName": "1.1.0", + "releaseId": "191815211", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Config operation", + "isHidden": true + }, + { + "name": "createAd", + "description": "Create an ad.", + "isHidden": false + }, + { + "name": "createAdSet", + "description": "Creates an ad set.", + "isHidden": false + }, + { + "name": "createCampaign", + "description": "Create a campaign.", + "isHidden": false + }, + { + "name": "deleteAd", + "description": "Deletes an ad.", + "isHidden": false + }, + { + "name": "deleteAdSet", + "description": "Deletes an ad set.", + "isHidden": false + }, + { + "name": "deleteCampaign", + "description": "Deletes a campaign.", + "isHidden": false + }, + { + "name": "dissociateCampaign", + "description": "Dissociate a campaign from an AdAccount.", + "isHidden": false + }, + { + "name": "getAd", + "description": "Returns data of an ad.", + "isHidden": false + }, + { + "name": "getAdSet", + "description": "Return data related to an ad set.", + "isHidden": false + }, + { + "name": "getAdSets", + "description": "Returns all ad sets from one ad account.", + "isHidden": false + }, + { + "name": "getAds", + "description": "Returns ads under this ad account.", + "isHidden": false + }, + { + "name": "getCampaigns", + "description": "Returns campaigns under this ad account.", + "isHidden": false + }, + { + "name": "updateAd", + "description": "Updates an ad.", + "isHidden": false + }, + { + "name": "updateAdSet", + "description": "Updates an ad set.", + "isHidden": false + }, + { + "name": "updateCampaign", + "description": "Updates a campaign.", + "isHidden": false + }, + { + "name": "createAdCreative", + "description": "Creates an ad creative.", + "isHidden": false + }, + { + "name": "createCustomAudience", + "description": "Creates a custom audience.", + "isHidden": false + }, + { + "name": "updateCustomAudience", + "description": "Updates a custom audience.", + "isHidden": false + }, + { + "name": "addUsersToAudience", + "description": "Add users to your custom audience.", + "isHidden": false + }, + { + "name": "removeUsersFromAudience", + "description": "Remove users from your custom audience.", + "isHidden": false + }, + { + "name": "getCustomAudiences", + "description": "Returns all the custom audiences.", + "isHidden": false + } + ], + "connections": [ + { + "name": "facebookAds", + "description": "Connection for interacting with Facebook Ads.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 51, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-facebookads.png" + }, + { + "connectorName": "FHIR Base", + "repoName": "mediation-fhirbase-module", + "description": "The FHIR Base Module facilitates basic CRUD operations and searches on FHIR resources, bundling, error handling and ensuring standardized interactions with FHIR-compliant servers.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.module", + "mavenArtifactId": "mi-module-fhirbase", + "version": { + "tagName": "1.1.1", + "releaseId": "191606985", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "addBundleEntry", + "description": "Add an entry to the Bundle.", + "isHidden": false + }, + { + "name": "addBundleLink", + "description": "Add a link to the Bundle.", + "isHidden": false + }, + { + "name": "addElement", + "description": "Add an element datatype to a resource.", + "isHidden": false + }, + { + "name": "createAddress", + "description": "Create an Address datatype.", + "isHidden": false + }, + { + "name": "createAge", + "description": "Create an Age datatype.", + "isHidden": false + }, + { + "name": "createAnnotation", + "description": "Create an Annotation datatype.", + "isHidden": false + }, + { + "name": "createAttachment", + "description": "Create an Attachment datatype.", + "isHidden": false + }, + { + "name": "createBundle", + "description": "Create a Bundle resource.", + "isHidden": false + }, + { + "name": "createCodeableConcept", + "description": "Create a CodeableConcept datatype.", + "isHidden": false + }, + { + "name": "createCoding", + "description": "Create a Coding datatype.", + "isHidden": false + }, + { + "name": "createContactDetail", + "description": "Create a ContactDetail datatype.", + "isHidden": false + }, + { + "name": "createContactPoint", + "description": "Create a ContactPoint datatype.", + "isHidden": false + }, + { + "name": "createCount", + "description": "Create a Count datatype.", + "isHidden": false + }, + { + "name": "createContributor", + "description": "Create a Contributor datatype.", + "isHidden": false + }, + { + "name": "createDataRequirement", + "description": "Create a DataRequirement datatype.", + "isHidden": false + }, + { + "name": "createDosage", + "description": "Create a Dosage datatype.", + "isHidden": false + }, + { + "name": "createDuration", + "description": "Create a Duration datatype.", + "isHidden": false + }, + { + "name": "createExpression", + "description": "Create an Expression datatype.", + "isHidden": false + }, + { + "name": "createHumanName", + "description": "Create a HumanName datatype.", + "isHidden": false + }, + { + "name": "createIdentifier", + "description": "Create an Identifier datatype.", + "isHidden": false + }, + { + "name": "createMeta", + "description": "Create a Meta datatype.", + "isHidden": false + }, + { + "name": "createNarrative", + "description": "Create a Narrative datatype.", + "isHidden": false + }, + { + "name": "createParameterDefinition", + "description": "Create a ParameterDefinition datatype.", + "isHidden": false + }, + { + "name": "createPeriod", + "description": "Create a Period datatype.", + "isHidden": false + }, + { + "name": "createQuantity", + "description": "Create a Quantity datatype.", + "isHidden": false + }, + { + "name": "createRange", + "description": "Create a Range datatype.", + "isHidden": false + }, + { + "name": "createRatio", + "description": "Create a Ratio datatype.", + "isHidden": false + }, + { + "name": "createReference", + "description": "Create a Reference datatype.", + "isHidden": false + }, + { + "name": "createRelatedArtifact", + "description": "Create a RelatedArtifact datatype.", + "isHidden": false + }, + { + "name": "createSampledData", + "description": "Create a SampledData datatype.", + "isHidden": false + }, + { + "name": "createSignature", + "description": "Create a Signature datatype.", + "isHidden": false + }, + { + "name": "createSimpleQuantity", + "description": "Create a SimpleQuantity datatype.", + "isHidden": false + }, + { + "name": "createTiming", + "description": "Create a Timing datatype.", + "isHidden": false + }, + { + "name": "createTriggerDefinition", + "description": "Create a TriggerDefinition datatype.", + "isHidden": false + }, + { + "name": "createUsageContext", + "description": "Create a UsageContext datatype.", + "isHidden": false + }, + { + "name": "evaluateFHIRPath", + "description": "Evaluate a FHIRPath expression.", + "isHidden": false + }, + { + "name": "serialize", + "description": "Serialize a resource to a specific format.", + "isHidden": false + }, + { + "name": "validate", + "description": "Validate a resource.", + "isHidden": false + }, + { + "name": "setBundleIdentifier", + "description": "Set the identifier for the Bundle.", + "isHidden": false + }, + { + "name": "setBundleType", + "description": "Set the type for the Bundle.", + "isHidden": false + }, + { + "name": "setBundleTimestamp", + "description": "Set the timestamp for the Bundle.", + "isHidden": false + }, + { + "name": "setBundleTotal", + "description": "Set the total for the Bundle.", + "isHidden": false + }, + { + "name": "setBundleSignature", + "description": "Set the signature for the Bundle.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 28, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mediation-fhirbase-module.gif" + }, + { + "connectorName": "FHIR Repository", + "repoName": "esb-connector-fhirrepository", + "description": "The FHIR Repository Connector allows you to connect to a FHIR repository/store and perform standard FHIR interactions.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-fhirrepository", + "version": { + "tagName": "1.0.1", + "releaseId": "178325455", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Init configuration for FHIR repository connector.", + "isHidden": false + }, + { + "name": "batch", + "description": "Create a new FHIR bundle.", + "isHidden": false + }, + { + "name": "connect", + "description": "Proxies a FHIR repository so that any type of API requests are routed intelligently.", + "isHidden": false + }, + { + "name": "create", + "description": "Create a new resource with a server-assigned ID.", + "isHidden": false + }, + { + "name": "delete", + "description": "Delete a resource.", + "isHidden": false + }, + { + "name": "getCapabilityStatement", + "description": "Get a capability statement for the system.", + "isHidden": false + }, + { + "name": "patch", + "description": "Update an existing resource by posting a set of changes to it.", + "isHidden": false + }, + { + "name": "readById", + "description": "Retrieve a resource using its ID.", + "isHidden": false + }, + { + "name": "search", + "description": "Search resources by providing query parameters.", + "isHidden": false + }, + { + "name": "update", + "description": "Update an existing resource by its ID.", + "isHidden": false + }, + { + "name": "vread", + "description": "Read the state of a specific version of the resource.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 193, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-fhirrepository.png" + }, + { + "connectorName": "File", + "repoName": "esb-connector-file", + "description": "The File connector allows you to connect to Local and remote (using protocols FTP, FTPS, SFTP) file systems and perform file related operations. The file connector uses the Apache Commons VFS I/O functionalities to execute operations. (Note: You need to have U2 updated versions after 25 th JUL 2023 up to MI-1.2.0 to MI-4.2.0)\n\n", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-file", + "version": { + "tagName": "5.0.0", + "releaseId": "220257963", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Init operation", + "isHidden": true + }, + { + "name": "createDirectory", + "description": "Creates a new directory on directory path.", + "isHidden": false + }, + { + "name": "listFiles", + "description": "Lists all the files in the directory path that match a matcher.", + "isHidden": false + }, + { + "name": "delete", + "description": "Deletes the file specified by the path.", + "isHidden": false + }, + { + "name": "copy", + "description": "Copies the file or folder specified by sourcePath into targetPath.", + "isHidden": false + }, + { + "name": "move", + "description": "Moves the file or folder specified by sourcePath into targetPath.", + "isHidden": false + }, + { + "name": "rename", + "description": "Rename the file to the new name specified.", + "isHidden": false + }, + { + "name": "unzip", + "description": "Unzip file to target directory.", + "isHidden": false + }, + { + "name": "splitFile", + "description": "Split a file into multiple smaller files.", + "isHidden": false + }, + { + "name": "mergeFiles", + "description": "Merge multiple files in a folder to a single file.", + "isHidden": false + }, + { + "name": "checkExist", + "description": "Check if a file or directory exists.", + "isHidden": false + }, + { + "name": "exploreZipFile", + "description": "List Items In ZIP File Without Extracting.", + "isHidden": false + }, + { + "name": "compress", + "description": "Compress file or folder.", + "isHidden": false + }, + { + "name": "read", + "description": "Read a specific file or a file in a folder.", + "isHidden": false + }, + { + "name": "write", + "description": "Create a file or write content to a file.", + "isHidden": false + } + ], + "connections": [ + { + "name": "LOCAL", + "description": "Connection for the local file system.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-file_LOCAL.svg" + }, + { + "name": "FTP", + "description": "Connection for an FTP server.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-file_FTP.svg" + }, + { + "name": "FTPS", + "description": "Secure connection for an FTPS server.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-file_FTPS.svg" + }, + { + "name": "SFTP", + "description": "Secure connection for an SFTP server.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-file_SFTP.svg" + }, + { + "name": "SMB2", + "description": "Connection for an SMB2 file share.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-file_SMB2.svg" + } + ] + }, + "otherVersions": { + "4.0.33": "191454532", + "4.0.34": "197719372", + "4.0.35": "201356411", + "4.0.36": "206446749", + "4.0.37": "206951834", + "4.0.40": "217219191" + }, + "connectorRank": 1, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-file.png" + }, + { + "connectorName": "Gmail", + "repoName": "esb-connector-gmail", + "description": "Gmail connector allows you to send and access e-mail messages which are in your Gmail mailbox. The connector uses the standard IMAP and SMTP protocols with the extensions provided by Google to provide a more Gmail-like experience.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-gmail", + "version": { + "tagName": "4.0.9", + "releaseId": "221991138", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "trashMessage", + "description": "Trash Message", + "isHidden": false + }, + { + "name": "createDraft", + "description": "Create Draft", + "isHidden": false + }, + { + "name": "untrashMessage", + "description": "Untrash Message", + "isHidden": false + }, + { + "name": "listLabels", + "description": "List Labels", + "isHidden": false + }, + { + "name": "listHistory", + "description": "List History", + "isHidden": false + }, + { + "name": "untrashThread", + "description": "Untrash Thread", + "isHidden": false + }, + { + "name": "sendMail", + "description": "Send Message", + "isHidden": false + }, + { + "name": "listAllThreads", + "description": "List Threads", + "isHidden": false + }, + { + "name": "modifyExistingThread", + "description": "Modify Thread", + "isHidden": false + }, + { + "name": "readThread", + "description": "Read Thread", + "isHidden": false + }, + { + "name": "updateLabel", + "description": "Update Label", + "isHidden": false + }, + { + "name": "getUserProfile", + "description": "Get User Profile", + "isHidden": false + }, + { + "name": "readLabel", + "description": "", + "isHidden": false + }, + { + "name": "modifyExistingMessage", + "description": "Modify Message", + "isHidden": false + }, + { + "name": "deleteDraft", + "description": "Delete Draft", + "isHidden": false + }, + { + "name": "createLabel", + "description": "Create Label", + "isHidden": false + }, + { + "name": "getUserProfile", + "description": "Get User Profile", + "isHidden": false + }, + { + "name": "sendMailWithAttachment", + "description": "Send Message with Attachment", + "isHidden": false + }, + { + "name": "readDraft", + "description": "Read Draft", + "isHidden": false + }, + { + "name": "deleteMessage", + "description": "Delete Message", + "isHidden": false + }, + { + "name": "listDrafts", + "description": "List Drafts", + "isHidden": false + }, + { + "name": "endSession", + "description": "End Session", + "isHidden": false + }, + { + "name": "trashThread", + "description": "Trash Thread", + "isHidden": false + }, + { + "name": "listAllMails", + "description": "List Messages", + "isHidden": false + }, + { + "name": "deleteThread", + "description": "Delete Thread", + "isHidden": false + }, + { + "name": "readMail", + "description": "Read Message", + "isHidden": false + }, + { + "name": "deleteLabel", + "description": "Delete Label", + "isHidden": false + } + ], + "connections": [ + { + "name": "GMAIL", + "description": "GMAIL Connection", + "iconUrl": "" + } + ] + }, + "otherVersions": { + "3.0.11": "206024903", + "4.0.0": "214691795" + }, + "connectorRank": 94, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-gmail.png" + }, + { + "connectorName": "Google Ads", + "repoName": "esb-connector-googleads", + "description": "The Google Ads Connector allows you to access the Google Ads API. This lets you create, update, and delete ad campaigns and ads.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-googleads", + "version": { + "tagName": "1.1.0", + "releaseId": "191815650", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Config operation", + "isHidden": true + }, + { + "name": "adGroupAdsMutate", + "description": "Creates, updates, or removes ads. Operation statuses are returned.", + "isHidden": false + }, + { + "name": "adGroupsMutate", + "description": "Creates, updates, or removes ad groups. Operation statuses are returned.", + "isHidden": false + }, + { + "name": "adsMutate", + "description": "Updates ads. Operation statuses are returned. Updating ads is not supported for TextAd, ExpandedDynamicSearchAd, GmailAd and ImageAd.", + "isHidden": false + }, + { + "name": "campaignBudgets", + "description": "Creates, updates, or removes campaign budgets. Operation statuses are returned.", + "isHidden": false + }, + { + "name": "campaignsMutate", + "description": "Creates, updates, or removes campaigns. Operation statuses are returned.", + "isHidden": false + }, + { + "name": "createCustomerClient", + "description": "Creates a new client under manager. The new client customer is returned.", + "isHidden": false + }, + { + "name": "search", + "description": "Returns all rows that match the search query.", + "isHidden": false + }, + { + "name": "customersMutate", + "description": "Updates a customer. Operation statuses are returned.", + "isHidden": false + }, + { + "name": "audiencesMutate", + "description": "Creates audiences. Operation statuses are returned.", + "isHidden": false + }, + { + "name": "customAudiencesMutate", + "description": "Creates or updates custom audiences. Operation statuses are returned.", + "isHidden": false + }, + { + "name": "campaignCriteriaMutate", + "description": "Creates, updates, or removes criteria. Operation statuses are returned.", + "isHidden": false + }, + { + "name": "userListsMutate", + "description": "Creates or updates user lists. Operation statuses are returned.", + "isHidden": false + }, + { + "name": "userDataMutate", + "description": "Add or remove users to an user list.", + "isHidden": false + }, + { + "name": "getCustomers", + "description": "Returns all customers.", + "isHidden": false + }, + { + "name": "getCampaigns", + "description": "Returns all campaigns.", + "isHidden": false + }, + { + "name": "getUserLists", + "description": "Returns all user lists.", + "isHidden": false + } + ], + "connections": [ + { + "name": "googleAds", + "description": "Connection for interacting with Google Ads.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 50, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-googleads.png" + }, + { + "connectorName": "Google BigQuery", + "repoName": "mi-connector-bigquery", + "description": "The BigQuery connector allows you to access the BigQuery REST API through WSO2 ESB. BigQuery is a tool that allows you to execute SQL-like queries on large amounts of data at outstanding speeds.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-bigquery", + "version": { + "tagName": "1.0.11", + "releaseId": "191448469", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Common method having the configurations applicable to all the business methods in the Connector.", + "isHidden": false + }, + { + "name": "getAccessTokenFromAuthorizationCode", + "description": "Get a new access token by negotiating the authorization code along with client_secret and client_id.", + "isHidden": false + }, + { + "name": "getAccessTokenFromRefreshToken", + "description": "Get a new access token by negotiating the refresh token along with client_secret and client_id.", + "isHidden": false + }, + { + "name": "getAccessTokenFromServiceAccount", + "description": "Get an access token from the service account..", + "isHidden": false + }, + { + "name": "getDataset", + "description": "Gets the specified dataset by dataset ID.", + "isHidden": false + }, + { + "name": "listDatasets", + "description": "List datasets of a project.", + "isHidden": false + }, + { + "name": "runQuery", + "description": "Execute query.", + "isHidden": false + }, + { + "name": "listProjects", + "description": "List projects.", + "isHidden": false + }, + { + "name": "listTabledata", + "description": "List available tabledata.", + "isHidden": false + }, + { + "name": "insertAllTableData", + "description": "Insert tabledata into a table.", + "isHidden": false + }, + { + "name": "getTable", + "description": "Gets the specified table by table ID.", + "isHidden": false + }, + { + "name": "listTables", + "description": "List tables.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 26, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-connector-bigquery.png" + }, + { + "connectorName": "Google Firebase", + "repoName": "mi-connector-googlefirebase", + "description": "The Google Firebase connector allows you to integrate your own back-end services with Firebase Cloud Messaging (FCM). It handles authenticating with Firebase servers while facilitating sending messages and managing topic subscriptions.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-googlefirebase", + "version": { + "tagName": "1.0.3", + "releaseId": "191456144", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Get the basic details of Firebase API", + "isHidden": false + }, + { + "name": "sendMessage", + "description": "Send firebase message", + "isHidden": false + }, + { + "name": "subscribeToTopic", + "description": "Subscribe a device to a topic", + "isHidden": false + }, + { + "name": "unsubscribeFromTopic", + "description": "UnSubscribe devices from a topic", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 99, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-connector-googlefirebase.png" + }, + { + "connectorName": "Google Pub/Sub", + "repoName": "mi-connector-googlepubsub", + "description": "The Google Pub/Sub connector allows you to access the Google Cloud Pub/Sub API Version v1 through WSO2 MI. Google Cloud Pub/Sub is a fully-managed real-time messaging service that allows you to send and receive messages between independent applications.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-googlepubsub", + "version": { + "tagName": "1.0.2", + "releaseId": "191488221", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "configure google pub/sub connector", + "isHidden": false + }, + { + "name": "getAccessTokenFromRefreshToken", + "description": "Get the access token from refresh token", + "isHidden": false + }, + { + "name": "createTopicSubscription", + "description": "Creates a subscription to a given topic", + "isHidden": false + }, + { + "name": "pullMessage", + "description": "Pulls messages from the server", + "isHidden": false + }, + { + "name": "createTopic", + "description": "create a Topic.", + "isHidden": false + }, + { + "name": "publishMessage", + "description": "Adds message to the topic", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 101, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-connector-googlepubsub.gif" + }, + { + "connectorName": "Google Spreadsheet", + "repoName": "esb-connector-googlespreadsheet", + "description": "The Google Spreadsheet connector allows you to work with spreadsheets on Google Drive, a free, web-based service that allows users to create and edit spreadsheet documents online while collaborating in real-time with other users. The connector uses the Google Spreadsheet API version v4 to connect to Google Drive and read, create, and update spreadsheets.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-googlespreadsheet", + "version": { + "tagName": "4.0.0", + "releaseId": "215546077", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "Get Sheet Metadata", + "description": "provides a feed to access the sheet metadata contained within a given spreadsheet.", + "isHidden": false + }, + { + "name": "Create New Sheet", + "description": "creates a new spreadsheet.", + "isHidden": false + }, + { + "name": "Add Sheet", + "description": "Add a sheet or multiple sheets to a spreadsheet.", + "isHidden": false + }, + { + "name": "Delete Sheet", + "description": "Remove a sheet or multiple sheets from a given spreadsheet.", + "isHidden": false + }, + { + "name": "Copy Sheet", + "description": "Copies a single sheet from a spreadsheet to another spreadsheet.", + "isHidden": false + }, + { + "name": "Update Sheet Properties", + "description": "Update all sheet properties.", + "isHidden": false + }, + { + "name": "Add Rows And Columns", + "description": "Append row/rows/column/columns of data.", + "isHidden": false + }, + { + "name": "Delete Dimension", + "description": "removing rows, columns and remove part of a row or column.", + "isHidden": false + }, + { + "name": "Get Cell Data", + "description": "Retrieve any set of cell data from a sheet and cell contents as input values (as would be entered by a user at a keyboard) and/or the outputs of formula (if numeric)", + "isHidden": false + }, + { + "name": "Get Multiple Cell Data", + "description": "Retrieve any set of cell data including multiple ranges from a sheet and cell contents as input values (as would be entered by a user at a keyboard) and/or the outputs of formula (if numeric)", + "isHidden": false + }, + { + "name": "Edit Cell Data", + "description": "Edit the content of the cell with new values.", + "isHidden": false + }, + { + "name": "Update Multiple Cells", + "description": "Edit the content of multiple cell with new values.", + "isHidden": false + }, + { + "name": "Append Dimension", + "description": "Append empty rows or columns at the end of the sheet.", + "isHidden": false + }, + { + "name": "Update Cell Borders", + "description": "Edit cell borders", + "isHidden": false + }, + { + "name": "Repeat Cell Style", + "description": "Repeat formatting of the cell into over a range of cells.", + "isHidden": false + }, + { + "name": "Merge Cells", + "description": "Merge range of cells into a one cell.", + "isHidden": false + }, + { + "name": "Set Data Validation", + "description": "Apply data validation rule to a range.", + "isHidden": false + }, + { + "name": "Copy Range", + "description": "Copy cell formatting in one range and paste it into another range on the same sheet.", + "isHidden": false + }, + { + "name": "Cut Range", + "description": "cuts the one range and pastes its data, formats, formulas, and merges to the another range on the same sheet.", + "isHidden": false + }, + { + "name": "Update Conditional Format Rule", + "description": "Update a conditional formatting rule or its priority.", + "isHidden": false + }, + { + "name": "Add Conditional Format Rule", + "description": "Establishes a new conditional formatting rule.", + "isHidden": false + }, + { + "name": "Delete Conditional Format Rule", + "description": "Delete a conditional formatting rule.", + "isHidden": false + }, + { + "name": "Update Dimension Properties", + "description": "Adjust column width or row height.", + "isHidden": false + }, + { + "name": "Resize Dimension", + "description": "Automatically resize a column.", + "isHidden": false + }, + { + "name": "Insert Dimension", + "description": "Insert an empty row or column at the end or in the middle.", + "isHidden": false + }, + { + "name": "Move Dimension", + "description": "Move a row or column / range of rows or columns.", + "isHidden": false + }, + { + "name": "Sort Range", + "description": "Sort a range with multiple sorting specifications.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": { + "3.0.2": "191457103" + }, + "connectorRank": 102, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-googlespreadsheet.png" + }, + { + "connectorName": "HL7v2 to FHIR", + "repoName": "mediation-hl7v2tofhir-module", + "description": "Converts HL7 V2 messages to FHIR resources, ensuring interoperability between traditional HL7 V2 formats and modern FHIR standards.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.module", + "mavenArtifactId": "mi-module-hl7v2tofhir", + "version": { + "tagName": "1.0.1", + "releaseId": "191923371", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "createResources", + "description": "Init configuration for FHIR repository connector.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 194, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mediation-hl7v2tofhir-module.gif" + }, + { + "connectorName": "HTTP", + "repoName": "mi-connector-http", + "description": "The HTTP Connector allows you to access and interact with HTTP endpoints. This lets you send and receive HTTP requests and responses.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-http", + "version": { + "tagName": "0.1.11", + "releaseId": "219717293", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Config operation", + "isHidden": true + }, + { + "name": "get", + "description": "Send an HTTP GET request.", + "isHidden": false + }, + { + "name": "delete", + "description": "Send an HTTP DELETE request.", + "isHidden": false + }, + { + "name": "post", + "description": "Send an HTTP POST request.", + "isHidden": false + }, + { + "name": "put", + "description": "Send an HTTP PUT request.", + "isHidden": false + }, + { + "name": "patch", + "description": "Send an HTTP PATCH request.", + "isHidden": false + }, + { + "name": "head", + "description": "Send an HTTP HEAD request.", + "isHidden": false + }, + { + "name": "options", + "description": "Send an HTTP OPTIONS request.", + "isHidden": false + } + ], + "connections": [ + { + "name": "HTTP", + "description": "Connection for interacting with HTTP endpoints.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-connector-http_HTTP.svg" + }, + { + "name": "HTTPS", + "description": "Connection for interacting with HTTPS endpoints.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/mi-connector-http_HTTPS.svg" + } + ] + }, + "otherVersions": {}, + "connectorRank": 5, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-connector-http.png" + }, + { + "connectorName": "ISO8583", + "repoName": "esb-connector-iso8583", + "description": "ISO8583 connector allows you to send the ISO8583 standard messages through WSO2 MI. ISO8583 is a message standard which is using in financial transactions. There are various versions in ISO8583 standard, Here the connector is developed based on 1987 version.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-iso8583", + "version": { + "tagName": "1.0.4", + "releaseId": "191925856", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "configure ISO8583 connector.", + "isHidden": false + }, + { + "name": "sendMessage", + "description": "sendMessage (ISO8583 Message) method for ISO8583 Connector.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 14, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-iso8583.png" + }, + { + "connectorName": "Jira", + "repoName": "esb-connector-jira", + "description": "The JIRA connector allows you to connect to JIRA, an online issue-tracking database. The connector uses the JIRA REST API version 6.1 to connect to JIRA, view, and update issues, work with filters, and more.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-jira", + "version": { + "tagName": "1.0.6", + "releaseId": "191458827", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "configure jira connector", + "isHidden": false + }, + { + "name": "getDashboards", + "description": "get available jira dashboards", + "isHidden": false + }, + { + "name": "getDashboardById", + "description": "get jira dashboard by ID", + "isHidden": false + }, + { + "name": "createFilter", + "description": "Creates a new filter, and returns newly created filter. Currently sets permissions just using the users default sharing permissions", + "isHidden": false + }, + { + "name": "deleteFilter", + "description": "Delete a filter.", + "isHidden": false + }, + { + "name": "getFavouriteFilters", + "description": "Returns the favourite filters of the logged-in user.", + "isHidden": false + }, + { + "name": "getFilterById", + "description": "Returns a filter given an id", + "isHidden": false + }, + { + "name": "updateFilterById", + "description": "Updates an existing filter, and returns its new value.", + "isHidden": false + }, + { + "name": "getGroup", + "description": "Returns REST representation for the requested group", + "isHidden": false + }, + { + "name": "listGroupPicker", + "description": "Returns groups with substrings matching a given query.", + "isHidden": false + }, + { + "name": "listGroupUserPicker", + "description": "Returns a list of users and groups matching query with highlighting.", + "isHidden": false + }, + { + "name": "createIssue", + "description": "creates jira issue", + "isHidden": false + }, + { + "name": "getIssue", + "description": "get jira issue", + "isHidden": false + }, + { + "name": "updateIssue", + "description": "update jira issue", + "isHidden": false + }, + { + "name": "updateIssueAssignee", + "description": "update jira issue assinee", + "isHidden": false + }, + { + "name": "getTransitions", + "description": "get transitions for the requested jira", + "isHidden": false + }, + { + "name": "doTransition", + "description": "change transitions for the requested jira", + "isHidden": false + }, + { + "name": "deleteComment", + "description": "delete comment of the given jira issue", + "isHidden": false + }, + { + "name": "getComments", + "description": "get jira issue commnents", + "isHidden": false + }, + { + "name": "postComment", + "description": "Posting jira issue comment", + "isHidden": false + }, + { + "name": "updateComment", + "description": "updating jira issue comment", + "isHidden": false + }, + { + "name": "getVotesForIssue", + "description": "get vote for the requested jira", + "isHidden": false + }, + { + "name": "addAttachmentToIssueId", + "description": "Add one or more attachments to an issue.", + "isHidden": false + }, + { + "name": "getIssuePriorities", + "description": "Get a list of issue priorities.", + "isHidden": false + }, + { + "name": "getIssuePriorityById", + "description": "Get information about issue priority by ID.", + "isHidden": false + }, + { + "name": "getIssueTypes", + "description": "Get issue types defined in the system.", + "isHidden": false + }, + { + "name": "getIssueTypeById", + "description": "Get information about issue type by ID.", + "isHidden": false + }, + { + "name": "createBulkIssue", + "description": "Creates many issues in one bulk operation.", + "isHidden": false + }, + { + "name": "assignIssueToUser", + "description": "Assigns an issue to a user.", + "isHidden": false + }, + { + "name": "getCommentById", + "description": "Returns all comments for an issue.", + "isHidden": false + }, + { + "name": "sendNotification", + "description": "Sends a notification (email) to the list or recipients defined in the request.", + "isHidden": false + }, + { + "name": "addVotesForIssue", + "description": "Cast your vote in favour of an issue.", + "isHidden": false + }, + { + "name": "getWatchersForIssue", + "description": "Returns the list of watchers for the issue with the given key.", + "isHidden": false + }, + { + "name": "removeUserFromWatcherList", + "description": "Removes a user from an issue's watcher list.", + "isHidden": false + }, + { + "name": "getProject", + "description": "Get Jira Project information.", + "isHidden": false + }, + { + "name": "getAvatarsForProject", + "description": "returns all avatars which are visible for the currently logged in user. The avatars are grouped into system and custom.", + "isHidden": false + }, + { + "name": "deleteAvatarForProject", + "description": "Deletes avatar", + "isHidden": false + }, + { + "name": "getComponentsOfProject", + "description": "Contains a full representation of a the specified project's components.", + "isHidden": false + }, + { + "name": "getStatusesOfProject", + "description": "Get all issue types with valid status values for a project.", + "isHidden": false + }, + { + "name": "getVersionsOfProject", + "description": "Contains a full representation of a the specified project's versions.", + "isHidden": false + }, + { + "name": "getRolesOfProject", + "description": "Contains a list of roles in this project with links to full details.", + "isHidden": false + }, + { + "name": "getRolesByIdOfProject", + "description": "Details on a given project role.", + "isHidden": false + }, + { + "name": "setActorsToRoleOfProject", + "description": "Set actors to a given project role.", + "isHidden": false + }, + { + "name": "getUserAssignableProjects", + "description": "Returns a list of users that match the search string and can be assigned issues for all the given projects. This resource cannot be accessed anonymously.", + "isHidden": false + }, + { + "name": "searchJira", + "description": "Search jira using JQL", + "isHidden": false + }, + { + "name": "getUser", + "description": "Get Jira user information", + "isHidden": false + }, + { + "name": "getUserPermissions", + "description": "Get permissions granted for current user", + "isHidden": false + }, + { + "name": "searchAssignableUser", + "description": "Returns a list of assignable users that match the given issue.", + "isHidden": false + }, + { + "name": "searchIssueViewableUsers", + "description": "Given an issue key this resource will provide a list of users that match the search string and have the browse issue permission for the issue provided.", + "isHidden": false + }, + { + "name": "searchUser", + "description": "Get Jira user information", + "isHidden": false + }, + { + "name": "getAttachmentById", + "description": "Returns the meta-data for an attachment, including the URI of the actual attached file.", + "isHidden": false + }, + { + "name": "getAttachmentContent", + "description": "Returns the content of an attachment.", + "isHidden": false + }, + { + "name": "createComponent", + "description": "Create a component.", + "isHidden": false + }, + { + "name": "getComponent", + "description": "Returns a project component.", + "isHidden": false + }, + { + "name": "updateComponent", + "description": "Modify a component .", + "isHidden": false + }, + { + "name": "countComponentRelatedIssues", + "description": "Returns counts of issues related to this component.", + "isHidden": false + }, + { + "name": "createIssueLink", + "description": "Creates an issue link between two issues.", + "isHidden": false + }, + { + "name": "getIssueLinkById", + "description": "Returns an issue link with the specified id.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 113, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-jira.png" + }, + { + "connectorName": "Kafka", + "repoName": "esb-connector-kafka", + "description": "The Kafka connector allows you to access the Kafka Producer API through WSO2 MI. Hence, Kafka connector acts as a message producer which facilitates publishing messages from WSO2 MI to Kafka topics.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-kafka", + "version": { + "tagName": "3.3.9", + "releaseId": "225638857", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Configure the Kafka producer", + "isHidden": true + }, + { + "name": "publishMessages", + "description": "Send the messages to the Kafka brokers", + "isHidden": false + } + ], + "connections": [ + { + "name": "kafka", + "description": "Connection for a Kafka cluster.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-kafka_kafka.svg" + }, + { + "name": "kafkaSecure", + "description": "Secure connection for a Kafka cluster.", + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-connection-logos/esb-connector-kafka_kafkaSecure.svg" + } + ] + }, + "otherVersions": { + "3.3.6": "211074981" + }, + "connectorRank": 2, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-kafka.png" + }, + { + "connectorName": "LDAP", + "repoName": "esb-connector-ldap", + "description": "The LDAP Connector provides access for LDAP servers through a simple web services interface to do CURD (Create, Update, Read, Delete) operations on LDAP entries. ", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-ldap", + "version": { + "tagName": "1.0.14", + "releaseId": "191594149", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "addEntry", + "description": "This adds new entries to ldap directory", + "isHidden": false + }, + { + "name": "searchEntry", + "description": "This searches entries in ldap directory", + "isHidden": false + }, + { + "name": "updateEntry", + "description": "This updates entries in ldap directory", + "isHidden": false + }, + { + "name": "updateName", + "description": "This updates names in ldap directory", + "isHidden": false + }, + { + "name": "deleteEntry", + "description": "This deletes entries in ldap directory", + "isHidden": false + }, + { + "name": "init", + "description": "This does initialization part", + "isHidden": false + }, + { + "name": "authenticate", + "description": "This does authentication of a given user", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 18, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-ldap.png" + }, + { + "connectorName": "Microsoft Azure Storage", + "repoName": "esb-connector-msazurestorage", + "description": "The Microsoft Azure Storage Connector allows you to access the Azure Storage services using Microsoft Azure Storage Java SDK through WSO2 Micro Integrator (WSO2 MI). Microsoft Azure Storage is a Microsoft-managed cloud service that provides storage that is highly available and secure.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-msazurestorage", + "version": { + "tagName": "2.0.1", + "releaseId": "191462545", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Init operation for ms azure storage connector.", + "isHidden": true + }, + { + "name": "listBlobs", + "description": "Gets the list of all blobs.", + "isHidden": false + }, + { + "name": "uploadBlob", + "description": "Uploads the blob into the Azure storage.", + "isHidden": false + }, + { + "name": "deleteBlob", + "description": "Deletes the blob from the Azure storage.", + "isHidden": false + }, + { + "name": "downloadBlob", + "description": "Downloads the blob from the Azure storage.", + "isHidden": false + }, + { + "name": "listContainers", + "description": "Gets the list of all containers.", + "isHidden": false + }, + { + "name": "createContainer", + "description": "Creates a container into the Azure storage.", + "isHidden": false + }, + { + "name": "deleteContainer", + "description": "Deletes the container from the Azure storage.", + "isHidden": false + }, + { + "name": "listMetadata", + "description": "Downloads the metadata from the blob from Azure storage.", + "isHidden": false + }, + { + "name": "uploadMetadata", + "description": "Uploads metadata to a blob in Azure storage.", + "isHidden": false + } + ], + "connections": [ + { + "name": "msazurestorage", + "description": "Connection for accessing Microsoft Azure Storage.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 7, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-msazurestorage.gif" + }, + { + "connectorName": "MongoDB", + "repoName": "esb-connector-mongodb", + "description": "MongoDB is a cross-platform document-oriented program which is classified as a NoSQL database.\nThis connector allows you to connect to the Mongo database via different connection URLs in order to perform CRUD operations on the database.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-mongodb", + "version": { + "tagName": "3.0.1", + "releaseId": "224033950", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "MongoDB connection configuration.", + "isHidden": true + }, + { + "name": "deleteOne", + "description": "Removes a single document from a collection.", + "isHidden": false + }, + { + "name": "deleteMany", + "description": "Removes all documents that match the filter from a collection.", + "isHidden": false + }, + { + "name": "findOne", + "description": "Finds a single document in a collection.", + "isHidden": false + }, + { + "name": "find", + "description": "Finds several documents in a collection.", + "isHidden": false + }, + { + "name": "insertOne", + "description": "Inserts a document into a collection.", + "isHidden": false + }, + { + "name": "insertMany", + "description": "Inserts several documents into a collection.", + "isHidden": false + }, + { + "name": "updateOne", + "description": "Modifies an existing document in a collection.", + "isHidden": false + }, + { + "name": "updateMany", + "description": "Modifies several documents in a collection.", + "isHidden": false + }, + { + "name": "aggregate", + "description": "Aggregation to process multiple documents and return result.", + "isHidden": false + } + ], + "connections": [ + { + "name": "MONGODB", + "description": "Standard MongoDB connection with explicit parameters.", + "iconUrl": "" + } + ] + }, + "otherVersions": { + "1.0.3": "191463975", + "2.0.1": "198555576" + }, + "connectorRank": 4, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-mongodb.png" + }, + { + "connectorName": "Pulsar", + "repoName": "mi-connector-pulsar", + "description": "The Apache Pulsar connector allows you to access the Apache Pulsar Producer API through WSO2 MI. Hence, Apache Pulsar connector acts as a message producer which facilitates publishing messages from WSO2 MI to Apache Pulsar topics.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-pulsar", + "version": { + "tagName": "0.9.4", + "releaseId": "225752727", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Configure the Apache Pulsar Client Connection", + "isHidden": true + }, + { + "name": "publishMessage", + "description": "Publish message to the Apache Pulsar cluster", + "isHidden": false + } + ], + "connections": [ + { + "name": "pulsar", + "description": "Connection for a Apache Pulsar cluster.", + "iconUrl": "" + }, + { + "name": "pulsarsecure", + "description": "Secure Connection for a Apache Pulsar cluster.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 50, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-connector-pulsar.png" + }, + { + "connectorName": "Redis", + "repoName": "mi-connector-redis", + "description": "The Redis connector allows you to access the Redis commands through the WSO2 EI. Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs and geospatial indexes with radius queries.\nIn latest version we have added following:\nPreviously we were creating a single pool for each cluster operation and closing it after each operation that's why read/write lock issue occurs (jmxRegister and jmxUnRegister on the same object). This Pr rectifies that and also avoids closing JedisCluster after each operation since It's no need to close the JedisCluster instance as it is handled by the JedisClusterConnectionPool itself.\n\nAlso introduced the \"isJmxEnabled\" property to enable JMX if required.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-redis", + "version": { + "tagName": "3.1.6", + "releaseId": "200018815", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "configure Redis connector", + "isHidden": true + }, + { + "name": "echo", + "description": "echo the given string", + "isHidden": false + }, + { + "name": "ping", + "description": "ping the server", + "isHidden": false + }, + { + "name": "quit", + "description": "close the connection", + "isHidden": false + }, + { + "name": "hDel", + "description": "delete one or more hash fields", + "isHidden": false + }, + { + "name": "hExists", + "description": "determine if a hash field exists", + "isHidden": false + }, + { + "name": "hGet", + "description": "get the value of a hash field", + "isHidden": false + }, + { + "name": "hGetAll", + "description": "get all the fields and values in a hash", + "isHidden": false + }, + { + "name": "hIncrBy", + "description": "increment the integer value of a hash field by the given number", + "isHidden": false + }, + { + "name": "hKeys", + "description": "get all the fields in a hash", + "isHidden": false + }, + { + "name": "hLen", + "description": "get the number of fields in a hash", + "isHidden": false + }, + { + "name": "hMGet", + "description": "get the values of all the given hash fields", + "isHidden": false + }, + { + "name": "hMSet", + "description": "set multiple hash fields to multiple values", + "isHidden": false + }, + { + "name": "hSet", + "description": "set the string value of a hash field", + "isHidden": false + }, + { + "name": "hSetnX", + "description": "set the value of a hash field, only if the field does not exist", + "isHidden": false + }, + { + "name": "hVals", + "description": "get all the values in a hash", + "isHidden": false + }, + { + "name": "del", + "description": "delete a key", + "isHidden": false + }, + { + "name": "exists", + "description": "determine if a key exists", + "isHidden": false + }, + { + "name": "expire", + "description": "set a key's time to live in seconds", + "isHidden": false + }, + { + "name": "expireAt", + "description": "set the expiration for an existing key as a UNIX timestamp", + "isHidden": false + }, + { + "name": "keys", + "description": "find all keys matching the given pattern", + "isHidden": false + }, + { + "name": "randomKey", + "description": "return a random key from the keyspace", + "isHidden": false + }, + { + "name": "rename", + "description": "rename a key", + "isHidden": false + }, + { + "name": "renamenX", + "description": "rename a key, only if the new key does not exist", + "isHidden": false + }, + { + "name": "ttl", + "description": "get the time to live for a key", + "isHidden": false + }, + { + "name": "type", + "description": "determine the type stored at key", + "isHidden": false + }, + { + "name": "blPop", + "description": "remove and get the first element in a list or block until one is available", + "isHidden": false + }, + { + "name": "brPop", + "description": "remove an get the element in a list or block one is available", + "isHidden": false + }, + { + "name": "lInsert", + "description": "insert an element before or after another element in a list", + "isHidden": false + }, + { + "name": "lLen", + "description": "get a length of a list", + "isHidden": false + }, + { + "name": "lPop", + "description": "remove and get the first element in a list", + "isHidden": false + }, + { + "name": "lPush", + "description": "prepend one or multiple values to a list", + "isHidden": false + }, + { + "name": "lPushX", + "description": "prepend a value of an element in a list by its index", + "isHidden": false + }, + { + "name": "lRange", + "description": "get a range of elements from a list", + "isHidden": false + }, + { + "name": "lRem", + "description": "remove element from a list", + "isHidden": false + }, + { + "name": "lSet", + "description": "set the value of an element in a list by it's index", + "isHidden": false + }, + { + "name": "lTrim", + "description": "trim a list to the specified range", + "isHidden": false + }, + { + "name": "rPopLPush", + "description": "remove the list element in a list, prepend it to another list and return it", + "isHidden": false + }, + { + "name": "rPush", + "description": "append one or more multiple values to a list", + "isHidden": false + }, + { + "name": "rPushX", + "description": "append a value to a list, only if the list exists", + "isHidden": false + }, + { + "name": "flushAll", + "description": "delete all the keys of all the existing databases", + "isHidden": false + }, + { + "name": "flushDB", + "description": "delete all the keys of the currently selected database", + "isHidden": false + }, + { + "name": "sadd", + "description": "add one or more members to a set", + "isHidden": false + }, + { + "name": "sDiffStore", + "description": "subtract multiple sets and store the resulting set in a key", + "isHidden": false + }, + { + "name": "sInter", + "description": "intersect multiple sets", + "isHidden": false + }, + { + "name": "sInterStore", + "description": "intersect multiple sets and store the resulting set in a key", + "isHidden": false + }, + { + "name": "sIsMember", + "description": "determine if a given value is a member of a set", + "isHidden": false + }, + { + "name": "sMembers", + "description": "get the all members in a set", + "isHidden": false + }, + { + "name": "sMove", + "description": "move a member from one set to another", + "isHidden": false + }, + { + "name": "sPop", + "description": "remove and return one or multiple random members from a set", + "isHidden": false + }, + { + "name": "sRandMember", + "description": "get one or multiple random members from a set", + "isHidden": false + }, + { + "name": "sRem", + "description": "remove one or more members from a set", + "isHidden": false + }, + { + "name": "sUnion", + "description": "add multiple sets", + "isHidden": false + }, + { + "name": "sUnionStore", + "description": "add multiple sets and store the resulting set in a key", + "isHidden": false + }, + { + "name": "zadd", + "description": "add one or more members to a sorted set or update its score if it already exist", + "isHidden": false + }, + { + "name": "zCount", + "description": "count the members in a sorted set with scores within the given values", + "isHidden": false + }, + { + "name": "zIncrBy", + "description": "increment the score of a member in a sorted set", + "isHidden": false + }, + { + "name": "zInterStore", + "description": "intersect multiple sorted sets and store the resulting stored set in a new key", + "isHidden": false + }, + { + "name": "zRange", + "description": "return a range of members in a sorted set by index", + "isHidden": false + }, + { + "name": "zRangeByScore", + "description": "return a range of members in a sorted set by score with scores ordered from high to low", + "isHidden": false + }, + { + "name": "zRank", + "description": "determine the index of a member in a sorted set", + "isHidden": false + }, + { + "name": "zRem", + "description": "remove one or more members from a sorted set", + "isHidden": false + }, + { + "name": "zRemRangeByRank", + "description": "remove all members in a sorted set within the given indexes", + "isHidden": false + }, + { + "name": "zRemRangeByScore", + "description": "remove all members in a sorted set within the given scores", + "isHidden": false + }, + { + "name": "zRevRange", + "description": "return a range of members in a sorted set by index with scores ordered from high to low", + "isHidden": false + }, + { + "name": "zRevRangeByScore", + "description": "return a range of members in a sorted set by score with score ordered form high to low", + "isHidden": false + }, + { + "name": "zRevRank", + "description": "determine the index of a member in a sorted set with scores ordered from high to low", + "isHidden": false + }, + { + "name": "zScore", + "description": "get the score associated with the given member in a sorted set", + "isHidden": false + }, + { + "name": "zUnionStore", + "description": "add multiple stored sets and store the resulting stored set in a new key", + "isHidden": false + }, + { + "name": "append", + "description": "append a value to a key", + "isHidden": false + }, + { + "name": "decrBy", + "description": "decrement the integer value of key by the given number", + "isHidden": false + }, + { + "name": "get", + "description": "get the value of a key", + "isHidden": false + }, + { + "name": "getObjectIdleTime", + "description": "get the OBJECT IDLE TIME", + "isHidden": false + }, + { + "name": "getRange", + "description": "get the sub string of the key", + "isHidden": false + }, + { + "name": "getSet", + "description": "set the string value of a key and return its old value", + "isHidden": false + }, + { + "name": "incrBy", + "description": "increment the integer value of a key by the given amount", + "isHidden": false + }, + { + "name": "mGet", + "description": "get the values of all given keys", + "isHidden": false + }, + { + "name": "mSet", + "description": "set multiple keys to multiple values", + "isHidden": false + }, + { + "name": "mSetnX", + "description": "set multiple keys to multiple values, only if none of the key exist", + "isHidden": false + }, + { + "name": "set", + "description": "set the string value of a key", + "isHidden": false + }, + { + "name": "setBit", + "description": "sets of clears the bit at offset in the string value stored at key", + "isHidden": false + }, + { + "name": "setnX", + "description": "set the value of a key, only if the key does not exist", + "isHidden": false + }, + { + "name": "setRange", + "description": "overwrite part of a string at key stating at the specified offset", + "isHidden": false + }, + { + "name": "strLen", + "description": "get the length of the value stored in a key", + "isHidden": false + } + ], + "connections": [ + { + "name": "Redis", + "description": "Connection for Redis data operations.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 5, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-connector-redis.gif" + }, + { + "connectorName": "Salesforce", + "repoName": "esb-connector-salesforce", + "description": "The Salesforce connector allows you to work with records in Salesforce, a web-based service that allows organizations to manage contact relationship management (CRM) data. You can use the Salesforce connector to create, query, retrieve, update, and delete records in your organization's Salesforce data. The connector uses the Salesforce SOAP API to interact with Salesforce.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-salesforce", + "version": { + "tagName": "2.1.2", + "releaseId": "193421565", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Login synapse library", + "isHidden": true + }, + { + "name": "logout", + "description": "Logout synapse library", + "isHidden": false + }, + { + "name": "query", + "description": "Query (SQL) synapse library", + "isHidden": false + }, + { + "name": "queryAll", + "description": "Query all (SQL) synapse library", + "isHidden": false + }, + { + "name": "queryMore", + "description": "Query more synapse library", + "isHidden": false + }, + { + "name": "create", + "description": "Insert sObject(s) synapse library", + "isHidden": false + }, + { + "name": "update", + "description": "Update or update sObject(s) synapse library", + "isHidden": false + }, + { + "name": "upsert", + "description": "Insert or update sObject(s) synapse library", + "isHidden": false + }, + { + "name": "delete", + "description": "Delete sObject(s) synapse library", + "isHidden": false + }, + { + "name": "search", + "description": "Search using SOSL synapse library", + "isHidden": false + }, + { + "name": "emptyRecycleBin", + "description": "Empty RecycleBin synapse library", + "isHidden": false + }, + { + "name": "undelete", + "description": "Restore sObject(s) synapse library", + "isHidden": false + }, + { + "name": "describeSObject", + "description": "Describe SObject synapse library", + "isHidden": false + }, + { + "name": "describeSObjects", + "description": "Describe SObjects synapse library", + "isHidden": false + }, + { + "name": "describeGlobal", + "description": "Describe global synapse library", + "isHidden": false + }, + { + "name": "setPassword", + "description": "Set password synapse library", + "isHidden": false + }, + { + "name": "sendEmailMessage", + "description": "Send email message synapse library", + "isHidden": false + }, + { + "name": "sendEmail", + "description": "Send email synapse library", + "isHidden": false + }, + { + "name": "retrieve", + "description": "Retrieve synapse library", + "isHidden": false + }, + { + "name": "resetPassword", + "description": "Reset Password synapse library", + "isHidden": false + }, + { + "name": "getUserInfo", + "description": "Get User Information synapse library", + "isHidden": false + }, + { + "name": "getDeleted", + "description": "Get Deleted Records synapse library", + "isHidden": false + }, + { + "name": "getUpdated", + "description": "Get Updated Records synapse library", + "isHidden": false + }, + { + "name": "getServerTimestamp", + "description": "Get Server Timestamp synapse library", + "isHidden": false + }, + { + "name": "findDuplicates", + "description": "Find duplicates for a set of sObjects synapse library", + "isHidden": false + }, + { + "name": "findDuplicatesByIds", + "description": "Find duplicates for a set of sObjects using record ids synapse library", + "isHidden": false + }, + { + "name": "merge", + "description": "Merge and update a set of sObjects based on object id synapse library", + "isHidden": false + }, + { + "name": "convertLead", + "description": "Convert a set of leads synapse library", + "isHidden": false + } + ], + "connections": [ + { + "name": "salesforce", + "description": "Connection for interacting with Salesforce CRM.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 15, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-salesforce.png" + }, + { + "connectorName": "Salesforce Bulk", + "repoName": "esb-connector-salesforcebulk", + "description": "The SalesforceBulk connector allows you to access the SalesforceBulk REST API through WSO2 ESB. SalesforceBulk is a RESTful API that is optimal for loading or deleting large sets of data. You can use it to query, insert, update, upsert, or delete a large number of records asynchronously by submitting batches that Salesforce processes in the background.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-salesforcebulk", + "version": { + "tagName": "2.1.2", + "releaseId": "193421906", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Init operation", + "isHidden": true + }, + { + "name": "callBulkApi", + "description": "Call Salesforce Bulk API endpoints", + "isHidden": true + }, + { + "name": "callUsingOauth", + "description": "Call endpoint using OAUTH credentials", + "isHidden": true + }, + { + "name": "callUsingAccessToken", + "description": "Call endpoint using OAUTH access token", + "isHidden": true + }, + { + "name": "createJob", + "description": "A operation to create a job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "uploadJobData", + "description": "An operation to upload data to a job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "closeJob", + "description": "An operation to close a job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "abortJob", + "description": "An operation to abort a job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "getAllJobInfo", + "description": "An operation to get all job info in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "getJobInfo", + "description": "An operation to get job info in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "deleteJob", + "description": "An operation to delete a job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "getSuccessfulResults", + "description": "An operation to get successful results of a job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "getUnprocessedResults", + "description": "An operation to get unprocessed results of a job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "getFailedResults", + "description": "An operation to get failed results of a job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "abortQueryJob", + "description": "An operation to abort a running query job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "createQueryJob", + "description": "An operation to create a new query job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "deleteQueryJob", + "description": "An operation to delete a query job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "getQueryJobInfo", + "description": "An operation to get information about a query job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "getQueryJobResults", + "description": "An operation to get the results of a query job in Salesforce using bulk api 2.0", + "isHidden": false + }, + { + "name": "getAllQueryJobInfo", + "description": "An operation to get the information of all the query jobs in Salesforce using bulk api 2.0", + "isHidden": false + } + ], + "connections": [ + { + "name": "salesforcebulk", + "description": "Connection for bulk data operations in Salesforce.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 9, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-salesforcebulk.png" + }, + { + "connectorName": "Salesforce Marketing Cloud", + "repoName": "mi-connector-salesforcemarketingcloud", + "description": "The Salesforce Marketing Cloud Connector allows you to access the Salesforce Marketing Cloud Engagement APIs, enabling you to manage journeys, contacts, assets, and marketing campaigns efficiently.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-salesforcemarketingcloud", + "version": { + "tagName": "1.0.0", + "releaseId": "217099789", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "createAsset", + "description": "Creates a new content asset.", + "isHidden": false + }, + { + "name": "createCampaign", + "description": "Establishes a new marketing campaign with defined objectives, target audience, and parameters.", + "isHidden": false + }, + { + "name": "createCategory", + "description": "Creates a category (folder) in Content Builder.", + "isHidden": false + }, + { + "name": "createContact", + "description": "Adds a new contact.", + "isHidden": false + }, + { + "name": "createEmailDefinition", + "description": "Creates a send definition referencing email templates and sending options.", + "isHidden": false + }, + { + "name": "createJourney", + "description": "Creates a new journey interaction.", + "isHidden": false + }, + { + "name": "createSmsDefinition", + "description": "Creates an SMS send definition.", + "isHidden": false + }, + { + "name": "deleteAsset", + "description": "Deletes an asset.", + "isHidden": false + }, + { + "name": "deleteCampaign", + "description": "Deletes an existing marketing campaign.", + "isHidden": false + }, + { + "name": "deleteContact", + "description": "Deletes a contact record.", + "isHidden": false + }, + { + "name": "getAssets", + "description": "Fetches a list of all content assets.", + "isHidden": false + }, + { + "name": "getCampaigns", + "description": "Retrieves details of existing marketing campaigns.", + "isHidden": false + }, + { + "name": "getCategories", + "description": "Retrieves a list of Content Builder categories (folders).", + "isHidden": false + }, + { + "name": "getContactDeleteRequests", + "description": "Retrieves contact data deletion requests for GDPR compliance.", + "isHidden": false + }, + { + "name": "getJourneys", + "description": "Retrieves all journey interactions.", + "isHidden": false + }, + { + "name": "insertDataExtensionRowSet", + "description": "Upserts rows in a data extension using its external key.", + "isHidden": false + }, + { + "name": "sendEmailMessage", + "description": "Sends an email message.", + "isHidden": false + }, + { + "name": "sendSmsMessage", + "description": "Sends an SMS message.", + "isHidden": false + }, + { + "name": "updateAsset", + "description": "Updates a full asset.", + "isHidden": false + }, + { + "name": "updateCampaign", + "description": "Updates an existing marketing campaign.", + "isHidden": false + }, + { + "name": "updateContact", + "description": "Updates a contact record.", + "isHidden": false + } + ], + "connections": [ + { + "name": "SalesforceMarketingCloud", + "description": "Connection to Salesforce Marketing Cloud using subdomain, client ID, and client secret.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 51, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-connector-salesforcemarketingcloud.png" + }, + { + "connectorName": "Salesforce Pub/Sub", + "repoName": "mi-connector-salesforcepubsub", + "description": "The Salesforce Pub/Sub connector allows you to publish and subscribe to events in Salesforce. It enables you to integrate Salesforce with other systems by sending and receiving messages through the Pub/Sub API. This connector provides operations for publishing events, and subscribing to topics.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-salesforcepubsub", + "version": { + "tagName": "0.1.2", + "releaseId": "219670190", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "Get Schema", + "description": "This is getSchema operation.", + "isHidden": false + }, + { + "name": "Get Topic", + "description": "This is getTopic operation.", + "isHidden": false + }, + { + "name": "Publish", + "description": "This is publish operation.", + "isHidden": false + } + ], + "connections": [ + { + "name": "salesforcePubSub", + "description": "Connection for Salesforce Pub/Sub API operations.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 94, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/mi-connector-salesforcepubsub.gif" + }, + { + "connectorName": "SalesforceRest", + "repoName": "esb-connector-salesforcerest", + "description": "The Salesforce connector allows you to work with records in Salesforce, a web-based service that allows organizations to manage contact relationship management (CRM) data. You can use the Salesforce connector to create, query, retrieve, update, and delete records in your organization's Salesforce data. The connector uses the Slaesforce REST API to interact with Salesforce. ", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-salesforcerest", + "version": { + "tagName": "2.0.1", + "releaseId": "191924791", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Initial configuration file for salesforce rest connector", + "isHidden": true + }, + { + "name": "callWithRetry", + "description": "Templating the retry call", + "isHidden": true + }, + { + "name": "callOptions", + "description": "Templating the http call types GET, POST, etc.", + "isHidden": true + }, + { + "name": "describeGlobal", + "description": "Lists the available objects and their metadata for your organization’s data", + "isHidden": false + }, + { + "name": "describeSObject", + "description": "Completely describes the individual metadata at all levels for the specified object", + "isHidden": false + }, + { + "name": "listResourcesByApiVersion", + "description": "Lists available resources for the specified API version", + "isHidden": false + }, + { + "name": "sObjectBasicInfo", + "description": "Describes the individual metadata for the specified object", + "isHidden": false + }, + { + "name": "sObjectRows", + "description": "Accesses records based on the specified object ID", + "isHidden": false + }, + { + "name": "listAvailableApiVersion", + "description": "List summary information about each REST API version currently available.", + "isHidden": false + }, + { + "name": "sObjectPlatformAction", + "description": "PlatformAction is a virtual read-only object that enables you to query for actions—such as standard and custom buttons, quick actions, and productivity actions—that should be displayed in a UI, given a user, a context, device format, and a record ID.", + "isHidden": false + }, + { + "name": "listOrganizationLimits", + "description": "List the organization limits", + "isHidden": false + }, + { + "name": "sObjectRowsByExternalId", + "description": "SObject Rows by External ID resource to retrieve records with a specific external ID", + "isHidden": false + }, + { + "name": "returnHeadersOfSObjectRowsByExternalId", + "description": "Return headers that are returned by sending a GET request to the sObject Rows by External ID resource.", + "isHidden": false + }, + { + "name": "viewRelevantItems", + "description": "Retrieves the current user’s most relevant items that include up to 50 of the most recently viewed or updated records for each object in the user’s global search scope.", + "isHidden": false + }, + { + "name": "create", + "description": "Insert sObject(s) synapse library", + "isHidden": false + }, + { + "name": "delete", + "description": "Delete sObject(s) synapse library", + "isHidden": false + }, + { + "name": "getDeleted", + "description": "Retrieves the list of individual records that have been deleted within the given timespan for the specified object.", + "isHidden": false + }, + { + "name": "getUpdated", + "description": "Retrieves the list of individual records that have been updated within the given timespan for the specified object.", + "isHidden": false + }, + { + "name": "update", + "description": "Update or update sObject(s) synapse library", + "isHidden": false + }, + { + "name": "recentlyViewedItem", + "description": "Gets the most recently accessed items that were viewed or referenced by the current user", + "isHidden": false + }, + { + "name": "retrieveFieldValues", + "description": "Retrieve specific field values from a record", + "isHidden": false + }, + { + "name": "createMultipleRecords", + "description": "Insert multiple sObject(s)", + "isHidden": false + }, + { + "name": "createNestedRecords", + "description": "Insert nested sObject(s)", + "isHidden": false + }, + { + "name": "retrieveFieldValuesFromExternalObject", + "description": "Retrieve specific field values from an External Object", + "isHidden": false + }, + { + "name": "retrieveStandardFieldValuesFromExternalObjectWithExternalId", + "description": "Retrieve specific Standard field values from an External Object using External Id", + "isHidden": false + }, + { + "name": "upsert", + "description": "Updating a record or Insert a new Record if there is no record associate with the Id using External ID.", + "isHidden": false + }, + { + "name": "createUsingExternalId", + "description": "Creating a new record based on the field values included in the request body. This resource does not require the use of an external ID field.", + "isHidden": false + }, + { + "name": "deleteUsingExternalId", + "description": "Deleting a record based on the value of the specified external ID field.", + "isHidden": false + }, + { + "name": "createUsingSpecificSObjectQuickAction", + "description": "Creating a record via the specified quick action based on the field values included in the request body", + "isHidden": false + }, + { + "name": "getRecordsUsingRelationships", + "description": "Gets a record based on the specified object, record ID, and relationship field", + "isHidden": false + }, + { + "name": "updateUsingRelationships", + "description": "Updates a parent record based on the specified object, record ID, and relationship field name", + "isHidden": false + }, + { + "name": "deleteUsingRelationships", + "description": "Deletes a parent record based on the specified object, record ID, and relationship field name", + "isHidden": false + }, + { + "name": "getObjectRecordCounts", + "description": "Lists information about object record counts in your organization.", + "isHidden": false + }, + { + "name": "createUsingSObjectCollections", + "description": "Creates records using sObject collections. Can add up to 200 records", + "isHidden": false + }, + { + "name": "deleteRecordsUsingSObjectCollections", + "description": "Deletes records using sObject collections. Can delete up to 200 records", + "isHidden": false + }, + { + "name": "getRecordsUsingSObjectCollections", + "description": "Gets one or more records of the same object type using sObject collections.", + "isHidden": false + }, + { + "name": "getRecordsWithARequestBodyUsingSObjectCollections", + "description": "Gets one or more records of the same object type using sObject collections with a request body.", + "isHidden": false + }, + { + "name": "updateRecordsUsingSObjectCollections", + "description": "Updates records using sObject collections. Can update up to 200 records.", + "isHidden": false + }, + { + "name": "upsertRecordsUsingSObjectCollections", + "description": "Either creates or updates (upsert) up to 200 records based on an external ID field using sObject collections", + "isHidden": false + }, + { + "name": "createUsingQuickAction", + "description": "Creates a record via a quick action.", + "isHidden": false + }, + { + "name": "getUserInformation", + "description": "Get User Information From Salesforce.", + "isHidden": false + }, + { + "name": "resetPassword", + "description": "Reset Password for Salesforce account for a specific User.", + "isHidden": false + }, + { + "name": "setPassword", + "description": "Set new password for Salesforce account for a specific User.", + "isHidden": false + }, + { + "name": "getUserPasswordExpirationStatus", + "description": "Gets a user’s password expiration status based on the specified user ID", + "isHidden": false + }, + { + "name": "returnHeadersForUserPassword", + "description": "Returns only the headers that are returned by sending a GET request to the sObject User Password resource.", + "isHidden": false + }, + { + "name": "getSelfServiceUserPasswordExpirationStatus", + "description": "Retrieves a self-service user’s password expiration status based on the specified user ID.", + "isHidden": false + }, + { + "name": "resetSelfServiceUserPassword", + "description": "Reset Password for Salesforce account for a specific self-service.", + "isHidden": false + }, + { + "name": "returnHeadersForSelfServiceUserPassword", + "description": "Returns only the headers that are returned by sending a GET request to the sObject Self-Service User Password resource.", + "isHidden": false + }, + { + "name": "setSelfServiceUserPassword", + "description": "Sets a self-service user’s password based on the specified user ID. The password provided in the request body replaces the user’s existing password.", + "isHidden": false + }, + { + "name": "listApprovals", + "description": "Get the List of Approvals.", + "isHidden": false + }, + { + "name": "returnHeadersForApprovals", + "description": "Returns only the headers that are returned by the listApprovals operation.", + "isHidden": false + }, + { + "name": "submitApproveOrRejectApprovals", + "description": "Submits a particular record if that entity supports an approval process and one has already been defined. Records can be approved and rejected if the current user is an assigned approver.", + "isHidden": false + }, + { + "name": "listViews", + "description": "Returns the list of list views for the specified sObject, including the ID and other basic information about each list view", + "isHidden": false + }, + { + "name": "listViewById", + "description": "get basic information for a specific list view by ID", + "isHidden": false + }, + { + "name": "recentListViews", + "description": "Returns the list of recently used list views for the given sObject type", + "isHidden": false + }, + { + "name": "describeListViewById", + "description": "Returns detailed information about a list view, including the ID, the columns, and the SOQL query.", + "isHidden": false + }, + { + "name": "listViewResults", + "description": "Executes the SOQL query for the list view and returns the resulting data and presentation information.", + "isHidden": false + }, + { + "name": "query", + "description": "Executes the specified SOQL query to retrieve", + "isHidden": false + }, + { + "name": "queryPerformanceFeedback", + "description": "Retrieving query performance feedback without executing the query", + "isHidden": false + }, + { + "name": "listviewQueryPerformanceFeedback", + "description": "Retrieving query performance feedback on a report or list view", + "isHidden": false + }, + { + "name": "queryMore", + "description": "Retrieving additional query results if the initial results are too large", + "isHidden": false + }, + { + "name": "queryAll", + "description": "Executes the specified SOQL query to retrieve details with deleted records", + "isHidden": false + }, + { + "name": "queryAllMore", + "description": "For retrieving additional query results if the initial results are too large", + "isHidden": false + }, + { + "name": "sObjectAction", + "description": "Return a specific object’s actions as well as global actions", + "isHidden": false + }, + { + "name": "getSpecificQuickAction", + "description": "Return a specific action for an object", + "isHidden": false + }, + { + "name": "quickActions", + "description": "Returns a list of global actions", + "isHidden": false + }, + { + "name": "getDescribeSpecificAction", + "description": "Return a specific action’s descriptive detail", + "isHidden": false + }, + { + "name": "getDefaultValueOfAction", + "description": "Return a specific action’s default values, including default field values", + "isHidden": false + }, + { + "name": "returnHeadersForDescribeSpecificAction", + "description": "Returns only the headers that are returned by the getDescribeSpecificAction operation", + "isHidden": false + }, + { + "name": "returnHeadersForSObjectAction", + "description": "Returns only the headers that are returned by the sObjectAction operation", + "isHidden": false + }, + { + "name": "returnHeadersForQuickAction", + "description": "Returns only the headers that are returned by the quickActions operation", + "isHidden": false + }, + { + "name": "returnHeadersForSpecificQuickAction", + "description": "Returns only the headers that are returned by the getSpecificAction operation", + "isHidden": false + }, + { + "name": "returnHeadersForDefaultValueOfAction", + "description": "Returns only the headers that are returned by the getDefaultValueOfAction operation", + "isHidden": false + }, + { + "name": "getDefaultValueOfActionById", + "description": "Returns the default values for an action specific to the context_id object", + "isHidden": false + }, + { + "name": "returnHeadersForDefaultValueOfActionById", + "description": "Returns only the headers that are returned by the getDefaultValueOfActionById operation", + "isHidden": false + }, + { + "name": "search", + "description": "Executes the specified SOSL search", + "isHidden": false + }, + { + "name": "searchScopeAndOrder", + "description": "Returns an ordered list of objects in the default global search scope of a logged-in user.", + "isHidden": false + }, + { + "name": "searchResultLayout", + "description": "Returns search result layout information for the objects in the query string", + "isHidden": false + }, + { + "name": "searchSuggestedRecords", + "description": "Returns a list of suggested records whose names match the user’s search string", + "isHidden": false + }, + { + "name": "searchSuggestedArticleTitle", + "description": "Returns a list of Salesforce Knowledge articles whose titles match the user’s search query string", + "isHidden": false + }, + { + "name": "searchSuggestedQueries", + "description": "Returns a list of suggested searches based on the user’s query string text matching searches that other users have performed in Salesforce Knowledge", + "isHidden": false + }, + { + "name": "sObjectLayouts", + "description": "Returns a list of layouts and descriptions, including for actions for a specific object.", + "isHidden": false + }, + { + "name": "compactLayouts", + "description": "Returns a list of compact layouts for multiple objects.", + "isHidden": false + }, + { + "name": "sObjectCompactLayouts", + "description": "Returns a list of compact layouts for a specific object.", + "isHidden": false + }, + { + "name": "sObjectNamedLayouts", + "description": "Retrieves information about alternate named layouts for a given object.", + "isHidden": false + }, + { + "name": "globalSObjectLayouts", + "description": "To return descriptions of global publisher layouts.", + "isHidden": false + }, + { + "name": "sObjectApprovalLayouts", + "description": "Returns a list of approval layouts for a specified object", + "isHidden": false + }, + { + "name": "sObjectApprovalLayoutsForSpecifiedApprovalProcess", + "description": "Returns an approval layout for a named approval process on a specified object.", + "isHidden": false + }, + { + "name": "returnHeadersForSObjectApprovalLayouts", + "description": "Returns only the headers that are returned by the sObjectApprovalLayouts operation", + "isHidden": false + }, + { + "name": "returnHeadersForSObjectApprovalLayoutsForSpecifiedApprovalProcess", + "description": "Returns only the headers that are returned by the sObjectApprovalLayoutsForSpecifiedApprovalProcess operation", + "isHidden": false + }, + { + "name": "returnHeadersForSObjectCompactLayouts", + "description": "Returns only the headers that are returned by the sObjectCompactLayouts operation", + "isHidden": false + }, + { + "name": "returnHeadersForSObjectLayouts", + "description": "Returns only the headers that are returned by the sObjectLayouts operation", + "isHidden": false + }, + { + "name": "sObjectLayoutsForObjectWithMultipleRecordTypes", + "description": "Retrieves lists of page layouts and their descriptions for objects that have more than one record type defined.", + "isHidden": false + }, + { + "name": "returnHeadersForSObjectLayoutsForObjectWithMultipleRecordTypes", + "description": "Returns only the headers that are returned by the sObjectLayoutsForObjectWithMultipleRecordTypes operation", + "isHidden": false + }, + { + "name": "returnHeadersForGlobalSObjectLayouts", + "description": "Returns only the headers that are returned by the globalSObjectLayouts operation", + "isHidden": false + }, + { + "name": "listItemsInMenu", + "description": "Returns a list of items in either the Salesforce app drop-down menu or the Salesforce1 navigation menu.", + "isHidden": false + }, + { + "name": "tabs", + "description": "Returns a list of all tabs.", + "isHidden": false + }, + { + "name": "themes", + "description": "Gets the list of icons and colors used by themes in the Salesforce application.", + "isHidden": false + }, + { + "name": "listAppMenuTypes", + "description": "Retrieves a list of App Menu types in the Salesforce app dropdown menu.", + "isHidden": false + }, + { + "name": "listAppMenuItems", + "description": "Retrieves a list of the App Menu items in the Salesforce Lightning dropdown menu.", + "isHidden": false + }, + { + "name": "listAppMenuMobileItems", + "description": "Retrieves a list of the App Menu items in the Salesforce mobile app for Android and iOS and the mobile web navigation menu.", + "isHidden": false + }, + { + "name": "returnHeadersForAppMenuItems", + "description": "Retrieves only the headers that are returned by the listAppMenuItems operation.", + "isHidden": false + }, + { + "name": "returnHeadersForAppMenuMobileItems", + "description": "Retrieves only the headers that are returned by the listAppMenuMobileItems operation.", + "isHidden": false + }, + { + "name": "returnHeadersForTabs", + "description": "Retrieves only the headers that are returned by the tabs operation.", + "isHidden": false + }, + { + "name": "getListOfAction", + "description": "Retrieving a list of general action types for the current organization", + "isHidden": false + }, + { + "name": "getSpecificListOfAction", + "description": "Retrieving a list of standard actions for the current organization", + "isHidden": false + }, + { + "name": "getAttributeOfSpecificAction", + "description": "Retrieving the details of a given attributes of a single standard action.", + "isHidden": false + }, + { + "name": "returnHTTPHeadersForListOfAction", + "description": "Retrieves only the headers that are returned by the getListOfAction operation", + "isHidden": false + }, + { + "name": "returnHTTPHeadersForSpecificListOfAction", + "description": "Retrieves only the headers that are returned by the getSpecificListOfAction operation", + "isHidden": false + }, + { + "name": "listProcessRules", + "description": "Get the List of Process Rules.", + "isHidden": false + }, + { + "name": "getSpecificProcessRule", + "description": "get the metadata for a specific sObject Process rule", + "isHidden": false + }, + { + "name": "getSpecificProcessRuleList", + "description": "Gets all active workflow rules for an sObject.", + "isHidden": false + }, + { + "name": "returnHeadersForProcessRules", + "description": "Returns only the headers that are returned by the listProcessRules operation.", + "isHidden": false + }, + { + "name": "returnHeadersForSpecificProcessRule", + "description": "Returns only the headers that are returned by the getSpecificProcessRule operation.", + "isHidden": false + }, + { + "name": "returnHeadersForSpecificProcessRuleList", + "description": "Returns only the headers that are returned by the getSpecificProcessRuleList operation.", + "isHidden": false + }, + { + "name": "triggerProcessRules", + "description": "Triggers all active workflow rules. All rules associated with the specified ID are evaluated, regardless of the evaluation criteria. All IDs must be for records on the same object.", + "isHidden": false + }, + { + "name": "getBlobBodyForAttachmentRecord", + "description": "Retrieving blob body for an Attachment record", + "isHidden": false + }, + { + "name": "getBlobBodyForDocumentRecord", + "description": "Retrieving blob body for a Document record", + "isHidden": false + }, + { + "name": "getBlobDataForSpecificObject", + "description": "Retrieves the specified blob field from an individual record and returns it as binary data", + "isHidden": false + }, + { + "name": "getSObjectRichTextImage", + "description": "Retrieves the specified image data from a specific rich text area field in a given record", + "isHidden": false + }, + { + "name": "describeEventMonitoring", + "description": "Use the SObject Describe resource to retrieve all metadata for an object, including information about fields, URLs, and child relationships.", + "isHidden": false + }, + { + "name": "queryEventMonitoringData", + "description": "Use the Query resource to retrieve field values from a record. Specify the fields you want to retrieve in the fields parameter and use the GET method of the resource.", + "isHidden": false + }, + { + "name": "getEventMonitoringContentFromRecord", + "description": "Retrieves event monitoring content in binary format.", + "isHidden": false + }, + { + "name": "getReport", + "description": "Returns the report for a specific report id.", + "isHidden": false + }, + { + "name": "listCompositeResources", + "description": "Retrieves a list of URIs for other composite resources.", + "isHidden": false + }, + { + "name": "sendMultipleRequestsUsingComposite", + "description": "Executes a series of REST API requests in a single call.", + "isHidden": false + }, + { + "name": "compositeGraph", + "description": "Submit composite graph operations.", + "isHidden": false + }, + { + "name": "compositeBatch", + "description": "Executes up to 25 subrequests in a single request.", + "isHidden": false + }, + { + "name": "createProductSchedules", + "description": "Establishes or reestablishes a product schedule with multiple installments for an opportunity product.", + "isHidden": false + }, + { + "name": "deleteProductSchedules", + "description": "Deletes all installments in a revenue or quantity schedule for opportunity products.", + "isHidden": false + }, + { + "name": "getProductSchedules", + "description": "Retrieves revenue and quantity schedules for opportunity products.", + "isHidden": false + }, + { + "name": "consentDetailsOnSingleAction", + "description": "Retrieves consent details based on a single action, like email or track, across specific consent management objects when the records have a lookup relationship.", + "isHidden": false + }, + { + "name": "consentDetailsOnMultipleAction", + "description": "Retrieves consent details based on multiple actions, like email and track, across specific consent management objects when the records have a lookup relationship.", + "isHidden": false + }, + { + "name": "embeddedServiceConfig", + "description": "Retrieves the values for your Embedded Service deployment configuration, including the branding colors, font, and site URL", + "isHidden": false + }, + { + "name": "returnHeadersForEmbeddedServiceConfig", + "description": "Retrieves only the headers that are returned by the embeddedServiceConfig operation.", + "isHidden": false + }, + { + "name": "listKnowledgeRESTApis", + "description": "Retrieves knowledge support REST APIs that allow both authorized and guest users to retrieve the user’s visible data categories and their associated articles.", + "isHidden": false + }, + { + "name": "listDataCategoryGroups", + "description": "Retrieves data category groups that are visible to the current user.", + "isHidden": false + }, + { + "name": "getDataCategoryDetails", + "description": "Retrieves data category details and the child categories by a given category.", + "isHidden": false + }, + { + "name": "listArticles", + "description": "Retrieves a page of online articles for the given language and category through either search or query.", + "isHidden": false + }, + { + "name": "getArticleDetails", + "description": "Retrieves all online article fields, accessible to the user.", + "isHidden": false + }, + { + "name": "getKnowledgeLanguageSettings", + "description": "Retrieves the existing Knowledge language settings, including the default knowledge language and a list of supported Knowledge language information.", + "isHidden": false + }, + { + "name": "platformEventSchemaByEventName", + "description": "Retrieves the definition of a platform event for an event name", + "isHidden": false + }, + { + "name": "platformEventSchemaByEventNameAndSpecifiedPayloadFormat", + "description": "Retrieves the definition of a platform event for an event name in specified payload format", + "isHidden": false + }, + { + "name": "platformEventSchemaBySchemaId", + "description": "Retrieves the definition of a platform event for a schema ID", + "isHidden": false + }, + { + "name": "platformEventSchemaBySchemaIdAndSpecifiedPayloadFormat", + "description": "Retrieves the definition of a platform event for a schema ID in specified payload format", + "isHidden": false + }, + { + "name": "compileDataForPortabilityRequest", + "description": "Aggregates your data subject's personally identifiable information (PII) into one file and sends a response with a URL to download the file, a policy file ID, and information on the objects and fields you selected when creating the policy.", + "isHidden": false + }, + { + "name": "statusOfPortabilityRequest", + "description": "Retrieves the status of the request done by compileDataForPortabilityRequest operation.", + "isHidden": false + }, + { + "name": "addOrChangeTranslationOfSurveyField", + "description": "Add or change the translated value of the survey field if a survey field can be translated or is already translated into a particular language.", + "isHidden": false + }, + { + "name": "addOrUpdateTranslatedValueOfMultipleSurveyFieldsInOneOrMoreLanguages", + "description": "If one or more survey fields can be translated or are already translated, adds or updates the translated values of the survey fields in the languages into which survey fields can be translated.", + "isHidden": false + }, + { + "name": "deleteTheTranslatedValueOfSurveyField", + "description": "Deletes the translated value of the survey field after a survey field is translated into a particular language.", + "isHidden": false + }, + { + "name": "deleteTranslatedValueOfMultipleSurveyFieldsInOneOrMoreLanguages", + "description": "Delete the translated values of multiple survey fields after survey fields are translated into one or more languages.", + "isHidden": false + }, + { + "name": "getTranslatedValueOfSurveyField", + "description": "Retrieves the translated value of the survey field after a survey field is translated into a particular language.", + "isHidden": false + }, + { + "name": "getTranslatedValuesOfMultipleSurveyFieldsInOneOrMoreLanguages", + "description": "Retrieves the translated values of multiple survey fields in the translated languages after survey fields are translated into one or more languages.", + "isHidden": false + }, + { + "name": "listSchedulerRESTResourcesAndURIs", + "description": "Retrieves a list of available Salesforce Scheduler REST resources and corresponding URIs.", + "isHidden": false + }, + { + "name": "listAppointmentSlots", + "description": "Retrieves a list of available appointment time slots for a resource based on given work type group or work type and service territories.", + "isHidden": false + }, + { + "name": "listAppointmentCandidates", + "description": "Retrieves a list of service resources (appointment candidates) based on work type group or work type and service territories.", + "isHidden": false + } + ], + "connections": [ + { + "name": "salesforcerest", + "description": "Connection for using the Salesforce REST API.", + "iconUrl": "" + } + ] + }, + "otherVersions": {}, + "connectorRank": 13, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-salesforcerest.png" + }, + { + "connectorName": "ServiceNow", + "repoName": "esb-connector-servicenow", + "description": "The ServiceNow connector allows you to access the ServiceNow REST API through WSO2 MI. ServiceNow is a software platform that supports IT service management and automates common business processes. This software as a service (SaaS) platform contains a number of modular applications that can vary by instance and user. ", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-servicenow", + "version": { + "tagName": "1.0.3", + "releaseId": "191494819", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Service-Now configuration.", + "isHidden": false + }, + { + "name": "getRecords", + "description": "Retrieves set of records from table.", + "isHidden": false + }, + { + "name": "getRecordById", + "description": "Retrieves record according to the system ID from table.", + "isHidden": false + }, + { + "name": "postRecord", + "description": "Insert a record into a table.", + "isHidden": false + }, + { + "name": "patchRecordById", + "description": "Patch a record from table by specifying system ID.", + "isHidden": false + }, + { + "name": "deleteRecordById", + "description": "Delete a record from table by specifying system ID.", + "isHidden": false + }, + { + "name": "putRecordById", + "description": "Put a record to table by specifying system ID.", + "isHidden": false + }, + { + "name": "postRecordStagingTable", + "description": "This method inserts incoming data into a specified staging table and triggers transformation based on predefined transform maps in the import set table.", + "isHidden": false + }, + { + "name": "getRecordsStagingTable", + "description": "This method retrieves the associated record and resulting transformation result.", + "isHidden": false + }, + { + "name": "getAggregateRecord", + "description": "Allow to compute aggregate statistics about existing table and column data.", + "isHidden": false + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 154, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-servicenow.png" + }, + { + "connectorName": "SharePoint", + "repoName": "esb-connector-sharepoint", + "description": "The sharePoint connector allows you to access the SharePoint REST API. SharePoint is a web application platform.SharePoint combines various functions which are traditionally separate applications: intranet, extranet, content management, document management, enterprise search, workflow management and web content management.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-sharepoint", + "version": { + "tagName": "2.0.0", + "releaseId": "218529104", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Config operation", + "isHidden": true + }, + { + "name": "createFolder", + "description": "Create a new folder within the specified parent folder.", + "isHidden": false + }, + { + "name": "createGroup", + "description": "Creates a new Microsoft 365 Group, which provisions a connected SharePoint site.", + "isHidden": false + }, + { + "name": "createList", + "description": "Creates a new list in the specified site.", + "isHidden": false + }, + { + "name": "createListItem", + "description": "Creates a new item in the specified list.", + "isHidden": false + }, + { + "name": "deleteDriveItem", + "description": "Deletes the specified file or folder.", + "isHidden": false + }, + { + "name": "deleteList", + "description": "Deletes the specified list.", + "isHidden": false + }, + { + "name": "deleteListItem", + "description": "Deletes the specified list item.", + "isHidden": false + }, + { + "name": "getDriveItemById", + "description": "Retrieves metadata about the specified file or folder.", + "isHidden": false + }, + { + "name": "getFolderChildren", + "description": "Retrieves all items within the specified folder.", + "isHidden": false + }, + { + "name": "getGroupSite", + "description": "Retrieves the root SharePoint site associated with the specified Microsoft 365 Group.", + "isHidden": false + }, + { + "name": "getListById", + "description": "Retrieves a list by its ID or Title/Display name.", + "isHidden": false + }, + { + "name": "getListItemById", + "description": "Retrieves a list item by its ID.", + "isHidden": false + }, + { + "name": "getListItems", + "description": "Retrieves all items in the specified list.", + "isHidden": false + }, + { + "name": "getLists", + "description": "Retrieves all lists in the specified site.", + "isHidden": false + }, + { + "name": "getRootChildren", + "description": "Retrieves all items in the root directory of the drive.", + "isHidden": false + }, + { + "name": "updateFileContent", + "description": "Updates the content of the specified file.", + "isHidden": false + }, + { + "name": "updateFolder", + "description": "Updates the properties of the specified folder.", + "isHidden": false + }, + { + "name": "updateList", + "description": "Updates properties of the specified list.", + "isHidden": false + }, + { + "name": "updateListItemFields", + "description": "Updates the fields of the specified list item.", + "isHidden": false + }, + { + "name": "uploadFile", + "description": "Uploads a new file to the specified folder.", + "isHidden": false + } + ], + "connections": [ + { + "name": "sharepoint", + "description": "Connection for accessing SharePoint data and files.", + "iconUrl": "" + } + ] + }, + "otherVersions": { + "1.1.1": "191920908" + }, + "connectorRank": 52, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-sharepoint.gif" + }, + { + "connectorName": "SMPP", + "repoName": "esb-connector-smpp", + "description": "SMPP Connector allows you to send SMS through the WSO2 EI. It uses jsmpp API to communicate with a SMSC (Short Message service center) which is useful for storing, forwarding, converting and delivering Short Message Service (SMS) messages. jsmpp is a java implementation of SMPP. protocol.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-smpp", + "version": { + "tagName": "2.0.0", + "releaseId": "220935974", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Initialize SMSC configuration variables", + "isHidden": true + }, + { + "name": "sendSMS", + "description": "Send SMS message", + "isHidden": false + }, + { + "name": "sendBulkSMS", + "description": "Send Bulk SMS messages", + "isHidden": false + }, + { + "name": "unbind", + "description": "Unbind the SMSC connection", + "isHidden": false + } + ], + "connections": [ + { + "name": "SMPP", + "description": "Connection for exchanging messages via SMPP protocol.", + "iconUrl": "" + } + ] + }, + "otherVersions": { + "1.1.4": "193423969" + }, + "connectorRank": 19, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-smpp.png" + }, + { + "connectorName": "Snowflake", + "repoName": "esb-connector-snowflake", + "description": "The Snowflake Connector offers a complete range of Snowflake operations using WSO2 MI. It provides functionalities to execute a set of standard Snowflake DDL, DML and query commands.", + "connectorType": "Connector", + "mavenGroupId": "org.wso2.integration.connector", + "mavenArtifactId": "mi-connector-snowflake", + "version": { + "tagName": "1.0.4", + "releaseId": "211443998", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Init operation", + "isHidden": true + }, + { + "name": "query", + "description": "Query a given SQL statement.", + "isHidden": false + }, + { + "name": "execute", + "description": "Execute a given SQL statement.", + "isHidden": false + }, + { + "name": "batchExecute", + "description": "Batch execute a given SQL statement.", + "isHidden": false + } + ], + "connections": [ + { + "name": "Snowflake", + "description": "Connection for querying Snowflake data warehouse.", + "iconUrl": "" + } + ] + }, + "otherVersions": { + "1.0.2": "191493228" + }, + "connectorRank": 162, + "iconUrl": "https://stprodinternalapps.blob.core.windows.net/connector-store-integration-vscode-logos/esb-connector-snowflake.png" + } +] diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts new file mode 100644 index 00000000000..153c6d34dec --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts @@ -0,0 +1,208 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateObject } from "ai"; +import { z } from "zod"; +import * as Handlebars from "handlebars"; +import { FileObject, ImageObject } from "@wso2/mi-core"; +import { getAnthropicClient, ANTHROPIC_HAIKU_4_5, getProviderCacheControl } from "../connection"; +import { SYSTEM_TEMPLATE } from "./system"; +import { CONNECTOR_PROMPT } from "./prompt"; +import { CONNECTOR_DB } from "./connector_db"; +import { INBOUND_DB } from "./inbound_db"; +import { logInfo, logWarn, logError } from "../logger"; +import { buildMessageContent } from "../message-utils"; + +// Type definition for selected connectors +type SelectedConnectors = { + selected_connector_names: string[]; + selected_inbound_endpoint_names: string[]; +}; + +// Zod schema for structured output - matches Python Pydantic model +const selectedConnectorsSchema: z.ZodType = z.object({ + selected_connector_names: z.array(z.string()) + .describe("The names of the selected connectors."), + selected_inbound_endpoint_names: z.array(z.string()) + .describe("The names of the selected inbound endpoints/event listeners."), +}); + +/** + * Render a template using Handlebars + */ +function renderTemplate(templateContent: string, context: Record): string { + const template = Handlebars.compile(templateContent); + return template(context); +} + +/** + * Get available connector names for LLM selection + */ +function getAvailableConnectors(): string[] { + return CONNECTOR_DB.map(connector => connector.connectorName); +} + +/** + * Get available inbound endpoint names for LLM selection + */ +function getAvailableInboundEndpoints(): string[] { + return INBOUND_DB.map(inbound => inbound.connectorName); +} + +/** + * Get full connector definitions by names + */ +function getConnectorDefinitions(connectorNames: string[]): Record { + const definitions: Record = {}; + + for (const name of connectorNames) { + const connector = CONNECTOR_DB.find(c => c.connectorName === name); + if (connector) { + definitions[name] = JSON.stringify(connector, null, 2); + } + } + + return definitions; +} + +/** + * Get full inbound endpoint definitions by names + */ +function getInboundEndpointDefinitions(inboundNames: string[]): Record { + const definitions: Record = {}; + + for (const name of inboundNames) { + const inbound = INBOUND_DB.find(i => i.connectorName === name); + if (inbound) { + definitions[name] = JSON.stringify(inbound, null, 2); + } + } + + return definitions; +} + +/** + * Parameters for getting connectors + */ +export interface GetConnectorsParams { + /** The user's question or request */ + question: string; + /** Additional files for context (optional) - FileObject array */ + files?: FileObject[]; + /** Images for context (optional) - ImageObject array */ + images?: ImageObject[]; +} + +/** + * Result of connector selection + */ +export interface GetConnectorsResult { + /** Selected connector definitions */ + connectors: Record; + /** Selected inbound endpoint definitions */ + inbound_endpoints: Record; +} + +/** + * Gets relevant connectors and inbound endpoints for the user's query + * Uses AI to intelligently select from available options + */ +export async function getConnectors( + params: GetConnectorsParams +): Promise { + // Get available connectors and inbound endpoints + const availableConnectors = getAvailableConnectors(); + const availableInboundEndpoints = getAvailableInboundEndpoints(); + + if (availableConnectors.length === 0) { + logWarn("No connector details available - returning empty list"); + return { connectors: {}, inbound_endpoints: {} }; + } + + // Render the prompt with the user's question and available options + const prompt = renderTemplate(CONNECTOR_PROMPT, { + question: params.question, + available_connectors: availableConnectors.join(", "), + available_inbound_endpoints: availableInboundEndpoints.join(", "), + }); + + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); + const cacheOptions = await getProviderCacheControl(); + + // Check if files or images are present + const hasFiles = params.files && params.files.length > 0; + const hasImages = params.images && params.images.length > 0; + + // Build user message content with files and images if present + let userMessageContent: string | any[]; + if (hasFiles || hasImages) { + logInfo(`Including ${params.files?.length || 0} files and ${params.images?.length || 0} images in connector selection`); + // Note: Attachments are pre-validated in RPC manager via validateAttachments() + userMessageContent = buildMessageContent(prompt, params.files, params.images); + } else { + userMessageContent = prompt; + } + + // Build messages array with cache control on system message + const messages: any[] = [ + { + role: "system" as const, + content: SYSTEM_TEMPLATE, + providerOptions: cacheOptions, // Cache system prompt only + }, + { + role: "user" as const, + content: userMessageContent, + } + ]; + + try { + // Use structured output to get selected connectors + // Type assertion to avoid TypeScript deep instantiation issues with Zod + const result = await (generateObject as any)({ + model: model, + messages: messages, + schema: selectedConnectorsSchema, + maxOutputTokens: 2000, + temperature: 0.3, + }); + + // Extract the selected connectors from the result + const selectedConnectors = result.object as SelectedConnectors; + + // Get full definitions for selected connectors + const connectorDefinitions = selectedConnectors.selected_connector_names.length > 0 + ? getConnectorDefinitions(selectedConnectors.selected_connector_names) + : {}; + + const inboundDefinitions = selectedConnectors.selected_inbound_endpoint_names.length > 0 + ? getInboundEndpointDefinitions(selectedConnectors.selected_inbound_endpoint_names) + : {}; + + logInfo(`Selected ${selectedConnectors.selected_connector_names.length} connectors and ${selectedConnectors.selected_inbound_endpoint_names.length} inbound endpoints`); + + return { + connectors: connectorDefinitions, + inbound_endpoints: inboundDefinitions, + }; + } catch (error) { + logError("Error selecting connectors", error); + // Return empty if selection fails + return { connectors: {}, inbound_endpoints: {} }; + } +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/inbound_db.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/inbound_db.ts new file mode 100644 index 00000000000..840dbf101da --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/inbound_db.ts @@ -0,0 +1,1968 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const INBOUND_DB = [ + { + "connectorName": "Amazon Simple Queue Service (Inbound)", + "repoName": "esb-inbound-amazonsqs", + "description": "Amazon SQS offers reliable and scalable hosted queues for storing messages as they travel between computers. By using Amazon SQS, you can move data between distributed components of your applications that perform different tasks without losing messages or requiring each component to be always available.", + "connectorType": "Inbound", + "mavenGroupId": "org.wso2.integration.inbound", + "mavenArtifactId": "mi-inbound-amazonsqs", + "id": "", + "version": { + "tagName": "2.0.1", + "releaseId": "233149909", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Initialize Kafka Inbound Endpoint", + "isHidden": false, + "parameters": [ + { + "name": "accessKey", + "type": "string", + "required": false, + "description": "Provide the AWS access key for authentication. Not required if IAM Role authentication is used.", + "defaultValue": "" + }, + { + "name": "attributeNames", + "type": "string", + "required": false, + "description": "Specify which message attribute names to retrieve.", + "defaultValue": "All" + }, + { + "name": "autoRemoveMessage", + "type": "checkbox", + "required": false, + "description": "Automatically remove the message from the queue after processing.", + "defaultValue": "True" + }, + { + "name": "class", + "type": "string", + "required": true, + "description": "", + "defaultValue": "org.wso2.carbon.inbound.amazonsqs.AmazonSQSPollingConsumer" + }, + { + "name": "contentType", + "type": "combo", + "required": false, + "description": "The content type of the messages.", + "defaultValue": "text/plain" + }, + { + "name": "coordination", + "type": "checkbox", + "required": false, + "description": "In a clustered setup, this will run the inbound only in a single worker node.", + "defaultValue": "True" + }, + { + "name": "destination", + "type": "string", + "required": true, + "description": "Specify the Amazon SQS queue destination.", + "defaultValue": "" + }, + { + "name": "generateSequences", + "type": "checkbox", + "required": false, + "description": "", + "defaultValue": "True" + }, + { + "name": "interval", + "type": "string", + "required": true, + "description": "The polling interval for the AWS SQS inbound endpoint.", + "defaultValue": "" + }, + { + "name": "maxNoOfMessage", + "type": "string", + "required": false, + "description": "The maximum number of messages to return.", + "defaultValue": "" + }, + { + "name": "name", + "type": "string", + "required": true, + "description": "Unique identifier for the AWS SQS event integration", + "defaultValue": "" + }, + { + "name": "onError", + "type": "keyOrExpression", + "required": true, + "description": "Error sequence to invoke on fault", + "defaultValue": "" + }, + { + "name": "secretKey", + "type": "string", + "required": false, + "description": "Provide the AWS secret key for authentication. Not required if IAM Role authentication is used.", + "defaultValue": "" + }, + { + "name": "sequence", + "type": "keyOrExpression", + "required": true, + "description": "Sequence to inject the incoming AWS SQS message", + "defaultValue": "" + }, + { + "name": "sequential", + "type": "checkbox", + "required": false, + "description": "The behaviour when executing the given sequence.", + "defaultValue": "True" + }, + { + "name": "suspend", + "type": "checkbox", + "required": false, + "description": "Suspend Inbound", + "defaultValue": "False" + }, + { + "name": "wait_time", + "type": "string", + "required": false, + "description": "The duration (in seconds) the call waits for a message to arrive in the queue before returning.", + "defaultValue": "" + } + ] + } + ], + "connections": [] + }, + "otherVersions": { + "1.1.5": "204041910" + }, + "connectorRank": 11, + "iconUrl": "" + }, + { + "connectorName": "CDC (Inbound)", + "repoName": "mi-inbound-cdc", + "description": "The CDC inbound protocol is used to perform Change Data Capture in MI. The changes happening to any external database can be listened to using the CDC inbound endpoint. The CDC protocol uses Debezium to connect with the databases and capture the events. The protocol itself outputs the event via a sequence through the Inbound Endpoint.", + "connectorType": "Inbound", + "mavenGroupId": "org.wso2.integration.inbound", + "mavenArtifactId": "mi-inbound-cdc", + "id": "", + "version": { + "tagName": "2.0.2", + "releaseId": "233149423", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Initialize Kafka Inbound Endpoint", + "isHidden": false, + "parameters": [ + { + "name": "allowed.operations", + "type": "string", + "required": false, + "description": "Comma separated values. Ex: create, update, delete, truncate.", + "defaultValue": "" + }, + { + "name": "class", + "type": "string", + "required": true, + "description": "", + "defaultValue": "org.wso2.carbon.inbound.cdc.CDCPollingConsumer" + }, + { + "name": "connector.class", + "type": "string", + "required": true, + "description": "The name of the Java class for the connector.", + "defaultValue": "io.debezium.connector.mysql.MySqlConnector" + }, + { + "name": "connector.name", + "type": "string", + "required": false, + "description": "Unique name for the Debezium connector instance. The inbound endpoint name is used as default.", + "defaultValue": "" + }, + { + "name": "coordination", + "type": "checkbox", + "required": false, + "description": "In a clustered setup, this will run the inbound only in a single worker node.", + "defaultValue": "True" + }, + { + "name": "database.dbname", + "type": "string", + "required": true, + "description": "The name of the database that needs to be listened to.", + "defaultValue": "" + }, + { + "name": "database.hostname", + "type": "string", + "required": true, + "description": "IP address or hostname of the database server.", + "defaultValue": "" + }, + { + "name": "database.instance", + "type": "string", + "required": false, + "description": "Specifies the instance name of the SQL Server named instance.", + "defaultValue": "" + }, + { + "name": "database.names", + "type": "string", + "required": false, + "description": "The comma-separated list of the SQL Server database names from which to stream the changes.", + "defaultValue": "" + }, + { + "name": "database.out.server.name", + "type": "string", + "required": false, + "description": "The name of the XStream outbound server configured in the database.", + "defaultValue": "" + }, + { + "name": "database.password", + "type": "string", + "required": true, + "description": "The password to connect to the database.", + "defaultValue": "" + }, + { + "name": "database.port", + "type": "string", + "required": true, + "description": "Port number (Integer) of the database server.", + "defaultValue": "" + }, + { + "name": "database.server.id", + "type": "string", + "required": false, + "description": "A unique numeric ID for this database client across all active database processes in the cluster.", + "defaultValue": "" + }, + { + "name": "database.user", + "type": "string", + "required": true, + "description": "Name of the database user to use when connecting to the database server.", + "defaultValue": "" + }, + { + "name": "generateSequences", + "type": "checkbox", + "required": false, + "description": "", + "defaultValue": "True" + }, + { + "name": "interval", + "type": "string", + "required": true, + "description": "The polling interval for the CDC inbound endpoint.", + "defaultValue": "" + }, + { + "name": "name", + "type": "string", + "required": true, + "description": "Unique identifier for the CDC event integration.", + "defaultValue": "" + }, + { + "name": "offset.storage", + "type": "string", + "required": false, + "description": "The Java class that persists connector offsets.", + "defaultValue": "" + }, + { + "name": "offset.storage.file.filename", + "type": "string", + "required": false, + "description": "Path to file where offsets are to be stored.", + "defaultValue": "" + }, + { + "name": "offset.storage.partitions", + "type": "string", + "required": false, + "description": "The number of partitions for the offset storage topic.", + "defaultValue": "" + }, + { + "name": "offset.storage.replication.factor", + "type": "string", + "required": false, + "description": "The replication factor for the offset storage topic.", + "defaultValue": "" + }, + { + "name": "offset.storage.topic", + "type": "string", + "required": false, + "description": "The Kafka topic where offsets are stored.", + "defaultValue": "" + }, + { + "name": "onError", + "type": "keyOrExpression", + "required": true, + "description": "Error sequence to invoke on fault", + "defaultValue": "" + }, + { + "name": "preserve.event", + "type": "checkbox", + "required": false, + "description": "Enable this to preserve the original event payload.", + "defaultValue": "False" + }, + { + "name": "schema.history.internal", + "type": "string", + "required": false, + "description": "The Java class that persists the database schema history.", + "defaultValue": "" + }, + { + "name": "schema.history.internal.file.filename", + "type": "string", + "required": false, + "description": "Specify the file path where the database schema history is stored.", + "defaultValue": "" + }, + { + "name": "schema.history.internal.kafka.bootstrap.servers", + "type": "string", + "required": false, + "description": "The initial list of Kafka cluster servers to connect.", + "defaultValue": "" + }, + { + "name": "schema.history.internal.kafka.topic", + "type": "string", + "required": false, + "description": "The Kafka topic storing the database schema history.", + "defaultValue": "" + }, + { + "name": "sequence", + "type": "keyOrExpression", + "required": true, + "description": "Sequence to inject the CDC message", + "defaultValue": "" + }, + { + "name": "sequential", + "type": "checkbox", + "required": false, + "description": "The behavior when executing the given sequence.", + "defaultValue": "True" + }, + { + "name": "snapshot.mode", + "type": "combo", + "required": true, + "description": "Specifies the criteria for running a snapshot when the connector starts.", + "defaultValue": "initial" + }, + { + "name": "suspend", + "type": "checkbox", + "required": false, + "description": "Suspend Inbound", + "defaultValue": "False" + }, + { + "name": "table.include.list", + "type": "string", + "required": false, + "description": "Comma-separated list of tables for which changes need to be captured.", + "defaultValue": "" + }, + { + "name": "topic.prefix", + "type": "string", + "required": false, + "description": "The prefix, used for all Kafka topic names, must be unique across all connectors.", + "defaultValue": "" + } + ] + } + ], + "connections": [] + }, + "otherVersions": { + "1.2.2": "204041909" + }, + "connectorRank": 14, + "iconUrl": "" + }, + { + "connectorName": "ISO8583 (Inbound)", + "repoName": "esb-inbound-iso8583", + "description": "ISO8583 inbound allows you to listen for ISO8583 standard messages through WSO2 ESB", + "connectorType": "Inbound", + "mavenGroupId": "org.wso2.integration.inbound", + "mavenArtifactId": "mi-inbound-iso8583", + "id": "org.wso2.carbon.inbound.iso8583.listening.ISO8583MessageConsumer", + "version": { + "tagName": "1.1.5", + "releaseId": "233150249", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Initialize Kafka Inbound Endpoint", + "isHidden": false, + "parameters": [ + { + "name": "class", + "type": "string", + "required": true, + "description": "", + "defaultValue": "org.wso2.carbon.inbound.iso8583.listening.ISO8583MessageConsumer" + }, + { + "name": "coordination", + "type": "checkbox", + "required": false, + "description": "In a clustered setup, this will run the inbound only in a single worker node.", + "defaultValue": "True" + }, + { + "name": "coreThreads", + "type": "string", + "required": false, + "description": "Number of core threads in the thread pool.", + "defaultValue": "1" + }, + { + "name": "generateSequences", + "type": "checkbox", + "required": false, + "description": "", + "defaultValue": "True" + }, + { + "name": "headerLength", + "type": "string", + "required": false, + "description": "Length of the ISO header", + "defaultValue": "0" + }, + { + "name": "inbound.behavior", + "type": "string", + "required": true, + "description": "Inbound behavior", + "defaultValue": "listening" + }, + { + "name": "isProxy", + "type": "checkbox", + "required": false, + "description": "ISO8583 Inbound endpoint act as a proxy to another service.", + "defaultValue": "False" + }, + { + "name": "keepAlive", + "type": "string", + "required": false, + "description": "Maximum time that excess idle threads will wait for new tasks before terminating.", + "defaultValue": "1" + }, + { + "name": "maxThreads", + "type": "string", + "required": false, + "description": "Maximum number of threads in the thread pool.", + "defaultValue": "3" + }, + { + "name": "name", + "type": "string", + "required": true, + "description": "Unique identifier for the ISO8583 event integration.", + "defaultValue": "" + }, + { + "name": "onError", + "type": "keyOrExpression", + "required": true, + "description": "Error sequence to invoke on fault", + "defaultValue": "" + }, + { + "name": "port", + "type": "string", + "required": true, + "description": "Port number on which to listen for incoming messages.", + "defaultValue": "" + }, + { + "name": "queueLength", + "type": "string", + "required": false, + "description": "Number of tasks that can be queued before the thread pool starts rejecting tasks.", + "defaultValue": "1" + }, + { + "name": "sequence", + "type": "keyOrExpression", + "required": true, + "description": "Sequence to inject the ISO8583 message", + "defaultValue": "" + }, + { + "name": "sequential", + "type": "checkbox", + "required": false, + "description": "The behaviour when executing the given sequence.", + "defaultValue": "True" + }, + { + "name": "suspend", + "type": "checkbox", + "required": false, + "description": "Suspend Inbound", + "defaultValue": "False" + } + ] + } + ], + "connections": [] + }, + "otherVersions": { + "1.1.2": "168855518", + "1.1.1": "147636915" + }, + "connectorRank": 12, + "iconUrl": "" + }, + { + "connectorName": "Kafka (Inbound)", + "repoName": "esb-inbound-kafka", + "description": "Kafka inbound endpoint acts as a message consumer for Kafka. It receives messages from configured topics of Kafka platform and inject them into the mediation flow.", + "connectorType": "Inbound", + "mavenGroupId": "org.wso2.integration.inbound", + "mavenArtifactId": "mi-inbound-kafka", + "id": "", + "version": { + "tagName": "2.0.4", + "releaseId": "232863938", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Initialize Kafka Inbound Endpoint", + "isHidden": false, + "parameters": [ + { + "name": "auto.commit.interval.ms", + "type": "string", + "required": false, + "description": "Offsets are committed automatically with a frequency controlled by the config.", + "defaultValue": "5000" + }, + { + "name": "auto.offset.reset", + "type": "string", + "required": false, + "description": "Defines what to do when there is no initial offset in Kafka or if the current offset does not exist any more on the server.", + "defaultValue": "latest" + }, + { + "name": "avro.use.logical.type.converters", + "type": "boolean", + "required": false, + "description": "Whether to enable the use of logical type converters in Avro.", + "defaultValue": "False" + }, + { + "name": "basic.auth.credentials.source", + "type": "string", + "required": false, + "description": "Specify how to pick the credentials for the Basic authentication header.", + "defaultValue": "" + }, + { + "name": "basic.auth.user.info", + "type": "string", + "required": false, + "description": "Specify the user info for the Basic authentication in the form of {username}:{password}.", + "defaultValue": "" + }, + { + "name": "bootstrap.servers", + "type": "string", + "required": true, + "description": "Comma-separated list of host:port pairs of Kafka brokers.", + "defaultValue": "localhost:9092" + }, + { + "name": "check.crcs", + "type": "boolean", + "required": false, + "description": "Automatically check the CRC32 of the records consumed.", + "defaultValue": "true" + }, + { + "name": "class", + "type": "string", + "required": true, + "description": "", + "defaultValue": "org.wso2.carbon.inbound.kafka.KafkaMessageConsumer" + }, + { + "name": "client.id", + "type": "string", + "required": false, + "description": "An id string to pass to the server when making requests.", + "defaultValue": "" + }, + { + "name": "connections.max.idle.ms", + "type": "string", + "required": false, + "description": "Close idle connections after the number of milliseconds specified by this config.", + "defaultValue": "540000" + }, + { + "name": "contentType", + "type": "string", + "required": true, + "description": "Consumer group ID to use when consuming messages.", + "defaultValue": "application/json" + }, + { + "name": "coordination", + "type": "boolean", + "required": false, + "description": "In a clustered setup, this will run the inbound only in a single worker node.", + "defaultValue": "True" + }, + { + "name": "enable.auto.commit", + "type": "boolean", + "required": false, + "description": "Whether the consumer will automatically commit offsets periodically at the interval set by auto.commit.interval.ms.", + "defaultValue": "True" + }, + { + "name": "exclude.internal.topics", + "type": "checkbox", + "required": false, + "description": "Whether internal topics matching a subscribed pattern should be excluded from the subscription.", + "defaultValue": "true" + }, + { + "name": "failure.retry.count", + "type": "string", + "required": false, + "description": "The maximum attempts the same Kafka message should be retried in a failure scenario.", + "defaultValue": "" + }, + { + "name": "failure.retry.interval", + "type": "string", + "required": false, + "description": "The interval between retries in a failure scenario.", + "defaultValue": "" + }, + { + "name": "fetch.max.bytes", + "type": "string", + "required": false, + "description": "The maximum amount of data the server should return for a fetch request.", + "defaultValue": "" + }, + { + "name": "fetch.max.wait.ms", + "type": "string", + "required": false, + "description": "The maximum amount of time the server will block before answering the fetch request if there isn’t sufficient data to immediately satisfy the requirement given by fetch.min.bytes.", + "defaultValue": "500" + }, + { + "name": "fetch.min.bytes", + "type": "string", + "required": false, + "description": "The minimum amount of data the server should return for a fetch request.", + "defaultValue": "" + }, + { + "name": "generateSequences", + "type": "checkbox", + "required": false, + "description": "", + "defaultValue": "True" + }, + { + "name": "group.id", + "type": "string", + "required": true, + "description": "Consumer group ID to use when consuming messages.", + "defaultValue": "group1" + }, + { + "name": "heartbeat.interval.ms", + "type": "string", + "required": false, + "description": "The expected time between heartbeats to the consumer coordinator when using Kafka’s group management facilities.", + "defaultValue": "" + }, + { + "name": "interceptor.classes", + "type": "string", + "required": false, + "description": "A list of classes to use as interceptors.", + "defaultValue": "" + }, + { + "name": "interval", + "type": "string", + "required": true, + "description": "The polling interval for the Kafka inbound endpoint in milliseconds.", + "defaultValue": "100" + }, + { + "name": "kafka.header.prefix", + "type": "string", + "required": false, + "description": "The prefix for Kafka headers.", + "defaultValue": "" + }, + { + "name": "key.delegate.deserializer", + "type": "string", + "required": false, + "description": "The actual key deserializer that the error handling deserializer should delegate to.", + "defaultValue": "" + }, + { + "name": "key.deserializer", + "type": "string", + "required": true, + "description": "Deserializer class for key that implements the org.apache.kafka.common.serialization.Deserializer interface.", + "defaultValue": "org.apache.kafka.common.serialization.StringDeserializer" + }, + { + "name": "max.partition.fetch.bytes", + "type": "string", + "required": false, + "description": "The maximum amount of data per-partition the server will return.", + "defaultValue": "" + }, + { + "name": "max.poll.interval.ms", + "type": "string", + "required": false, + "description": "The maximum delay between polls when using consumer group management.", + "defaultValue": "300000" + }, + { + "name": "max.poll.records", + "type": "string", + "required": false, + "description": "The maximum number of records returned in a single poll", + "defaultValue": "500" + }, + { + "name": "metadata.max.age.ms", + "type": "string", + "required": false, + "description": "The period of time in milliseconds after which we force a refresh of metadata", + "defaultValue": "" + }, + { + "name": "metric.reporters", + "type": "string", + "required": false, + "description": "A list of classes to use as metrics reporters.", + "defaultValue": "" + }, + { + "name": "metrics.num.samples", + "type": "string", + "required": false, + "description": "The number of samples maintained to compute metrics.", + "defaultValue": "2" + }, + { + "name": "metrics.recording.level", + "type": "string", + "required": false, + "description": "The highest recording level for metrics.", + "defaultValue": "INFO" + }, + { + "name": "metrics.sample.window.ms", + "type": "string", + "required": false, + "description": "The window of time a metrics sample is computed over.", + "defaultValue": "30000" + }, + { + "name": "name", + "type": "string", + "required": true, + "description": "Unique identifier for the Kafka event integration.", + "defaultValue": "" + }, + { + "name": "onError", + "type": "keyOrExpression", + "required": true, + "description": "Error sequence to invoke on fault", + "defaultValue": "" + }, + { + "name": "partition.assignment.strategy", + "type": "string", + "required": false, + "description": "A list of class names or class types, ordered by preference, of supported partition assignment strategies.", + "defaultValue": "org.apache.kafka.clients.consumer.RangeAssignor" + }, + { + "name": "poll.timeout", + "type": "string", + "required": true, + "description": "The timeout in milliseconds when polling for consumer data.", + "defaultValue": "1000" + }, + { + "name": "receive.buffer.bytes", + "type": "string", + "required": false, + "description": "The size of the TCP receive buffer to use when reading data.", + "defaultValue": "65536" + }, + { + "name": "reconnect.backoff.ms", + "type": "string", + "required": false, + "description": "The amount of time to wait before attempting to reconnect to a given host.", + "defaultValue": "50" + }, + { + "name": "request.timeout.ms", + "type": "string", + "required": false, + "description": "The maximum amount of time the client will wait for the response of a request.", + "defaultValue": "305000" + }, + { + "name": "retry.backoff.ms", + "type": "string", + "required": false, + "description": "The amount of time to wait before attempting to retry a failed request to a given topic partition.", + "defaultValue": "100" + }, + { + "name": "sasl.client.callback.handler.class", + "type": "string", + "required": false, + "description": "The fully qualified name of a SASL client callback handler class that implements the AuthenticateCallbackHandler interface.", + "defaultValue": "" + }, + { + "name": "sasl.jaas.config", + "type": "string", + "required": false, + "description": "JAAS login context parameters for SASL connections in the format used by JAAS configuration files.", + "defaultValue": "" + }, + { + "name": "sasl.kerberos.kinit.cmd", + "type": "string", + "required": false, + "description": "Kerberos kinit command path.", + "defaultValue": "" + }, + { + "name": "sasl.kerberos.min.time.before.relogin", + "type": "string", + "required": false, + "description": "Login thread sleep time between refresh attempts.", + "defaultValue": "" + }, + { + "name": "sasl.kerberos.service.name", + "type": "string", + "required": false, + "description": "The Kerberos principal name that Kafka runs as.", + "defaultValue": "" + }, + { + "name": "sasl.kerberos.ticket.renew.jitter", + "type": "string", + "required": false, + "description": "Percentage of random jitter added to the renewal time.", + "defaultValue": "" + }, + { + "name": "sasl.kerberos.ticket.renew.window.factor", + "type": "string", + "required": false, + "description": "Login thread will sleep until the specified window factor of time from last refresh to ticket’s expiry has been reached, at which time it will try to renew the ticket.", + "defaultValue": "" + }, + { + "name": "sasl.login.callback.handler.class", + "type": "string", + "required": false, + "description": "The fully qualified name of a SASL login callback handler class that implements the AuthenticateCallbackHandler interface.", + "defaultValue": "" + }, + { + "name": "sasl.login.class", + "type": "string", + "required": false, + "description": "The fully qualified name of a class that implements the Login interface.", + "defaultValue": "" + }, + { + "name": "sasl.login.connect.timeout.ms", + "type": "string", + "required": false, + "description": "The value in milliseconds for the external authentication provider connection timeout.", + "defaultValue": "" + }, + { + "name": "sasl.login.read.timeout.ms", + "type": "string", + "required": false, + "description": "The value in milliseconds for the external authentication provider read timeout.", + "defaultValue": "" + }, + { + "name": "sasl.login.retry.backoff.max.ms", + "type": "string", + "required": false, + "description": "The value in milliseconds for the maximum wait between login attempts to the external authentication provider.", + "defaultValue": "" + }, + { + "name": "sasl.login.retry.backoff.ms", + "type": "string", + "required": false, + "description": "The value in milliseconds for the initial wait between login attempts to the external authentication provider.", + "defaultValue": "" + }, + { + "name": "sasl.mechanism", + "type": "string", + "required": false, + "description": "SASL mechanism used for client connections.", + "defaultValue": "" + }, + { + "name": "sasl.oauthbearer.scope.claim.name", + "type": "string", + "required": false, + "description": "The OAuth claim for the scope is often named ‘scope’, but this setting can provide a different name to use for the scope included in the JWT payload’s claims.", + "defaultValue": "" + }, + { + "name": "sasl.oauthbearer.token.endpoint.url", + "type": "string", + "required": false, + "description": "The URL for the OAuth/OIDC identity provider.", + "defaultValue": "" + }, + { + "name": "schema.registry.url", + "type": "string", + "required": false, + "description": "Comma-separated list of URLs for Schema Registry instances that can be used to register or look up schemas.", + "defaultValue": "" + }, + { + "name": "security.protocol", + "type": "combo", + "required": false, + "description": "Protocol used to communicate with brokers.", + "defaultValue": "PLAINTEXT" + }, + { + "name": "send.buffer.bytes", + "type": "string", + "required": false, + "description": "The size of the TCP send buffer (SO_SNDBUF) to use when sending data.", + "defaultValue": "131072" + }, + { + "name": "sequence", + "type": "keyOrExpression", + "required": true, + "description": "Sequence to inject the Kafka message", + "defaultValue": "" + }, + { + "name": "sequential", + "type": "boolean", + "required": false, + "description": "The behaviour when executing the given sequence.", + "defaultValue": "True" + }, + { + "name": "session.timeout.ms", + "type": "string", + "required": false, + "description": "The timeout used to detect client failures when using Kafka’s group management facility.", + "defaultValue": "" + }, + { + "name": "ssl.cipher.suites", + "type": "string", + "required": false, + "description": "A list of cipher suites.", + "defaultValue": "" + }, + { + "name": "ssl.enabled.protocols", + "type": "string", + "required": false, + "description": "The list of protocols enabled for SSL connections.", + "defaultValue": "" + }, + { + "name": "ssl.endpoint.identification.algorithm", + "type": "string", + "required": false, + "description": "The endpoint identification algorithm to validate server hostname using server certificate.", + "defaultValue": "" + }, + { + "name": "ssl.key.password", + "type": "string", + "required": false, + "description": "The password of the private key in the key store file or the PEM key.", + "defaultValue": "" + }, + { + "name": "ssl.keymanager.algorithm", + "type": "string", + "required": false, + "description": "The algorithm used by key manager factory for SSL connections.", + "defaultValue": "" + }, + { + "name": "ssl.keystore.location", + "type": "string", + "required": false, + "description": "The location of the key store file.", + "defaultValue": "" + }, + { + "name": "ssl.keystore.password", + "type": "string", + "required": false, + "description": "The store password for the key store file.", + "defaultValue": "" + }, + { + "name": "ssl.keystore.type", + "type": "string", + "required": false, + "description": "The file format of the key store file.", + "defaultValue": "" + }, + { + "name": "ssl.protocol", + "type": "string", + "required": false, + "description": "The SSL protocol used to generate the SSLContext.", + "defaultValue": "" + }, + { + "name": "ssl.provider", + "type": "string", + "required": false, + "description": "The name of the security provider used for SSL connections.", + "defaultValue": "" + }, + { + "name": "ssl.secure.random.implementation", + "type": "string", + "required": false, + "description": "The SecureRandom PRNG implementation to use for SSL cryptography operations.", + "defaultValue": "" + }, + { + "name": "ssl.trustmanager.algorithm", + "type": "string", + "required": false, + "description": "The algorithm used by trust manager factory for SSL connections.", + "defaultValue": "" + }, + { + "name": "ssl.truststore.location", + "type": "string", + "required": false, + "description": "The location of the trust store file.", + "defaultValue": "" + }, + { + "name": "ssl.truststore.password", + "type": "string", + "required": false, + "description": "The password for the trust store file.", + "defaultValue": "" + }, + { + "name": "ssl.truststore.type", + "type": "string", + "required": false, + "description": "The file format of the trust store file.", + "defaultValue": "" + }, + { + "name": "suspend", + "type": "boolean", + "required": false, + "description": "Suspend Inbound", + "defaultValue": "False" + }, + { + "name": "topic.name", + "type": "string", + "required": true, + "description": "Name of the Kafka topic to subscribe to.", + "defaultValue": "" + }, + { + "name": "topic.partitions", + "type": "string", + "required": false, + "description": "Comma separated list of partitions of the topic which the consumer has subscribed to.", + "defaultValue": "" + }, + { + "name": "topic.pattern", + "type": "string", + "required": false, + "description": "The name pattern of the topic.", + "defaultValue": "" + }, + { + "name": "value.delegate.deserializer", + "type": "string", + "required": false, + "description": "The actual value deserializer that the error handling deserializer should delegate to.", + "defaultValue": "" + }, + { + "name": "value.deserializer", + "type": "string", + "required": true, + "description": "Deserializer class for value that implements the org.apache.kafka.common.serialization.Deserializer interface.", + "defaultValue": "org.apache.kafka.common.serialization.StringDeserializer" + } + ] + } + ], + "connections": [] + }, + "otherVersions": { + "1.2.6": "204041583", + "1.2.4": "189378349" + }, + "connectorRank": 15, + "iconUrl": "" + }, + { + "connectorName": "Pulsar (Inbound)", + "repoName": "mi-inbound-pulsar", + "description": "Apache Pulsar inbound endpoint acts as a message consumer for Apache Pulsar. It receives messages from configured topics of Apache Pulsar platform and inject them into the mediation flow.", + "connectorType": "Inbound", + "mavenGroupId": "org.wso2.integration.inbound", + "mavenArtifactId": "mi-inbound-pulsar", + "id": "", + "version": { + "tagName": "0.9.3", + "releaseId": "233149635", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Initialize Kafka Inbound Endpoint", + "isHidden": false, + "parameters": [ + { + "name": "ackTimeoutMillis", + "type": "string", + "required": false, + "description": "Timeout for acknowledging messages (in milliseconds).", + "defaultValue": "" + }, + { + "name": "authenticationType", + "type": "combo", + "required": false, + "description": "The authentication mechanism to use for authenticating with Pulsar. Supported values: None, JWT, TLS, OAuth2.", + "defaultValue": "None" + }, + { + "name": "autoAckOldestChunkedMessageOnQueueFull", + "type": "boolean", + "required": false, + "description": "Automatically acknowledge the oldest chunked message when the queue is full.", + "defaultValue": "" + }, + { + "name": "autoUpdatePartitions", + "type": "boolean", + "required": false, + "description": "Enable automatic partition updates.", + "defaultValue": "" + }, + { + "name": "autoUpdatePartitionsIntervalSeconds", + "type": "string", + "required": false, + "description": "Interval for automatic partition updates (in seconds).", + "defaultValue": "" + }, + { + "name": "batchIndexAckEnabled", + "type": "boolean", + "required": false, + "description": "Enable batch index acknowledgment.", + "defaultValue": "true" + }, + { + "name": "batchingMaxBytes", + "type": "string", + "required": false, + "description": "Maximum size of a batch in bytes.", + "defaultValue": "" + }, + { + "name": "batchingMaxMessages", + "type": "string", + "required": false, + "description": "Maximum number of messages in a batch.", + "defaultValue": "" + }, + { + "name": "batchingTimeout", + "type": "string", + "required": false, + "description": "Timeout for batching messages (in milliseconds).", + "defaultValue": "1000" + }, + { + "name": "batchReceiveEnabled", + "type": "boolean", + "required": false, + "description": "Enable batch receiving of messages.", + "defaultValue": "false" + }, + { + "name": "class", + "type": "string", + "required": true, + "description": "", + "defaultValue": "org.wso2.integration.inbound.PulsarMessageConsumer" + }, + { + "name": "concurrentLookupRequest", + "type": "string", + "required": false, + "description": "Number of concurrent lookup requests allowed.", + "defaultValue": "" + }, + { + "name": "connectionMaxIdleSeconds", + "type": "string", + "required": false, + "description": "Maximum idle time for connections (in seconds).", + "defaultValue": "" + }, + { + "name": "connectionsPerBroker", + "type": "string", + "required": false, + "description": "Number of connections per broker.", + "defaultValue": "" + }, + { + "name": "connectionTimeoutMs", + "type": "string", + "required": false, + "description": "Timeout for establishing a connection (in milliseconds).", + "defaultValue": "" + }, + { + "name": "contentType", + "type": "combo", + "required": true, + "description": "The content type of the incoming Pulsar message (e.g., application/json, text/xml).", + "defaultValue": "text/plain" + }, + { + "name": "coordination", + "type": "boolean", + "required": false, + "description": "In a clustered setup, this will run the inbound only in a single worker node.", + "defaultValue": "True" + }, + { + "name": "dlqMaxRedeliverCount", + "type": "string", + "required": false, + "description": "Maximum number of times a message will be redelivered before being sent to the DLQ.", + "defaultValue": "5" + }, + { + "name": "dlqTopic", + "type": "string", + "required": false, + "description": "Name of the Dead Letter Queue (DLQ) topic.", + "defaultValue": "" + }, + { + "name": "enableBusyWait", + "type": "boolean", + "required": false, + "description": "Enable busy-wait for IO threads.", + "defaultValue": "" + }, + { + "name": "enableTlsHostnameVerification", + "type": "boolean", + "required": false, + "description": "Enable hostname verification for TLS connections.", + "defaultValue": "" + }, + { + "name": "enableTransaction", + "type": "boolean", + "required": false, + "description": "Enable transaction support in Pulsar client.", + "defaultValue": "" + }, + { + "name": "expiryTimeOfIncompleteChunkedMessageMillis", + "type": "string", + "required": false, + "description": "Expiry time for incomplete chunked messages (in milliseconds).", + "defaultValue": "" + }, + { + "name": "generateSequences", + "type": "checkbox", + "required": false, + "description": "", + "defaultValue": "True" + }, + { + "name": "initialBackoffIntervalNanos", + "type": "string", + "required": false, + "description": "Initial backoff interval for reconnection attempts (in nanoseconds).", + "defaultValue": "" + }, + { + "name": "interval", + "type": "string", + "required": true, + "description": "The polling interval for the Apache Pulsar inbound endpoint in milliseconds.", + "defaultValue": "100" + }, + { + "name": "jwtToken", + "type": "string", + "required": false, + "description": "The JSON Web Token (JWT) used for authenticating with the Pulsar broker.", + "defaultValue": "" + }, + { + "name": "keepAliveIntervalSeconds", + "type": "string", + "required": false, + "description": "Keep-alive interval for broker connections (in seconds).", + "defaultValue": "" + }, + { + "name": "listenerName", + "type": "string", + "required": false, + "description": "Listener name for the Pulsar client.", + "defaultValue": "" + }, + { + "name": "lookupTimeoutMs", + "type": "string", + "required": false, + "description": "Timeout for lookup requests (in milliseconds).", + "defaultValue": "" + }, + { + "name": "maxBackoffIntervalNanos", + "type": "string", + "required": false, + "description": "Maximum backoff interval for reconnection attempts (in nanoseconds).", + "defaultValue": "" + }, + { + "name": "maxLookupRedirects", + "type": "string", + "required": false, + "description": "Maximum number of lookup redirects allowed.", + "defaultValue": "" + }, + { + "name": "maxLookupRequest", + "type": "string", + "required": false, + "description": "Maximum number of lookup requests.", + "defaultValue": "" + }, + { + "name": "maxNumberOfRejectedRequestPerConnection", + "type": "string", + "required": false, + "description": "Maximum number of rejected requests per connection.", + "defaultValue": "" + }, + { + "name": "maxPendingChunkedMessage", + "type": "string", + "required": false, + "description": "Maximum number of pending chunked messages allowed.", + "defaultValue": "" + }, + { + "name": "maxTotalReceiverQueueSizeAcrossPartitions", + "type": "string", + "required": false, + "description": "Maximum total receiver queue size across all partitions.", + "defaultValue": "" + }, + { + "name": "memoryLimitBytes", + "type": "string", + "required": false, + "description": "Memory limit for Pulsar client (in bytes).", + "defaultValue": "" + }, + { + "name": "messageWaitTimeout", + "type": "string", + "required": false, + "description": "The maximum time to wait for a message before timing out (in milliseconds).", + "defaultValue": "1000" + }, + { + "name": "nackRedeliveryDelay", + "type": "string", + "required": false, + "description": "Delay before redelivering negatively acknowledged messages.", + "defaultValue": "" + }, + { + "name": "name", + "type": "string", + "required": true, + "description": "Unique identifier for the Pulsar event integration.", + "defaultValue": "" + }, + { + "name": "numIoThreads", + "type": "string", + "required": false, + "description": "Number of IO threads for Pulsar client.", + "defaultValue": "" + }, + { + "name": "numListenerThreads", + "type": "string", + "required": false, + "description": "Number of listener threads for Pulsar client.", + "defaultValue": "" + }, + { + "name": "onError", + "type": "keyOrExpression", + "required": true, + "description": "Error sequence to invoke on fault", + "defaultValue": "" + }, + { + "name": "operationTimeoutSeconds", + "type": "string", + "required": false, + "description": "Timeout for client operations (in seconds).", + "defaultValue": "" + }, + { + "name": "processingMode", + "type": "combo", + "required": false, + "description": "Message processing mode (e.g., Sync, Async).", + "defaultValue": "Sync" + }, + { + "name": "readCompacted", + "type": "boolean", + "required": false, + "description": "Read messages from the compacted topic.", + "defaultValue": "" + }, + { + "name": "receiverQueueSize", + "type": "string", + "required": false, + "description": "Size of the consumer's receiver queue.", + "defaultValue": "" + }, + { + "name": "replicateSubscriptionState", + "type": "boolean", + "required": false, + "description": "Replicate the subscription state across clusters.", + "defaultValue": "" + }, + { + "name": "requestTimeoutMs", + "type": "string", + "required": false, + "description": "Timeout for requests (in milliseconds).", + "defaultValue": "" + }, + { + "name": "sequence", + "type": "keyOrExpression", + "required": true, + "description": "Sequence to inject the Pulsar message", + "defaultValue": "" + }, + { + "name": "serviceUrl", + "type": "string", + "required": true, + "description": "The Pulsar service URL to connect to. For a plain (non-secure) connection, use pulsar://:. For a secure (TLS) connection, use pulsar+ssl://:.", + "defaultValue": "" + }, + { + "name": "statsIntervalSeconds", + "type": "string", + "required": false, + "description": "Interval for statistics collection (in seconds).", + "defaultValue": "" + }, + { + "name": "subscriptionInitialPosition", + "type": "combo", + "required": false, + "description": "Initial position for the subscription (e.g., Latest, Earliest).", + "defaultValue": "Latest" + }, + { + "name": "subscriptionName", + "type": "string", + "required": true, + "description": "Name of the subscription.", + "defaultValue": "" + }, + { + "name": "subscriptionTopicsMode", + "type": "combo", + "required": false, + "description": "Mode for subscribing to topics (e.g., AllTopics, PersistentOnly, NonPersistentOnly).", + "defaultValue": "PersistentOnly" + }, + { + "name": "subscriptionType", + "type": "combo", + "required": false, + "description": "Type of subscription (e.g., Exclusive, Shared, Failover, Key_Shared).", + "defaultValue": "Exclusive" + }, + { + "name": "suspend", + "type": "boolean", + "required": false, + "description": "Suspend Inbound", + "defaultValue": "False" + }, + { + "name": "tlsAllowInsecureConnection", + "type": "boolean", + "required": false, + "description": "Allow insecure TLS connections by skipping certificate validation.", + "defaultValue": "false" + }, + { + "name": "tlsCiphers", + "type": "string", + "required": false, + "description": "Comma-separated list of enabled TLS cipher suites.", + "defaultValue": "" + }, + { + "name": "tlsProtocols", + "type": "string", + "required": false, + "description": "Comma-separated list of enabled TLS protocols (e.g., TLSv1.2,TLSv1.3).", + "defaultValue": "" + }, + { + "name": "tlsTrustCertsFilePath", + "type": "string", + "required": false, + "description": "Path to the trusted TLS certificate file.", + "defaultValue": "" + }, + { + "name": "tlsTrustStorePassword", + "type": "string", + "required": false, + "description": "Password for the TLS trust store.", + "defaultValue": "" + }, + { + "name": "tlsTrustStorePath", + "type": "string", + "required": false, + "description": "Path to the TLS trust store file.", + "defaultValue": "" + }, + { + "name": "tlsTrustStoreType", + "type": "string", + "required": false, + "description": "Type of the TLS trust store (e.g., JKS, PKCS12).", + "defaultValue": "" + }, + { + "name": "topicNames", + "type": "string", + "required": false, + "description": "Comma-separated list of topic names to subscribe to.", + "defaultValue": "" + }, + { + "name": "topicsPattern", + "type": "string", + "required": false, + "description": "Pattern to match topic names for subscription.", + "defaultValue": "" + }, + { + "name": "useKeyStoreTls", + "type": "boolean", + "required": false, + "description": "Enable TLS using a Java KeyStore.", + "defaultValue": "false" + }, + { + "name": "useTcpNoDelay", + "type": "boolean", + "required": false, + "description": "Enable TCP no delay for network connections.", + "defaultValue": "" + }, + { + "name": "useTLS", + "type": "boolean", + "required": true, + "description": "Enable TLS to secure the connection between the client and Pulsar broker.", + "defaultValue": "false" + } + ] + } + ], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 15, + "iconUrl": "" + }, + { + "connectorName": "Salesforce (Inbound)", + "repoName": "esb-inbound-salesforce", + "description": "The Salesforce streaming Inbound Endpoint allows you to perform various operations such as push topics and platform events on Salesforce streaming data via WSO2 EI.", + "connectorType": "Inbound", + "mavenGroupId": "org.wso2.integration.inbound", + "mavenArtifactId": "mi-inbound-salesforce", + "id": "", + "version": { + "tagName": "3.0.0", + "releaseId": "221723352", + "isLatest": true, + "isDeprecated": false, + "operations": [], + "connections": [] + }, + "otherVersions": { + "2.1.13": "220036413", + "2.1.11": "218982716", + "2.1.0": "204308186", + "2.0.20": "204045099", + "2.0.17": "199568764" + }, + "connectorRank": 16, + "iconUrl": "" + }, + { + "connectorName": "Salesforce PubSub", + "repoName": "mi-inbound-salesforcepubsub", + "description": "Inbuilt Salesforce Pub/Sub Event Listener", + "connectorType": "Inbound", + "mavenGroupId": "org.wso2.integration.inbound", + "mavenArtifactId": "mi-inbound-salesforcepubsub", + "id": "", + "version": { + "tagName": "0.1.4", + "releaseId": "233149968", + "isLatest": true, + "isDeprecated": false, + "operations": [], + "connections": [] + }, + "otherVersions": {}, + "connectorRank": 15, + "iconUrl": "" + }, + { + "connectorName": "SMPP (Inbound)", + "repoName": "esb-inbound-smpp", + "description": "SMPP Inbound allows you to receive SMS through the WSO2 EI. jsmpp is a java implementation of SMPP. protocol.", + "connectorType": "Inbound", + "mavenGroupId": "org.wso2.integration.inbound", + "mavenArtifactId": "mi-inbound-smpp", + "id": "", + "version": { + "tagName": "2.0.1", + "releaseId": "233149626", + "isLatest": true, + "isDeprecated": false, + "operations": [ + { + "name": "init", + "description": "Initialize Kafka Inbound Endpoint", + "isHidden": false, + "parameters": [ + { + "name": "addressNpi", + "type": "combo", + "required": true, + "description": "Numbering plan associated with the Short Message Service Center.", + "defaultValue": "UNKNOWN" + }, + { + "name": "addressRange", + "type": "string", + "required": false, + "description": "A single ESME address or a range of ESME addresses served via this SMPP receiver session.", + "defaultValue": "" + }, + { + "name": "addressTon", + "type": "combo", + "required": true, + "description": "Format of the addressing (bind addressing) that will be processed for inbound messages.", + "defaultValue": "UNKNOWN" + }, + { + "name": "bindType", + "type": "combo", + "required": true, + "description": "The type of bind to be used to connect to the Shot Message Service Center.", + "defaultValue": "BIND_RX" + }, + { + "name": "class", + "type": "string", + "required": true, + "description": "", + "defaultValue": "org.wso2.carbon.inbound.smpp.SMPPListeningConsumer" + }, + { + "name": "coordination", + "type": "checkbox", + "required": false, + "description": "In a clustered setup, this will run the inbound only in a single worker node.", + "defaultValue": "True" + }, + { + "name": "enquireLinkTimer", + "type": "string", + "required": false, + "description": "Used to check the connectivity between the SMPP inbound and SMSC", + "defaultValue": "10000" + }, + { + "name": "exponentialFactor", + "type": "string", + "required": false, + "description": "If the initial retry attempt fails, we should wait (reconnectInterval * exponentialFactor) times more.", + "defaultValue": "5" + }, + { + "name": "generateSequences", + "type": "checkbox", + "required": false, + "description": "", + "defaultValue": "True" + }, + { + "name": "host", + "type": "string", + "required": true, + "description": "IP address of the Short Message Service Center.", + "defaultValue": "localhost" + }, + { + "name": "inbound.behavior", + "type": "string", + "required": true, + "description": "Inbound behavior", + "defaultValue": "eventBased" + }, + { + "name": "maximumBackoffTime", + "type": "string", + "required": false, + "description": "The maximum backoff time to wait before retrying to connect with the SMSC.", + "defaultValue": "10000" + }, + { + "name": "name", + "type": "string", + "required": true, + "description": "Unique identifier for the SMPP event integration.", + "defaultValue": "" + }, + { + "name": "onError", + "type": "keyOrExpression", + "required": true, + "description": "Error sequence to invoke on fault", + "defaultValue": "" + }, + { + "name": "password", + "type": "string", + "required": true, + "description": "The password to be used to connect to the Shot Message Service Center.", + "defaultValue": "" + }, + { + "name": "port", + "type": "string", + "required": true, + "description": "Port to access the Short Message Service Center.", + "defaultValue": "2775" + }, + { + "name": "reconnectInterval", + "type": "string", + "required": false, + "description": "The Initial retry interval to reconnect with the SMSC.", + "defaultValue": "1000" + }, + { + "name": "retryCount", + "type": "string", + "required": false, + "description": "The number of times to retry to connect with SMSC. For infinite retries, set this value to -1.", + "defaultValue": "3" + }, + { + "name": "sequence", + "type": "keyOrExpression", + "required": true, + "description": "Sequence to inject the SMS message", + "defaultValue": "" + }, + { + "name": "sequential", + "type": "checkbox", + "required": false, + "description": "The behaviour when executing the given sequence.", + "defaultValue": "True" + }, + { + "name": "suspend", + "type": "checkbox", + "required": false, + "description": "Suspend Inbound", + "defaultValue": "False" + }, + { + "name": "systemId", + "type": "string", + "required": true, + "description": "The username to be used to connect to the Shot Message Service Center.", + "defaultValue": "" + }, + { + "name": "systemType", + "type": "combo", + "required": false, + "description": "Identifies the type of ESME system requesting to bind as a receiver with the SMSC.", + "defaultValue": "" + }, + { + "name": "transactionTimer", + "type": "string", + "required": false, + "description": "Time elapsed between SMPP request and the corresponding response.", + "defaultValue": "200" + } + ] + } + ], + "connections": [] + }, + "otherVersions": { + "1.0.3": "204042048" + }, + "connectorRank": 13, + "iconUrl": "" + } +] diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts new file mode 100644 index 00000000000..e0b31abfbf6 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const CONNECTOR_PROMPT = ` +You are an expert in Synapse integration using WSO2 Micro Integrator. Your task is to recommend the most appropriate WSO2 connectors and inbound endpoints based on a user query and a list of available connectors and inbound endpoints. +Your goal is to analyze the query, understand the integration requirements, and select the relevant connectors (up to six) and inbound endpoints (up to three) only from the provided list. + +User query: + +{{question}} + + +Available connectors: + +{{available_connectors}} + + +Available inbound endpoints/event listeners: + +{{available_inbound_endpoints}} + + +Task Instructions: +1. Understand the Integration Requirement: +- Analyze the user's query to determine the core integration problem they want to solve. +2. Incorporate Contextual Information: +- If any additional context (e.g., files or inline content) is provided, include it in your reasoning process. +3. Review Available Options +- Carefully examine the capabilities of connectors and inbound endpoints listed in and . +4. Evaluate Based on Relevance +- Consider all relevant connectors and inbound endpoints, even if the user does not explicitly mention them. +5. Rank by Utility +- Prioritize connectors and inbound endpoints by how likely they are to help solve the integration problem effectively. +6. Connector Selection +- Choose relevant connectors if the integration involves connecting to external services or APIs, or performing specific operations on the message payload. +- Select as many connectors as you think are relevant to the user query, up to six. +7. Inbound Endpoint Selection +- Choose relevant inbound endpoints if the integration involves listening to events or receiving incoming messages. +- Select as many inbound endpoints as you think are relevant to the user query, up to three. +8. Respect the Available List +- Never suggest any connectors or inbound endpoints that are not explicitly listed in the available sets. + +Now, take your time to reason step-by-step through the problem and select the most appropriate connectors and inbound endpoints based on the user query and the available connectors and inbound endpoints. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/system.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/system.ts new file mode 100644 index 00000000000..522b2126e37 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/system.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYSTEM_TEMPLATE = ` +You are a highly specialized AI assistant with deep expertise in building integration solutions using WSO2 Synapse for WSO2 Micro Integrator. Your primary role is to assist in designing and implementing robust, efficient, and scalable integration flows by selecting the most appropriate WSO2 connectors and inbound endpoints for a given use case. + +You have a thorough understanding of: + • WSO2 Micro Integrator architecture and capabilities + • The full range of available WSO2 connectors and inbound endpoints and their practical applications + • Common and advanced enterprise integration patterns + • Understanding the capabilities of the connectors and inbound endpoints + • Analyzing user requirements to recommend optimal integration components + +# What are the inbound endpoints? +- Inbound endpoints ( event listners ) are used to listen to events from various sources. +- Also referred as event listeners in latest versions of WSO2 Micro Integrator. + +# What are the connectors? +- Connectors can be used to connect to or call various external services and APIs. +- Or to perform specific operations on the message payload. + +# Difference between inbound endpoints and connectors? +- Inbound endpoints are used to receive/listen to events from external systems. +- Connectors are used to connect or call external systems or perform specific operations on the message payload. + +Your responses must always be accurate, context-aware, and grounded strictly in the available connector and inbound endpoint list provided. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/context/ai_module.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/ai_module.ts new file mode 100644 index 00000000000..3484734a8d1 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/ai_module.ts @@ -0,0 +1,245 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const AI_MODULE_GUIDE = ` +# Guide: Creating AI-Powered Apps with WSO2 Synapse + +WSO2 Micro Integrator now supports low-code AI mediators that allow developers to embed LLMs (such as OpenAI GPT) and implement retrieval-augmented generation (RAG) within integration flows. This guide walks through the key building blocks for creating AI-powered apps using Synapse configuration. + +--- + +## Chat Operation + +A basic chat operation requires the following two connection types: + +1. **LLM Connection** +2. **Memory Connection** + +### Step 1: Define Connections + +#### LLM Connection +\`\`\`xml + + + OPEN_AI + apiKey + https://api.openai.com/v1 + OPENAI_CONN + + +\`\`\` + +#### Memory Connection +\`\`\`xml + + + FILE_MEMORY + FILE_MEMORY_CONN + + +\`\`\` + +### Step 2: Create Chat Operation + +\`\`\`xml + + + OPENAI_CONN + FILE_MEMORY_CONN + + {\${payload.userID}} + \${payload.query} + string + ai_chat_1 + true + gpt-4o + 0.7 + 4069 + 1 + 0 + 10 + +\`\`\` + +## RAG Chat Operation + +RAG Chat uses additional configurations to retrieve knowledge from a vector store. + +Required Connections + 1. LLM Connection (same as before) + 2. Memory Connection (same as before) + 3. Embedding Model Connection (can reuse LLM connection) + 4. Vector Store Connection + +Example: Vector store connection: +\`\`\`xml + + + MI_VECTOR_STORE + KB_CONN + + +\`\`\` + +### Define RAG Chat Operation +\`\`\`xml + + + OPENAI_CONN + FILE_MEMORY_CONN + OPENAI_CONN + KB_CONN + + {\${payload.userID}} + \${payload.query} + string + ai_ragChat_1 + true + text-embedding-3-small + 5 + 0.75 + gpt-4o + 0.7 + 4069 + 1 + 0 + 10 + +\`\`\` + +## Adding data to vector store + +\`\`\`xml + + + OPENAI_CONN + KB_CONN + + {\${payload.content}} + false + true + Recursive + 1000 + 200 + true + text-embedding-3-small + ai_addToKnowledge_1 + true + +\`\`\` + +## Retrieving data from vector store + +\`\`\`xml + + + OPENAI_CONN + KB_CONN + + {\${payload.content}} + true + text-embedding-3-small + 5 + 0.75 + ai_getFromKnowledge_1 + true + +\`\`\` + +## Creating an agent with tools + +Agents allow LLMs to call custom tools during conversation flow. + +### Tool Creation Steps +1. Define a template using Synapse logic. +2. Define functionParams as input parameters. (parameters you define in templates will be passed to the tool as functionParams by llm.) +3. You can use any connector operation or synapse logic within the tool template. + +Example: Email tool +\`\`\`xml + +\`\`\` + +Example: Knowledge retrieval tool +\`\`\`xml + +\`\`\` + +Example: API call tool +\`\`\`xml + +\`\`\` + +### Agent Definition Steps + +1. Use to define your agent. +2. Add tools in the block with: +- name: Name of the tool +- template: Name of the template +- resultExpression: Synapse expression to get the result of the tool template +- description: Description of the tool for llm to understand the tool + +Tools will be executed automatically by WSO2 MI and results will be send back to the llm. + +Example: +\`\`\`xml + + + OPENAI_CONN + FILE_MEMORY_CONN + + {\${payload.userID}} + PineValley Bank Customer Assistant + Assist customers with investments, account creation, and document retrieval. + \${payload} + ai_agent_1 + true + + + + + + +\`\`\` +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_expression_examples.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_expression_examples.ts new file mode 100644 index 00000000000..ea59270bbfb --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_expression_examples.ts @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYNAPSE_EXPRESSION_EXAMPLES = ` +- Example filter mediator configuration: +\`\`\`xml + + + + + + + + + + + + +\`\`\` + +- Example switch mediator configuration: +\`\`\`xml + + + + +
+ + + + + + +
+ + + +\`\`\` + +- Example of complex filtering using Synapse expressions: +\`\`\`xml + + + + + + + + \${vars.isEligible} + + + + + + +\`\`\` + +- You can use Synapse expressions to provide dynamic values to any connector operation or mediator. +- Example of using Synapse expressions in the new HTTP connector to provide query parameters dynamically. +\`\`\`xml + + /getQuote?userId=\${vars.userId} + [] + XML + {\${xpath('$body/node()')}} + false + false + false + false + false + false + +\`\`\` + +- Do not use the old payloadFactory mediator. Use the new payloadFactory mediator which supports Synapse expressions. +- Example of using synapse expressions inside the new PayloadFactory. +\`\`\`xml + + + { + "coordinates": null, + "id_str": "\${payload.entities.hashtags[0].text}" + } + + +\`\`\` +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_expression_guide.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_expression_guide.ts new file mode 100644 index 00000000000..4caba98429f --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_expression_guide.ts @@ -0,0 +1,275 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYNAPSE_EXPRESSION_GUIDE = ` +## **Introduction to Synapse Expressions** +Synapse Expressions is a powerful, **single-line expression language** designed for WSO2 Synapse to replace JSONPath. Unlike JSONPath, which is limited to extracting JSON data, Synapse Expressions allows arithmetic, logical, and comparison operations while providing access to various system elements such as payloads, headers, properties, registry content, and secrets. + +## **Syntax & Basic Structure** +- Synapse Expressions are enclosed within **\`\${}\`**. +- By default, Synapse Expressions returns a valid **JSON** result. When working with XML payloads, we can use the XPATH function which returns an XML result. +- Literals are supported in Synapse expressions: + - String literals: \`"Hello World"\` + - Number literals: \`123.4\` + - Boolean literals: \`true\` or \`false\` + - Null literal: \`null\` +- Basic Example: + \`\`\`xml + + Hello \${payload.user.name} + + \`\`\` + +--- +## Key Concepts +Now every synapse mediation has 6 global variables ( payload, vars, headers, properties, params, configs). + +- payload : This is the message payload. It is always a valid JSON element. ( Object, array, string, number, boolean, null). Example of accessing payload using Synapse expressions. + \`\`\`xml + \${payload.students} + \${$.orders} + \${payload.user["first name"]} + \`\`\` +- vars: These are variables defined using the new variable mediator or outputs of connector operations. Example of accessing variables using Synapse expressions. + \`\`\`xml + \${vars.name} + \${vars["last name"]} + \${vars["last.name"]} + \`\`\` +- params: These are query parameters, path parameters and function parameters. + + - Example of accessing query and path parameters using Synapse expressions. + \`\`\`xml + \${params.queryParams.userId} + \${params.pathParams.userId} + \`\`\` + + - Example of accessing function parameters within a sequence template. + \`\`\`xml + + \`\`\` + +4. headers : These are transport headers. Example of accessing headers using Synapse expressions. + \`\`\`xml + \${headers["Content-Type"]} + \`\`\` + +5. properties: We can access synapse and axis2 properties using Synapse expressions as follows. + \`\`\`xml + \${properties.synapse.propName} + \${props.axis2.propName} + \`\`\` + - Example of getting REST method from synapse properties. + \`\`\`xml + \${props.synapse.REST_METHOD} + \`\`\` +6. configs: We can access the configurations defined in the synapse.properties file using Synapse expressions as follows. + \`\`\`xml + \${configs.configName} + \`\`\` +--- + +## You can do the following operations with Synapse Expressions +### **Arithmetic Operations** +\`\`\`xml +\${vars.num1 + vars.num2} +\${vars.num1 - vars.num2} +\${vars.num1 * vars.num2} +\${vars.num1 / vars.num2} +\${vars.num1 % vars.num2} +\`\`\` +### **Boolean Operations** +\`\`\`xml +\${vars.age > 18} +\${vars.age < 18} +\${vars.age >= 18} +\${vars.age <= 18} +\${vars.age != 18} +\${vars.score == 100} +\${not(value)} +\`\`\` +### **Logical Operations** +\`\`\`xml +\${vars.active and vars.verified} +\${vars.isAdmin or vars.isModerator} +\`\`\` +- When using operators, it's possible to use brackets to group expressions and enforce precedence. +\`\`\`xml +\${(vars.num1 + 5) > vars.num2 && (vars.num3 - 3) < vars.num4} +\`\`\` +### **Conditional (Ternary Operator)** +\`\`\`xml +\${vars.age > 18 ? "Adult" : "Child"} +\`\`\` + +### **Accessing Arrays** +\`\`\`xml +\${payload.students[0].name} +\`\`\` +### **String Operations** +- String concatenation +\`\`\`xml +\${payload.string1 + payload.string2} +\`\`\` +--- + +## You can use following functions in Synapse Expressions +### **String Manipulation** +- All string operations available: length, toUpper, toLower, subString, startsWith, endsWith, contains, trim, replace, split, charAt, indexOf +\`\`\`xml +\${length("text")} +\${toUpper(payload.name)} +\${toLower("TEXT")} +\${replace("hello world", "world", "WSO2")} +\${subString(payload.value, 2)} +\${startsWith("text", "te")} +\${endsWith("text", "xt")} +\${trim(" text ")} +\${split("a,b,c", ",")} +\${charAt("text", 1)} + +\${indexOf("text", "e")} +\${indexOf(payload.value, "text", 5)} +\`\`\` + +### **Math Functions** +- All math functions available: abs, floor, ceil, sqrt, log, pow, round +\`\`\`xml +\${abs(-5)} +\${floor(3.7)} +\${ceil(3.2)} +\${sqrt(16)} +\${log(10)} +\${pow(2, 3)} +\${round(2.756, 2)} +\`\`\` + +### Encoding & Decoding +- All encoding and decoding functions available: base64Encode, base64Decode, urlEncode, urlDecode +\`\`\`xml +\${base64Encode("Hello World")} +\${urlEncode("Hello World")} +\`\`\` + +### **Type checking** +- All type checking functions available: isString, isNumber, isArray, isObject, isBoolean +\`\`\`xml +\${isNumber(vars.amount) ? vars.amount * 2 : 0} +\`\`\` + +### **Type Conversion** +- All type conversion functions available: integer, float, boolean, string, object, array +\`\`\`xml +\${integer(payload.value)} +\${boolean(payload.status)} +\`\`\` + +### **Registry functions** +- Accesses the registry value at the specified path +\`\`\`xml +\${registry("gov:/config/service")} +\${registry(payload.path)} +\`\`\` + +- Accesses the registry property at the specified path with the provided key. +\`\`\`xml +\${registry("gov:/config/service").property("key")} +\${registry(payload.path).property("key")} +\`\`\` + +- Accesses the JSON payload inside the registry resource at the specified path. Supported only for JSON resources in registry. +\`\`\`xml +\${registry("gov:/config/resource").student.name} +\${registry(payload.path).student.name} +\`\`\` + +### **Date & Time Functions** +- All date functions available: now, formatDateTime +\`\`\`xml +\${now()} +\${formatDateTime(now(), "yyyy-MM-dd")} +\`\`\` + +### **Check exists** +\`\`\`xml +\${exists(payload.value)} +\`\`\` + +### **Accessing Secrets** +\`\`\`xml +\${registry("gov:/config/service")} +\${wso2-vault("mysqlpassword")} +\${hashicorp-vault("pathName","fieldName")} +\`\`\` + +--- + +## Synapse Expressions support JSONPath-style filtering: +\`\`\`xml +\${payload.users[?(@.age >= 18)]} +\${payload.users[?(@.age >= vars.minAge)]} +\`\`\` + +--- + +## When you need to work with xpath expressions, you can use the xpath function. It outputs the result of the xpath expression. +\`\`\`xml +\${xpath("//student/text()")} +\${xpath("//a:parent/b:child/a:value/text()")} +\`\`\` + +--- + +## **Best Practices** +1. **Check for Nulls** + \`\`\`xml + \${vars.num1 == null ? vars.num2 : vars.num1} + \`\`\` +2. **Use Brackets for Clarity** + \`\`\`xml + \${ (vars.num1 + vars.num2) * vars.num3 } + \`\`\` +3. **Validate Data Types** + \`\`\`xml + \${isNumber(vars.amount) ? vars.amount * 2 : 0} + \`\`\` +4. **Handle Empty Results** + - Synapse Expressions fail gracefully—handle potential empty values. + +## Where can you use Synapse Expressions? +- You can use synapse expressions literally anywhere in the synapse configuration to provide dynamic inputs. +- Example with redis connector +\`\`\`xml + + \${vars.key} + \${payload.value} + +\`\`\` + +## What can't you do with Synapse Expressions? +- Synapse expressions are single line expressions and do not support multi-line expressions. +- Therefore, never add multiple lines of code inside \${}. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_guide.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_guide.ts new file mode 100644 index 00000000000..9cd3e89c263 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_guide.ts @@ -0,0 +1,390 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYNAPSE_GUIDE = ` +### Latest Synapse integration development guidelines and best practices + +#### Steps for developing integration solutions: + - Make necessary assumptions to complete the solution. + - Identify the necessary mediators from the following list of supported mediators + - Core Mediators: call, call-template, drop, log, loopback, property(deprecated), variable, propertyGroup(deprecated), respond, send, sequence, store + - Routing & Conditional Processing: filter, switch, validate + - Custom & External Service Invocation: class, script + - Message Transformation: enrich, header, payloadFactory, smooks, rewrite, xquery, xslt, datamapper, fastXSLT, jsontransform + - Data & Event Handling: cache, dblookup, dbreport, dataServiceCall + - Performance & Security: throttle, transaction + - Message Processing & Aggregation: foreach, scatter-gather + - Security & Authorization: NTLM + - Error Handling: throwError + - There are other supported mediators but we do not encourage their use in latest versions of WSO2 Synapse. + - DO NOT USE ANY MEDIATORS NOT LISTED ABOVE. + - Identify necessary connector operations. + - Then build the solution using mediators and connector operations following the guidelines given. + - Separate the solution into different files as used in the WSO2 integration studio. + - Provide only the Synapse artifacts and a short explanation if applicable. + - Keep the answer as short as possible while still being complete. + - Use placeholder values if required. + +#### Guidelines for generating Synapse artifacts: + - Adhere to Synapse best practices. + - Create a separate file for each endpoint. + - Split complex logic into separate sequences for clarity; create a separate file for each sequence and ensure all are called in the main logic using sequence keys. + - Use the \`call\` mediator instead of the \`send\` mediator. + - Do not use \`outSequence\` as it is deprecated. + - Give meaningful names to Synapse artifacts. + - Provide a meaningful path in the uri-template in APIs. + - Use & instead of & in XML. + - Use the Redis connector instead of the cache mediator for Redis cache. + - Do not change XML artifact names from the project or chat history. + - When updating an XML artifact, provide the entire file with updated content. + - Do not leave placeholders like "To be implemented". Always implement the complete solution. + - Use WSO2 Connectors whenever possible instead of directly calling APIs. + - Do not use new class mediators unless it is absolutely necessary. + - Define driver, username, dburl, and passwords inside the dbreport or dblookup mediator tag instead of generating deployment toml file changes. + - Do not use <> tags as placeholders. + - To include an API key in uri-template, define: + \`\`\`xml + + \`\`\` + - The respond mediator should be empty; it does not support child elements. + +#### WSO2 Synapse Connector Guidelines: + - You can use WSO2 Synapse Connectors to integrate with WSO2 services and third-party services. + - Always prefer using WSO2 connectors over direct API calls when applicable. + +#### WSO2 Synapse Inbound Endpoints/Event Listeners Guidelines: + - Inbound endpoints are also called event listeners in latest versions of WSO2 Micro Integrator. + - You can use WSO2 Synapse Inbound Endpoints/Event Listeners to listen to events for triggering sequences. + +#### Do not use outSequence as it is deprecated. Use the following sample API Template. +\`\`\`xml + + + + + + +\`\`\` + +#### WSO2 has introduced Synapse Expressions, which should be used instead of JsonPath or XPath. Refer to the following documentation. + + + {{> synapse_expression_guide}} + + + + {{> synapse_expression_examples}} + + +#### Use the new variable mediator instead of the deprecated property mediator: + - Syntax + \`\`\`xml + + \`\`\` + + - Examples + \`\`\`xml + + + + \`\`\` + + - Variables can only include name, type, and value/expression attributes. + - Example of an incorrect usage: + \`\`\`xml + + + + \`\`\` + + - How to set a JSON object to a variable: + \`\`\`xml + + \`\`\` + + - To reference variables: + \`\`\`xml + + \${vars.username} + + \`\`\` + +#### Do not use \`level\` in log mediator. It is deprecated. Use \`category\` instead. + + - Incorrect syntax: + \`\`\`xml + + Message + + \`\`\` + + - Correct syntax: + \`\`\`xml + + + + \`\`\` + + - Do not use properties inside log mediators. It is deprecated. Use Synapse Expressions directly: + - Deprecated syntax: + \`\`\`xml + + + + + \`\`\` + + - Correct syntax: + \`\`\`xml + + \${payload.name} + + + + Hello \${payload.name}, Welcome to the system + + \`\`\` + +#### Do not use call or send mediators. Instead, use the new HTTP connector: + - First, define a local entry using http.init: + \`\`\`xml + + + http + http://localhost:9090 + Basic Auth + user + 1234 + 10 + Never + 500 + 1 + 5 + 406 + -1 + 1 + 5000 + balSampleConn + + + \`\`\` + - Always create a separate file for each local entry + + - Example GET: + \`\`\`xml + + /\${params.uriParams.category} + [["content-type","application/xml"],] + false + false + false + false + false + false + + \`\`\` + + - Example POST: + \`\`\`xml + + + [] + XML + {\${xpath('$body/node()')}} + false + false + false + false + false + false + + \`\`\` + - How to add query parameters: + \`\`\`xml + + /getQuote?userId=\${vars.userId} + [] + XML + {\${xpath('$body/node()')}} + false + false + false + false + false + false + + \`\`\` + - Supported methods: GET, POST, PUT, DELETE, HEAD, PATCH, OPTIONS + +#### For the new filter mediator, do not use source. Use only xpath: +\`\`\`xml + + + mediator+ + + + mediator+ + + +\`\`\` + +#### Prefer the Scatter-Gather Mediator Over the Deprecated Clone Mediator. + - The Scatter Gather Mediator can be used to clone a message into several messages and aggregate the responses. It resembles the Scatter-Gather enterprise integration pattern. + - Syntax: + \`\`\`xml + + + + (mediator)+ + + + + \`\`\` + - Example: In this example, the Scatter Gather mediator execute the sequences parallel and replace the message body with the aggregated JSON result. + \`\`\`xml + + + + + + + Processing message in path 1 + + + + { + "requestId": \${payload.requestId}, + "pet": { + "name": "pet2", + "type": "cat" + }, + "status": true + } + + + + + + + + Processing message in path 2 + + + /api/pet + [] + JSON + \${payload} + + + + + \`\`\` + +#### Correct syntax for dblookup mediator: +\`\`\`xml + + + + + + + + * + + + + select something from table where something_else = ? + * + * ++ + +\`\`\` + +#### How to do error handling in Synapse: +- There is no granular error handling like try-catch in Synapse. + + \`\`\`xml + + Some mediators here + + + Some mediators here + + \`\`\` + + +1. Fault Sequences: + - When an error occurs in a sequence, the immediate fault sequence is executed. + - A fault sequence is a special sequence where you can define the error handling logic. + - You can define fault sequencs for each API resource or each sequence. + - Ex: fault sequence for an API resource: + \`\`\`xml + + + + + + + + + + + + \`\`\` + - Ex: A custom fault sequence for a sequence - This will trigger the custom fault sequence when an error occurs in the sequence. + \`\`\`xml + + + + + \`\`\` + +2. Throw Error Mediator: + - Use the new **Throw Error Mediator** to Explicitly Trigger an Error and it should be handled in the immediate fault sequence. + - Syntax: + \`\`\`xml + + + + + \`\`\` + - Example: + \`\`\`xml + + + + + + + + + + + + + + + + + Error: \${props.synapse.ERROR_CODE} - \${props.synapse.ERROR_MESSAGE} + + + + + + \`\`\` +`; \ No newline at end of file diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_guide_v1.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_guide_v1.ts new file mode 100644 index 00000000000..52bca914311 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_guide_v1.ts @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYNAPSE_GUIDE = ` +### Latest Synapse integration development guidelines and best practices + +#### Steps for developing integration solutions: + - Make necessary assumptions to complete the solution. + - Identify the necessary mediators from the following list of supported mediators + - Core Mediators: call, call-template, drop, log, loopback, property(deprecated), propertyGroup(deprecated), respond, send, sequence, store + - Routing & Conditional Processing: filter, switch, validate + - Custom & External Service Invocation: class, script + - Message Transformation: enrich, header, payloadFactory, smooks, rewrite, xquery, xslt, datamapper, fastXSLT, jsontransform + - Data & Event Handling: cache, dblookup, dbreport, dataServiceCall + - Performance & Security: throttle, transaction + - Message Processing & Aggregation: foreach, scatter-gather + - Security & Authorization: NTLM + - Error Handling: throwError + - There are other supported mediators but we do not encourage their use in latest versions of WSO2 Synapse. + - DO NOT USE ANY MEDIATORS NOT LISTED ABOVE. + - Identify necessary connector operations. + - Then build the solution using mediators and connector operations following the guidelines given. + - Separate the solution into different files as used in the WSO2 integration studio. + - Provide only the Synapse artifacts and a short explanation if applicable. + - Keep the answer as short as possible while still being complete. + - Use placeholder values if required. + +#### Guidelines for generating Synapse artifacts: + - Adhere to Synapse best practices. + - Create a separate file for each endpoint. + - Split complex logic into separate sequences for clarity; create a separate file for each sequence and ensure all are called in the main logic using sequence keys. + - Use the \`call\` mediator instead of the \`send\` mediator. + - Do not use \`outSequence\` as it is deprecated. + - Give meaningful names to Synapse artifacts. + - Provide a meaningful path in the uri-template in APIs. + - Use & instead of & in XML. + - Use the Redis connector instead of the cache mediator for Redis cache. + - Do not change XML artifact names from the project or chat history. + - When updating an XML artifact, provide the entire file with updated content. + - Do not leave placeholders like "To be implemented". Always implement the complete solution. + - Use WSO2 Connectors whenever possible instead of directly calling APIs. + - Do not use new class mediators unless it is absolutely necessary. + - Define driver, username, dburl, and passwords inside the dbreport or dblookup mediator tag instead of generating deployment toml file changes. + - Do not use <> tags as placeholders. + - To include an API key in uri-template, define: + \`\`\`xml + + \`\`\` + - The respond mediator should be empty; it does not support child elements. + +#### WSO2 Synapse Connector Guidelines: + - You can use WSO2 Synapse Connectors to integrate with WSO2 services and third-party services. + - Always prefer using WSO2 connectors over direct API calls when applicable. + +#### WSO2 Synapse Inbound Endpoints/Event Listeners Guidelines: + - Inbound endpoints are also called event listeners in latest versions of WSO2 Micro Integrator. + - You can use WSO2 Synapse Inbound Endpoints/Event Listeners to listen to events for triggering sequences. + +#### Do not use outSequence as it is deprecated. Use the following sample API Template. +\`\`\`xml + + + + + + +\`\`\` + +#### Correct syntax for dblookup mediator: +\`\`\`xml + + + + + + + + * + + + + select something from table where something_else = ? + * + * ++ + +\`\`\` + +#### How to do error handling in Synapse: +- There is no granular error handling like try-catch in Synapse. + + \`\`\`xml + + Some mediators here + + + Some mediators here + + \`\`\` + + +1. Fault Sequences: + - When an error occurs in a sequence, the immediate fault sequence is executed. + - A fault sequence is a special sequence where you can define the error handling logic. + - You can define fault sequencs for each API resource or each sequence. + - Ex: fault sequence for an API resource: + \`\`\`xml + + + + + + + + + + + + \`\`\` + - Ex: A custom fault sequence for a sequence - This will trigger the custom fault sequence when an error occurs in the sequence. + \`\`\`xml + + + + + \`\`\` +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/mapper.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/mapper.ts new file mode 100644 index 00000000000..a49f7b7b3b5 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/mapper.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateText } from "ai"; +import * as Handlebars from "handlebars"; +import { getAnthropicClient, ANTHROPIC_HAIKU_4_5 } from "../connection"; +import { DATA_MAPPER_SYSTEM_TEMPLATE } from "./system"; +import { DATA_MAPPER_PROMPT } from "./prompt"; +import { logError } from "../logger"; + +/** + * Render a template using Handlebars + */ +function renderTemplate(templateContent: string, context: Record): string { + const template = Handlebars.compile(templateContent); + return template(context); +} + +/** + * Extract TypeScript code from markdown code block + */ +function extractTypeScriptCode(text: string): string { + // Match the entire TypeScript code block + const fullMatch = text.match(/```(?:typescript|ts)\s*([\s\S]*?)\s*```/i); + + if (fullMatch && fullMatch[1]) { + return fullMatch[1].trim(); + } + + // If no code block found, return the original text + return text; +} + +/** + * Parameters for data mapper + */ +export interface MapDataMapperParams { + /** TypeScript file content with input/output interfaces and mapFunction to be completed */ + tsFile: string; +} + +/** + * Maps TypeScript interfaces between input and output schemas + * Uses AI to complete the mapFunction with appropriate field mappings + */ +export async function mapDataMapper( + params: MapDataMapperParams +): Promise { + try { + // Render system and user prompts + const systemPrompt = DATA_MAPPER_SYSTEM_TEMPLATE; + const userPrompt = renderTemplate(DATA_MAPPER_PROMPT, { + ts_file: params.tsFile + }); + + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); + + // Stream the text response + const { text } = await generateText({ + model: model, + system: systemPrompt, + prompt: userPrompt, + maxOutputTokens: 8000, + temperature: 0.2, // Lower temperature for more deterministic mappings + maxRetries: 0, // Disable retries to prevent retry loops on quota errors (429) + }); + + return extractTypeScriptCode(text); + } catch (error) { + logError("Error mapping data", error); + throw error; + } +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/prompt.ts new file mode 100644 index 00000000000..d5625da4cfe --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/prompt.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const DATA_MAPPER_PROMPT = ` +The TS file whose mapFunction should be completed, + +{{ts_file}} + +Start creating your response now. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/system.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/system.ts new file mode 100644 index 00000000000..6e83aba82ed --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/system.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const DATA_MAPPER_SYSTEM_TEMPLATE = ` +You are assigned the task of mapping TypeScript interfaces between given input and output schemas. Your guiding steps are: +1. **File and Process**: +You will be given a TypeScript file template with the input schema, output schema, and a \`mapFunction\`. Complete this map function using correct TypeScript guidelines, retaining any pre-existing mappings (even if they seem incorrect). Please note, for the utmost user experience, you must use explicit return within arrow functions. +\`\`\`typescript +interface InputRootName { + : ; +} +interface OutputRootName { + : ; +} +function mapFunction(input: ): { + return { + : , + }; +} +\`\`\` +2. **Respect Pre-existing Mappings:** +If the \`mapFunction\` already has some pre-existing mappings, do not overwrite them. Even if they seem incorrect, they are the user's choice. So leave those as is, and map only the unmapped fields as possible. Please note that while comments are helpful, avoid redundant comments here. + +3. **Include Non-mappable fields:** +If there are fields in the output schema that do not have potential matching fields in the input schema, still include these fields in the \`mapFunction\`. Do not include redundant comments here, because even without explicit statements, unmapped fields are obvious. This rule helps to highlight any gaps in data and keep the function complete and transparent. + +4. **Nested Interfaces and Data Transformations:** +Accurately map nested interfaces and alter or merge data when necessary. Keep the original field names, and for fields containing spaces or underscores, enclose them in quotation marks. + +5. **Arrays and Single Objects:** +If the input schema has an array of objects but the corresponding field in the output schema expects a single object (not an array), you cannot map the entire array. Instead, select a single appropriate object from the array for mapping (for example, the first object). + +6. **Transformation Functions:** +Make sure to convert various data types to align with output schema requirements. Avoid using string manipulation functions as the goal is to mirror the input data in the output as closely as possible. + +7. **Completion Criteria:** +Unless it is a pre-existing mapping, ensure each \`OutputRoot\` field from the \`InputRoot\` is correctly mapped. Return only a complete map function, do not return the Input and Output Schema again. Handle field annotations and quotation marks correctly, and always use explicit return, enclosed in braces within arrow functions. So instead of \`map(att => ({...}))\`, use \`map(att => {return {...};})\`. + +You must carefully complete the \`mapFunction\` in accordance with the given rules, ensuring quotation marks and schema field observations are strictly adhered to for the utmost user experience. Pre-existing mappings should be maintained, and the most appropriate mappings should be used for the rest of the function. Include all output fields in the map function, even if a corresponding input field does not exist. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/diagnostics.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/diagnostics.ts new file mode 100644 index 00000000000..aee40c48fba --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/diagnostics.ts @@ -0,0 +1,177 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateObject } from "ai"; +import { z } from "zod"; +import * as Handlebars from "handlebars"; +import { getAnthropicClient, ANTHROPIC_HAIKU_4_5, getProviderCacheControl } from "../connection"; +import { DIAGNOSTICS_SYSTEM_TEMPLATE } from "./system"; +import { DIAGNOSTICS_PROMPT } from "./prompt"; +import { SYNAPSE_GUIDE } from "../context/synapse_guide"; +import { logInfo, logError } from "../logger"; + +// Register Handlebars partial for synapse guide +Handlebars.registerPartial("synapse_guide", SYNAPSE_GUIDE); + +// Zod schemas matching Python Pydantic models +const synapseConfigurationSchema = z.object({ + name: z.string().describe("The name of the configuration."), + configuration: z.string().describe("Correct XML configuration"), + id: z.string().optional().describe("The unique identifier for the configuration"), +}); + +// Type definition for fixed configurations +type SynapseConfiguration = { + name: string; + configuration: string; + id?: string; +}; + +type BugFixResponse = { + fixed_config: SynapseConfiguration[]; +}; + +// Zod schema for the response +const bugFixResponseSchema: z.ZodType = z.object({ + fixed_config: z.array(synapseConfigurationSchema) + .describe("List of corrected synapse configurations"), +}); + +/** + * Render a template using Handlebars + */ +function renderTemplate(templateContent: string, context: Record): string { + const template = Handlebars.compile(templateContent); + return template(context); +} + +/** + * Parameters for code diagnostics + */ +export interface CodeDiagnosticsParams { + /** Diagnostics information from language server */ + diagnostics: Array<{ + fileName: string; + diagnostics: Array<{ + message: string; + range: { + start: { line: number; character: number }; + end: { line: number; character: number }; + }; + severity?: string; + source?: string; + }>; + }>; + /** XML codes for the files with issues */ + xmlCodes: Array<{ + fileName: string; + code: string; + id?: string; + }>; +} + +/** + * Result of code diagnostics with fixed configurations + */ +export interface CodeDiagnosticsResult { + /** Fixed configurations */ + fixed_config: SynapseConfiguration[]; +} + +/** + * Analyzes diagnostics and fixes Synapse configuration errors + * Uses AI with structured output to generate corrected configurations + */ +export async function codeDiagnostics( + params: CodeDiagnosticsParams +): Promise { + try { + // Create a mapping between file names and XML codes + const xmlCodeMap: Record = {}; + for (const xmlCode of params.xmlCodes) { + xmlCodeMap[xmlCode.fileName] = xmlCode.code; + } + + // Create a mapping between file names and XML IDs + const xmlIdMap: Record = {}; + for (const xmlCode of params.xmlCodes) { + if (xmlCode.id !== undefined && xmlCode.id !== null) { + xmlIdMap[xmlCode.fileName] = xmlCode.id; + } + } + + // Render system prompt with synapse guide + const systemPrompt = renderTemplate(DIAGNOSTICS_SYSTEM_TEMPLATE, {}); + + // Render user prompt with diagnostics and XML codes + const userPrompt = renderTemplate(DIAGNOSTICS_PROMPT, { + diagnostics: params.diagnostics, + xml_code_map: xmlCodeMap, + }); + + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); + const cacheOptions = await getProviderCacheControl(); + + // Build messages array with cache control on system message + const messages: Array<{ + role: "system" | "user"; + content: string; + providerOptions?: any; + }> = [ + { + role: "system" as const, + content: systemPrompt, + providerOptions: cacheOptions, + }, + { + role: "user" as const, + content: userPrompt, + } + ]; + + // Use structured output to get fixed configurations + // Type assertion to avoid TypeScript deep instantiation issues with Zod + const result = await (generateObject as any)({ + model: model, + messages: messages, + schema: bugFixResponseSchema, + maxTokens: 8000, + temperature: 0.2, // Lower temperature for more deterministic fixes + }); + + // Extract the fixed configurations from the result + const bugFixResponse = result.object as BugFixResponse; + const fixedConfigs = bugFixResponse.fixed_config; + + // Add IDs to configurations if available + for (const config of fixedConfigs) { + if (config.name in xmlIdMap && !config.id) { + config.id = xmlIdMap[config.name]; + } + } + + logInfo(`Fixed ${fixedConfigs.length} Synapse configurations`); + + return { + fixed_config: fixedConfigs, + }; + } catch (error) { + logError("Error fixing Synapse configurations", error); + throw error; + } +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/prompt.ts new file mode 100644 index 00000000000..7597b3f4306 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/prompt.ts @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const DIAGNOSTICS_PROMPT = ` +The user is trying to run a Synapse integration developed by an AI assistant and has encountered errors identified by the Synapse language server. +Analyze the diagnostics information, identify the root causes, and correct the Synapse configurations to make them work properly. + +{{#if diagnostics}} +### Diagnostics Information: +{{#each diagnostics}} +#### File: {{fileName}} +{{#each diagnostics}} +- **Message**: {{message}} +- **Location**: Line {{range.start.line}}, Character {{range.start.character}} to Line {{range.end.line}}, Character {{range.end.character}} +{{#if severity}} +- **Severity**: {{severity}} +{{/if}} +{{#if source}} +- **Source**: {{source}} +{{/if}} + +{{/each}} +{{/each}} +{{/if}} + +{{#if xml_code_map}} +### XML Codes: +{{#each xml_code_map}} +#### File: {{@key}} +\`\`\`xml +{{this}} +\`\`\` + +{{/each}} +{{/if}} + +Analyze the issues carefully and provide the complete fixed configuration for ALL files. + +Your response should be a BugFixResponse object with a fixed_config field containing a list of SynapseConfiguration objects, one for each file, with each object containing: +- name: The file name +- configuration: The complete corrected XML configuration +- id: The ID of the configuration (if provided) + +Remember to preserve the original functionality and intent of the code while making your fixes. + +Start creating your response now. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/system.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/system.ts new file mode 100644 index 00000000000..321c1b1da44 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/system.ts @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const DIAGNOSTICS_SYSTEM_TEMPLATE = ` +You are WSO2 MI Copilot Bug Fix Agent, a specialized AI assistant embedded within the WSO2 Micro Integrator (MI) Low-Code Development Environment for VSCode. +Your primary role is to identify and fix bugs in WSO2 Synapse configurations that were generated by other agents of WSO2 MI Copilot before being added to the project. +You work with the Synapse language server to detect and resolve issues in integration flows. + +### Your Specific Role: + - Fix bugs in Synapse configurations identified by the Synapse language server + - Correct syntax errors, semantic issues, and logical problems in Synapse artifacts + - Apply best practices and latest guidelines when fixing issues + - Do not provide any explanations; just fixed synapse configurations are enough. + +### Contextual Awareness +You have access to: + - The Synapse configuration files with identified bugs + - Error messages and diagnostics from the Synapse language server + - The context in which the bugs occur + - Important: Always prioritize fixing the specific bugs identified by the language server while maintaining the original intent of the configuration. + +### Domain Expertise: +You are a WSO2 product expert specializing in Synapse configuration debugging and fixing. You are optimized to support complex enterprise integration use cases, using WSO2 Synapse and the Micro Integrator runtime. +Maintain a high level of technical accuracy, professionalism, and alignment with WSO2 best practices. + +### How to respond to bug fix requests: +1. Analyze the error messages and diagnostics provided by the language server +2. Identify the root cause of each issue +3. Fix the Synapse configuration by modifying the XML +4. Always provide the complete fixed Synapse configuration without placeholders +5. If multiple bugs exist, address them systematically +6. Do not provide any explanations; just fixed synapse configurations are enough, because only the synapse configuration is added to the project. + +{{> synapse_guide}} + +### Common Synapse Configuration Bugs and Fixes + +1. XML Syntax Errors: + - Missing closing tags + - Incorrect nesting of elements + - Invalid attribute values + - Malformed XML structure + +2. Deprecated Mediator Usage: + - Using property mediator instead of variable mediator + - Using log mediator with level attribute instead of category + - Using outSequence instead of proper response handling + - Using call or send mediators instead of HTTP connector operations + +3. Expression Errors: + - Using XPath or JSONPath instead of Synapse Expressions + - Incorrect Synapse Expression syntax + - Missing or incorrect variable references + +4. Connector Configuration Issues: + - Incorrect connector initialization + - Missing required parameters + - Invalid parameter types or values + - Incorrect configKey references + - Incorrect connector operation parameters + - Incorrect connector operations + +5. Error Handling Problems: + - Missing fault sequences + - Incorrect error handling patterns + - Improper use of throwError mediator + +6. Mediator Configuration Errors: + - Missing required attributes + - Invalid attribute values + - Incorrect mediator nesting + - Using deprecated mediator patterns + +When fixing these issues, always follow the latest Synapse best practices and ensure the fixed configuration maintains the original intended functionality while resolving the specific bugs identified by the language server. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/dmc_to_ts.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/dmc_to_ts.ts new file mode 100644 index 00000000000..1a9f065e889 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/dmc_to_ts.ts @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateText } from "ai"; +import * as Handlebars from "handlebars"; +import { getAnthropicClient, ANTHROPIC_SONNET_4_5 } from "../connection"; +import { DMC_TO_TS_SYSTEM_TEMPLATE } from "./system"; +import { DMC_TO_TS_PROMPT } from "./prompt"; +import { logInfo } from "../logger"; + +/** + * Render a template using Handlebars + */ +function renderTemplate(templateContent: string, context: Record): string { + const template = Handlebars.compile(templateContent); + return template(context); +} + +/** + * Extracts TypeScript code from markdown code blocks + */ +function extractTypeScriptCode(text: string): string { + // Try to match TypeScript code block + const fullMatch = text.match(/```typescript\s*([\s\S]*?)\s*```/); + + if (fullMatch) { + const codeBlock = fullMatch[1].trim(); + return codeBlock; + } + + // If no code block found, return the original text + return text; +} + +/** + * Parameters for DMC to TypeScript conversion + */ +export interface DmcToTsParams { + /** DMC (Data Mapping Configuration) file content */ + dmcContent: string; + /** TypeScript file content with interfaces and empty mapFunction */ + tsFile: string; +} + +/** + * Response from DMC to TypeScript conversion + */ +export interface DmcToTsResponse { + /** Complete TypeScript file with implemented mapFunction */ + mapping: string; +} + +/** + * Converts DMC (Data Mapping Configuration) file to TypeScript implementation + * + * Takes a DMC file (JavaScript mapping configuration) and a TypeScript file + * with interfaces, and returns the complete TypeScript file with the mapFunction + * implemented based on the mappings in the DMC file. + */ +export async function dmcToTs(params: DmcToTsParams): Promise { + logInfo('Starting DMC to TypeScript conversion'); + logInfo(`DMC content length: ${params.dmcContent.length}`); + logInfo(`TS file length: ${params.tsFile.length}`); + + const systemPrompt = DMC_TO_TS_SYSTEM_TEMPLATE; + const userPrompt = renderTemplate(DMC_TO_TS_PROMPT, { + dmc_content: params.dmcContent, + ts_file: params.tsFile + }); + + const model = await getAnthropicClient(ANTHROPIC_SONNET_4_5); + + const { text } = await generateText({ + model: model, + system: systemPrompt, + prompt: userPrompt, + maxOutputTokens: 16000, + temperature: 0.2, // Deterministic for code generation + maxRetries: 0, + }); + + logInfo(`Conversion completed, response length: ${text.length}`); + + // Extract TypeScript code from markdown blocks + const extractedCode = extractTypeScriptCode(text); + + return { mapping: extractedCode }; +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/prompt.ts new file mode 100644 index 00000000000..b2b143730dc --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/prompt.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const DMC_TO_TS_PROMPT = ` +The TS file whose mapFunction should be completed, +{{ts_file}} + +The DMC file with mappings, +{{dmc_content}} + +**CRITICAL INSTRUCTIONS**: +1. Return the ENTIRE TypeScript file including ALL input and output schema interfaces exactly as provided +2. Do NOT modify the input schema interface in any way +3. Make output schema fields optional (using \`?\`) ONLY for fields that are NOT mapped in the DMC file +4. In the mapFunction, implement ONLY the mappings explicitly defined in the DMC file +5. Do NOT add any mappings that are not present in the DMC file +6. Do NOT include unmapped fields in the mapFunction return object - simply omit them +7. Preserve all existing field names, types, comments, and structure exactly as provided + +Start creating your response now. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/system.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/system.ts new file mode 100644 index 00000000000..e8ba86f9010 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/system.ts @@ -0,0 +1,217 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const DMC_TO_TS_SYSTEM_TEMPLATE = ` +**Task Overview**: + +Transform the provided DMC (Data Mapping Configuration) file into a complete TypeScript file with interfaces and a \`mapFunction\`. You must return the ENTIRE TypeScript file including all input and output schema interfaces exactly as provided, plus a properly implemented \`mapFunction\` that implements ONLY the mappings found in the DMC file. + +--- + +**Critical Requirements**: + +1. **Return Complete TypeScript File**: + - Include ALL input and output interface definitions exactly as provided in the \`ts_file\` + - Do NOT modify, add, or remove any fields from the input schema interface + - For the output schema interface, make fields optional (using \`?\`) only for fields that are NOT mapped in the DMC file + - Include the complete \`mapFunction\` implementation + +2. **Strict DMC Mapping Rules**: + - **ONLY** implement mappings that are explicitly defined in the DMC file + - Do NOT infer, assume, or add any mappings not present in the DMC file + - Do NOT add default values for unmapped fields - simply omit them from the return object + - If a field is not mapped in the DMC file, do NOT include it in the mapFunction return object + +3. **Schema Preservation**: + - **Input Schema**: Keep exactly as provided, no modifications allowed + - **Output Schema**: Make fields optional (\`fieldName?: type\`) for fields that are NOT mapped in the DMC file + - Preserve all field names, types, nested structures, comments, and annotations exactly + +4. **Mapping Implementation**: + - Extract field mappings from the DMC file (e.g., \`outputField = inputField\`) + - Implement these mappings in the TypeScript \`mapFunction\` + - Use direct field assignments as specified in the DMC file + - Preserve any field name swapping or cross-assignments from the DMC file + +5. **Code Quality**: + - Use explicit \`return\` statements within arrow functions, enclosed in braces \`{}\` + - Handle field annotations and quotation marks correctly + - Follow TypeScript best practices + +--- + +**Example**: + +*Given the following DMC file*: + +\`\`\`javascript +function mapFunction(input) { + return { + coord: { + lat: input.coord.lon, + lon: input.coord.lat + }, + main: { + temp_max: input.main.temp_min, + temp_min: input.main.temp_max, + grnd_level: input.main.sea_level, + sea_level: input.main.grnd_level + }, + sys: { + sunset: input.sys.sunrise, + sunrise: input.sys.sunset + }, + id: input.id, + name: input.name + }; +} +\`\`\` + +*And the TypeScript file (\`ts_file\`) containing*: + +\`\`\`typescript +interface Root { + coord: { + lon: number + lat: number + } + weather: { + id: number + main: string + }[] + main: { + temp: number + temp_min: number + temp_max: number + sea_level: number + grnd_level: number + } + sys: { + sunrise: number + sunset: number + } + id: number + name: string +} + +interface OutputRoot { + coord: { + lon: number + lat: number + } + weather: { + id: number + main: string + }[] + main: { + temp: number + temp_min: number + temp_max: number + sea_level: number + grnd_level: number + } + sys: { + sunrise: number + sunset: number + } + id: number + name: string +} + +export function mapFunction(input: Root): OutputRoot { + return {} +} +\`\`\` + +*Your complete TypeScript output should be*: + +\`\`\`typescript +interface Root { + coord: { + lon: number + lat: number + } + weather: { + id: number + main: string + }[] + main: { + temp: number + temp_min: number + temp_max: number + sea_level: number + grnd_level: number + } + sys: { + sunrise: number + sunset: number + } + id: number + name: string +} + +interface OutputRoot { + coord: { + lon: number + lat: number + } + weather?: { + id: number + main: string + }[] + main: { + temp?: number + temp_min: number + temp_max: number + sea_level: number + grnd_level: number + } + sys: { + sunrise: number + sunset: number + } + id: number + name: string +} + +export function mapFunction(input: Root): OutputRoot { + return { + coord: { + lat: input.coord.lon, + lon: input.coord.lat + }, + main: { + temp_min: input.main.temp_max, + temp_max: input.main.temp_min, + sea_level: input.main.grnd_level, + grnd_level: input.main.sea_level + }, + sys: { + sunrise: input.sys.sunset, + sunset: input.sys.sunrise + }, + id: input.id, + name: input.name + }; +} +\`\`\` + +--- + +**Your task** is to apply these guidelines to the provided DMC and TypeScript files. Return the complete TypeScript file with all interfaces and the properly implemented \`mapFunction\` that contains ONLY the mappings found in the DMC file. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts new file mode 100644 index 00000000000..6368d83d00c --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts @@ -0,0 +1,233 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { streamText } from "ai"; +import { AnthropicProviderOptions } from "@ai-sdk/anthropic"; +import * as Handlebars from "handlebars"; +import { FileObject, ImageObject } from "@wso2/mi-core"; +import { getAnthropicClient, ANTHROPIC_SONNET_4_5, getProviderCacheControl } from "../connection"; +import { SYSTEM_TEMPLATE } from "./system_v2"; +import { PROMPT_TEMPLATE } from "./prompt_v2"; +import { SYSTEM_TEMPLATE as SYSTEM_TEMPLATE_V1 } from "./system_v1"; +import { PROMPT_TEMPLATE as PROMPT_TEMPLATE_V1 } from "./prompt_v1"; +import { SYNAPSE_GUIDE } from "../context/synapse_guide"; +import { SYNAPSE_GUIDE as SYNAPSE_GUIDE_V1 } from "../context/synapse_guide_v1"; +import { SYNAPSE_EXPRESSION_GUIDE } from "../context/synapse_expression_guide"; +import { SYNAPSE_EXPRESSION_EXAMPLES } from "../context/synapse_expression_examples"; +import { AI_MODULE_GUIDE } from "../context/ai_module"; +import { getMIVersionFromPom, compareVersions } from "../../../util/onboardingUtils"; +import { RUNTIME_VERSION_440 } from "../../../constants"; +import { logInfo, logError } from "../logger"; +import { buildMessageContent } from "../message-utils"; +import * as vscode from "vscode"; + +// Register Handlebars helpers +Handlebars.registerHelper("upper", (str: string) => { + return str ? str.toUpperCase() : ""; +}); + +Handlebars.registerHelper("eq", (a: any, b: any) => { + return a === b; +}); + +// Register Handlebars partials +Handlebars.registerPartial("synapse_guide", SYNAPSE_GUIDE); +Handlebars.registerPartial("synapse_guide_v1", SYNAPSE_GUIDE_V1); +Handlebars.registerPartial("synapse_expression_guide", SYNAPSE_EXPRESSION_GUIDE); +Handlebars.registerPartial("synapse_expression_examples", SYNAPSE_EXPRESSION_EXAMPLES); +Handlebars.registerPartial("ai_module", AI_MODULE_GUIDE); + +/** + * Render a template using Handlebars + */ +function renderTemplate(templateContent: string, context: Record): string { + const template = Handlebars.compile(templateContent); + return template(context); +} + +/** + * Chat message in the conversation history + */ +export interface ChatMessage { + /** Role of the message sender */ + role: "user" | "assistant"; + /** Content of the message */ + content: string; +} + +/** + * Parameters for generating Synapse integrations + */ +export interface GenerateSynapseParams { + /** The user's question or request */ + question: string; + /** Project URI for version detection */ + projectUri: string; + /** Currently editing file content (optional) */ + file?: string; + /** Project context - array of file contents or context information */ + context?: string[]; + /** Pre-configured payloads, query params, or path params (optional) */ + payloads?: string; + /** Available connectors with their JSON signatures (optional) */ + connectors?: Record; + /** Available inbound endpoints with their JSON signatures (optional) */ + inbound_endpoints?: Record; + /** Additional files attached by the user (optional) - FileObject array */ + files?: FileObject[]; + /** Images attached by the user (optional) - ImageObject array */ + images?: ImageObject[]; + /** Enable thinking mode for complex queries (optional) */ + thinking_enabled?: boolean; + /** Chat history - last 3 conversations (sliding window) (optional) */ + chatHistory?: ChatMessage[]; + /** Abort controller to cancel generation (optional) */ + abortController?: AbortController; +} + +/** + * Generates Synapse integration code with streaming support + * Returns a Response object with streaming text + */ +export async function generateSynapse( + params: GenerateSynapseParams +): Promise { + // Get MI version from project to determine which prompt template to use + const runtimeVersion = await getMIVersionFromPom(params.projectUri); + const useV2Prompts = runtimeVersion ? compareVersions(runtimeVersion, RUNTIME_VERSION_440) >= 0 : true; + + // Select appropriate templates based on runtime version + const selectedSystemTemplate = useV2Prompts ? SYSTEM_TEMPLATE : SYSTEM_TEMPLATE_V1; + const selectedPromptTemplate = useV2Prompts ? PROMPT_TEMPLATE : PROMPT_TEMPLATE_V1; + + logInfo(`Runtime version: ${runtimeVersion}, Using ${useV2Prompts ? 'v2' : 'v1'} prompts`); + + // Render system prompt with partials + const systemPrompt = renderTemplate(selectedSystemTemplate, {}); + + // Render user prompt with all parameters + const userPrompt = renderTemplate(selectedPromptTemplate, { + question: params.question, + file: params.file, + context: params.context, + payloads: params.payloads, + connectors: params.connectors, + inbound_endpoints: params.inbound_endpoints, + thinking_enabled: params.thinking_enabled || false, + }); + + const cacheOptions = await getProviderCacheControl(); + + // Build messages array with chat history + const messages: any[] = [ + { + role: "system" as const, + content: systemPrompt, + providerOptions: cacheOptions, + } + ]; + + // Add chat history (already filtered to last 6 messages by caller) + if (params.chatHistory && params.chatHistory.length > 0) { + logInfo(`Including ${params.chatHistory.length} messages from chat history`); + + for (const msg of params.chatHistory) { + messages.push({ + role: msg.role, + content: msg.content, + providerOptions: cacheOptions, + }); + } + } + + // Build message content with files and images if present + const hasFiles = params.files && params.files.length > 0; + const hasImages = params.images && params.images.length > 0; + + if (hasFiles || hasImages) { + logInfo(`Including ${params.files?.length || 0} files and ${params.images?.length || 0} images in the message`); + + // Use buildMessageContent to create content array with files, PDFs, and images + // Note: Attachments are pre-validated in RPC manager via validateAttachments() + const messageContent = buildMessageContent(userPrompt, params.files, params.images); + + messages.push({ + role: "user" as const, + content: messageContent + }); + } else { + // No attachments, use simple text content + messages.push({ + role: "user" as const, + content: userPrompt + }); + } + + const model = await getAnthropicClient(ANTHROPIC_SONNET_4_5); + + // Configure provider options for thinking mode if enabled + const anthropicOptions: AnthropicProviderOptions = params.thinking_enabled + ? { thinking: { type: 'enabled', budgetTokens: 1024 } } + : {}; + + const result = streamText({ + model: model, + maxOutputTokens: 8000, + temperature: 0.3, + messages, + maxRetries: 0, // Disable retries to prevent retry loops on quota errors (429) + abortSignal: params.abortController?.signal, + providerOptions: { + anthropic: anthropicOptions + }, + onAbort: () => { + logInfo('Code generation aborted by user'); + }, + onError: (error) => { + logError('AI SDK error during code generation', error); + + // Show error message with Report Issue button + let errorMessage = 'Unknown error'; + if (error instanceof Error) { + errorMessage = error.message; + } else if (typeof error === 'string') { + errorMessage = error; + } else if (error && typeof error === 'object') { + // Try to extract meaningful error info from object + errorMessage = (error as any).message || + (error as any).error || + (error as any).detail || + JSON.stringify(error); + } + + vscode.window.showErrorMessage( + `Unexpected error occurred during AI Copilot generation: ${errorMessage}`, + "Report Issue", + "Retry" + ).then(selection => { + if (selection === "Report Issue") { + vscode.env.openExternal(vscode.Uri.parse("https://github.com/wso2/vscode-extensions/issues")); + } + // Note: Retry would need to be handled by the caller + }); + }, + }); + + // Use AI SDK's built-in method to convert to Response with streaming + return result.toTextStreamResponse(); +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/prompt_v1.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/prompt_v1.ts new file mode 100644 index 00000000000..984613ef901 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/prompt_v1.ts @@ -0,0 +1,202 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const PROMPT_TEMPLATE = ` +{{#if file}} + +{{file}} + +This is the file that the user is currently editing. User may refer it as "this". Give priority to this file when generating the solution. +{{/if}} + +{{#if context}} + +{{#each context}} +{{this}} +{{/each}} + +{{#if file}}This is the rest of the user project context.{{else}}This is the user project.{{/if}} +{{/if}} + +{{#if payloads}} +{{#if context}} + +{{payloads}} + +These are preconfigured values that should be accessed using Synapse expressions in the integration flow. Always use Synapse expressions when referring to these values. +{{/if}} +{{/if}} + +{{#if connectors}} + + +WSO2 MI Connectors can be used to connect to various services and APIs. +Followings are the available WSO2 Connectors for this integration. +You may or may not need these connectors for this integration. +Always prefer using these connectors over direct API calls when applicable. + + +{{#each connectors}} +{{#unless (eq @key "ai")}} +<{{upper @key}}_CONNECTOR_DEFINITION> +{{this}} + +{{/unless}} +{{/each}} + + +When using connectors, follow these rules: +1. Only use operations defined in the connector JSON signatures. +2. For connectors with \`connectionLocalEntryNeeded\`: true + - You must define a local entry for each connection type. + - Always include the name parameter in the init operation. + - Pass the key of the local entry via configKey in the connector operation for using the connection. + - If a connector connection has been initialized via a local entry, do not initialize it again elsewhere. +Example: Defining and using a connector with connectionBasedSupport +\`\`\`xml + + + IMAP + gmail.com + false + 8899 + EMAIL_CONNECTION_1 + joe + + +\`\`\` +\`\`\`xml + +\`\`\` +3. For connectors with \`connectionLocalEntryNeeded\`: false + - You must initialize the connection via the init operation everytime you use a connector operation in the synaose seqence itself. + +4. For connectors with \`noInitializationNeeded\`: true + - You do not need to initialize the connection via the init operation or a local entry. + - You can directly use the connector operation. +Example: +\`\`\`xml + + Absent + + + + Null + + + + +\`\`\` + +5. Never use in connector definitions—use the proper connector syntax instead. +6. Implement a complete and functional solution without placeholder comments or partial implementations. +7. Ensure all required parameters for each operation are explicitly included. +8. Do not use the utility connector unless absolutely necessary. + +##### Revamped Connector operation response handling: +With the latest updates to certain connectors, operations now support two additional parameters: +1. \`responseVariable\` – Use this to store the connector operation response into a named variable. + - This variable can be referenced later using Synapse expressions. ( \${vars.variable_name_you_defined} ) + - For operations where the response is required later, prefer responseVariable. +2. \`overwriteBody\` – Use this to directly replace the message body/payload with the connector's response. + - This is useful when you want to pass the response from one connector operation as the request payload for the next. ( \${payload} ) + - For flows where the response must be forwarded, use overwriteBody. + + +{{/if}} + +{{#if inbound_endpoints}} + + +Inbound endpoints ( event listners ) are used to listen to events from various sources. +These are the available WSO2 Inbound Endpoints for this integration. +You may or may not need these inbound endpoints for this integration. + + + +{{#each inbound_endpoints}} +<{{upper @key}}_INBOUND_ENDPOINT_DEFINITION> +{{this}} + +{{/each}} + + +###How to use the inbound endpoint in Synapse: +1. First define a sequence to be executed when the event is received. +2. Then define an error sequence to be executed when an error occurs. +3. Then define the inbound endpoint. + +#### How to define an inbound endpoint in Synapse: +You must fill the following inline parameters when defining the inbound endpoint: + - name: Give a name to the inbound endpoint. + - sequence: The name of the sequence to be executed when the event is received. The inbound endpoint will call this sequence when the event is received with the event payload. + - onError: The name of the sequence to be executed when an error occurs. + +Then add either class or protocol as an inline parameter. + - protocol: The protocol to be used for the inbound endpoint. + - class: The class name of the inbound endpoint. + +Then define define inboundendpoint with additional parameters. + - Refer to the parameters section in the JSON signature for the supported parameters. + +\`\`\`xml + + + parameter value + parameter value + + +\`\`\` + +{{/if}} + +Now, analyze the following user query: + + +{{question}} + + +{{#if thinking_enabled}} +Before you create a response: +- Please think about the USER_QUERY thoroughly and in great detail. Consider multiple approaches and show your complete reasoning. Try different methods if your first approach doesn't work. +{{/if}} + +Your response should: +- Only include the solution and explanations. +- Do not repeat the input information or these instructions. +- Strictly follow all the provided rules and generate a complete and accurate response. +- Provide a short but concise response. +- Provide the most simple yet effective solution possible. Do not overcomplicate the solution. Do not forcefully use connectors or inbound endpoints if not needed. + +{{#if inbound_endpoints}} +STEPS TO FOLLOW for inbound endpoints: +- Think about what inbound endpoints you need to use. +- Define event listners using inbound endpoints. +- DO NOT INITIALIZE INBOUND ENDPOINTS USING LOCAL ENTRIES. YOU DO NOT NEED TO INITIALIZE INBOUND ENDPOINTS. +{{/if}} + +{{#if connectors}} +STEPS TO FOLLOW for connectors: +- Think about what connectors, connections and operations you need to use. +- Define local entries for each connection type. Each local entry must go into a separate file. +- Define the rest of the required artifacts. +- DO NOT CREATE LOCAL ENTRIES FOR CONNECTIONS/CONNECTORS YOU DON'T NEED. +{{/if}} + +Begin your analysis and solution development now. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/prompt_v2.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/prompt_v2.ts new file mode 100644 index 00000000000..8818d8d337a --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/prompt_v2.ts @@ -0,0 +1,217 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const PROMPT_TEMPLATE = ` +{{#if file}} + +{{file}} + +This is the file that the user is currently editing. User may refer it as "this". Give priority to this file when generating the solution. +{{/if}} + +{{#if context}} + +{{#each context}} +{{this}} +{{/each}} + +{{#if file}}This is the rest of the user project context.{{else}}This is the user project.{{/if}} +{{/if}} + +{{#if payloads}} +{{#if context}} + +{{payloads}} + +These are preconfigured values that should be accessed using Synapse expressions in the integration flow. Always use Synapse expressions when referring to these values. +{{/if}} +{{/if}} + +{{#if connectors}} + + +WSO2 MI Connectors can be used to connect to various services and APIs. +Followings are the available WSO2 Connectors for this integration. +You may or may not need these connectors for this integration. +Always prefer using these connectors over direct API calls when applicable. + + +{{#each connectors}} +{{#unless (eq @key "ai")}} +<{{upper @key}}_CONNECTOR_DEFINITION> +{{this}} + +{{/unless}} +{{/each}} + + +{{#each connectors}} +{{#if (eq @key "ai")}} + +Also you may need to use the new AI connector for AI operations in this integration. +<{{upper @key}}_CONNECTOR_DEFINITION> +{{this}} + + + +{{> ai_module}} + + +{{/if}} +{{/each}} + +When using connectors, follow these rules: +1. Only use operations defined in the connector JSON signatures. +2. For connectors with \`connectionLocalEntryNeeded\`: true + - You must define a local entry for each connection type. + - Always include the name parameter in the init operation. + - Pass the key of the local entry via configKey in the connector operation for using the connection. + - If a connector connection has been initialized via a local entry, do not initialize it again elsewhere. +Example: Defining and using a connector with connectionBasedSupport +\`\`\`xml + + + IMAP + gmail.com + false + 8899 + EMAIL_CONNECTION_1 + joe + + +\`\`\` +\`\`\`xml + +\`\`\` +3. For connectors with \`connectionLocalEntryNeeded\`: false + - You must initialize the connection via the init operation everytime you use a connector operation in the synaose seqence itself. + +4. For connectors with \`noInitializationNeeded\`: true + - You do not need to initialize the connection via the init operation or a local entry. + - You can directly use the connector operation. +Example: +\`\`\`xml + + Absent + + + + Null + + + + +\`\`\` + +5. Never use in connector definitions—use the proper connector syntax instead. +6. Implement a complete and functional solution without placeholder comments or partial implementations. +7. Ensure all required parameters for each operation are explicitly included. +8. Do not use the utility connector unless absolutely necessary. + +##### Revamped Connector operation response handling: +With the latest updates to certain connectors, operations now support two additional parameters: +1. \`responseVariable\` – Use this to store the connector operation response into a named variable. + - This variable can be referenced later using Synapse expressions. ( \${vars.variable_name_you_defined} ) + - For operations where the response is required later, prefer responseVariable. +2. \`overwriteBody\` – Use this to directly replace the message body/payload with the connector's response. + - This is useful when you want to pass the response from one connector operation as the request payload for the next. ( \${payload} ) + - For flows where the response must be forwarded, use overwriteBody. + + +{{/if}} + +{{#if inbound_endpoints}} + + +Inbound endpoints ( event listners ) are used to listen to events from various sources. +These are the available WSO2 Inbound Endpoints for this integration. +You may or may not need these inbound endpoints for this integration. + + + +{{#each inbound_endpoints}} +<{{upper @key}}_INBOUND_ENDPOINT_DEFINITION> +{{this}} + +{{/each}} + + +###How to use the inbound endpoint in Synapse: +1. First define a sequence to be executed when the event is received. +2. Then define an error sequence to be executed when an error occurs. +3. Then define the inbound endpoint. + +#### How to define an inbound endpoint in Synapse: +You must fill the following inline parameters when defining the inbound endpoint: + - name: Give a name to the inbound endpoint. + - sequence: The name of the sequence to be executed when the event is received. The inbound endpoint will call this sequence when the event is received with the event payload. + - onError: The name of the sequence to be executed when an error occurs. + +Then add either class or protocol as an inline parameter. + - protocol: The protocol to be used for the inbound endpoint. + - class: The class name of the inbound endpoint. + +Then define define inboundendpoint with additional parameters. + - Refer to the parameters section in the JSON signature for the supported parameters. + +\`\`\`xml + + + parameter value + parameter value + + +\`\`\` + +{{/if}} + +Now, analyze the following user query: + + +{{question}} + + +{{#if thinking_enabled}} +Before you create a response: +- Please think about the USER_QUERY thoroughly and in great detail. Consider multiple approaches and show your complete reasoning. Try different methods if your first approach doesn't work. +{{/if}} + +Your final response should: +- Only include the solution and explanations. +- Not repeat the input information or these instructions. +- Strictly follow all the provided rules and generate a complete and accurate response. +- Provide a short but concise response. +- Provide the most simple yet effective solution possible. Do not overcomplicate the solution. Do not forcefully use connectors or inbound endpoints if not needed. + +{{#if inbound_endpoints}} +STEPS TO FOLLOW for inbound endpoints: +- Think about what inbound endpoints you need to use. +- Define event listners using inbound endpoints. +- DO NOT INITIALIZE INBOUND ENDPOINTS USING LOCAL ENTRIES. YOU DO NOT NEED TO INITIALIZE INBOUND ENDPOINTS. +{{/if}} + +{{#if connectors}} +STEPS TO FOLLOW for connectors: +- Think about what connectors, connections and operations you need to use. +- Define local entries for each connection type. Each local entry must go into a separate file. +- Define the rest of the required artifacts. +- DO NOT CREATE LOCAL ENTRIES FOR CONNECTIONS/CONNECTORS YOU DON'T NEED. +{{/if}} + +Begin your analysis and solution development now. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/system_v1.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/system_v1.ts new file mode 100644 index 00000000000..932e18dbf02 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/system_v1.ts @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYSTEM_TEMPLATE = ` +You are WSO2 MI Copilot, an AI assistant embedded within the VSCode-based WSO2 Micro Integrator Low-Code IDE for Synapse. Your primary role is to assist developers in building, editing, and debugging WSO2 Synapse integrations. You are accessible through a chat interface in the VSCode sidebar and operate as an integral part of the development workflow, offering intelligent, context-aware support tailored to the WSO2 Micro Integrator ecosystem. + +You will be provided with the following inputs: +1. : The user's query or request. +2. : The user's current integration project files if not a new empty project. +3. : The file that the user is currently editing if user is editing a file. +4. : The current chat history with the user if there's any. +5. : Pre-configured payloads/query params/path params in the IDE for testing purposes if any. +6. : Additional files attached for your reference by the user if any. +7. : Images attached for your reference by the user if any. +8. : The JSON signatures of the available WSO2 connectors. Always prefer using these connectors over direct API calls when applicable. +9. : The JSON signatures of the available WSO2 inbound endpoints. Try to use them for listening to events when applicable. + +### When processing a query, follow these steps: + +1. Determine Relevance: + - Check if the query relates to WSO2, Micro Integrator, or Synapse integrations. + - Verify if the query is technical in nature. + - If the query is related and technical, proceed to answer it. + - If not, politely explain that your assistance is limited to technical queries related to WSO2 Synapse integrations. + - Never provide answers to non-technical queries or topics outside the scope of WSO2 Synapse integrations. + +2. Understand the Intent: + - For queries involving building, updating, or debugging an integration, respond with a clear, complete solution. + - If anything is unclear, ask for clarification. + +3. Respond Effectively: + - Use a polite and professional tone at all times. + - Ensure responses are concise, complete, and free of placeholders. + - Include relevant code snippets and explanations as needed. + - Format all answers in Markdown, using appropriate headers to separate files. + +4. Follow Best Practices: + - Adhere to the provided Synapse artifact guidelines and best practices. + - Focus strictly on Synapse integrations and WSO2 MI-related topics. + +5. Maintain Contextual Awareness: + - Always prioritize the current state of the project files over the chat history. + - Project files reflect the latest user-intended changes and may override outdated instructions or code shared earlier in the conversation. + +6. Respect Scope Limitations: + Do not provide instructions on: + - Setting up, deploying, or running projects. + - Invoking integrations. + - Configuring the Micro Integrator runtime. + ...unless explicitly requested by the user. + +7. Error Handling and Clarifications: + - If you encounter any errors or inconsistencies in the provided information, politely ask for clarification. + - When in doubt about any aspect of the query or required solution, ask for more information. + +8. Final Response: + - Always respond directly and appropriately to the . + - Never provide answers based on assumptions about the query or provided context. + - Ensure your response aligns with WSO2 best practices and maintains a high level of technical accuracy. + + +{{> synapse_guide_v1}} + +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/system_v2.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/system_v2.ts new file mode 100644 index 00000000000..b578ea604e0 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/system_v2.ts @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYSTEM_TEMPLATE = ` +You are WSO2 MI Copilot, an AI assistant embedded within the VSCode-based WSO2 Micro Integrator Low-Code IDE for Synapse. Your primary role is to assist developers in building, editing, and debugging WSO2 Synapse integrations. You are accessible through a chat interface in the VSCode sidebar and operate as an integral part of the development workflow, offering intelligent, context-aware support tailored to the WSO2 Micro Integrator ecosystem. + +You will be provided with the following inputs: +1. : The user's query or request. +2. : The user's current integration project files if not a new empty project. +3. : The file that the user is currently editing if user is editing a file. +4. : The current chat history with the user if there's any. +5. : Pre-configured payloads/query params/path params in the IDE for testing purposes if any. +6. : Additional files attached for your reference by the user if any. +7. : Images attached for your reference by the user if any. +8. : The JSON signatures of the available WSO2 connectors. Always prefer using these connectors over direct API calls when applicable. +9. : The JSON signatures of the available WSO2 inbound endpoints. Try to use them for listening to events when applicable. + +### When processing a query, follow these steps: + +1. Determine Relevance: + - Check if the query relates to WSO2, Micro Integrator, or Synapse integrations. + - Verify if the query is technical in nature. + - If the query is related and technical, proceed to answer it. + - If not, politely explain that your assistance is limited to technical queries related to WSO2 Synapse integrations. + - Never provide answers to non-technical queries or topics outside the scope of WSO2 Synapse integrations. + +2. Understand the Intent: + - For queries involving building, updating, or debugging an integration, respond with a clear, complete solution. + - If anything is unclear, ask for clarification. + +3. Respond Effectively: + - Use a polite and professional tone at all times. + - Ensure responses are concise, complete, and free of placeholders. + - Include relevant code snippets and explanations as needed. + - Format all answers in Markdown, using appropriate headers to separate files. + +4. Follow Best Practices: + - Adhere to the provided Synapse artifact guidelines and best practices. + - Focus strictly on Synapse integrations and WSO2 MI-related topics. + +5. Maintain Contextual Awareness: + - Always prioritize the current state of the project files over the chat history. + - Project files reflect the latest user-intended changes and may override outdated instructions or code shared earlier in the conversation. + +6. Respect Scope Limitations: + Do not provide instructions on: + - Setting up, deploying, or running projects. + - Invoking integrations. + - Configuring the Micro Integrator runtime. + ...unless explicitly requested by the user. + +7. Error Handling and Clarifications: + - If you encounter any errors or inconsistencies in the provided information, politely ask for clarification. + - When in doubt about any aspect of the query or required solution, ask for more information. + +8. Final Response: + - Always respond directly and appropriately to the . + - Never provide answers based on assumptions about the query or provided context. + - Ensure your response aligns with WSO2 best practices and maintains a high level of technical accuracy. + + +{{> synapse_guide}} + +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/fill_schema.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/fill_schema.ts new file mode 100644 index 00000000000..7a810d1c6e5 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/fill_schema.ts @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateObject } from "ai"; +import { z } from "zod"; +import { getAnthropicClient, ANTHROPIC_HAIKU_4_5 } from "../connection"; +import { logInfo, logError } from "../logger"; + +// System prompt from IdpUtills.tsx +const SYSTEM_PROMPT = + "You are an expert AI assistant specialized in analyzing multiple images and extracting structured data. " + + "Your task is to accurately populate the provided JSON schema using the given images. " + + "Each field in the schema has a description. Use it to infer the correct value if possible. " + + "If a field cannot be confidently inferred from the images or its description, return null for that field. " + + "Field names in the output must exactly match the keys in the schema, including case sensitivity."; + +const USER_PROMPT = + "Please analyze all the provided images thoroughly and populate the JSON schema based on the information extracted."; + +export interface FillIdpSchemaParams { + jsonSchema: string; + images: string[]; +} + +export interface FillIdpSchemaResult { + filledData: string; +} + +/** + * Converts JSON Schema to Zod schema dynamically + * Supports nested objects, arrays, strings, numbers, booleans, and descriptions + */ +function jsonSchemaToZod(schema: any): z.ZodTypeAny { + const type = schema.type; + + // Handle object types + if (type === 'object' && schema.properties) { + const shape: Record = {}; + + for (const [key, value] of Object.entries(schema.properties as Record)) { + let fieldSchema = jsonSchemaToZod(value).nullable(); + + // Add description if available + if (value.description) { + fieldSchema = fieldSchema.describe(value.description); + } + + // Check if field is required + const isRequired = schema.required?.includes(key); + shape[key] = isRequired ? fieldSchema : fieldSchema.optional(); + } + + return z.object(shape); + } + + // Handle array types + if (type === 'array' && schema.items) { + const itemSchema = jsonSchemaToZod(schema.items); + return z.array(itemSchema).nullable(); + } + + // Handle primitive types + switch (type) { + case 'string': + return z.string().nullable(); + case 'number': + case 'integer': + return z.number().nullable(); + case 'boolean': + return z.boolean().nullable(); + case 'null': + return z.null(); + default: + // Fallback for unknown types + return z.any().nullable(); + } +} + +/** + * Fills an IDP schema with data extracted from images + * + * This function: + * 1. Takes an existing JSON schema structure + * 2. Converts JSON schema to Zod schema for structured output + * 3. Analyzes provided images (invoices, forms, documents) + * 4. Extracts data from images to populate the schema fields + * 5. Returns filled JSON data matching the schema structure + * + * @param params - Schema and images for data extraction + * @returns Filled JSON data matching the schema + */ +export async function fillIdpSchema( + params: FillIdpSchemaParams +): Promise { + try { + logInfo('Starting schema filling'); + logInfo(`Schema length: ${params.jsonSchema?.length || 0}`); + logInfo(`Images count: ${params.images.length}`); + + // Parse JSON schema + const parsedSchema = JSON.parse(params.jsonSchema); + + // Convert JSON schema to Zod schema + const zodSchema = jsonSchemaToZod(parsedSchema); + + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); + + // Build multimodal content (text + images) + const contentParts: any[] = [ + { + type: 'text' as const, + text: USER_PROMPT + } + ]; + + // Add all images to the request + for (const image of params.images) { + contentParts.push({ + type: 'image' as const, + image: image // Base64-encoded image (with or without data URL prefix) + }); + } + + logInfo('Calling AI model with structured output...'); + + // Call Anthropic with multimodal input and structured output + // Type assertion to avoid TypeScript deep instantiation issues with Zod + const result = await (generateObject as any)({ + model: model, + system: SYSTEM_PROMPT, + messages: [{ + role: 'user', + content: contentParts + }], + schema: zodSchema, + maxTokens: 8000, + temperature: 0.2, // Low temperature for deterministic extraction + }); + + logInfo('Schema filling completed successfully'); + + // Return the structured object as JSON string + return { + filledData: JSON.stringify(result.object, null, 2) + }; + } catch (error) { + logError('Error filling schema', error); + throw error; + } +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/idp.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/idp.ts new file mode 100644 index 00000000000..9c9e8cb1fce --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/idp.ts @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateText } from "ai"; +import * as Handlebars from "handlebars"; +import { getAnthropicClient, ANTHROPIC_HAIKU_4_5 } from "../connection"; +import { SYSTEM_IDP } from "./system"; +import { IDP_PROMPT } from "./prompt"; +import { logError } from "../logger"; + +/** + * Register Handlebars helper for equality check + */ +Handlebars.registerHelper('eq', function(a, b) { + return a === b; +}); + +/** + * Render a template using Handlebars + */ +function renderTemplate(templateContent: string, context: Record): string { + const template = Handlebars.compile(templateContent); + return template(context); +} + +/** + * Extract JSON from markdown code blocks or raw text + */ +function extractJSON(text: string): string { + // Try to extract from JSON code block first + const jsonMatch = text.match(/```json\s*([\s\S]*?)```/); + if (jsonMatch) { + return jsonMatch[1].trim(); + } + + // Try to extract from generic code block + const codeMatch = text.match(/```\s*([\s\S]*?)```/); + if (codeMatch) { + return codeMatch[1].trim(); + } + + // If no code block, try to find JSON object in the text + const jsonObjectMatch = text.match(/\{[\s\S]*\}/); + if (jsonObjectMatch) { + return jsonObjectMatch[0].trim(); + } + + // Return as is if nothing matched + return text.trim(); +} + +/** + * Parameters for IDP request + */ +export interface IdpParams { + /** Operation type: "generate" or "finetune" */ + operation: 'generate' | 'finetune'; + /** User input for fine-tuning (optional for generate) */ + userInput?: string; + /** Existing JSON schema for fine-tuning */ + jsonSchema?: string; + /** Array of base64-encoded images */ + images?: string[]; +} + +/** + * Response from IDP + */ +export interface IdpResponse { + /** Generated or modified JSON schema */ + schema: string; +} + +/** + * Intelligent Document Processor - Generates or modifies JSON schemas + * + * Two modes: + * 1. **Generate**: Extract schema from images + * 2. **Finetune**: Modify existing schema based on user input and/or images + */ +export async function processIdp(params: IdpParams): Promise { + try { + // Render user prompt + const userPrompt = renderTemplate(IDP_PROMPT, { + operation: params.operation, + user_input: params.userInput || '', + json_schema: params.jsonSchema || '', + image_provided: params.images && params.images.length > 0 + }); + + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); + + // Build messages array + const messages: any[] = []; + + // Add text content + const textContent = { + type: 'text' as const, + text: userPrompt + }; + + // If images are provided, create a multi-part message + if (params.images && params.images.length > 0) { + const contentParts: any[] = [textContent]; + + // Add all images + for (const image of params.images) { + contentParts.push({ + type: 'image' as const, + image: image // Base64-encoded image + }); + } + + messages.push({ + role: 'user', + content: contentParts + }); + } else { + // No images, just text + messages.push({ + role: 'user', + content: userPrompt + }); + } + + // Generate response + const { text } = await generateText({ + model: model, + system: SYSTEM_IDP, + messages: messages, + maxOutputTokens: 8000, // JSON schemas can be large + temperature: 0.2, // Deterministic for schema generation + maxRetries: 0, // Prevent retry loops on quota errors + }); + + // Extract JSON from response + const extractedJSON = extractJSON(text); + + return { + schema: extractedJSON + }; + } catch (error) { + logError("Error processing IDP", error); + throw error; + } +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/prompt.ts new file mode 100644 index 00000000000..032e072be5d --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/prompt.ts @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const IDP_PROMPT = ` +{{#if (eq operation "generate")}} +### Task: Generate JSON Schema from Image(s) + +You are tasked with generating a JSON schema based on the content of the provided image. + +**Instructions:** +- Extract all relevant fields and their data from the image. +- Structure this information into a new JSON schema. +- Adhere strictly to the JSON schema standards and general rules defined in your system prompt. +- Also refer to the sample schema provided in the system prompt for guidance. +- **Return only the generated JSON schema.** + +{{else if (eq operation "finetune")}} +### Task: Fine-tune JSON Schema + +You are tasked with modifying an existing JSON schema. The modifications should be based on the user's explicit instructions and, if provided, additional information from an image. + +**Instructions:** +- **Analyze the \`user_input\`**: Understand the specific changes requested by the user. +{{#if image_provided}} +- **Analyze the provided image**: Extract additional information or context that might inform the schema modifications. +{{/if}} +- **Apply changes to the \`json_schema\`**: Update the provided JSON schema to reflect the user's requirements. This includes adding, removing, or modifying fields and their properties (\`type\`, \`description\`). +- Adhere strictly to the JSON schema standards and general rules defined in your system prompt. Also refer to the sample schema provided in the system prompt for guidance. +- **Return only the modified JSON schema.** + +**Current JSON Schema:** +{{json_schema}} + +{{#if user_input}} +**User's Modification Request:** +{{user_input}} +{{/if}} +{{/if}} + +Start creating your response now. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/system.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/system.ts new file mode 100644 index 00000000000..f13794c9fbe --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/system.ts @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYSTEM_IDP = ` +Your role is generating and modifying **JSON schemas** efficiently and correctly. + +### Core Tasks +1. **Schema Generation** + - Extract field names and types from inputs (e.g., images). + - Return a complete and valid JSON schema. + +2. **Schema Modification** + - Add, update, or remove properties in an existing JSON schema. + +### JSON Schema Rules + +#### Allowed Keywords +Use **only** the following keywords: +- \`type\` +- \`properties\` +- \`items\` +- \`description\` +- \`required\` +- \`additionalProperties\` + +#### Naming Conventions +- Use \`snake_case\` only. +- No spaces, camelCase, or special characters. + +#### Type-Specific Rules +If the \`type\` is: +- \`object\`: Only allowed keys are \`type\`, \`properties\`, \`required\`, and \`additionalProperties\`. +- \`array\`: Only allowed keys are \`type\` and \`items\`. +- \`string\`, \`number\`, \`integer\`, \`boolean\`: Only allowed keys are \`type\` and \`description\`. + +#### Type Values +- The \`type\` field can only be a single value (not an array). If multiple types are possible, always use string as the type. + +### Output Requirements +- Always return a **valid JSON object**. +- Do **not** include extra text, comments, or markdown formatting. +- Follow strict schema compliance. + +Sample schema for your reference: +\`\`\`json +{ + "type": "object", + "properties": { + "store_name": { + "type": "string", + "description": "Name of the store." + }, + "reference_number": { + "type": "integer", + "description": "Reference number of the transaction." + }, + "address": { + "type": "string", + "description": "Address of the store." + }, + "description_": { + "type": "string", + "description": "Description of the store." + }, + "contact_numbers": { + "type": "array", + "items": { + "type": "string", + "description": "Contact number of the store." + } + }, + "store_code": { + "type": "string", + "description": "Code of the store." + }, + "shop_owner": { + "type": "string", + "description": "Name of the shop owner." + }, + "date_and_time": { + "type": "string", + "description": "Date and time of the transaction." + }, + "receipt_number": { + "type": "string", + "description": "Receipt number of the transaction." + }, + "products": { + "type": "array", + "items": { + "type": "object", + "properties": { + "line_number": { + "type": "integer", + "description": "Line number of the item." + }, + "item_code": { + "type": "string", + "description": "Code of the item." + }, + "description": { + "type": "string", + "description": "Description of the item." + }, + "price": { + "type": "number", + "description": "Price of the item." + }, + "quantity": { + "type": "number", + "description": "Quantity of the item." + }, + "amount": { + "type": "number", + "description": "Total amount for the item." + } + }, + "required": [ + "line_number", + "item_code", + "description", + "price", + "quantity", + "amount" + ], + "additionalProperties": false + } + }, + "gross_amount": { + "type": "number", + "description": "Gross amount of the transaction." + }, + "promotion_discount": { + "type": "number", + "description": "Total promotion discount applied." + }, + "net_amount": { + "type": "number", + "description": "Net amount after discounts." + }, + "payment_method": { + "type": "string", + "description": "Payment method used." + }, + "total_savings": { + "type": "number", + "description": "Total savings from promotions." + }, + "points_earned": { + "type": "integer", + "description": "Points earned from the transaction." + }, + "total_points_redeemable": { + "type": "number", + "description": "Total points redeemable as of a specific date." + } + }, + "required": [ + "store_name", + "reference_number", + "address", + "description_", + "contact_numbers", + "store_code", + "shop_owner", + "date_and_time", + "receipt_number", + "products", + "gross_amount", + "promotion_discount", + "net_amount", + "payment_method", + "total_savings", + "points_earned", + "total_points_redeemable" + ], + "additionalProperties": false +} +\`\`\` + +Your goal is to ensure fast, reliable, and accurate schema creation and editing directly within the WSO2 Micro Integrator environment. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/logger.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/logger.ts new file mode 100644 index 00000000000..225306db810 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/logger.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { logWithDebugLevel } from '../../util/logger'; + +const COPILOT_LABEL = 'AI Copilot'; + +/** + * Log info message to the MI Extension output channel + */ +export function logInfo(message: string): void { + logWithDebugLevel(message, COPILOT_LABEL, 'INFO'); +} + +/** + * Log debug message to the MI Extension output channel + */ +export function logDebug(message: string): void { + logWithDebugLevel(message, COPILOT_LABEL, 'DEBUG'); +} + +/** + * Log warning message to the MI Extension output channel + */ +export function logWarn(message: string): void { + logWithDebugLevel(message, COPILOT_LABEL, 'WARN'); +} + +/** + * Log error message to the MI Extension output channel + * For critical errors that should also go to console, use console.error directly + */ +export function logError(message: string, error?: unknown): void { + const errorMessage = error === undefined + ? message + : error instanceof Error + ? `${message}: ${error.message}\n${error.stack}` + : `${message}: ${String(error)}`; + logWithDebugLevel(errorMessage, COPILOT_LABEL, 'ERROR'); +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/message-utils.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/message-utils.ts new file mode 100644 index 00000000000..627e3d8b28b --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/message-utils.ts @@ -0,0 +1,261 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import * as Handlebars from "handlebars"; +import { FileObject, ImageObject } from "@wso2/mi-core"; +import { logWarn } from "./logger"; + +/** + * Template for text files content + */ +const TEXT_FILES_TEMPLATE = ` +{{#if files}} +The following text files are provided for your reference: +{{#each files}} +--- +File: {{this.name}} +--- +{{this.content}} +--- +{{/each}} +{{/if}} +`; + +/** + * Supported text file mimetypes that can be safely processed as text + * Includes common text formats: plain text, markdown, CSV, JSON, XML, YAML, HTML, etc. + */ +const TEXT_MIMETYPES = new Set([ + // Plain text + "text/plain", + + // Markdown + "text/markdown", + "text/x-markdown", + + // CSV + "text/csv", + + // JSON + "application/json", + + // XML + "application/xml", + "text/xml", + + // YAML + "application/x-yaml", + "text/yaml", + "application/yaml", + "text/x-yaml", + + // HTML + "text/html", + + // JavaScript/TypeScript + "text/javascript", + "application/javascript", + "text/typescript", + + // CSS + "text/css", + + // Other common text formats + "text/rtf", + "application/rtf" +]); + +/** + * Validates if a string is properly base64-encoded + * @param str - The string to validate + * @returns true if the string is valid base64, false otherwise + */ +function isValidBase64(str: string): boolean { + if (!str || typeof str !== 'string') { + return false; + } + + // Base64 regex: alphanumeric + / + (plus padding with =) + // Must have length divisible by 4 (with padding) + const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/; + + if (!base64Regex.test(str)) { + return false; + } + + // Check length is divisible by 4 + if (str.length % 4 !== 0) { + return false; + } + + return true; +} + +/** + * Validates if a string is a properly formatted image data URI + * @param dataUri - The data URI string to validate + * @returns true if the string is a valid image data URI, false otherwise + */ +function isValidImageDataUri(dataUri: string): boolean { + if (!dataUri || typeof dataUri !== 'string') { + return false; + } + + // Data URI format: data:image/;base64, + // Supported image types: jpeg, jpg, png, gif, webp, svg+xml + const dataUriRegex = /^data:image\/(jpeg|jpg|png|gif|webp|svg\+xml);base64,/; + + if (!dataUriRegex.test(dataUri)) { + return false; + } + + return true; +} + +/** + * Filters files into text files and PDF files based on mimetype + * Assumes validation has already been done - all files should be supported types + */ +export function filterFiles(files: FileObject[]): { textFiles: FileObject[]; pdfFiles: FileObject[] } { + const textFiles: FileObject[] = []; + const pdfFiles: FileObject[] = []; + + for (const file of files) { + if (file.mimetype === "application/pdf") { + pdfFiles.push(file); + } else if (TEXT_MIMETYPES.has(file.mimetype)) { + textFiles.push(file); + } + // Note: Invalid file types are rejected upfront in RPC manager via validateAttachments() + } + + return { textFiles, pdfFiles }; +} + +/** + * Builds message content array for Anthropic API including files, PDFs, and images + * This follows the same pattern as the Python backend implementation + * + * Note: Validation should be done upfront via validateAttachments() before calling this function + * + * @param prompt - The main user prompt text + * @param files - Array of file objects (text files and PDFs) - must be pre-validated + * @param images - Array of image objects with base64 encoded data - must be pre-validated + * @returns Array of content blocks for the Anthropic API + */ +export function buildMessageContent( + prompt: string, + files?: FileObject[], + images?: ImageObject[] +): any[] { + const content: any[] = []; + + // Add files if provided (assumes pre-validation) + if (files && files.length > 0) { + const { textFiles, pdfFiles } = filterFiles(files); + + // Add PDF files as file blocks (AI SDK format) + for (const pdfFile of pdfFiles) { + content.push({ + type: "file", + data: pdfFile.content, // Base64 encoded content (pre-validated) + mediaType: "application/pdf" + }); + } + + // Add text files as a formatted text block + if (textFiles.length > 0) { + const template = Handlebars.compile(TEXT_FILES_TEMPLATE); + const textFilesContent = template({ files: textFiles }); + + content.push({ + type: "text", + text: textFilesContent.trim() + }); + } + } + + // Add images if provided (assumes pre-validation) + if (images && images.length > 0) { + content.push({ + type: "text", + text: "Following additional images are provided for your reference." + }); + + for (const image of images) { + // Use AI SDK format: { type: 'image', image: dataUri } + // The AI SDK will convert this to the provider's format internally + content.push({ + type: "image", + image: image.imageBase64 // Use the full data URI (pre-validated) + }); + } + } + + // Add the main prompt at the end + content.push({ + type: "text", + text: prompt + }); + + return content; +} + +/** + * Checks if files or images are present + */ +export function hasAttachments(files?: FileObject[], images?: ImageObject[]): boolean { + return !!(files && files.length > 0) || !!(images && images.length > 0); +} + +/** + * Validates attachments and returns warnings for any issues found + * This allows early validation before processing attachments + * + * @param files - Array of file objects to validate + * @param images - Array of image objects to validate + * @returns Array of warning messages for invalid attachments + */ +export function validateAttachments(files?: FileObject[], images?: ImageObject[]): string[] { + const warnings: string[] = []; + + // Validate files + if (files && files.length > 0) { + for (const file of files) { + // Check for unsupported file types + if (file.mimetype !== "application/pdf" && !TEXT_MIMETYPES.has(file.mimetype)) { + warnings.push(`Unsupported file type (${file.mimetype}): ${file.name}`); + } + // Check PDF base64 encoding + else if (file.mimetype === "application/pdf" && !isValidBase64(file.content)) { + warnings.push(`Invalid base64 encoding: ${file.name}`); + } + } + } + + // Validate images + if (images && images.length > 0) { + for (const image of images) { + if (!isValidImageDataUri(image.imageBase64)) { + warnings.push(`Invalid image data URI format`); + } + } + } + + return warnings; +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/prompt.ts new file mode 100644 index 00000000000..a425bbae0c6 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/prompt.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const PROMPT_TEMPLATE = ` +{{#if chat_history}} +You have an ongoing conversation with MI Copilot. Here's your current chat history: + +{{{chat_history}}} + +{{/if}} + +{{#if context}} +You are currently working on the following integration project: + +{{#each context}} +{{this}} +{{/each}} + + +Now ask MI Copilot to enhance your integration by adding new artifacts (such as APIs, Sequences, Endpoints, etc.), features, or improvements. +{{else}} +You are starting a new integration project. Describe an integration challenge or requirement for MI Copilot to solve. +{{/if}} + +Your output should be a single line of query you ask from MI Copilot. Nothing else. +Start creating your response now. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts new file mode 100644 index 00000000000..c341a45dec7 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateText } from "ai"; +import * as Handlebars from "handlebars"; +import { getAnthropicClient, ANTHROPIC_HAIKU_4_5, getProviderCacheControl } from "../connection"; +import { SYSTEM_TEMPLATE } from "./system"; +import { PROMPT_TEMPLATE } from "./prompt"; +import { logError } from "../logger"; + +/** + * Render a template using Handlebars + */ +function renderTemplate(templateContent: string, context: Record): string { + const template = Handlebars.compile(templateContent); + return template(context); +} + +/** + * Format chat history for the prompt + */ +function formatChatHistory(chatHistory: any[]): string { + if (!chatHistory || chatHistory.length === 0) { + return ""; + } + + return chatHistory + .map((entry) => { + const role = entry.role === "user" ? "User" : "Assistant"; + return `${role}: ${entry.content}`; + }) + .join("\n\n"); +} + +/** + * Generates suggestions based on the provided parameters + * Returns the generated suggestions text + */ +export async function generateSuggestions( + codeContext: string[], + chatHistory: any[] +): Promise { + const formattedChatHistory = formatChatHistory(chatHistory); + const projectContext = codeContext; + + // Render system prompt + const systemPrompt = renderTemplate(SYSTEM_TEMPLATE, {}); + + // Render user prompt + const userPrompt = renderTemplate(PROMPT_TEMPLATE, { + chat_history: formattedChatHistory, + context: projectContext, + }); + + const cacheOptions = await getProviderCacheControl(); + + const messages = [ + { + role: "system" as const, + content: systemPrompt, + providerOptions: cacheOptions, // Cache system prompt only + }, + { + role: "user" as const, + content: userPrompt, + }, + ]; + + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); + + try{ + const { text } = await generateText({ + model: model, + maxOutputTokens: 100, + temperature: 0.6, // Slightly higher temperature for suggestions + messages, + maxRetries: 0, // Disable retries to prevent retry loops on quota errors (429) + }); + return text; + } catch (error) { + logError("Error generating suggestions", error); + return ""; + } +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/system.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/system.ts new file mode 100644 index 00000000000..b472473927f --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/system.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYSTEM_TEMPLATE = ` +You are an integration engineer specialized in building WSO2 Synapse integrations for the WSO2 Micro Integrator using the WSO2 MI Copilot feature in the new WSO2 MI VS Code extension. +- WSO2 MI Copilot is an AI assistant embedded in the VS Code extension that helps you develop Synapse integrations more efficiently via a conversational interface. +- It has access to your integration project and can make changes on your behalf, including creating new integrations, modifying existing ones, or adding new features. +- You interact with the Copilot by providing prompts instead of writing code manually. Think of it as instructing an assistant to build or improve your integrations. +- Focus solely on tasks related to WSO2 Synapse integrations. Avoid asking unrelated questions—the Copilot is not designed for general-purpose queries. +- Keep your prompts clear, specific, and concise to ensure accurate results.`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/prompt_case_generate.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/prompt_case_generate.ts new file mode 100644 index 00000000000..82ddcafe061 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/prompt_case_generate.ts @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const PROMPT_CASE_GENERATE = ` +{{#if full_context}} + +{{#each full_context}} +{{this}} +{{/each}} + +Add a new test case to the existing unit test suite for the WSO2 Synapse artifacts in the above project. +{{else}} +{{#if context}} + +{{#each context}} +{{this}} +{{/each}} + +Add a new test case to the existing unit test suite for the WSO2 Synapse artifacts in the above project. +{{/if}} +{{/if}} + + +{{test_suite_file}} + + + +{{test_case_description}} + + +{{#if existing_mock_services}} +{{#if existing_mock_service_names}} + +{{#each existing_mock_services}} +### {{lookup ../existing_mock_service_names @index}} +{{this}} + +{{/each}} + +The above mock services already exist in the project. Reuse them if the new test case requires the same external endpoints. Only create new mock services if the new test case needs external endpoints not covered by the existing ones. +{{/if}} +{{/if}} + +{{#if pom_file}} + +{{pom_file}} + +Analyze the POM file to identify internal WSO2 connectors. Look for dependencies with groupId "org.wso2.integration.connector" and use format {artifactId}-{version} for connector-resources (e.g., mi-connector-http-0.1.11). Only add new connectors not already present in the existing test suite. +{{/if}} + +{{#if external_connectors}} + +The following external/custom connectors are used in this project: +{{#each external_connectors}} +- {{this}} +{{/each}} + +These are custom connectors stored in src/main/wso2mi/resources/connectors/. Use full path format with .zip extension: src/main/wso2mi/resources/connectors/{connectorName}.zip when referencing them in connector-resources. Only add new connectors not already present in the existing test suite. +{{/if}} + +{{#if test_file_name}} + +{{test_file_name}} + +Use the above file name for the updated unit test file. +{{/if}} + +**CRITICAL INSTRUCTIONS - PRESERVE EXISTING CONTENT:** + +1. **DO NOT MODIFY EXISTING CONTENT**: You must preserve ALL existing test cases, mock services references, connector resources, and any other existing content in the test suite file EXACTLY as they are. + +2. **ONLY ADD THE NEW TEST CASE**: Based on the test case description provided, add ONLY the new test case to the existing \u003ctest-cases\u003e section. + +3. **REUSE EXISTING MOCK SERVICES**: Check if any existing mock services can handle the external calls needed by the new test case. Only create new mock services if absolutely necessary. + +4. **MAINTAIN EXACT STRUCTURE**: Preserve the exact XML structure, indentation, comments, and formatting of the existing test suite file. + +5. **ADD TO APPROPRIATE SECTIONS**: + - Add the new test case in the existing \u003ctest-cases\u003e section + - If new mock services are needed, add them to the existing \u003cmock-services\u003e section + - If new connector resources are needed, add them to the existing \u003cconnector-resources\u003e section + +6. **COMPLETE FILE OUTPUT**: Your response must contain the COMPLETE updated unit test file with ALL existing content preserved and the new test case added. + +**EXAMPLE OF CORRECT BEHAVIOR:** +If the existing test suite has: +\`\`\`xml + + + ... + ... + + +\`\`\` + +Your output should be: +\`\`\`xml + + + ... + ... + ...NEW TEST CASE CONTENT... + + +\`\`\` + +**Important Instructions for Assertions:** +The assertions you can use depend on the artifact type being tested: + +**For API Artifacts - ONLY these assertions are allowed:** +- \`$body\` - Full response body +- \`$statusCode\` - HTTP status code +- \`$trp:\` - Transport headers (e.g., \`$trp:Content-Type\`) +- \`$httpVersion\` - HTTP version + +**For Sequence Artifacts - Full synapse expression support:** +- \`$body\` - Full response body +- \`$trp:\` - Transport headers +- \`\${payload}\` - Full payload access +- \`\${payload.fieldName}\` - Payload field access +- \`\${vars.variableName}\` - Variable access +- Any other valid synapse expressions + +**Important Instructions for Mock Services:** +**CRITICAL: ALL MOCK SERVICES MUST USE PORT 9090 - NO EXCEPTIONS** +**REUSE EXISTING MOCK SERVICES FIRST:** +1. **ANALYZE EXISTING MOCK SERVICES**: Review the existing mock services to understand what endpoints are already mocked +2. **REUSE WHEN POSSIBLE**: If existing mock services cover the external endpoints needed by the new test case, reference them in the updated \u003cmock-services\u003e section +3. **CREATE ONLY NEW ONES**: Only create new mock services for external endpoints not covered by existing ones +4. **MANDATORY ENDPOINT CONFIGURATION ANALYSIS**: Before creating any new mock service, you MUST: + - **READ THE ACTUAL ENDPOINT CONFIGURATION**: Analyze the endpoint definition to understand its real configuration + - **EXTRACT HTTP METHOD**: Get the actual method (GET, POST, PUT, DELETE) from the endpoint's \`method\` attribute + - **PARSE THE EXTERNAL URL**: Extract the full \`uri-template\` to understand the target service + - **ALWAYS USE PORT 9090**: All mock services must use port 9090 regardless of original endpoint configuration + - **EXTRACT CONTEXT PATH**: Parse the URL to get the base path (e.g., \`/v1\` from \`https://mocki.io/v1/resource\`) + - **GET SUB-CONTEXT**: Extract the specific resource path (e.g., \`/a757849d-80b6...\` from the full URL) + - **NEVER USE GENERIC DEFAULTS**: Do not use placeholder values like \`/api\` context without analyzing the real endpoint +5. **MANDATORY REQUIREMENT**: When you add new mock services to the \u003cmock-services\u003e section, you MUST provide BOTH: + - **Mock service XML content**: Include complete mock service XML in the \`mock_services\` response array + - **Mock service file names**: Include descriptive file names in the \`mock_service_names\` response array + - **Mock service references**: Use full path format \`src/test/resources/mock-services/MockServiceName.xml\` in unit test \u003cmock-services\u003e section +6. **CRITICAL NAMING RULES**: + - Unit test \u003cmock-service\u003e should use full path format: \`src/test/resources/mock-services/MockServiceName.xml\` + - Mock service XML \u003cservice-name\u003e must be the exact endpoint key reference from the API code + - Mock service XML \u003cport\u003e must always be \`9090\` + +**MANDATORY RESPONSE FORMAT:** +You MUST provide the complete updated unit test file that includes: +1. **ALL EXISTING CONTENT PRESERVED EXACTLY**: Every existing test case, mock service reference, connector resource, artifact definition, and any other element from the original test suite +2. **THE NEW TEST CASE ADDED**: The new test case added to the existing \u003ctest-cases\u003e section +3. **ANY NEW MOCK SERVICE REFERENCES**: Only if new mock services are needed, add them to the existing \u003cmock-services\u003e section +4. **ANY NEW CONNECTOR RESOURCES**: Only if new connector resources are needed, add them to the existing \u003cconnector-resources\u003e section + +**CRITICAL**: Your response must start with the complete updated XML file wrapped in markdown code blocks: + +\`\`\`markdown +## Updated Unit Test File: [filename] +\`\`\`xml + + + + + +\`\`\` + +## New Mock Service Files (only if new ones are created): +### NewMockService.xml +\`\`\`xml + + + +\`\`\` +\`\`\` + +**DO NOT:** +- Create a completely new test file +- Remove or modify any existing test cases +- Change the structure of existing sections +- Generate only the new test case without the existing content +3. Any new mock service references added to the \u003cmock-services\u003e section (if needed) +4. Any new connector resources added to the \u003cconnector-resources\u003e section (if needed) + +If you add new mock services, provide them in markdown format after the updated test file: + +\`\`\`markdown +## Updated Unit Test File: [filename] +\`\`\`xml + + + +\`\`\` + +## New Mock Service Files (if any): +### NewMockService.xml +\`\`\`xml + + + +\`\`\` +\`\`\` + +**CRITICAL**: Ensure the response includes the complete updated test file with all existing content preserved and only the new test case added. + +Start creating your response now. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/prompt_suite_generate.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/prompt_suite_generate.ts new file mode 100644 index 00000000000..837a94fbc2b --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/prompt_suite_generate.ts @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const PROMPT_SUITE_GENERATE = ` +{{#if full_context}} + +{{#each full_context}} +{{this}} +{{/each}} + +Generate comprehensive unit tests for the WSO2 Synapse artifacts in the above project. Create multiple test cases when necessary to thoroughly cover the artifact's functionality, including different scenarios, edge cases, and execution paths. For simple artifacts where one test case adequately validates the functionality, a single comprehensive test case is sufficient. +{{else}} +{{#if context}} + +{{#each context}} +{{this}} +{{/each}} + +Generate comprehensive unit tests for the WSO2 Synapse artifacts in the above project. Create multiple test cases when necessary to thoroughly cover the artifact's functionality, including different scenarios, edge cases, and execution paths. For simple artifacts where one test case adequately validates the functionality, a single comprehensive test case is sufficient. +{{/if}} +{{/if}} + +{{#if pom_file}} + +{{pom_file}} + +Analyze the POM file to identify internal WSO2 connectors. Look for dependencies with groupId "org.wso2.integration.connector" and use format {artifactId}-{version} for connector-resources (e.g., mi-connector-http-0.1.11). +{{/if}} + +{{#if external_connectors}} + +The following external/custom connectors are used in this project: +{{#each external_connectors}} +- {{this}} +{{/each}} + +These are custom connectors stored in src/main/wso2mi/resources/connectors/. Use full path format with .zip extension: src/main/wso2mi/resources/connectors/{connectorName}.zip when referencing them in connector-resources. +{{/if}} + +{{#if test_file_name}} + +{{test_file_name}} + +Use the above file name for the generated unit test file. +{{/if}} + +**Important Instructions for Assertions:** +The assertions you can use depend on the artifact type being tested: + +**For API Artifacts - ONLY these assertions are allowed:** +- \`$body\` - Full response body +- \`$statusCode\` - HTTP status code +- \`$trp:\` - Transport headers (e.g., \`$trp:Content-Type\`) +- \`$httpVersion\` - HTTP version + +**For Sequence Artifacts - Full synapse expression support:** +- \`$body\` - Full response body +- \`$trp:\` - Transport headers +- \`\${payload}\` - Full payload access +- \`\${payload.fieldName}\` - Payload field access +- \`\${vars.variableName}\` - Variable access +- Any other valid synapse expressions + +**Important Instructions for Mock Services:** +**CRITICAL: ALL MOCK SERVICES MUST USE PORT 9090 - NO EXCEPTIONS** +**STRICT MOCK SERVICE RULES WITH ENDPOINT ANALYSIS:** +1. **CAREFULLY ANALYZE CODE** for external calls that need mocking: + - \u003ccall\u003e\u003cendpoint key="endpointName"/\u003e - use \u003cservice-name\u003eendpointName\u003c/service-name\u003e in mock service + - \u003ccall\u003e\u003cendpoint key="resources:filename.xml"/\u003e - use \u003cservice-name\u003eresources:filename.xml\u003c/service-name\u003e in mock service + - HTTP connector operations calling external services +2. **MANDATORY ENDPOINT CONFIGURATION ANALYSIS**: Before creating any mock service, you MUST: + - **READ THE ACTUAL ENDPOINT CONFIGURATION**: Analyze the endpoint definition to understand its real configuration + - **EXTRACT HTTP METHOD**: Get the actual method (GET, POST, PUT, DELETE) from the endpoint's \`method\` attribute + - **PARSE THE EXTERNAL URL**: Extract the full \`uri-template\` to understand the target service + - **ALWAYS USE PORT 9090**: All mock services must use port 9090 regardless of original endpoint configuration + - **CRITICAL PORT RULE**: NEVER analyze or extract port from the original endpoint - ALWAYS use 9090 + - **FORBIDDEN**: Do NOT use ports like 443, 80, 8080, or any other ports - ONLY use 9090 + - **EXTRACT CONTEXT PATH**: Parse the URL to get the base path (e.g., \`/v1\` from \`https://mocki.io/v1/resource\`) + - **GET SUB-CONTEXT**: Extract the specific resource path (e.g., \`/a757849d-80b6...\` from the full URL) + - **NEVER USE GENERIC DEFAULTS**: Do not use placeholder values like \`/api\` context or \`/\` sub-context without analyzing the real endpoint +3. **EXAMPLE OF PROPER ENDPOINT ANALYSIS**: + - Endpoint config: \u003chttp method="get" uri-template="https://mocki.io/v1/a757849d-80b6-4d00-a1be-1d517ede86fa"\u003e + - **CORRECT** mock service: \u003cport\u003e9090\u003c/port\u003e, \u003ccontext\u003e/v1\u003c/context\u003e, \u003csub-context\u003e/a757849d-80b6-4d00-a1be-1d517ede86fa\u003c/sub-context\u003e, \u003cmethod\u003eGET\u003c/method\u003e + - **WRONG** mock service: \u003ccontext\u003e/api\u003c/context\u003e, \u003csub-context\u003e/\u003c/sub-context\u003e +4. **ONLY** create mock services for external endpoint calls or database calls that are being used in the artifact being tested +5. **DO NOT** write mock services for everything - only for external calls like HTTP endpoints, database connections, web services +6. **DO NOT** mock internal sequences, local entries, or other internal artifacts unless they make external calls +7. **DO NOT HALLUCINATE RESPONSES**: Mock services must reflect the actual expected behavior and response structure of the real endpoint being mocked, not create fictional or fantasy responses +8. **MANDATORY REQUIREMENT**: When you declare mock services in the \u003cmock-services\u003e section, you MUST provide BOTH: + - **Mock service XML content**: Include complete mock service XML in the \`mock_services\` response array + - **Mock service file names**: Include descriptive file names in the \`mock_service_names\` response array + - **Mock service references**: Use full path format \`src/test/resources/mock-services/MockServiceName.xml\` in unit test \u003cmock-services\u003e section + - The \u003cservice-name\u003e inside mock service XML should be the exact endpoint key reference + - The \u003cport\u003e inside mock service XML should always be \`9090\` + - **ALWAYS** provide complete mock service implementations that support the test scenarios + - **REALISTIC RESPONSES**: Use meaningful response data that matches what the actual endpoint would return + - **DO NOT CREATE FANTASY DATA**: Base responses on realistic, expected behavior of the actual endpoint + - Follow the correct WSO2 mock service XML structure as documented in the guidelines +9. **CRITICAL**: The \`service-name\` in mock service XML must be the exact endpoint key reference from the API code: + - For \u003ccall\u003e\u003cendpoint key="testEpSeq"/\u003e → \u003cservice-name\u003etestEpSeq\u003c/service-name\u003e + - For \u003ccall\u003e\u003cendpoint key="resources:testregistry.xml"/\u003e → \u003cservice-name\u003eresources:testregistry.xml\u003c/service-name\u003e +10. **CRITICAL MOCK SERVICE REFERENCE FORMAT**: + - **ALWAYS use full path format** in unit test \u003cmock-services\u003e section + - **MANDATORY FORMAT**: \`src/test/resources/mock-services/MockServiceName.xml\` + - **WRONG**: \u003cmock-service\u003eExternalEndpointMock\u003c/mock-service\u003e + - **CORRECT**: \u003cmock-service\u003esrc/test/resources/mock-services/ExternalEndpointMock.xml\u003c/mock-service\u003e +11. Mock service file names can be descriptive and different from the service-name they mock + +**CRITICAL**: If you declare ANY mock services in the \u003cmock-services\u003e section, you MUST provide BOTH the mock service XML content AND the file names. + +**MANDATORY RESPONSE FORMAT:** +If you declare mock services in the unit test XML, your response MUST provide the mock service files. + +For API code: \u003ccall\u003e\u003cendpoint key="resources:testregistry.xml"/\u003e\u003c/call\u003e + +**You MUST generate the mock service file in markdown format like this:** + +\`\`\`markdown +## Unit Test File: testRegAPI_UnitTest.xml +\`\`\`xml + + + + src/test/resources/mock-services/RegistryEndpointMock.xml + + +\`\`\` + +## Mock Service Files: +### RegistryEndpointMock.xml +\`\`\`xml + + resources:testregistry.xml + 9090 + /v1 + + + /a757849d-80b6-4d00-a1be-1d517ede86fa + GET + + + + + 200 + +
+ + + + + + +\`\`\` +\`\`\` + +**IF YOU DON'T PROVIDE MOCK SERVICE FILES WHEN DECLARING MOCK SERVICES, THE RESPONSE IS INCOMPLETE AND INCORRECT.** + +**KEY POINTS:** +- Unit test \u003cmock-service\u003e: \`src/test/resources/mock-services/RegistryEndpointMock.xml\` (full path to mock service file) +- Mock service XML \u003cservice-name\u003e: \`resources:testregistry.xml\` (exact endpoint key reference from API code) +- Mock service XML \u003cport\u003e: \`9090\` (always use port 9090) +- File name in \`mock_service_names\`: "RegistryEndpointMock.xml" (descriptive file name with .xml extension) + +Start creating your response now. +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/system_case_generate.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/system_case_generate.ts new file mode 100644 index 00000000000..d6aea9138e5 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/system_case_generate.ts @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYSTEM_CASE_GENERATE = ` +You are WSO2 MI Copilot, an AI assistant specialized in adding individual test cases to existing WSO2 Synapse unit test suites within the WSO2 Micro Integrator ecosystem. Your primary role is to add a single new test case to an existing unit test file based on user requirements while preserving all existing content and structure. + +**CORE RESPONSIBILITY**: You MUST output the COMPLETE updated unit test file with ALL existing content preserved exactly and ONLY the new test case added. + +**CRITICAL**: You are NOT creating a new test file. You are updating an existing one by adding content. + +You will be provided with the following inputs: +1. : The specific WSO2 Synapse artifacts for which a new test case needs to be added. +2. : The current unit test file content that already contains existing test cases. +3. : A description of the new test case that needs to be added. +4. : Array of existing mock service file contents (if any). +5. : Array of names corresponding to existing mock services. +6. : Complete project files to provide comprehensive understanding of the project structure, dependencies, and relationships between artifacts (optional). +7. : The desired name for the unit test file. +8. : The Maven POM file content to understand project dependencies and structure (optional). +9. : List of external connectors used in the project that may need to be considered for testing (optional). + +### CRITICAL REQUIREMENTS - PRESERVE EXISTING CONTENT: + +**ABSOLUTELY FORBIDDEN ACTIONS:** +- DO NOT modify, remove, or change ANY existing test cases +- DO NOT modify existing \`\` section unless adding new mock services +- DO NOT change existing \`\` section unless adding new connectors +- DO NOT modify existing \`\` section +- DO NOT change the structure, formatting, or content of any existing elements +- DO NOT reorganize or reorder existing test cases + +**REQUIRED ACTIONS - ADDITIVE ONLY:** +- ONLY ADD the new test case as specified in the test case description +- ADD new mock services ONLY if the new test case requires external calls not covered by existing mocks +- ADD new connector resources ONLY if the new test case uses connectors not already included +- PRESERVE the exact formatting, indentation, and structure of the existing test file +- MAINTAIN all existing XML comments, spacing, and organization + +### When processing a unit test case addition request, follow these steps: + +1. **CAREFULLY Analyze the Existing Test Suite**: + - Understand the current structure and existing test cases + - Identify existing mock services and their coverage + - Note existing connector resources and dependencies + - **CRITICAL**: Do not modify anything that already exists + +2. **Analyze the Test Case Description**: + - Understand what specific test case needs to be added + - Identify if the new test case requires external calls + - Check if existing mock services can be reused for the new test case + - Determine if new mock services are needed + +3. **Analyze Dependencies - NEW TEST CASE ONLY**: + - **POM Analysis**: Extract internal WSO2 connectors from POM dependencies, but ONLY include NEW ones not already in existing test + - **External Connectors**: Process external/custom connectors from the provided list, but ONLY include NEW ones needed for the new test case + - **Supporting Artifacts**: ONLY identify and include NEW artifacts that are directly referenced by the new test case + - **Registry Resources**: ONLY include NEW registry resources referenced by the new test case + - **REUSE EXISTING**: If existing mock services already cover the external calls needed by the new test case, reuse them + +4. **Generate the New Test Case**: + - Create ONLY the new test case that covers the functionality described in the test case description + - Focus on the specific scenario requested in the description + - Ensure the test case validates the expected behavior for that specific scenario + - Use appropriate assertion types based on the artifact being tested + +5. **Follow Technical Standards**: + - Adhere to the WSO2 MI unit testing XSD schema and best practices + - Use appropriate assertion types and validation methods based on artifact type + - Include meaningful error messages and test descriptions + - **API Assertions**: Only use \`$body\`, \`$statusCode\`, \`$trp:\`, \`$httpVersion\` + - **Sequence Assertions**: Use \`$body\`, \`$trp:\`, or full synapse expressions like \`${'${payload}'}\`, \`${'${vars.abc}'}\` + +6. **Mock Services - REUSE FIRST, ADD ONLY IF NEEDED**: + - **MANDATORY PORT RULE: ALL MOCK SERVICES MUST USE PORT 9090 - NO EXCEPTIONS** + - **REUSE EXISTING MOCK SERVICES**: Check if existing mock services already cover the external calls needed by the new test case + - **ONLY CREATE NEW MOCK SERVICES**: If the new test case requires external calls not covered by existing mocks + - **EXISTING MOCK SERVICE ANALYSIS**: Examine existing mock service names and content to understand what endpoints are already mocked + - **CORRESPONDING INDEXES**: Remember that existing_mock_services and existing_mock_service_names have corresponding indexes (0th position correlates) + - **ONLY** create mock services for external endpoint calls or database calls that are actually being used by the NEW test case + - **DO NOT** write mock services for internal sequences, local entries, or other internal processing artifacts + - **DO NOT HALLUCINATE**: Mock services must reflect the actual expected behavior of the real endpoint being mocked + - **USE REALISTIC DATA**: Base mock responses on what the actual endpoint would realistically return + - **IDENTIFY ENDPOINT CALLS**: Look for these patterns in the NEW test case that require mocking: + - \`\` - check if existing mocks cover this endpoint + - \`\` - check if existing mocks cover this endpoint + - HTTP connector operations calling external services + - **CRITICAL ENDPOINT ANALYSIS REQUIREMENT**: + - **ALWAYS READ AND ANALYZE THE ENDPOINT CONFIGURATION** before creating mock services + - **EXTRACT REAL ENDPOINT DETAILS**: Parse the actual endpoint definition to get HTTP method, URL, context, sub-context + - **ALWAYS USE PORT 9090**: All mock services must use port 9090 regardless of the original endpoint configuration + - **NEVER USE GENERIC DEFAULTS**: Do not use placeholder values without analyzing the real endpoint + - **CRITICAL MOCK SERVICE REFERENCE FORMAT**: + - **ALWAYS use full path format** in unit test \`\` section + - **MANDATORY FORMAT**: \`src/test/resources/mock-services/MockServiceName.xml\` + - **CRITICAL RULE**: Only generate mock service XML files for NEW services that are not already covered by existing mock services + - **MANDATORY**: If you add new mock services in \`\`, you MUST provide BOTH: + 1. The complete mock service XML files in the \`mock_services\` response array + 2. The mock service file names in the \`mock_service_names\` response array + +7. **Response Format**: + - Always respond in Markdown format + - Provide the complete updated unit test file in XML format + - **CRITICAL**: When new mock services are added, you MUST provide the mock service XML files in markdown format + - **FORMAT**: Use \`\`\`### MockServiceFileName.xml\`\`\` followed by \`\`\`xml blocks for each NEW mock service + - Keep responses complete and preserve all existing content exactly + + +{{> unit_test_guide}} + +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/system_suite_generate.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/system_suite_generate.ts new file mode 100644 index 00000000000..7d82eac3255 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/system_suite_generate.ts @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const SYSTEM_SUITE_GENERATE = ` +You are WSO2 MI Copilot, an AI assistant specialized in creating comprehensive unit tests for WSO2 Synapse integrations within the WSO2 Micro Integrator ecosystem. Your primary role is to generate high-quality, accurate unit test files that validate the behavior and functionality of WSO2 Synapse artifacts. + +You will be provided with the following inputs: +1. : The specific WSO2 Synapse artifacts for which unit tests need to be generated. +2. : Complete project files to provide comprehensive understanding of the project structure, dependencies, and relationships between artifacts (optional). +3. : The desired name for the unit test file. +4. : The Maven POM file content to understand project dependencies and structure (optional). +5. : List of external connectors used in the project that may need to be considered for testing (optional). + +### When processing a unit test generation request, follow these steps: + +1. **CAREFULLY Analyze the Artifacts**: + - Identify the main artifacts that need testing (APIs, sequences, endpoints, etc.) + - **CRITICALLY IMPORTANT**: Perform detailed code analysis to understand the flow and dependencies + - Look for actual references in the code: + - \`\` - endpoint dependency + - \`\` - sequence dependency + - \`configKey="localEntryName"\` - local entry dependency + - \`key="resources:filename.xml"\` - registry resource dependency + - **DO NOT** assume dependencies based on naming or project structure + - Understand the expected behavior of each artifact + +2. **Analyze Dependencies - STRICT RELEVANCE ONLY**: + - **POM Analysis**: Extract internal WSO2 connectors from POM dependencies with format \`{artifactId}-{version}\`, but ONLY include those actually used in the artifact + - **External Connectors**: Process external/custom connectors from the provided list using full path format, but ONLY include those actually used in the artifact + - **Supporting Artifacts**: ONLY identify and include artifacts that are directly referenced in the code being tested: + - Look for \`\` patterns + - Look for \`\` patterns + - Look for \`configKey="localEntryName"\` in connector operations + - Include transitive dependencies if artifact A references B and B references C + - **Registry Resources**: ONLY include registry resources referenced as \`key="resources:filename.xml"\` + - **CRITICAL DISTINCTION**: \`\` means registry resource, NOT supportive artifact + - **DO NOT**: Include any artifacts, connectors, or resources just because they exist in the project - they must be actually referenced in the code + +3. **Generate Comprehensive Tests**: + - Create test cases that cover the main functionality and edge cases + - Focus only on the provided artifacts - do not create tests for unspecified components + - Ensure tests validate the expected response types and behavior + - **CRITICAL**: Use only valid assertions based on the artifact type being tested + +4. **Follow Technical Standards**: + - Adhere to the WSO2 MI unit testing XSD schema and best practices + - Use appropriate assertion types and validation methods based on artifact type + - Include meaningful error messages and test descriptions + - **API Assertions**: Only use \\\`$body\\\`, \\\`$statusCode\\\`, \\\`$trp:\\\`, \\\`$httpVersion\\\` + - **Sequence Assertions**: Use \\\`$body\\\`, \\\`$trp:\\\`, or full synapse expressions like \\\`\\\${payload}\\\`, \\\`\\\${vars.abc}\\\` + +5. **Maintain Quality Standards**: + - Provide a brief explanation of the test structure when applicable + - Use placeholder values where real data is not available + - Do not include setup, deployment, or runtime configuration instructions unless explicitly requested + +6. **Mock Services - STRICT EXTERNAL-ONLY RULE WITH ENDPOINT ANALYSIS**: + - **MANDATORY PORT RULE: ALL MOCK SERVICES MUST USE PORT 9090 - NO EXCEPTIONS** + - **ONLY** create mock services for external endpoint calls or database calls that are actually being used + - **DO NOT** write mock services for everything - only for external calls like HTTP endpoints, database connections, web services + - **DO NOT** mock internal sequences, local entries, or other internal processing artifacts unless they make external calls + - **DO NOT HALLUCINATE**: Mock services must reflect the actual expected behavior of the real endpoint being mocked, not create fictional responses + - **USE REALISTIC DATA**: Base mock responses on what the actual endpoint would realistically return + - **IDENTIFY ENDPOINT CALLS**: Look for these patterns that require mocking: + - \`\` - create mock service with descriptive name, use \`endpointName\` in mock service XML + - \`\` - create mock service with descriptive name, use \`resources:filename.xml\` in mock service XML + - HTTP connector operations calling external services + - **CRITICAL ENDPOINT ANALYSIS REQUIREMENT**: + - **ALWAYS READ AND ANALYZE THE ENDPOINT CONFIGURATION** before creating mock services + - **EXTRACT REAL ENDPOINT DETAILS**: Parse the actual endpoint definition to get: + * HTTP method (GET, POST, PUT, DELETE) from \`method\` attribute + * External URL from \`uri-template\` attribute + * Host/domain from the URL (e.g., \`mocki.io\` from \`https://mocki.io/v1/...\`) + * Context path from URL structure (e.g., \`/v1\` from \`https://mocki.io/v1/abc...\`) + * Sub-context from remaining URL path (e.g., \`/abc-123\` from the full path) + - **ALWAYS USE PORT 9090**: All mock services must use port 9090 regardless of the original endpoint configuration + - **CRITICAL PORT RULE**: NEVER analyze or extract port from the original endpoint - ALWAYS use 9090 + - **FORBIDDEN**: Do NOT use ports like 443, 80, 8080, or any other ports - ONLY use 9090 + - **NEVER USE GENERIC DEFAULTS**: Do not use placeholder values like \`/api\` context without analyzing the actual endpoint + - **CORRECT MOCK SERVICE CONFIGURATION EXAMPLE**: + - For endpoint: \`\` + - Mock service should use: \`9090\`, \`/v1\`, \`/a757849d-80b6-4d00-a1be-1d517ede86fa\`, \`GET\` + - **CRITICAL MOCK SERVICE REFERENCE FORMAT**: + - **ALWAYS use full path format** in unit test \`\` section + - **MANDATORY FORMAT**: \`src/test/resources/mock-services/MockServiceName.xml\` + - **WRONG**: \`ExternalEndpointMock\` + - **CORRECT**: \`src/test/resources/mock-services/ExternalEndpointMock.xml\` + - **CRITICAL RULE**: Only generate mock service XML files for services explicitly listed in the \`\` section of your unit test + - The count of mock service XML files must EXACTLY match the count of \`\` entries in the unit test + - **MANDATORY**: If you declare mock services in \`\`, you MUST provide BOTH: + 1. The complete mock service XML files in the \`mock_services\` response array + 2. The mock service file names in the \`mock_service_names\` response array + - **CRITICAL NAMING RULES**: + - Unit test \`\` should use full path format: \`src/test/resources/mock-services/MockServiceName.xml\` + - Mock service XML \`\` must be the exact endpoint key reference from the API code: + - For \`\` → \`testEpSeq\` + - For \`\` → \`resources:testregistry.xml\` + - Mock service XML \`\` must always be \`9090\` + - **CRITICAL**: Mock service file names MUST be sent in the \`mock_service_names\` parameter when mock services are used + - **TWO SEPARATE THINGS**: + - \`mock_services\` array = actual mock service XML content + - \`mock_service_names\` array = descriptive file names for those mock services + - Do NOT generate extra mock services that are not referenced in the \`\` section + - When your generated unit tests require mock services, include them in the \`\` section of the unit test XML + - For each mock service used in test cases, you must also provide the corresponding mock service XML file + - **REALISTIC MOCKING**: Ensure mock responses simulate the actual behavior and response structure of the real endpoint + - Follow the correct WSO2 mock service XML structure with \`\`, \`\`, \`\`, and \`\` elements + - Each resource must include \`\`, \`\`, \`\`, and \`\` elements + - Support multiple resources within a single mock service for different endpoints + - Include request and response headers using \`
\` syntax when needed + - Add request payloads for POST/PUT methods wrapped in CDATA sections + - Use CDATA sections for JSON payloads in mock service responses + +7. **Response Format**: + - Always respond in Markdown format + - Provide unit tests in XML format following WSO2 standards + - **CRITICAL**: When mock services are declared in \`\` section, you MUST provide the mock service XML files in markdown format + - **INCOMPLETE RESPONSE**: If you declare mock services but don't provide the mock service files, your response is wrong and incomplete + - **FORMAT**: Use \`### MockServiceFileName.xml\` followed by \`\`\`xml blocks for each mock service + - Keep responses concise, complete, and free of unnecessary placeholders + + +{{> unit_test_guide}} + +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_case_generate.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_case_generate.ts new file mode 100644 index 00000000000..8bd2e97b169 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_case_generate.ts @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateText } from "ai"; +import * as Handlebars from "handlebars"; +import { getAnthropicClient, ANTHROPIC_HAIKU_4_5, getProviderCacheControl } from "../connection"; +import { SYSTEM_CASE_GENERATE } from "./system_case_generate"; +import { PROMPT_CASE_GENERATE } from "./prompt_case_generate"; +import { UNIT_TEST_GUIDE } from "./unit_test_guide"; +import { UNIT_TEST_EXAMPLES } from "./unit_test_examples"; +import { logError } from "../logger"; + +// Register Handlebars partials for unit test case generation +Handlebars.registerPartial("unit_test_guide", UNIT_TEST_GUIDE); +Handlebars.registerPartial("unit_test_examples", UNIT_TEST_EXAMPLES); + +/** + * Render a template using Handlebars + */ +function renderTemplate(templateContent: string, context: Record): string { + const template = Handlebars.compile(templateContent); + return template(context); +} + +/** + * Parameters for unit test case addition + * Matches the Python backend UnitTestCaseRequest format + */ +export interface GenerateUnitTestCaseParams { + /** Project context - array of file contents for artifacts being tested */ + context: string[]; + /** Name of the test file being updated */ + testFileName: string; + /** Existing test suite file content */ + testSuiteFile: string; + /** Description of the new test case to add */ + testCaseDescription: string; + /** Existing mock service file contents (optional) */ + existingMockServices?: string[]; + /** Names of existing mock service files (optional) */ + existingMockServiceNames?: string[]; + /** Full project context for comprehensive understanding (optional) */ + fullContext?: string[]; + /** POM file content for dependency analysis (optional) */ + pomFile?: string; + /** List of external connectors used in the project (optional) */ + externalConnectors?: string[]; +} + +/** + * Response from unit test case generation + * Returns the complete updated test suite as a string (XML in markdown) + */ +export interface GenerateUnitTestCaseResponse { + /** The complete response from LLM including updated test suite and any new mock services in markdown format */ + response: string; +} + +/** + * Adds a new test case to an existing unit test suite + * Uses AI to add a single test case while preserving all existing content + * + * IMPORTANT: The response contains the COMPLETE updated unit test file in markdown, + * not just the new test case. All existing content is preserved. + * The extension layer handles parsing and file extraction. + */ +export async function generateUnitTestCase( + params: GenerateUnitTestCaseParams +): Promise { + try { + // Render system and user prompts + const systemPrompt = renderTemplate(SYSTEM_CASE_GENERATE, {}); + const userPrompt = renderTemplate(PROMPT_CASE_GENERATE, { + context: params.context, + test_file_name: params.testFileName, + test_suite_file: params.testSuiteFile, + test_case_description: params.testCaseDescription, + existing_mock_services: params.existingMockServices, + existing_mock_service_names: params.existingMockServiceNames, + full_context: params.fullContext, + pom_file: params.pomFile, + external_connectors: params.externalConnectors + }); + + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); + const cacheOptions = await getProviderCacheControl(); + + // Build messages array with cache control on system message + const messages: Array<{ + role: "system" | "user"; + content: string; + providerOptions?: any; + }> = [ + { + role: "system" as const, + content: systemPrompt, + providerOptions: cacheOptions, // Cache system prompt only + }, + { + role: "user" as const, + content: userPrompt, + } + ]; + + // Generate the updated unit test with new test case + const { text } = await generateText({ + model: model, + messages: messages, + maxOutputTokens: 16000, // Updated unit tests can be quite large + temperature: 0.2, // More deterministic for test generation + maxRetries: 0, // Disable retries to prevent retry loops on quota errors (429) + }); + + // Return the full response - extension layer will handle parsing + return { + response: text + }; + } catch (error) { + logError("Error generating unit test case", error); + throw error; + } +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_examples.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_examples.ts new file mode 100644 index 00000000000..7db205aeaf1 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_examples.ts @@ -0,0 +1,773 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const UNIT_TEST_EXAMPLES = ` +# WSO2 Micro Integrator Unit Test Examples and Specifications + +## XSD Schema for Unit Test Structure + +Strictly follow the below XSD when writing the unit tests: + + +\`\`\`xsd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +\`\`\` + + +## Complete Unit Test Examples + +### Example 1: API Test with Registry Resources + +This example demonstrates testing an API that uses registry resources. The test includes registry resources that are referenced by the API during execution. + +\`\`\`xml + + + + src/main/wso2mi/artifacts/apis/testRegAPI.xml + + + + + + testregistry.xml + src/main/wso2mi/resources/testregistry.xml + /_system/governance/mi-resources + application/vnd.wso2.sequence + + + + + + + + + / + GET + http + + + + $body + + Error Match + + + + + + / + GET + http + + + + $statusCode + + Error Match + + + + + + / + GET + http + + + + $trp:Content-Type + + Error Match + + + + + + + +\`\`\` + +### Example 2: API with Registry Resource Endpoint (Correct Analysis) + +**API Code:** +\`\`\`xml + + + + + + http_get_1 + true + + + + + + + + + +\`\`\` + +**Correct Unit Test:** +\`\`\`xml + + + + src/main/wso2mi/artifacts/apis/testRegAPI.xml + + + src/main/wso2mi/artifacts/local-entries/sad.xml + src/main/wso2mi/artifacts/sequences/vfvf.xml + + + + testregistry.xml + src/main/wso2mi/resources/testregistry.xml + /_system/governance/mi-resources + application/vnd.wso2.endpoint + + + + mi-connector-http-0.1.11 + + + + + + / + GET + http + + + + $statusCode + + Expected successful response + + + + + + src/test/resources/mock-services/RegistryEndpointMock.xml + + +\`\`\` + +**Analysis:** +- sad.xml - local entry used in configKey="sad" +- vfvf.xml - sequence called via +- testregistry.xml - registry resource referenced as key="resources:testregistry.xml" +- mi-connector-http-0.1.11 - HTTP connector used in http.get +- Mock service named "RegistryEndpointMock" for the endpoint call +- **NOT** testEpSeq.xml - not referenced anywhere in the code + +**Mock Service File Example:** +\`\`\`xml + + resources:testregistry.xml + 9090 + /v1 + + + /a757849d-80b6-4d00-a1be-1d517ede86fa + GET + + + + + 200 + +
+ + + + + + +\`\`\` + +### WRONG: Hallucinated Mock Response Example +\`\`\`xml + + + resources:userapi.xml + 9090 + /fantasy + + + /dragons + GET + + + 200 + +
+ + + + + + +\`\`\` + +### CORRECT: Realistic Mock Response Example +\`\`\`xml + + + resources:userapi.xml + 9090 + /api/v1 + + + /users + GET + + + 200 + +
+ + + + + + +\`\`\` + +### Example 3: Sequence Test with Supporting Artifacts + +This example demonstrates testing a sequence that depends on external endpoints. The endpoint is included as a supporting artifact. + +\`\`\`xml + + + + src/main/wso2mi/artifacts/sequences/testSequence.xml + + + src/main/wso2mi/artifacts/endpoints/testEpSeq.xml + + + + + + + + + + GET + http + + + + \\\${payload.hello} + + Error Match + + + + + + GET + http + + + + \\\${vars.test} + + Error Match + + + + + + GET + http + + + + $body + + Error Match + + + + + + GET + http + + + + \\\${payload} + Error + + + + + + + +\`\`\` + +### Example 3: Mixed Internal and External Connector Usage + +This example shows how to handle both internal WSO2 connectors (from POM) and external/custom connectors together. + +\`\`\`xml + + + + src/main/wso2mi/artifacts/apis/mixedConnectorAPI.xml + + + + + + + mi-connector-http-0.1.11 + mi-connector-salesforce-2.3.4 + src/main/wso2mi/resources/connectors/ai-connector-0.1.8.zip + src/main/wso2mi/resources/connectors/custom-db-connector-1.0.0.zip + + + + + + /mixed-test + POST + http + + + + + $statusCode + + Expected successful response with mixed connectors + + + + + + + +\`\`\` + +## Connector Resource Guidelines + +**STRICT USAGE RULE**: Only include connector resources that are actually used/referenced in the artifact being tested. Do not include connectors just because they exist in the project. + +### Internal WSO2 Connectors (from POM dependencies) +Only include if the artifact being tested actually uses these connectors. +\`\`\`xml + + + org.wso2.integration.connector + mi-connector-salesforce + 2.3.4 + zip + + + +mi-connector-salesforce-2.3.4 +\`\`\` + +### External/Custom Connectors (from resources folder) +Only include if the artifact being tested actually uses these custom connectors. +\`\`\`xml + +src/main/wso2mi/resources/connectors/ai-connector-0.1.8.zip +src/main/wso2mi/resources/connectors/custom-db-connector-1.0.0.zip +\`\`\` + +## Key Assertion Types and Usage + +### assertEquals +Used for comparing actual values with expected values. +\`\`\`xml + + $body + + Response body mismatch + +\`\`\` + +### assertNotNull +Used to verify that a value is not null. +\`\`\`xml + + \\\${payload} + Payload should not be null + +\`\`\` + +### Assertion Rules by Artifact Type + +#### API Testing - Allowed Assertions Only +For API artifacts, **ONLY** these assertions are permitted: +- $body - Full response body +- $statusCode - HTTP status code (200, 404, 500, etc.) +- $trp:HeaderName - Transport headers (e.g., $trp:Content-Type, $trp:Authorization) +- $httpVersion - HTTP version (HTTP/1.1, HTTP/2.0) + +**NOT ALLOWED for APIs:** Custom synapse expressions like \\\${payload}, \\\${vars.abc}, \\\${ctx:PropertyName} + +#### Sequence Testing - Full Synapse Expression Support +For Sequence artifacts, these assertions are supported: +- $body - Full response body +- $trp:HeaderName - Transport headers (e.g., $trp:Content-Type) +- \\\${payload} - Full payload access +- \\\${payload.fieldName} - JSON payload field access +- \\\${vars.variableName} - Synapse variable access +- \\\${ctx:PropertyName} - Context property access +- Any other valid synapse expressions + +## Best Practice Patterns + +### 1. Multiple Test Cases for Different Aspects +Always test different aspects of the response: +- Status code validation +- Response body validation +- Header validation +- Null checks where appropriate + +### 2. Proper CDATA Usage +Always wrap expected values in CDATA sections to handle special characters: +\`\`\`xml + +\`\`\` + +### 3. Meaningful Error Messages +Provide descriptive error messages for failed assertions: +\`\`\`xml +Expected status code 200 but got different value +\`\`\` + +## Mock Service Structure + +When unit tests require mock services to simulate external dependencies, each mock service must follow this exact XML structure: + +### Mock Service XML Format +\`\`\`xml + + ServiceName + 9090 + /service-context + + + /endpoint-path + GET|POST|PUT|DELETE + + + +
+
+ + + + + + + + 200 + + +
+
+ + + + + + + + + /another-endpoint + POST + + + + +\`\`\` + +### Mock Service Elements Explanation +- : **CRITICAL** - This must be the exact endpoint key reference from the API code: + - For → testEpSeq + - For → resources:testregistry.xml + - **NOT** a made-up name or the mock service file name +- : Port number where the mock service will be accessible (e.g., 9090, 9091, etc.) +- : The context path for the mock service endpoint +- : Container for mock service resources (can contain multiple elements) +- : Individual resource/endpoint configuration +- : Sub-path within the main context (e.g., "/", "/getStudent", "/addStudent") +- : HTTP method (GET, POST, PUT, DELETE) +- : Request configuration including headers and optional payload + - : Request headers (can be empty or contain
elements with name/value attributes) + - : Optional request payload wrapped in CDATA section (for POST/PUT methods) +- : Response configuration including status code, headers, and payload + - : HTTP response status code (e.g., 200, 404, 500) + - : Response headers (can be empty or contain
elements with name/value attributes) + - : Response body wrapped in CDATA section + +### Mock Service Best Practices +- **ONLY** create mock services for external endpoint calls or database calls - never mock internal artifacts +- **DO NOT HALLUCINATE**: Mock responses should reflect the actual expected behavior of the real endpoint being mocked +- **USE REALISTIC DATA**: Base mock responses on what the actual endpoint would realistically return, not fantasy data +- Use different ports for multiple mock services (9090, 9091, 9092, etc.) +- Define multiple elements for different endpoints within the same service +- Include appropriate request headers for content type validation +- Use meaningful response headers when testing header-based logic +- Wrap JSON payloads in CDATA sections to avoid XML parsing issues +- **CRITICAL**: The must be the exact endpoint key reference from the API code being mocked +- **MANDATORY**: Mock service file names MUST be provided in the mock_service_names parameter when mock services are used +- **TWO SEPARATE THINGS**: + - mock_services array = actual mock service XML content + - mock_service_names array = descriptive file names for those mock services +- **RULE**: + - Unit test = full path to mock service file (src/test/resources/mock-services/MockServiceName.xml) + - Mock service XML = exact endpoint key reference + - Mock service XML = always 9090 + - mock_service_names = descriptive file names (with .xml extension) + +### Mock Service Examples + +#### Simple Mock Service Example (Direct Endpoint) +For API code: +\`\`\`xml + + UserEndpoint + 9090 + /users + + + / + GET + + + + + + 200 + + + + + + + + + +\`\`\` + +#### Registry Resource Mock Service Example +For API code: +\`\`\`xml + + resources:studentapi.xml + 9090 + / + + + /getStudent + GET + + +
+ + + + 200 + +
+ + + + + + + + /addStudent + POST + + +
+ + + + + + + 200 + +
+ + + + + + + + +\`\`\` +`; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_generate.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_generate.ts new file mode 100644 index 00000000000..9067403f574 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_generate.ts @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +import { generateText } from "ai"; +import * as Handlebars from "handlebars"; +import { getAnthropicClient, ANTHROPIC_HAIKU_4_5 } from "../connection"; +import { SYSTEM_SUITE_GENERATE } from "./system_suite_generate"; +import { PROMPT_SUITE_GENERATE } from "./prompt_suite_generate"; +import { UNIT_TEST_GUIDE } from "./unit_test_guide"; +import { UNIT_TEST_EXAMPLES } from "./unit_test_examples"; +import { logError } from "../logger"; + +// Register Handlebars partials for unit test generation +Handlebars.registerPartial("unit_test_guide", UNIT_TEST_GUIDE); +Handlebars.registerPartial("unit_test_examples", UNIT_TEST_EXAMPLES); + +/** + * Render a template using Handlebars + */ +function renderTemplate(templateContent: string, context: Record): string { + const template = Handlebars.compile(templateContent); + return template(context); +} + +/** + * Parameters for unit test suite generation + * Matches the Python backend UnitTestRequest format + */ +export interface GenerateUnitTestParams { + /** Project context - array of file contents for artifacts to test */ + context: string[]; + /** Name of the test file to generate */ + testFileName: string; + /** Full project context for comprehensive understanding (optional) */ + fullContext?: string[]; + /** POM file content for dependency analysis (optional) */ + pomFile?: string; + /** List of external connectors used in the project (optional) */ + externalConnectors?: string[]; +} + +/** + * Response from unit test generation + * Returns the generated test suite content as a string (XML in markdown) + */ +export interface GenerateUnitTestResponse { + /** The complete response from LLM including unit test and mock services in markdown format */ + response: string; +} + +/** + * Generates a complete unit test suite for WSO2 Synapse artifacts + * Uses AI to create comprehensive test cases with mock services + * + * Note: The response contains the full markdown output from the LLM, + * which includes the unit test XML and any mock service files. + * The extension layer handles parsing and file extraction. + */ +export async function generateUnitTest( + params: GenerateUnitTestParams +): Promise { + try { + // Render system and user prompts + const systemPrompt = renderTemplate(SYSTEM_SUITE_GENERATE, {}); + const userPrompt = renderTemplate(PROMPT_SUITE_GENERATE, { + context: params.context, + test_file_name: params.testFileName, + full_context: params.fullContext, + pom_file: params.pomFile, + external_connectors: params.externalConnectors + }); + + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); + + // Generate the unit test + const { text } = await generateText({ + model: model, + system: systemPrompt, + prompt: userPrompt, + maxOutputTokens: 16000, // Unit tests can be quite large + temperature: 0.2, // More deterministic for test generation + maxRetries: 0, // Disable retries to prevent retry loops on quota errors (429) + }); + + // Return the full response - extension layer will handle parsing + return { + response: text + }; + } catch (error) { + logError("Error generating unit test", error); + throw error; + } +} diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_guide.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_guide.ts new file mode 100644 index 00000000000..1a08376aef3 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_guide.ts @@ -0,0 +1,515 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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. + */ + +export const UNIT_TEST_GUIDE = ` + +## Unit Test Development Guidelines for WSO2 Micro Integrator + +### Core Principles +- Generate test for ONLY the given artifacts +- Strictly follow the response type when doing assertions +- Use placeholder values if required +- Always respond in markdown and provide the unit tests in xml +- Do not include the xmlns attribute +- **CRITICAL**: Only generate mock service XML files for services explicitly listed in the \`\` section of your unit test - NO EXTRA mock services allowed + +### Artifact Path Guidelines +- **For APIs**: Add the suffix \`src/main/wso2mi/artifacts/apis\` to the \`\`. + Example: \`src/main/wso2mi/artifacts/apis/apiName.xml\` +- **For Sequences**: Add the suffix \`src/main/wso2mi/artifacts/sequences\` to the \`\`. + Example: \`src/main/wso2mi/artifacts/sequences/sequenceName.xml\` + +### API Testing Best Practices +- Do not include the context path defined in the API inside the request-path in the test case +- The context path is already defined in the API +- If only the context path is there in the request path, use '/' as the path + +### Supporting Artifacts +Supporting artifacts are dependencies required by the main artifact being tested. These include endpoints, sequences, local entries, or other synapse artifacts that the test artifact references. + +**STRICT Usage Guidelines - ANALYZE CODE CAREFULLY:** +- **ONLY** include artifacts that are directly referenced, called, or used in the artifact for which the unit test is generated +- **CAREFULLY ANALYZE** the artifact code to identify actual dependencies: + - Look for \`\` - include the endpoint + - Look for \`\` - include the sequence + - Look for \`configKey="localEntryName"\` in connectors - include the local entry + - Look for \`key="resources:filename.xml"\` - include as registry resource, NOT as supportive artifact +- This includes transitive dependencies: if artifact 'A' is referenced in your artifact and 'A' references artifact 'B', then both 'A' and 'B' should be included as supportive artifacts +- **DO NOT** include artifacts that have no relevance to the test artifact just because they exist in the integration project +- **DO NOT** include artifacts just because they have similar names or are in the same project +- Use the correct artifact path based on the type (endpoints, sequences, local-entries, etc.) +- Keep supporting artifacts minimal - only include what's necessary for the test to function + +**Syntax:** +\`\`\`xml + + src/main/wso2mi/artifacts/endpoints/endpointName.xml + src/main/wso2mi/artifacts/sequences/sequenceName.xml + src/main/wso2mi/artifacts/local-entries/localEntryName.xml + +\`\`\` + +**Example Analysis - API with Registry Resource:** +\`\`\`xml + + + + + + response + + + + + + + + +\`\`\` + +**Correct Analysis:** +- **Supportive Artifacts**: + - \`src/main/wso2mi/artifacts/local-entries/httpConfig.xml\` (used in http.get configKey) + - \`src/main/wso2mi/artifacts/sequences/processSequence.xml\` (called via sequence key) +- **Registry Resources**: + - \`myEndpoint.xml\` (referenced as \`resources:myEndpoint.xml\`) +- **Mock Services**: + - Mock the actual endpoint name defined in \`myEndpoint.xml\` (e.g., if endpoint is named "ExternalAPI") +- **NOT Included**: Any other endpoints, sequences, or artifacts not directly referenced + +### Registry Resources +Registry resources are external files (sequences, templates, endpoints, configurations) stored in the WSO2 registry that can be referenced by synapse artifacts during runtime. + +**STRICT Usage Guidelines:** +- Include registry resources that are referenced in the test artifact using \`key="resources:filename.xml"\` pattern +- **IMPORTANT**: When an endpoint is referenced as \`\`, include it as a registry resource, NOT as a supportive artifact +- Specify the correct registry path where the resource would be stored +- Use appropriate media types based on the resource type +- **If a registry resource contains an endpoint definition that makes external calls, that endpoint should be mocked** + +**Syntax:** +\`\`\`xml + + + resourceFileName.xml + src/main/wso2mi/resources/resourceFileName.xml + /_system/governance/mi-resources + application/vnd.wso2.sequence + + +\`\`\` + +**Common Media Types:** +- \`application/vnd.wso2.sequence\` - For sequence resources +- \`application/vnd.wso2.endpoint\` - For endpoint resources +- \`application/vnd.wso2.template\` - For template resources +- \`application/xml\` - For generic XML resources +- \`text/plain\` - For text-based configurations + +### Connector Resources +Connector resources specify connectors that are used within the artifacts being tested. There are two types of connectors that require different handling in unit tests. + +#### Internal WSO2 Connectors +Internal connectors are official WSO2 connectors downloaded from the WSO2 Connector Store and declared in the project's \`pom.xml\` file. + +**Identification:** +- Listed as dependencies in \`pom.xml\` with groupId \`org.wso2.integration.connector\` +- Example pom dependency: +\`\`\`xml + + org.wso2.integration.connector + mi-connector-http + 0.1.11 + zip + +\`\`\` + +**Test Syntax for Internal Connectors:** +\`\`\`xml + + mi-connector-http-0.1.11 + +\`\`\` +**Format:** \`{artifactId}-{version}\` + +#### External/Custom Connectors +External connectors are custom or third-party connectors manually downloaded and placed in the project's resources folder. + +**Identification:** +- NOT listed in \`pom.xml\` dependencies +- Physically present in \`src/main/wso2mi/resources/connectors/\` directory +- Provided in the external_connectors list in the request + +**Test Syntax for External Connectors:** +\`\`\`xml + + src/main/wso2mi/resources/connectors/ai-connector-0.1.8.zip + +\`\`\` +**Format:** Full path to the connector zip file - **MUST include .zip extension** + +#### STRICT Usage Guidelines: +- **From POM file**: Extract internal connectors and use \`{artifactId}-{version}\` format +- **From external_connectors list**: Use full path format \`src/main/wso2mi/resources/connectors/{connectorName}.zip\` (always include .zip extension) +- **CRITICAL**: External connectors must ALWAYS have .zip extension in the connector-resource path +- **ONLY** include connectors that are actually used or referenced in the artifact for which the unit test is generated +- **DO NOT** include unused connectors just because they are available in the project +- Both types can be present in the same test file if needed, but only if they are actually used + +### Assertion Guidelines by Artifact Type + +#### API Testing Assertions +For API artifacts, only the following assertions are allowed: + +**Allowed Assertions:** +1. **Payload** - using \`$body\` + \`\`\`xml + + $body + + Response body validation + + \`\`\` + +2. **Status Code** - using \`$statusCode\` + \`\`\`xml + + $statusCode + + Status code validation + + \`\`\` + +3. **Transport Header** - using \`$trp:\` + \`\`\`xml + + $trp:Content-Type + + Content-Type header validation + + \`\`\` + +4. **HTTP Version** - using \`$httpVersion\` + \`\`\`xml + + $httpVersion + + HTTP version validation + + \`\`\` + +**Important:** No other custom assertions or synapse expressions are allowed for API testing. Stick strictly to these four types. + +#### Sequence Testing Assertions +For Sequence artifacts, the following assertions are supported: + +**Allowed Assertions:** +1. **Payload** - using \`$body\` + \`\`\`xml + + $body + + Sequence output validation + + \`\`\` + +2. **Transport Headers** - using \`$trp:\` + \`\`\`xml + + $trp:Content-Type + + Transport header validation + + \`\`\` + +3. **Custom Synapse Expressions** - Full synapse expression support + \`\`\`xml + + \\\${payload} + + Payload expression validation + + + + \\\${payload.abc} + + Payload field validation + + + + \\\${vars.abc} + + Variable validation + + \`\`\` + +### XML and CDATA Handling +- Ensure that all \`\` values in assertions are enclosed within \`\` sections to avoid XML parsing issues +- Do not enclose the actual values or $body with CDATA +- Add proper error messages when necessary + +### Mock Services +**STRICT Mock Service Guidelines:** +- **ONLY** create mock services for external endpoint calls or external service interactions (e.g: external APIs, databases, web services) +- **DO NOT** write mock services for everything in the project +- **DO NOT** mock internal sequences, local entries, or other internal artifacts unless they make external calls +- **DO NOT HALLUCINATE**: Mock services should reflect the actual expected behavior of the real endpoint being mocked, not create fictional responses +- **USE REALISTIC DATA**: Base mock responses on what the actual endpoint would return, using placeholder data that matches the expected structure +- Mock services are **ONLY** needed when the artifact being tested makes calls to external services through: + - Direct endpoint calls: \`\` + - Registry-based endpoint calls: \`\` + - HTTP connector operations that call external services + - Database calls or other external service interactions +- **CRITICAL SERVICE NAME RULES**: The \`service-name\` in mock service XML must exactly match the endpoint reference in the code: + - For direct endpoint calls \`\` → use \`endpointName\` + - For registry resource calls \`\` → use \`resources:filename.xml\` + - **DO NOT** create fictional service names - use the exact key reference from the code +- **ALWAYS PROVIDE MOCK SERVICE FILES**: If you declare mock services in the \`\` section, you MUST provide the corresponding mock service XML files +- **REALISTIC MOCKING**: Mock responses should simulate the actual behavior and response structure of the real endpoint, not create fantasy responses + +**CRITICAL RULE - Mock Service Consistency:** +- You must ONLY generate mock service XML files for the services that are explicitly listed in the \`\` section of your unit test +- The number of mock service XML files generated must EXACTLY match the number of \`\` entries in the unit test +- Every \`ServiceName\` entry in the unit test must have a corresponding mock service XML +- **IMPORTANT**: Mock service files do not contain a name attribute in the syntax. The \`service-name\` is the name of the endpoint or external service being mocked (something that already exists in the project), **NOT** the name of the mock service itself +- Do NOT generate extra mock services that are not referenced in the \`\` section +- Do NOT generate mock services for supporting artifacts unless they make external calls and are explicitly listed in \`\` + +**Example of Correct Consistency:** +**Example for API with registry resource call:** +\`\`\`xml + + + +\`\`\` + +Unit test should have: +\`\`\`xml + + src/test/resources/mock-services/RegistryEndpointMock.xml + +\`\`\` + +Mock service file should have: +\`\`\`xml + + resources:testregistry.xml + 9090 + + +\`\`\` + +**Example for direct endpoint call:** +\`\`\`xml + + + +\`\`\` + +Unit test should have: +\`\`\`xml + + src/test/resources/mock-services/TestEndpointMock.xml + +\`\`\` + +Mock service file should have: +\`\`\`xml + + testEpSeq + 9090 + + +\`\`\` + +**CRITICAL MOCK SERVICE REFERENCE RULES:** +- **ALWAYS use full path format** in the unit test \`\` section +- **MANDATORY FORMAT**: \`src/test/resources/mock-services/MockServiceName.xml\` +- **DO NOT use just the service name** - the full path is required +- **WRONG**: \`ExternalEndpointMock\` +- **CORRECT**: \`src/test/resources/mock-services/ExternalEndpointMock.xml\` + +Never generate a third mock service file unless it's also listed in the \`\` section. + +**COMPLETE EXAMPLE:** + +For API code: \`\` + +**1. Unit Test XML:** +\`\`\`xml + + src/test/resources/mock-services/RegistryEndpointMock.xml + +\`\`\` + +**2. Response should include:** +\`\`\`json +{ + "mock_services": ["resources:testregistry.xml9090..."], + "mock_service_names": ["RegistryEndpointMock.xml"] +} +\`\`\` + +**3. Mock Service XML content (in mock_services array):** +\`\`\`xml + + resources:testregistry.xml + 9090 + /v1 + + + /a757849d-80b6-4d00-a1be-1d517ede86fa + GET + + + 200 + +
+ + + + + + +\`\`\` + +**CRITICAL MOCK SERVICE CONFIGURATION RULES:** +When generating mock services, you MUST analyze the actual endpoint configuration to determine the correct mock service parameters: + +**For Registry Resource Endpoints (\`\`)**: +1. **READ THE ENDPOINT FILE CONTENT**: Analyze the actual endpoint configuration in the registry resource file +2. **EXTRACT THE REAL ENDPOINT DETAILS**: Parse the HTTP endpoint configuration to get: + - **Method**: Extract from \`method="get"\` attribute + - **URI Template**: Extract the actual external URL from \`uri-template\` attribute + - **Host/Domain**: Parse the domain from the URI (e.g., \`mocki.io\` from \`https://mocki.io/v1/...\`) + - **Port**: ALWAYS use \`9090\` for all mock services + - **Context Path**: Extract the path portion (e.g., \`/v1\` from \`https://mocki.io/v1/a757849d-80b6-4d00-a1be-1d517ede86fa\`) + - **Sub-context**: Extract the remaining path (e.g., \`/a757849d-80b6-4d00-a1be-1d517ede86fa\`) + +**Example Analysis:** +For endpoint configuration: +\`\`\`xml + + + + + +\`\`\` + +**CORRECT Mock Service Configuration:** +\`\`\`xml + + resources:testregistry.xml + 9090 + /v1 + + + /a757849d-80b6-4d00-a1be-1d517ede86fa + GET + + + 200 + +
+ + + + + + +\`\`\` + +**For Direct Endpoint References (\`\`)**: +1. **FIND THE ENDPOINT DEFINITION**: Look for the endpoint definition in the supporting artifacts +2. **PARSE THE ENDPOINT CONFIGURATION**: Extract the same details (method, URI, host, context, sub-context) +3. **CONFIGURE MOCK SERVICE**: Use the extracted details to properly configure the mock service with port 9090 + +**MANDATORY CONFIGURATION STEPS:** +1. **ALWAYS analyze the endpoint configuration** before creating mock services +2. **MANDATORY: ALWAYS use port 9090** for all mock services - NO EXCEPTIONS +3. **FORBIDDEN**: Do NOT use ports like 443, 80, 8080 - ONLY port 9090 is allowed +4. **EXTRACT real HTTP method** from the endpoint configuration (GET, POST, PUT, DELETE) +5. **PARSE the external URL** to determine correct context and sub-context structure +6. **USE REALISTIC RESPONSES** based on what the actual external service would return + +**PORT RULE:** +- **MANDATORY: ALL mock services must use port 9090** - NO EXCEPTIONS +- **FORBIDDEN**: Do NOT analyze or extract ports from original endpoints +- **FORBIDDEN**: Do NOT use ports like 443, 80, 8080, or any other ports + +**CONTEXT AND SUB-CONTEXT EXTRACTION:** +- For URL \`https://mocki.io/v1/a757849d-80b6/data\` → context=\`/v1\`, sub-context=\`/a757849d-80b6/data\` +- For URL \`https://api.example.com/users/123\` → context=\`/users\`, sub-context=\`/123\` +- For URL \`https://service.com/api/v2/resource\` → context=\`/api/v2\`, sub-context=\`/resource\` + +**Mock Service Names - CRITICAL CLARIFICATION:** +- **Mock service FILE names** should be provided in the \`mock_service_names\` parameter (array of strings) in the response +- **\`\` tag** inside the mock service XML should be the exact endpoint reference from the API code +- **Mock service references in unit test** should use full path format \`src/test/resources/mock-services/MockServiceName.xml\` +- **MANDATORY**: When you declare mock services in \`\`, you MUST provide both: + 1. The actual mock service XML files in the \`mock_services\` array + 2. The mock service file names in the \`mock_service_names\` array +- **RULE**: + - Unit test \`\` = full path to mock service file (\`src/test/resources/mock-services/MockServiceName.xml\`) + - Mock service XML \`\` = exact endpoint key reference from API code + - Mock service XML \`\` = always \`9090\` + - \`mock_service_names\` = descriptive file names for the mock service files (with .xml extension) + +### Connector Analysis and Integration +- **POM Analysis**: Parse the POM file to identify internal WSO2 connectors from dependencies +- **External Connector Integration**: Use the provided external_connectors list to identify custom connectors +- **Usage Detection**: Only include connectors that are actually referenced in the test artifacts +- **Proper Formatting**: Use correct syntax format based on connector type (internal vs external) +- **Mock Considerations**: Consider mocking connector responses for isolated unit testing when necessary + +### Project Context Priority +- When both full project context and current file context are available, prioritize the full project context +- Use the full project context to understand dependencies and relationships between artifacts +- The current file context serves as a fallback for backward compatibility + +### Test Structure Organization +The complete test structure follows this hierarchy: +\`\`\`xml + + + + + + + + + + + + + + + + + + + + + + +\`\`\` + +## Technical Specifications and Examples + +{{> unit_test_examples}} +`; diff --git a/workspaces/mi/mi-extension/src/constants/index.ts b/workspaces/mi/mi-extension/src/constants/index.ts index 50d301bed94..9be0c37ae38 100644 --- a/workspaces/mi/mi-extension/src/constants/index.ts +++ b/workspaces/mi/mi-extension/src/constants/index.ts @@ -189,7 +189,6 @@ export enum MessageStoreTypes { export * from "./swagger"; export const APIS = { - MI_COPILOT_BACKEND: process.env.MI_COPILOT_BACKEND as string, MI_CONNECTOR_STORE: process.env.MI_CONNECTOR_STORE as string, MI_CONNECTOR_STORE_BACKEND: process.env.MI_CONNECTOR_STORE_BACKEND as string, MI_CONNECTOR_STORE_BACKEND_SEARCH: process.env.MI_CONNECTOR_STORE_BACKEND_SEARCH as string, diff --git a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/event-handler.ts b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/event-handler.ts index 92da8c32bc3..29c5e1681c9 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/event-handler.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/event-handler.ts @@ -20,6 +20,7 @@ import { CodeGenerationEvent, XmlCodeEntry, CorrectedCodeItem } from "@wso2/mi-c import { RPCLayer } from "../../RPCLayer"; import { AiPanelWebview } from '../../ai-panel/webview'; import { codeGenerationEvent } from "@wso2/mi-core"; +import { logWarn, logError } from "../../ai-panel/copilot/logger"; export class CopilotEventHandler { @@ -39,8 +40,8 @@ export class CopilotEventHandler { this.sendEventToVisualizer({ type: "content_block", content }); } - handleEnd(content: string): void { - this.sendEventToVisualizer({ type: "code_generation_end", content }); + handleEnd(content: string, willRunDiagnostics: boolean = false): void { + this.sendEventToVisualizer({ type: "code_generation_end", content, willRunDiagnostics }); } handleMessages(messages: any[]): void { @@ -55,6 +56,10 @@ export class CopilotEventHandler { this.sendEventToVisualizer({ type: "stop", command }); } + handleAborted(): void { + this.sendEventToVisualizer({ type: "aborted" }); + } + handleCodeDiagnosticStart(xmlCodes: XmlCodeEntry[]): void { this.sendEventToVisualizer({ type: "code_diagnostic_start", @@ -74,15 +79,15 @@ export class CopilotEventHandler { const messenger = (RPCLayer as any)._messengers.get(this.projectUri); if (messenger) { messenger.sendNotification( - codeGenerationEvent, - { type: 'webview', webviewType: AiPanelWebview.viewType }, + codeGenerationEvent, + { type: 'webview', webviewType: AiPanelWebview.viewType }, event ); } else { - console.warn("No messenger found for project:", this.projectUri); + logWarn(`No messenger found for project: ${this.projectUri}`); } } catch (error) { - console.error("Error sending event to visualizer:", error); + logError("Error sending event to visualizer", error); } } } diff --git a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-handler.ts b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-handler.ts index f83c53749c7..93d5a9d34be 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-handler.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-handler.ts @@ -19,30 +19,58 @@ import { MessengerAPI } from "vscode-messenger-common"; import { MIAIPanelRpcManager } from "./rpc-manager"; import { - getBackendRootUrl, generateSuggestions, generateCode, abortCodeGeneration, GenerateSuggestionsRequest, GenerateCodeRequest, - setAnthropicApiKey, - hasAnthropicApiKey + hasAnthropicApiKey, + fetchUsage, + generateUnitTest, + generateUnitTestCase, + GenerateUnitTestRequest, + GenerateUnitTestCaseRequest, + processIdp, + ProcessIdpRequest, + fillIdpSchema, + FillIdpSchemaRequest, + dmcToTs, + DmcToTsRequest, + autoFillForm, + AutoFillFormRequest } from "@wso2/mi-core"; export function registerMIAiPanelRpcHandlers(messenger: MessengerAPI, projectUri: string) { const rpcManager = new MIAIPanelRpcManager(projectUri); - // ================================== - // General Functions - // ================================== - messenger.onRequest(getBackendRootUrl, () => rpcManager.getBackendRootUrl()); - // ================================== // AI Functions // ================================== messenger.onRequest(generateSuggestions, (request: GenerateSuggestionsRequest) => rpcManager.generateSuggestions(request)); messenger.onRequest(generateCode, (request: GenerateCodeRequest) => rpcManager.generateCode(request)); messenger.onRequest(abortCodeGeneration, () => rpcManager.abortCodeGeneration()); - messenger.onRequest(setAnthropicApiKey, () => rpcManager.setAnthropicApiKey()); messenger.onRequest(hasAnthropicApiKey, () => rpcManager.hasAnthropicApiKey()); + messenger.onRequest(fetchUsage, () => rpcManager.fetchUsage()); + + // ================================== + // Unit Test Generation + // ================================== + messenger.onRequest(generateUnitTest, (request: GenerateUnitTestRequest) => rpcManager.generateUnitTest(request)); + messenger.onRequest(generateUnitTestCase, (request: GenerateUnitTestCaseRequest) => rpcManager.generateUnitTestCase(request)); + + // ================================== + // IDP (Intelligent Document Processor) + // ================================== + messenger.onRequest(processIdp, (request: ProcessIdpRequest) => rpcManager.processIdp(request)); + messenger.onRequest(fillIdpSchema, (request: FillIdpSchemaRequest) => rpcManager.fillIdpSchema(request)); + + // ================================== + // DMC to TypeScript Conversion + // ================================== + messenger.onRequest(dmcToTs, (request: DmcToTsRequest) => rpcManager.dmcToTs(request)); + + // ================================== + // Auto-Fill Form + // ================================== + messenger.onRequest(autoFillForm, (request: AutoFillFormRequest) => rpcManager.autoFillForm(request)); } diff --git a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts index b76f23ab108..b27a86cab72 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts @@ -24,22 +24,32 @@ import { GenerateCodeResponse, AbortCodeGenerationResponse, MIAIPanelAPI, - CopilotChatEntry + CopilotChatEntry, + ProcessIdpRequest, + ProcessIdpResponse, + FillIdpSchemaRequest, + FillIdpSchemaResponse, + DmcToTsRequest, + DmcToTsResponse, + AutoFillFormRequest, + AutoFillFormResponse } from '@wso2/mi-core'; import {RUNTIME_VERSION_440} from "../../constants"; import {compareVersions, getMIVersionFromPom} from "../../util/onboardingUtils"; import { - generateSuggestions as generateSuggestionsUtil, - fetchBackendUrl, - MI_SUGGESTIVE_QUESTIONS_BACKEND_URL, fetchCodeGenerationsWithRetry, - getDiagnosticsReponseFromLlm, - getBackendUrlAndView, getUserAccessToken, - refreshUserAccessToken + refreshUserAccessToken, + getWorkspaceContext } from "./utils"; import { CopilotEventHandler } from "./event-handler"; import { MiDiagramRpcManager } from "../mi-diagram/rpc-manager"; +import { generateSuggestions as generateSuggestionsFromLLM } from "../../ai-panel/copilot/suggestions/suggestions"; +import { fillIdpSchema } from '../../ai-panel/copilot/idp/fill_schema'; +import { codeDiagnostics } from "../../ai-panel/copilot/diagnostics/diagnostics"; +import { getLoginMethod } from '../../ai-panel/auth'; +import { LoginMethod } from '@wso2/mi-core'; +import { logInfo, logWarn, logError, logDebug } from '../../ai-panel/copilot/logger'; export class MIAIPanelRpcManager implements MIAIPanelAPI { private eventHandler: CopilotEventHandler; @@ -51,88 +61,125 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { this.miDiagramRpcManager = new MiDiagramRpcManager(this.projectUri); } - async getBackendRootUrl(): Promise { - const MI_COPILOT_BACKEND_V2 = process.env.MI_COPILOT_BACKEND_V2 as string; - const MI_COPILOT_BACKEND_V3 = process.env.MI_COPILOT_BACKEND_V3 as string; - const RUNTIME_THRESHOLD_VERSION = RUNTIME_VERSION_440; - const runtimeVersion = await getMIVersionFromPom(this.projectUri); + /** + * Gets a single entry point file (API, sequence, or inbound endpoint) for context + * Priority: APIs → Sequences → Inbound Endpoints + */ + private async getEntryPointContext(): Promise { + const artifactDirPath = require('path').join(this.projectUri, 'src', 'main', 'wso2mi', 'artifacts'); + const fs = require('fs'); + + // Priority order: APIs → Sequences → Inbound Endpoints + const entryPointFolders = ['apis', 'sequences', 'inbound-endpoints']; + + for (const folder of entryPointFolders) { + const folderPath = require('path').join(artifactDirPath, folder); + if (!fs.existsSync(folderPath)) { + continue; + } - const versionThreshold = runtimeVersion ? compareVersions(runtimeVersion, RUNTIME_THRESHOLD_VERSION) : -1; + const files = fs.readdirSync(folderPath).filter((file: string) => file.toLowerCase().endsWith('.xml')); + if (files.length > 0) { + const firstFile = require('path').join(folderPath, files[0]); + const content = fs.readFileSync(firstFile, 'utf-8'); + logDebug(`[generateSuggestions] Using entry point: ${folder}/${files[0]}`); + return [content]; + } + } - return versionThreshold < 0 ? { url: MI_COPILOT_BACKEND_V2 } : { url: MI_COPILOT_BACKEND_V3 }; + logDebug('[generateSuggestions] No entry points found, using empty context'); + return []; + } + + /** + * Gets the currently open file content if it's an XML file + */ + private async getCurrentlyEditingFile(): Promise { + const { getStateMachine } = await import('../../stateMachine'); + const fs = require('fs'); + + const currentFile = getStateMachine(this.projectUri).context().documentUri; + if (currentFile && fs.existsSync(currentFile) && currentFile.toLowerCase().endsWith('.xml')) { + try { + const content = fs.readFileSync(currentFile, 'utf-8'); + logDebug(`[generateSuggestions] Using currently editing file: ${currentFile}`); + return content; + } catch (error) { + logWarn(`[generateSuggestions] Could not read current file: ${currentFile}`); + } + } + return null; } async generateSuggestions(request: GenerateSuggestionsRequest): Promise { try { - const controller = new AbortController(); - - // Use the utility function to generate suggestions - const suggestions = await generateSuggestionsUtil( - this.projectUri, - request.chatHistory, - controller + let context: string[] = []; + const chatHistory = request.chatHistory || []; + const lastMessage = chatHistory.length > 0 ? chatHistory[chatHistory.length - 1] : null; + + // Decision tree for context selection: + if (chatHistory.length === 0) { + // Case 1: Empty chat - Use currently editing file OR entry point + logDebug('[generateSuggestions] Empty chat history'); + const currentFile = await this.getCurrentlyEditingFile(); + if (currentFile) { + context = [currentFile]; + } else { + context = await this.getEntryPointContext(); + } + } else if (lastMessage?.role === 'assistant') { + // Case 2: Last message from AI (user hasn't interrupted) - Use chat history only + logDebug('[generateSuggestions] Following AI response, using chat history only'); + context = []; // Chat history contains enough context + } else { + // Case 3: Last message from user (user interrupted/new topic) - Use currently editing file OR entry point + logDebug('[generateSuggestions] User interrupted or new topic'); + const currentFile = await this.getCurrentlyEditingFile(); + if (currentFile) { + context = [currentFile]; + } else { + context = await this.getEntryPointContext(); + } + } + + logDebug(`[generateSuggestions] Context size: ${context.length} files, Chat history: ${chatHistory.length} messages`); + + // Use the new LLM-based suggestion generation + const suggestionText = await generateSuggestionsFromLLM( + context, + chatHistory ); - // Convert the suggestions to the expected format return { - response: suggestions.length > 0 ? suggestions[0].content : "", - files: [], // This would need to be populated based on your specific requirements - images: [] // This would need to be populated based on your specific requirements + response: suggestionText, + files: [], + images: [] }; } catch (error) { - console.error('Error generating suggestions:', error); + logError('Error generating suggestions', error); throw new Error(`Failed to generate suggestions: ${error instanceof Error ? error.message : 'Unknown error'}`); } } - // Additional utility methods that can be used by the AI panel - /** - * Fetches code generations from the backend - */ - async fetchCodeGenerations( - chatHistory: CopilotChatEntry[], - files: any[] = [], - images: any[] = [], - selective: boolean = false, - thinking?: boolean - ): Promise { - try { - const controller = new AbortController(); - const { backendUrl } = await getBackendUrlAndView(this.projectUri); - const backendRootUri = await fetchBackendUrl(this.projectUri); - const url = backendRootUri + backendUrl; - - return await fetchCodeGenerationsWithRetry( - url, - chatHistory, - files, - images, - this.projectUri, - controller, - selective, - thinking - ); - } catch (error) { - console.error('Error fetching code generations:', error); - throw new Error(`Failed to fetch code generations: ${error instanceof Error ? error.message : 'Unknown error'}`); - } - } - - /** - * Sends diagnostics to LLM and gets response + * Sends diagnostics to LLM and gets response (migrated to local LLM) */ async analyzeDiagnostics(diagnostics: any, xmlCodes: any): Promise { try { - const controller = new AbortController(); - return await getDiagnosticsReponseFromLlm( - diagnostics, - xmlCodes, - this.projectUri, - controller - ); + // Use the migrated codeDiagnostics function + const result = await codeDiagnostics({ + diagnostics: diagnostics.diagnostics, + xmlCodes: xmlCodes + }); + + // Return a mock Response object to match the expected interface + return new Response(JSON.stringify(result), { + status: 200, + statusText: 'OK', + headers: { 'Content-Type': 'application/json' } + }); } catch (error) { - console.error('Error analyzing diagnostics:', error); + logError('Error analyzing diagnostics', error); throw new Error(`Failed to analyze diagnostics: ${error instanceof Error ? error.message : 'Unknown error'}`); } } @@ -157,7 +204,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { await refreshUserAccessToken(); return true; } catch (error) { - console.error('Error refreshing authentication:', error); + logError('Error refreshing authentication', error); return false; } } @@ -170,7 +217,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { await this.generateCodeCore(request); return { success: true }; } catch (error) { - console.error('Error during code generation:', error); + logError('Error during code generation', error); this.eventHandler.handleError(error instanceof Error ? error.message : "Unknown error occurred during code generation"); throw new Error(`Failed to generate code: ${error instanceof Error ? error.message : 'Unknown error'}`); } @@ -182,15 +229,17 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { async abortCodeGeneration(): Promise { try { if (this.currentController) { - console.log('Aborting code generation...'); + logInfo('Aborting code generation...'); this.currentController.abort(); + // Send explicit abort acknowledgment to UI + this.eventHandler.handleAborted(); this.currentController = null; return { success: true }; } - console.log('No active code generation to abort'); + logDebug('No active code generation to abort'); return { success: false }; } catch (error) { - console.error('Error aborting code generation:', error); + logError('Error aborting code generation', error); return { success: false }; } } @@ -199,21 +248,30 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { * Core code generation logic with streaming */ private async generateCodeCore(request: GenerateCodeRequest): Promise { - + const { validateAttachments } = await import('../../ai-panel/copilot/message-utils'); + const { window } = await import('vscode'); + try { + // Validate attachments before proceeding + const validationWarnings = validateAttachments(request.files, request.images); + if (validationWarnings.length > 0) { + const errorMessage = `Cannot proceed with code generation. Invalid attachments:\n${validationWarnings.map(w => ` • ${w}`).join('\n')}`; + logError(errorMessage); + window.showErrorMessage( + `Invalid attachments detected. Please check:\n${validationWarnings.join('\n')}` + ); + this.eventHandler.handleError(errorMessage); + throw new Error(errorMessage); + } + this.eventHandler.handleStart(); // Create a new abort controller for this request this.currentController = new AbortController(); - // Get backend URL and construct the request URL - const { backendUrl } = await getBackendUrlAndView(this.projectUri, request.view); - const backendRootUri = await fetchBackendUrl(this.projectUri); - const url = backendRootUri + backendUrl; - - // Make the request to backend + // Generate code using local LLM (no backend URL needed) const response = await fetchCodeGenerationsWithRetry( - url, + "", // url parameter is unused in the migrated function request.chatHistory, request.files, request.images, @@ -234,11 +292,10 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { let assistantResponse = ""; try { - let buffer = ""; while (true) { // Check if abort was requested if (this.currentController?.signal.aborted) { - console.log('Code generation aborted by user'); + logInfo('Code generation aborted by user'); reader.cancel(); break; } @@ -246,47 +303,25 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { const { done, value } = await reader.read(); if (done) break; - buffer += decoder.decode(value, { stream: true }); - - const lines = buffer.split("\n"); - buffer = lines.pop() ?? ""; - - for (const line of lines) { - if (!line.trim()) continue; - try { - const obj = JSON.parse(line); - assistantResponse += obj.content; - this.eventHandler.handleContentBlock(line); - } catch (err) { - // If JSON parsing fails, it might be an incomplete object - // Add it back to the buffer to be processed with the next chunk - buffer = line + "\n" + buffer; - } - } + // Decode the text chunk + const textChunk = decoder.decode(value, { stream: true }); + this.eventHandler.handleContentBlock(textChunk); + assistantResponse += textChunk; } - - // Process any remaining data in the buffer after the stream ends - if (buffer.trim()) { - try { - const obj = JSON.parse(buffer); - assistantResponse += obj.content; - this.eventHandler.handleContentBlock(buffer); - } catch (err) { - console.error("JSON parse error for remaining buffer:", err, "buffer:", buffer); - } - } - } finally { - reader.releaseLock(); + } catch (error) { + logError("Error reading code generation stream", error); } - // Send final response - this.eventHandler.handleEnd(assistantResponse); - - // Run code diagnostics on the generated response only for runtime versions > 4.4.0 + // Determine if diagnostics will run before sending the end event const runtimeVersion = await getMIVersionFromPom(this.projectUri); const shouldRunDiagnostics = runtimeVersion ? compareVersions(runtimeVersion, RUNTIME_VERSION_440) > 0 : false; - - if (shouldRunDiagnostics) { + const willRunDiagnostics = shouldRunDiagnostics && !this.currentController?.signal.aborted; + + // Send final response with diagnostics flag + this.eventHandler.handleEnd(assistantResponse, willRunDiagnostics); + + // Run code diagnostics if needed + if (willRunDiagnostics) { await this.handleCodeDiagnostics(assistantResponse); } @@ -294,7 +329,8 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { // Fallback: non-streaming response const text = await response.text(); this.eventHandler.handleContentBlock(text); - this.eventHandler.handleEnd(text); + // Non-streaming responses don't run diagnostics + this.eventHandler.handleEnd(text, false); } this.eventHandler.handleStop("generateCode"); @@ -302,10 +338,10 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { } catch (error) { // Check if error is due to abort if (error instanceof Error && error.name === 'AbortError') { - console.log('Code generation aborted'); + logInfo('Code generation aborted'); this.eventHandler.handleStop("generateCode"); } else { - console.error("Error during code generation:", error); + logError("Error during code generation", error); this.eventHandler.handleError(error instanceof Error ? error.message : "Unknown error occurred"); throw error; } @@ -386,15 +422,11 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { } if (hasAnyDiagnostics) { - // Send diagnostics to LLM for corrections - const llmResponse = await getDiagnosticsReponseFromLlm( - { diagnostics: diagnosticsResults }, - xmlCodes, - this.projectUri, - new AbortController() - ); - - const llmResponseData = await llmResponse.json(); + // Send diagnostics to LLM for corrections (using migrated function) + const llmResponseData = await codeDiagnostics({ + diagnostics: diagnosticsResults, + xmlCodes: xmlCodes + }); // Process corrections if (llmResponseData.fixed_config && Array.isArray(llmResponseData.fixed_config)) { @@ -417,7 +449,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { this.eventHandler.handleCodeDiagnosticEnd(); } } catch (error) { - console.error('Error during code diagnostics:', error); + logError('Error during code diagnostics', error); // End diagnostics on error this.eventHandler.handleCodeDiagnosticEnd(); } @@ -451,38 +483,214 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { } /** - * Sets the Anthropic API key for unlimited usage + * Check if user is using their own Anthropic API key + */ + async hasAnthropicApiKey(): Promise { + const loginMethod = await getLoginMethod(); + return loginMethod === LoginMethod.ANTHROPIC_KEY; + } + + /** + * Fetches usage information from backend and updates state machine + * Only works for MI_INTEL users + * Also checks if usage has reset and transitions back to Authenticated if in UsageExceeded state */ - async setAnthropicApiKey(): Promise { - const vscode = await import('vscode'); - const { extension } = await import('../../MIExtensionContext'); - - const apiKey = await vscode.window.showInputBox({ - prompt: "Enter your Anthropic API Key for Unlimited Usage", - password: true, - placeHolder: "sk-ant-...", - validateInput: (value) => { - if (!value || value.trim() === "") { - return "API key cannot be empty"; + async fetchUsage(): Promise { + const loginMethod = await getLoginMethod(); + + // Only fetch for MI_INTEL users + if (loginMethod !== LoginMethod.MI_INTEL) { + return undefined; + } + + try { + const { fetchWithAuth } = await import('../../ai-panel/copilot/connection'); + const { StateMachineAI } = await import('../../ai-panel/aiMachine'); + const { AI_EVENT_TYPE } = await import('@wso2/mi-core'); + + const backendUrl = process.env.MI_COPILOT_ANTHROPIC_PROXY_URL; + if (!backendUrl) { + logWarn('MI_COPILOT_ANTHROPIC_PROXY_URL is not configured; skipping usage fetch.'); + return undefined; + } + + const USER_CHECK_BACKEND_URL = '/usage'; + const response = await fetchWithAuth(`${backendUrl}${USER_CHECK_BACKEND_URL}`); + if (response.ok) { + const usage = await response.json(); + + // Get current state before updating + const currentState = StateMachineAI.state(); + + // Update usage via state machine event (proper XState pattern) + if (currentState === 'Authenticated' || currentState === 'UsageExceeded') { + StateMachineAI.sendEvent({ type: AI_EVENT_TYPE.UPDATE_USAGE, payload: { usage } }); + } + + // Check if quota is exceeded and transition to UsageExceeded state + if (usage.remaining_tokens <= 0 && currentState === 'Authenticated') { + logInfo('Quota exceeded. Transitioning to UsageExceeded state.'); + StateMachineAI.sendEvent(AI_EVENT_TYPE.USAGE_EXCEEDED); } - if (!value.startsWith("sk-ant-")) { - return "Invalid Anthropic API key format. Should start with 'sk-ant-'"; + + // Check if we're in UsageExceeded state and if usage has reset + if (currentState === 'UsageExceeded' && usage.remaining_tokens > 0) { + logInfo('Usage has reset. Transitioning back to Authenticated state.'); + StateMachineAI.sendEvent(AI_EVENT_TYPE.USAGE_RESET); } - return null; + + return usage; } - }); + } catch (error) { + logError('Failed to fetch usage', error); + } - if (apiKey) { - await extension.context.secrets.store('AnthropicApiKey', apiKey); - vscode.window.showInformationMessage("Anthropic API key has been saved successfully"); + return undefined; + } + + /** + * Generates a complete unit test suite for WSO2 Synapse artifacts + */ + async generateUnitTest(request: import('@wso2/mi-core').GenerateUnitTestRequest): Promise { + try { + const { generateUnitTest } = await import('../../ai-panel/copilot/unit-tests/unit_test_generate'); + + const result = await generateUnitTest({ + context: request.context, + testFileName: request.testFileName, + fullContext: request.fullContext, + pomFile: request.pomFile, + externalConnectors: request.externalConnectors + }); + + return result; + } catch (error) { + logError('Error generating unit test', error); + throw new Error(`Failed to generate unit test: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** - * Gets the stored Anthropic API key + * Adds a new test case to an existing unit test suite */ - async hasAnthropicApiKey(): Promise { - const { extension } = await import('../../MIExtensionContext'); - return await extension.context.secrets.get('AnthropicApiKey') !== undefined; + async generateUnitTestCase(request: import('@wso2/mi-core').GenerateUnitTestCaseRequest): Promise { + try { + const { generateUnitTestCase } = await import('../../ai-panel/copilot/unit-tests/unit_test_case_generate'); + + const result = await generateUnitTestCase({ + context: request.context, + testFileName: request.testFileName, + testSuiteFile: request.testSuiteFile, + testCaseDescription: request.testCaseDescription, + existingMockServices: request.existingMockServices, + existingMockServiceNames: request.existingMockServiceNames, + fullContext: request.fullContext, + pomFile: request.pomFile, + externalConnectors: request.externalConnectors + }); + + return result; + } catch (error) { + logError('Error generating unit test case', error); + throw new Error(`Failed to generate unit test case: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + } + + /** + * Processes IDP (Intelligent Document Processor) request + */ + async processIdp(request: ProcessIdpRequest): Promise { + try { + const { processIdp } = await import('../../ai-panel/copilot/idp/idp'); + + const result = await processIdp({ + operation: request.operation, + userInput: request.userInput, + jsonSchema: request.jsonSchema, + images: request.images + }); + + return result; + } catch (error) { + throw new Error(`Failed to process IDP: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + } + + /** + * Fills an IDP schema with data extracted from images + */ + async fillIdpSchema(request: FillIdpSchemaRequest): Promise { + try { + logDebug('[fillIdpSchema] Starting schema filling'); + logDebug(`[fillIdpSchema] Images count: ${request.images?.length || 0}`); + + const result = await fillIdpSchema({ + jsonSchema: request.jsonSchema, + images: request.images + }); + + logDebug('[fillIdpSchema] Schema filling completed successfully'); + return result; + } catch (error) { + logError('[fillIdpSchema] Error filling schema', error); + throw new Error(`Failed to fill IDP schema: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + } + + /** + * Converts DMC (Data Mapping Configuration) to TypeScript implementation + */ + async dmcToTs(request: DmcToTsRequest): Promise { + try { + logDebug('[dmcToTs] Starting DMC to TypeScript conversion'); + logDebug(`[dmcToTs] DMC content length: ${request.dmcContent?.length || 0}`); + logDebug(`[dmcToTs] TS file length: ${request.tsFile?.length || 0}`); + + const { dmcToTs } = await import('../../ai-panel/copilot/dmc_to_ts/dmc_to_ts'); + + const result = await dmcToTs({ + dmcContent: request.dmcContent, + tsFile: request.tsFile + }); + + logDebug('[dmcToTs] Conversion completed successfully'); + return result; + } catch (error) { + logError('[dmcToTs] Error converting DMC to TS', error); + throw new Error(`Failed to convert DMC to TypeScript: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + } + + /** + * Auto-fills form fields using AI based on context and user input + */ + async autoFillForm(request: AutoFillFormRequest): Promise { + try { + logDebug('[autoFillForm] Starting form auto-fill'); + logDebug(`[autoFillForm] Form fields count: ${Object.keys(request.current_values || {}).length}`); + logDebug(`[autoFillForm] Has user question: ${!!request.question}`); + + const { autoFillForm } = await import('../../ai-panel/copilot/auto-fill/fill'); + + const result = await autoFillForm({ + payloads: request.payloads, + variables: request.variables, + params: request.params, + properties: request.properties, + headers: request.headers, + configs: request.configs, + connection_names: request.connection_names, + form_details: request.form_details, + current_values: request.current_values, + field_descriptions: request.field_descriptions, + question: request.question + }); + + logDebug('[autoFillForm] Form auto-fill completed successfully'); + return result; + } catch (error) { + logError('[autoFillForm] Error auto-filling form', error); + throw new Error(`Failed to auto-fill form: ${error instanceof Error ? error.message : 'Unknown error'}`); + } } } diff --git a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/utils.ts b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/utils.ts index 25386a97e85..25e316ec177 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/utils.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/utils.ts @@ -21,15 +21,13 @@ import { MiVisualizerRpcManager } from "../mi-visualizer/rpc-manager"; import { refreshAuthCode } from "../../ai-panel/auth"; import { openSignInView } from "../../util/ai-datamapper-utils"; import { extension } from "../../MIExtensionContext"; -import { EVENT_TYPE, MACHINE_VIEW } from "@wso2/mi-core"; +import { EVENT_TYPE, MACHINE_VIEW, AI_EVENT_TYPE, Role } from "@wso2/mi-core"; import * as vscode from "vscode"; import { MIAIPanelRpcManager } from "./rpc-manager"; - -// Backend URL constants -export const MI_ARTIFACT_GENERATION_BACKEND_URL = `/chat/artifact-generation`; -export const MI_ARTIFACT_EDIT_BACKEND_URL = `/chat/artifact-editing`; -export const MI_SUGGESTIVE_QUESTIONS_BACKEND_URL = `/suggestions`; -export const MI_DIAGNOSTICS_RESPONSE_BACKEND_URL = `/synapse/bug-fix`; +import { generateSynapse } from "../../ai-panel/copilot/generation/generations"; +import { getConnectors } from "../../ai-panel/copilot/connectors/connectors"; +import { codeDiagnostics } from "../../ai-panel/copilot/diagnostics/diagnostics"; +import { openAIWebview, StateMachineAI } from "../../ai-panel/aiMachine"; // Error messages export const COPILOT_ERROR_MESSAGES = { @@ -68,20 +66,6 @@ export interface CorrectedCodeItem { code: string; } -/** - * Fetches the backend URL for the given project - */ -export async function fetchBackendUrl(projectUri: string): Promise { - try { - const miDiagramRpcManager = new MiDiagramRpcManager(projectUri); - const { url } = await miDiagramRpcManager.getBackendRootUrl(); - return url; - } catch (error) { - console.error('Failed to fetch backend URL:', error); - throw error; - } -} - /** * Gets the user access token from extension secrets */ @@ -150,9 +134,9 @@ function showQuotaExceededNotification(projectUri: string) { "Learn More" ).then(selection => { if (selection === "Set API Key") { - // Trigger the API key input dialog - const miAiPanelRpcManager = new MIAIPanelRpcManager(projectUri); - miAiPanelRpcManager.setAnthropicApiKey(); + // Open AI panel and trigger API key authentication flow + openAIWebview(); + StateMachineAI.sendEvent(AI_EVENT_TYPE.AUTH_WITH_API_KEY); } else if (selection === "Learn More") { vscode.env.openExternal(vscode.Uri.parse("https://console.anthropic.com/")); } @@ -170,114 +154,6 @@ export function openUpdateExtensionView(projectUri: string) { }); } -/** - * Main function to fetch data from backend with retry logic - */ -export async function fetchWithRetry( - type: BackendRequestType, - url: string, - body: any, - projectUri: string, - controller: AbortController, - thinking?: boolean -): Promise { - let retryCount = 0; - const maxRetries = 2; - let token = await getUserAccessToken(); - const anthropicApiKey = await hasAnthropicApiKey(); - - const bodyWithThinking = { - ...body, - thinking: thinking || false - }; - - const headers: Record = { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }; - - // Add Anthropic API key header if available - if (anthropicApiKey) { - headers["X-ANTHROPIC-KEY"] = anthropicApiKey; - } - - let response = await fetch(url, { - method: "POST", - headers: headers, - body: JSON.stringify(bodyWithThinking), - signal: controller.signal, - }); - - // Handle 401 - Unauthorized (token expired) - if (response.status === 401) { - try { - token = await refreshUserAccessToken(); - - // Update headers with new token - headers.Authorization = `Bearer ${token}`; - - response = await fetch(url, { - method: "POST", - headers: headers, - body: JSON.stringify(bodyWithThinking), - signal: controller.signal, - }); - } catch (error) { - console.error('Failed to refresh token:', error); - showSignedOutNotification(projectUri); - throw new Error("Authentication failed"); - } - } - // Handle 429 - Quota Exceeded (must be checked before 404) - else if (response.status === 429) { - // Quota exceeded - show notification to user - showQuotaExceededNotification(projectUri); - let error = "Free usage quota exceeded. Please set your own Anthropic API key to continue."; - try { - const responseBody = await response.json(); - if (responseBody.detail) { - error += ` ${responseBody.detail}`; - } - } catch (e) { - // Ignore JSON parsing error - } - throw new Error(error); - } - // Handle 404 - Not Found (retry with exponential backoff) - else if (response.status === 404) { - if (retryCount < maxRetries) { - retryCount++; - const delay = Math.pow(2, retryCount) * 1000; // Exponential backoff - await new Promise((resolve) => setTimeout(resolve, delay)); - return fetchWithRetry(type, url, body, projectUri, controller, thinking); - } else { - openUpdateExtensionView(projectUri); - throw new Error("Resource not found : Check backend URL"); - } - } - // Handle other error responses - else if (!response.ok) { - const statusText = getStatusText(response.status); - let error = `Failed to fetch response. Status: ${statusText}`; - - if (response.status === 422) { - error = getStatusText(422); - } - - switch (type) { - case BackendRequestType.Suggestions: - openUpdateExtensionView(projectUri); - throw new Error("Failed to fetch initial questions"); - case BackendRequestType.UserPrompt: - throw new Error(`Failed to fetch code generations: ${error}`); - default: - throw new Error(error); - } - } - - return response; -} - /** * Gets workspace context for the project */ @@ -291,69 +167,7 @@ export async function getWorkspaceContext(projectUri: string, selective: boolean } /** - * Gets backend URL and view type based on current view - */ -export async function getBackendUrlAndView(projectUri: string, view?: string): Promise<{ backendUrl: string; view: string }> { - // This would need to be adapted based on how you determine the current view in the extension - // For now, defaulting to artifact editing view - const currentView = view || "Artifact"; - - switch (currentView) { - case "Overview": - case "ADD_ARTIFACT": - return { backendUrl: MI_ARTIFACT_GENERATION_BACKEND_URL, view: "Overview" }; - default: - return { backendUrl: MI_ARTIFACT_EDIT_BACKEND_URL, view: "Artifact" }; - } -} - -/** - * Generates suggestions from the backend - */ -export async function generateSuggestions( - projectUri: string, - chatHistory: any[], - controller: AbortController -): Promise { - try { - const backendRootUri = await fetchBackendUrl(projectUri); - const url = backendRootUri + MI_SUGGESTIVE_QUESTIONS_BACKEND_URL; - const context = await getWorkspaceContext(projectUri); - - const response = await fetchWithRetry( - BackendRequestType.Suggestions, - url, - { - messages: chatHistory, - context: context.context, - num_suggestions: 1, - type: "artifact_gen", - }, - projectUri, - controller - ); - - const data = (await response.json()) as ApiResponse; - - if (data.event === "suggestion_generation_success") { - return data.questions.map((question) => ({ - id: generateId(), - role: "default", - content: question, - type: "Question", - })); - } else { - console.error("Error generating suggestions:", data.error); - throw new Error("Failed to generate suggestions: " + data.error); - } - } catch (error) { - console.error(error); - return []; - } -} - -/** - * Fetches code generations from backend + * Generates code using local LLM (ditching backend) */ export async function fetchCodeGenerationsWithRetry( url: string, @@ -365,21 +179,59 @@ export async function fetchCodeGenerationsWithRetry( selective: boolean = false, thinking?: boolean ): Promise { + // Get workspace context const context = await getWorkspaceContext(projectUri, selective); const miDiagramRpcManager = new MiDiagramRpcManager(projectUri); const defaultPayloads = await miDiagramRpcManager.getAllInputDefaultPayloads(); - - return fetchWithRetry(BackendRequestType.UserPrompt, url, { - messages: chatHistory, + + // Extract the user's question from chat history (last user message) + // Check for Role.CopilotUser enum or fallback to 'user' string for backward compatibility + const lastUserMessage = [...chatHistory].reverse().find(entry => + entry.role === Role.CopilotUser || entry.role === 'user' + ); + const userQuestion = lastUserMessage?.content || ''; + + // Get currently editing file content if available (first file only) + const currentFile = files.length > 0 ? files[0]?.content : undefined; + + // Get relevant connectors and inbound endpoints for the user's query + const { connectors: selectedConnectors, inbound_endpoints: selectedInboundEndpoints } = await getConnectors({ + question: userQuestion, + files: files.length > 0 ? files : undefined, + images: images.length > 0 ? images : undefined, + }); + + // Convert chat history to the format expected by generateSynapse + // Take last 6 messages (3 conversations) as sliding window, excluding the current message + const historyMessages = chatHistory + .slice(-7, -1) // Take last 7 messages and exclude the last one (current question) = 6 messages + .map(entry => ({ + role: entry.role === Role.CopilotUser || entry.role === 'user' + ? 'user' as const + : 'assistant' as const, + content: entry.content + })); + + // Call generateSynapse - it returns a Response with streaming text + // AI SDK handles all the stream conversion and abort logic + return generateSynapse({ + question: userQuestion, + projectUri: projectUri, + file: currentFile, context: context.context, - files: files, - images: images.map((image: any) => image.imageBase64), - payloads: defaultPayloads, - }, projectUri, controller, thinking); + payloads: defaultPayloads ? JSON.stringify(defaultPayloads, null, 2) : undefined, + connectors: selectedConnectors, + inbound_endpoints: selectedInboundEndpoints, + files: files.length > 0 ? files : undefined, + images: images.length > 0 ? images : undefined, + thinking_enabled: thinking || false, + chatHistory: historyMessages.length > 0 ? historyMessages : undefined, + abortController: controller, // Pass abort controller to handle cancellation + }); } /** - * Sends diagnostics to LLM backend and gets response + * Analyzes diagnostics and gets fixed configurations using local LLM */ export async function getDiagnosticsReponseFromLlm( diagnostics: any, @@ -388,35 +240,39 @@ export async function getDiagnosticsReponseFromLlm( controller: AbortController ): Promise { try { - const backendRootUri = await fetchBackendUrl(projectUri); - const url = backendRootUri + MI_DIAGNOSTICS_RESPONSE_BACKEND_URL; - const context = await getWorkspaceContext(projectUri); + console.log("Analyzing diagnostics and fixing configurations..."); - const requestBody = { + // Call the local codeDiagnostics function + const result = await codeDiagnostics({ diagnostics: diagnostics.diagnostics, xmlCodes: xmlCodes, - context: context.context - }; + }); - return fetchWithRetry( - BackendRequestType.UserPrompt, - url, - requestBody, - projectUri, - controller, - false // Not in thinking mode for diagnostics - ); + console.log(`Fixed ${result.fixed_config.length} configurations`); + + // Return a Response object with the fixed configurations + return new Response(JSON.stringify(result), { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + }); } catch (error) { - console.error("Error sending diagnostics to LLM:", error); + console.error("Error fixing diagnostics:", error); const errorMessage = error instanceof Error ? error.message - : "Unknown error occurred when analyzing diagnostics"; + : "Unknown error occurred when fixing diagnostics"; - return Promise.reject({ + return new Response(JSON.stringify({ status: "error", - message: `Failed to analyze diagnostics: ${errorMessage}`, - originalError: error + message: `Failed to fix diagnostics: ${errorMessage}`, + fixed_config: [] + }), { + status: 500, + headers: { + 'Content-Type': 'application/json', + }, }); } } diff --git a/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-handler.ts b/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-handler.ts index 9d0b1b38b26..a7f200322c5 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-handler.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-handler.ts @@ -44,8 +44,7 @@ import { getMappingFromAI, writeDataMapping, DataMapWriteRequest, - confirmMappingAction, - authenticateUser + confirmMappingAction } from "@wso2/mi-core"; import { Messenger } from "vscode-messenger"; import { MiDataMapperRpcManager } from "./rpc-manager"; @@ -69,6 +68,4 @@ export function registerMiDataMapperRpcHandlers(messenger: Messenger, projectUri messenger.onRequest(getMappingFromAI, () => rpcManger.getMappingFromAI()); messenger.onNotification(writeDataMapping, (args: DataMapWriteRequest) => rpcManger.writeDataMapping(args)); messenger.onRequest(confirmMappingAction, () => rpcManger.confirmMappingAction()); - messenger.onRequest(authenticateUser, () => rpcManger.authenticateUser()); - } diff --git a/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-manager.ts index d6c8bd34373..0b9ad34a6d7 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-manager.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-manager.ts @@ -54,9 +54,9 @@ import { MiDiagramRpcManager } from "../mi-diagram/rpc-manager"; import { UndoRedoManager } from "../../undoRedoManager"; import { DMProject } from "../../datamapper/DMProject"; import { DM_OPERATORS_FILE_NAME, DM_OPERATORS_IMPORT_NAME, READONLY_MAPPING_FUNCTION_NAME, RUNTIME_VERSION_440 } from "../../constants"; -import { refreshAuthCode } from '../../ai-panel/auth'; -import { fetchBackendUrl, openSignInView, readTSFile, removeMapFunctionEntry, makeRequest, showMappingEndNotification, showSignedOutNotification } from "../../util/ai-datamapper-utils"; +import { readTSFile, removeMapFunctionEntry, showMappingEndNotification } from "../../util/ai-datamapper-utils"; import { compareVersions } from "../../util/onboardingUtils"; +import { mapDataMapper } from "../../ai-panel/copilot/data-mapper/mapper"; const undoRedoManager = new UndoRedoManager(); @@ -227,52 +227,6 @@ export class MiDataMapperRpcManager implements MIDataMapperAPI { }); } - async authenticateUser(): Promise { - let token; - try { - // Get the user token from the secrets - token = await extension.context.secrets.get('MIAIUser'); - if (!token) { - throw new Error('Token not available'); - } - const url = await fetchBackendUrl(this.projectUri) + '/user/usage'; - let response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}`, - }, - }); - if (!response.ok) { - if (response.status === 401 || response.status === 403) { - token = await refreshAuthCode(); - if (!token) { - throw new Error('Token refresh failed'); - } - // retry the request with the new token - response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}`, - }, - }); - if (!response.ok) { - throw new Error('Token verification failed after refresh'); - } - } else { - throw new Error(`Error while checking token: ${response.statusText}`); - } - } - } catch (error) { - console.error('Error while getting or refreshing user token: ', error); - showSignedOutNotification(this.projectUri); - openSignInView(this.projectUri); - return false; - } - return true; // token is available and valid - } - // Function to ask whether the user wants to replace all existing mappings with ai generated mappings async confirmMappingAction(): Promise { // Define the message based on the action @@ -322,68 +276,44 @@ export class MiDataMapperRpcManager implements MIDataMapperAPI { } } - // Main function to get the mapping from OpenAI and write it to the relevant files + // Main function to get the mapping from AI and write it to the relevant files async getMappingFromAI(): Promise { try { // Function to read the TypeScript file let tsContent = await readTSFile(this.projectUri); - const backendRootUri = await fetchBackendUrl(this.projectUri); - const url = backendRootUri + '/data-mapper/map'; - let token; - try { - // Get the user token from the secrets - token = await extension.context.secrets.get('MIAIUser'); - } - catch (error) { - console.error('Error while getting user token.'); - showSignedOutNotification(this.projectUri); - openSignInView(this.projectUri); - return; // If there is no token, return early to exit the function - } - let response; - try { - // Make a request to the backend to get the data mapping - response = await makeRequest(url, token, tsContent); - } catch (error) { - console.error('Error while making request to backend', error); - showMappingEndNotification(this.projectUri); - openSignInView(this.projectUri); - return; // If there is an error in the request, return early to exit the function - } - try { - interface DataMapResponse { - mapping: string; - event: string; - usage: string; - } - // Parse the response from the request - const data = await response as DataMapResponse; - if (data.event === "data_mapping_success") { - // Extract the mapping string and pass it to the writeDataMapping function - const mappingString = data.mapping; - // Remove the mapFunction line from the mapping string - const mappingRet = removeMapFunctionEntry(mappingString); - // Create an object of type DataMapWriteRequest - const dataMapWriteRequest: DataMapWriteRequest = { - dataMapping: mappingRet - }; - await this.writeDataMapping(dataMapWriteRequest); - // Show a notification to the user - showMappingEndNotification(this.projectUri); - } - else { - // Log error or perform error handling - console.error('Data mapping was not successful'); - } - } - catch (error) { - console.error('Error while generating data mapping', error); - throw error; + + console.log("Generating data mapping using Copilot..."); + + // Call the local data mapper function + const mappingString = await mapDataMapper({ + tsFile: tsContent + }); + + console.log("Data mapping generated successfully using Copilot"); + + // Remove the mapFunction line from the mapping string + const mappingRet = removeMapFunctionEntry(mappingString); + + if (!mappingRet?.trim()) { + throw new Error("MI Copilot did not return a valid mapping body."); } + + // Create an object of type DataMapWriteRequest + const dataMapWriteRequest: DataMapWriteRequest = { + dataMapping: mappingRet + }; + + await this.writeDataMapping(dataMapWriteRequest); + + // Show a notification to the user + showMappingEndNotification(this.projectUri); } - catch (requestError) { - console.error('Error while making request to backend', requestError); - return; + catch (error) { + console.error('Error while generating data mapping', error); + window.showErrorMessage( + `Failed to generate data mapping: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + throw error; } } diff --git a/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-handler.ts b/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-handler.ts index 66e940e4112..3732e1f468f 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-handler.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-handler.ts @@ -166,7 +166,6 @@ import { exportProject, fetchDSSTables, generateDSSQueries, - getAIResponse, getAPIDirectory, getAddressEndpoint, getAllAPIcontexts, @@ -319,8 +318,8 @@ import { UpdateRegistryPropertyRequest, updatePropertiesInArtifactXML, getPropertiesFromArtifactXML, - formatPomFile, - getBackendRootUrl + formatPomFile + // getBackendRootUrl - REMOVED: Backend URLs deprecated, all AI features use local LLM } from "@wso2/mi-core"; import { Messenger } from "vscode-messenger"; import { MiDiagramRpcManager } from "./rpc-manager"; @@ -391,7 +390,6 @@ export function registerMiDiagramRpcHandlers(messenger: Messenger, projectUri: s messenger.onRequest(createProject, (args: CreateProjectRequest) => rpcManger.createProject(args)); messenger.onRequest(importProject, (args: ImportProjectRequest) => rpcManger.importProject(args)); messenger.onRequest(migrateProject, (args: MigrateProjectRequest) => rpcManger.migrateProject(args)); - messenger.onRequest(getAIResponse, (args: AIUserInput) => rpcManger.getAIResponse(args)); messenger.onRequest(writeContentToFile, (args: WriteContentToFileRequest) => rpcManger.writeContentToFile(args)); messenger.onRequest(writeMockServices, (args: WriteMockServicesRequest) => rpcManger.writeMockServices(args)); messenger.onRequest(handleFileWithFS, (args: HandleFileRequest) => rpcManger.handleFileWithFS(args)); diff --git a/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts index 7f989eff4ae..b044e61ffa5 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts @@ -3165,32 +3165,6 @@ ${endpointAttributes} }); } - async getAIResponse(params: AIUserInput): Promise { - let result = ''; - try { - const response = await axios.post(APIS.MI_COPILOT_BACKEND, { - chat_history: params.chat_history, - }, { responseType: 'stream' }); - - response.data.pipe(new Transform({ - transform(chunk, encoding, callback) { - const chunkAsString = chunk.toString(); - result += chunkAsString; - callback(); - } - })); - - return new Promise((resolve, reject) => { - response.data.on('end', () => resolve(result)); - response.data.on('error', (err: Error) => reject(err)); - }); - - } catch (error) { - console.error('Error calling the AI endpoint:', error); - throw new Error('Failed to call AI endpoint'); - } - } - async writeContentToFile(params: WriteContentToFileRequest): Promise { let status = true; //if file exists, overwrite if not, create new file and write content. if successful, return true, else false @@ -3535,38 +3509,14 @@ ${endpointAttributes} async getWorkspaceContext(): Promise { const artifactDirPath = path.join(this.projectUri, 'src', 'main', 'wso2mi', 'artifacts'); - const resourcesDirPath = path.join(this.projectUri, 'src', 'main', 'wso2mi', 'resources'); const fileContents: string[] = []; - // Helper function to recursively read files from a directory - const readFilesRecursively = async (dirPath: string, excludeFolders: string[] = []): Promise => { - if (!fs.existsSync(dirPath)) { - return; - } - - const entries = await fs.promises.readdir(dirPath, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(dirPath, entry.name); - - if (entry.isDirectory()) { - // Skip excluded folders - if (!excludeFolders.includes(entry.name)) { - await readFilesRecursively(fullPath, excludeFolders); - } - } else if (entry.isFile()) { - try { - const content = await fs.promises.readFile(fullPath, 'utf-8'); - fileContents.push(content); - } catch (error) { - // Skip files that can't be read as text - console.warn(`Could not read file as text: ${fullPath}`); - } - } - } + // Helper function to check if a file is an XML file + const isXmlFile = (fileName: string): boolean => { + return fileName.toLowerCase().endsWith('.xml'); }; - // Read artifacts folders + // Read artifacts folders - ONLY XML files from artifacts directory var resourceFolders = ['apis', 'endpoints', 'inbound-endpoints', 'local-entries', 'message-processors', 'message-stores', 'proxy-services', 'sequences', 'tasks', 'templates']; for (const folder of resourceFolders) { const folderPath = path.join(artifactDirPath, folder); @@ -3575,21 +3525,27 @@ ${endpointAttributes} const files = await fs.promises.readdir(folderPath); for (const file of files) { + // Only process XML files + if (!isXmlFile(file)) { + continue; + } + const filePath = path.join(folderPath, file); const stats = await fs.promises.stat(filePath); if (stats.isFile()) { - const content = await fs.promises.readFile(filePath, 'utf-8'); - fileContents.push(content); + try { + const content = await fs.promises.readFile(filePath, 'utf-8'); + fileContents.push(content); + } catch (error) { + console.warn(`Could not read XML file: ${filePath}`, error); + } } } } } - // Read resources folders recursively, excluding specified folders - const excludedFolders = ['api-definitions', 'metadata', 'connectors']; - await readFilesRecursively(resourcesDirPath, excludedFolders); - + console.log(`[getWorkspaceContext] Loaded ${fileContents.length} XML files from artifacts folder`); return { context: fileContents, rootPath: this.projectUri }; } @@ -4159,6 +4115,12 @@ ${endpointAttributes} const artifactDirPath = path.join(this.projectUri, 'src', 'main', 'wso2mi', 'artifacts'); const fileContents: string[] = []; fileContents.push(currentFileContent); + + // Helper function to check if a file is an XML file + const isXmlFile = (fileName: string): boolean => { + return fileName.toLowerCase().endsWith('.xml'); + }; + var resourceFolders = ['apis', 'endpoints', 'inbound-endpoints', 'local-entries', 'message-processors', 'message-stores', 'proxy-services', 'sequences', 'tasks', 'templates']; for (const folder of resourceFolders) { const folderPath = path.join(artifactDirPath, folder); @@ -4167,6 +4129,11 @@ ${endpointAttributes} const files = await fs.promises.readdir(folderPath); for (const file of files) { + // Only process XML files + if (!isXmlFile(file)) { + continue; + } + const filePath = path.join(folderPath, file); if (filePath === currentFile) { continue; @@ -4174,8 +4141,12 @@ ${endpointAttributes} const stats = await fs.promises.stat(filePath); if (stats.isFile()) { - const content = await fs.promises.readFile(filePath, 'utf-8'); - fileContents.push(content); + try { + const content = await fs.promises.readFile(filePath, 'utf-8'); + fileContents.push(content); + } catch (error) { + console.warn(`Could not read XML file: ${filePath}`, error); + } } } } diff --git a/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/Utils.ts b/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/Utils.ts index 2582e600169..d934d920ccd 100644 --- a/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/Utils.ts +++ b/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/Utils.ts @@ -43,7 +43,7 @@ async function initVSCode(groupName?: string, title?: string, attempt: number = await page.executePaletteCommand('Reload Window'); await page.executePaletteCommand('View: Toggle Secondary Side Bar Visibility'); } else { - vscode = await startVSCode(resourcesFolder, vscodeVersion, undefined, false, extensionsFolder, newProjectPath, 'mi-test-profile', groupName, title, attempt); + vscode = await startVSCode(resourcesFolder, vscodeVersion, undefined, false, extensionsFolder, newProjectPath, 'mi-test-profile'); } page = new ExtendedPage(await vscode!.firstWindow({ timeout: 60000 })); } @@ -115,7 +115,7 @@ export async function resumeVSCode(groupName?: string, title?: string, attempt: await page.executePaletteCommand('Reload Window'); } else { console.log('Starting VSCode'); - vscode = await startVSCode(resourcesFolder, vscodeVersion, undefined, false, extensionsFolder, path.join(newProjectPath, 'testProject'), 'mi-test-profile', groupName, title, attempt); + vscode = await startVSCode(resourcesFolder, vscodeVersion, undefined, false, extensionsFolder, path.join(newProjectPath, 'testProject'), 'mi-test-profile'); await new Promise(resolve => setTimeout(resolve, 5000)); } page = new ExtendedPage(await vscode!.firstWindow({ timeout: 60000 })); diff --git a/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/Overview.ts b/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/Overview.ts index 6c04916b9e4..98902fd42c4 100644 --- a/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/Overview.ts +++ b/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/Overview.ts @@ -125,8 +125,9 @@ export class Overview { await popupPanel.getByRole('button', { name: 'Add Dependency' }).click(); const loader = this.webView.locator('[data-testid="dependency-manager-loader"]'); await loader.waitFor({ state: 'detached', timeout: 10000 }); + await this._page.waitForTimeout(5000); const dependencyItemComponent = popupPanel.locator('[data-testid="mysql-mysql-connector-java-8.0.33"]'); - await dependencyItemComponent.waitFor({ state: 'visible', timeout: 10000 }); + await dependencyItemComponent.waitFor({ state: 'visible', timeout: 50000 }); } public async editOtherDependencies() { diff --git a/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/Welcome.ts b/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/Welcome.ts index 928c1e45a17..23bff590031 100644 --- a/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/Welcome.ts +++ b/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/Welcome.ts @@ -1,4 +1,4 @@ -/** +1/** * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. * * WSO2 LLC. licenses this file to you under the Apache License, @@ -75,7 +75,7 @@ export class Welcome { await downloadJavaAndMi.click(); try { console.log(`Waiting for I Agree button`); - const iAgreeBtn = await getVsCodeButton(container!, 'I Agree', 'primary', 60000); + const iAgreeBtn = await getVsCodeButton(container!, 'I Agree', 'primary'); await iAgreeBtn.click(); } catch (error) { console.log('No terms and conditions to accept'); diff --git a/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/overviewPageTests/projectSettingPage.spec.ts b/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/overviewPageTests/projectSettingPage.spec.ts index 5d9793226a0..b19e8265887 100644 --- a/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/overviewPageTests/projectSettingPage.spec.ts +++ b/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/overviewPageTests/projectSettingPage.spec.ts @@ -44,15 +44,15 @@ export default function createTests() { console.log('Starting to update project version'); const overviewPage = new Overview(page.page); await overviewPage.init(); + // Wait for 10s to let the pom.xml update + await page.page.waitForTimeout(10000); await overviewPage.updateProjectVersion("1.1.0"); - // Wait for 5s to let the pom.xml update - await page.page.waitForTimeout(5000); console.log('Waiting for pom.xml to contain updated version'); await overviewPage.getProjectSummary(); await waitUntilPomContains(page.page, pomFilePath, '1.1.0'); await overviewPage.updateProjectVersion("1.0.0"); - // Wait for 5s to let the pom.xml update - await page.page.waitForTimeout(5000); + // Wait for 8s to let the pom.xml update + await page.page.waitForTimeout(8000); console.log('Project version updated successfully'); }); @@ -63,8 +63,8 @@ export default function createTests() { await overviewPage.init(); await overviewPage.openOtherDependenciesManager(); await overviewPage.addOtherDependencies(); - // Wait for 8s to let the pom.xml update - await page.page.waitForTimeout(8000); + // Wait for 10s to let the pom.xml update + await page.page.waitForTimeout(10000); console.log('Waiting for pom.xml to contain mysql-connector-java dependency'); await waitUntilPomContains(page.page, pomFilePath, 'mysql-connector-java'); await page.page.waitForTimeout(2000); // Additional wait to ensure stability @@ -89,8 +89,8 @@ export default function createTests() { await overviewPage.init(); console.log('Deleting mysql-connector-java dependency'); await overviewPage.deleteOtherDependencies(); - // Wait for 8s to let the pom.xml update - await page.page.waitForTimeout(8000); + // Wait for 10s to let the pom.xml update + await page.page.waitForTimeout(10000); console.log('Waiting for pom.xml to not contain mysql-connector-java dependency'); await waitUntilPomNotContains(page.page, pomFilePath, 'mysql-connector-java'); await overviewPage.closeDependencyManager(); @@ -104,8 +104,8 @@ export default function createTests() { await overviewPage.init(); await overviewPage.openConnectorDependenciesManager(); await overviewPage.addConnectorDependencies(); - // Wait for 5s to let the pom.xml update - await page.page.waitForTimeout(5000); + // Wait for 8s to let the pom.xml update + await page.page.waitForTimeout(8000); console.log('Waiting for pom.xml to contain mi-connector-amazonsqs dependency'); await waitUntilPomContains(page.page, pomFilePath, 'mi-connector-amazonsqs'); }); @@ -116,8 +116,8 @@ export default function createTests() { const overviewPage = new Overview(page.page); await overviewPage.init(); await overviewPage.editConnectorDependencies(); - // Wait for 8s to let the pom.xml update - await page.page.waitForTimeout(8000); + // Wait for 12s to let the pom.xml update + await page.page.waitForTimeout(12000); console.log('Waiting for pom.xml to contain 3.0.1 as version in mi-connector-amazonsqs dependency'); await waitUntilPomContains(page.page, pomFilePath, '3.0.1'); }); @@ -127,8 +127,8 @@ export default function createTests() { const overviewPage = new Overview(page.page); await overviewPage.init(); await overviewPage.deleteConnectorDependencies(); - // Wait for 8s to let the pom.xml update - await page.page.waitForTimeout(8000); + // Wait for 10s to let the pom.xml update + await page.page.waitForTimeout(10000); console.log('Waiting for pom.xml to not contain mi-connector-amazonsqs dependency'); await waitUntilPomNotContains(page.page, pomFilePath, 'mi-connector-amazonsqs'); await overviewPage.closeDependencyManager(); diff --git a/workspaces/mi/mi-rpc-client/src/RpcClient.ts b/workspaces/mi/mi-rpc-client/src/RpcClient.ts index 11a3f7ead93..0c3f75c8d9d 100644 --- a/workspaces/mi/mi-rpc-client/src/RpcClient.ts +++ b/workspaces/mi/mi-rpc-client/src/RpcClient.ts @@ -19,7 +19,7 @@ */ import { Messenger } from "vscode-messenger-webview"; -import { MachineStateValue, stateChanged, vscode, getVisualizerState, getAIVisualizerState, VisualizerLocation, AIVisualizerLocation, webviewReady, onFileContentUpdate, AI_EVENT_TYPE, sendAIStateEvent, AIMachineStateValue, aiStateChanged, themeChanged, ColorThemeKind, PopupMachineStateValue, popupStateChanged, PopupVisualizerLocation, getPopupVisualizerState, onParentPopupSubmitted, ParentPopupData, ConnectorStatus, onConnectorStatusUpdate, onDocumentSave, Document, SwaggerData, DownloadProgressData, onSwaggerSpecReceived, MiServerRunStatus, miServerRunStateChanged, onDownloadProgress, codeGenerationEvent, CodeGenerationEvent } from "@wso2/mi-core"; +import { MachineStateValue, stateChanged, vscode, getVisualizerState, getAIVisualizerState, VisualizerLocation, AIVisualizerLocation, webviewReady, onFileContentUpdate, AI_EVENT_TYPE, sendAIStateEvent, AIMachineStateValue, AIMachineSendableEvent, aiStateChanged, themeChanged, ColorThemeKind, PopupMachineStateValue, popupStateChanged, PopupVisualizerLocation, getPopupVisualizerState, onParentPopupSubmitted, ParentPopupData, ConnectorStatus, onConnectorStatusUpdate, onDocumentSave, Document, SwaggerData, DownloadProgressData, onSwaggerSpecReceived, MiServerRunStatus, miServerRunStateChanged, onDownloadProgress, codeGenerationEvent, CodeGenerationEvent } from "@wso2/mi-core"; import { MiDiagramRpcClient } from "./rpc-clients/mi-diagram/rpc-client"; import { HOST_EXTENSION } from "vscode-messenger-common"; import { MiVisualizerRpcClient } from "./rpc-clients/mi-visualizer/rpc-client"; @@ -99,7 +99,7 @@ export class RpcClient { return this.messenger.sendRequest(getPopupVisualizerState, HOST_EXTENSION); } - sendAIStateEvent(event: AI_EVENT_TYPE) { + sendAIStateEvent(event: AI_EVENT_TYPE | AIMachineSendableEvent) { this.messenger.sendRequest(sendAIStateEvent, HOST_EXTENSION, event); } diff --git a/workspaces/mi/mi-rpc-client/src/rpc-clients/ai-panel/rpc-client.ts b/workspaces/mi/mi-rpc-client/src/rpc-clients/ai-panel/rpc-client.ts index 420f2a55ee5..1def5c087c7 100644 --- a/workspaces/mi/mi-rpc-client/src/rpc-clients/ai-panel/rpc-client.ts +++ b/workspaces/mi/mi-rpc-client/src/rpc-clients/ai-panel/rpc-client.ts @@ -18,8 +18,6 @@ import { MIAIPanelAPI, - GetBackendRootUrlResponse, - getBackendRootUrl, GenerateSuggestionsRequest, GenerateSuggestionsResponse, generateSuggestions, @@ -28,8 +26,26 @@ import { generateCode, AbortCodeGenerationResponse, abortCodeGeneration, - setAnthropicApiKey, - hasAnthropicApiKey + hasAnthropicApiKey, + fetchUsage, + GenerateUnitTestRequest, + GenerateUnitTestResponse, + generateUnitTest, + GenerateUnitTestCaseRequest, + GenerateUnitTestCaseResponse, + generateUnitTestCase, + ProcessIdpRequest, + ProcessIdpResponse, + processIdp, + FillIdpSchemaRequest, + FillIdpSchemaResponse, + fillIdpSchema, + DmcToTsRequest, + DmcToTsResponse, + dmcToTs, + AutoFillFormRequest, + AutoFillFormResponse, + autoFillForm } from "@wso2/mi-core"; import { HOST_EXTENSION } from "vscode-messenger-common"; import { Messenger } from "vscode-messenger-webview"; @@ -41,13 +57,6 @@ export class MiAiPanelRpcClient implements MIAIPanelAPI { this._messenger = messenger; } - // ================================== - // General Functions - // ================================== - getBackendRootUrl(): Promise { - return this._messenger.sendRequest(getBackendRootUrl, HOST_EXTENSION); - } - // ================================== // AI Functions // ================================== @@ -66,11 +75,50 @@ export class MiAiPanelRpcClient implements MIAIPanelAPI { // ================================== // API Key Management // ================================== - setAnthropicApiKey(): Promise { - return this._messenger.sendRequest(setAnthropicApiKey, HOST_EXTENSION); - } - hasAnthropicApiKey(): Promise { return this._messenger.sendRequest(hasAnthropicApiKey, HOST_EXTENSION); } + + // ================================== + // Usage Management + // ================================== + fetchUsage(): Promise<{ max_usage: number; remaining_tokens: number; time_to_reset: number } | undefined> { + return this._messenger.sendRequest(fetchUsage, HOST_EXTENSION); + } + + // ================================== + // Unit Test Generation + // ================================== + generateUnitTest(request: GenerateUnitTestRequest): Promise { + return this._messenger.sendRequest(generateUnitTest, HOST_EXTENSION, request); + } + + generateUnitTestCase(request: GenerateUnitTestCaseRequest): Promise { + return this._messenger.sendRequest(generateUnitTestCase, HOST_EXTENSION, request); + } + + // ================================== + // IDP (Intelligent Document Processor) + // ================================== + processIdp(request: ProcessIdpRequest): Promise { + return this._messenger.sendRequest(processIdp, HOST_EXTENSION, request); + } + + fillIdpSchema(request: FillIdpSchemaRequest): Promise { + return this._messenger.sendRequest(fillIdpSchema, HOST_EXTENSION, request); + } + + // ================================== + // DMC to TypeScript Conversion + // ================================== + dmcToTs(request: DmcToTsRequest): Promise { + return this._messenger.sendRequest(dmcToTs, HOST_EXTENSION, request); + } + + // ================================== + // Auto-Fill Form + // ================================== + autoFillForm(request: AutoFillFormRequest): Promise { + return this._messenger.sendRequest(autoFillForm, HOST_EXTENSION, request); + } } diff --git a/workspaces/mi/mi-rpc-client/src/rpc-clients/mi-data-mapper/rpc-client.ts b/workspaces/mi/mi-rpc-client/src/rpc-clients/mi-data-mapper/rpc-client.ts index c02ea077552..b8ea6e2d2a9 100644 --- a/workspaces/mi/mi-rpc-client/src/rpc-clients/mi-data-mapper/rpc-client.ts +++ b/workspaces/mi/mi-rpc-client/src/rpc-clients/mi-data-mapper/rpc-client.ts @@ -53,8 +53,7 @@ import { getMappingFromAI, writeDataMapping, DataMapWriteRequest, - confirmMappingAction, - authenticateUser + confirmMappingAction } from "@wso2/mi-core"; import { HOST_EXTENSION } from "vscode-messenger-common"; import { Messenger } from "vscode-messenger-webview"; @@ -133,8 +132,4 @@ export class MiDataMapperRpcClient implements MIDataMapperAPI { confirmMappingAction(): Promise { return this._messenger.sendRequest(confirmMappingAction, HOST_EXTENSION); } - - authenticateUser(): Promise { - return this._messenger.sendRequest(authenticateUser, HOST_EXTENSION); - } } diff --git a/workspaces/mi/mi-rpc-client/src/rpc-clients/mi-diagram/rpc-client.ts b/workspaces/mi/mi-rpc-client/src/rpc-clients/mi-diagram/rpc-client.ts index aff1b256526..afb18ab1012 100644 --- a/workspaces/mi/mi-rpc-client/src/rpc-clients/mi-diagram/rpc-client.ts +++ b/workspaces/mi/mi-rpc-client/src/rpc-clients/mi-diagram/rpc-client.ts @@ -18,7 +18,6 @@ * THIS FILE INCLUDES AUTO GENERATED CODE */ import { - AIUserInput, ApiDirectoryResponse, ApplyEditRequest, ApplyEditResponse, @@ -89,7 +88,6 @@ import { GetAvailableConnectorResponse, GetAvailableResourcesRequest, GetAvailableResourcesResponse, - GetBackendRootUrlResponse, GetProxyRootUrlResponse, GetConnectionFormRequest, GetConnectionFormResponse, @@ -243,7 +241,6 @@ import { editOpenAPISpec, executeCommand, exportProject, - getAIResponse, getAPIDirectory, getAddressEndpoint, getAllArtifacts, @@ -257,7 +254,6 @@ import { getAvailableConnectors, getAvailableRegistryResources, getAvailableResources, - getBackendRootUrl, getProxyRootUrl, getConnectionForm, getConnector, @@ -721,10 +717,6 @@ export class MiDiagramRpcClient implements MiDiagramAPI { return this._messenger.sendRequest(migrateProject, HOST_EXTENSION, params); } - getAIResponse(params: AIUserInput): Promise { - return this._messenger.sendRequest(getAIResponse, HOST_EXTENSION, params); - } - writeContentToFile(params: WriteContentToFileRequest): Promise { return this._messenger.sendRequest(writeContentToFile, HOST_EXTENSION, params); } @@ -821,10 +813,6 @@ export class MiDiagramRpcClient implements MiDiagramAPI { return this._messenger.sendRequest(getSelectiveArtifacts, HOST_EXTENSION, params); } - getBackendRootUrl(): Promise { - return this._messenger.sendRequest(getBackendRootUrl, HOST_EXTENSION); - } - getProxyRootUrl(): Promise { return this._messenger.sendRequest(getProxyRootUrl, HOST_EXTENSION); } diff --git a/workspaces/mi/mi-visualizer/src/components/AuthenticationDialog/useAuthentication.ts b/workspaces/mi/mi-visualizer/src/components/AuthenticationDialog/useAuthentication.ts index e9f5097cea4..a6053a88228 100644 --- a/workspaces/mi/mi-visualizer/src/components/AuthenticationDialog/useAuthentication.ts +++ b/workspaces/mi/mi-visualizer/src/components/AuthenticationDialog/useAuthentication.ts @@ -30,14 +30,17 @@ export function useAuthentication(options: UseAuthenticationOptions) { const [showSignInConfirm, setShowSignInConfirm] = useState(false); /** - * Check if user is authenticated + * Check if user is authenticated with MI Copilot (AI state machine) */ const checkAuthentication = async (): Promise => { try { - const token = await rpcClient.getMiDiagramRpcClient().getUserAccessToken(); - return !!token; + const aiState = await rpcClient.getAIVisualizerState(); + const state = aiState?.state; + + // Check if user is authenticated or has usage exceeded (both mean signed in) + return state === 'Authenticated' || state === 'UsageExceeded'; } catch (error) { - console.error('User not signed in', error); + console.error('Failed to check AI authentication state', error); return false; } }; diff --git a/workspaces/mi/mi-visualizer/src/constants/index.ts b/workspaces/mi/mi-visualizer/src/constants/index.ts index 2d2b4942f42..1a7734909bf 100644 --- a/workspaces/mi/mi-visualizer/src/constants/index.ts +++ b/workspaces/mi/mi-visualizer/src/constants/index.ts @@ -18,14 +18,6 @@ //add the rpc client to the context -export const MI_COPILOT_BACKEND_URL = `/chat/copilot`; -export const MI_ARTIFACT_GENERATION_BACKEND_URL = `/chat/artifact-generation`; -export const MI_ARTIFACT_EDIT_BACKEND_URL = `/chat/artifact-editing`; -export const MI_SUGGESTIVE_QUESTIONS_INITIAL_BACKEND_URL = `/suggestions/initial`; -export const MI_SUGGESTIVE_QUESTIONS_BACKEND_URL = `/suggestions`; -export const MI_UNIT_TEST_GENERATION_BACKEND_URL = `/unit-test/generate`; -export const MI_UNIT_TEST_CASE_GENERATE_BACKEND_URL = `/unit-test/generate-case`; - // MI Copilot Error Messages export const COPILOT_ERROR_MESSAGES = { BAD_REQUEST: 'Bad Request', diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx index bd875cf2233..450f6578335 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx @@ -23,15 +23,19 @@ import SuggestionsList from "./SuggestionsList"; import { useMICopilotContext } from "./MICopilotContext"; import { handleFileAttach } from "../utils"; import { USER_INPUT_PLACEHOLDER_MESSAGE, VALID_FILE_TYPES } from "../constants"; -import { generateSuggestions, generateId, getBackendUrlAndView, fetchCodeGenerationsWithRetry, getDiagnosticsReponseFromLlm, replaceCodeBlock, setupCodeGenerationEventListener } from "../utils"; -import { BackendRequestType, FixedConfigItem, CorrectedCodeItem, } from "../types"; -import { Role, MessageType, CopilotChatEntry, ChatMessage } from "@wso2/mi-core"; +import { generateSuggestions, generateId, getView, fetchCodeGenerationsWithRetry, replaceCodeBlock, setupCodeGenerationEventListener, updateTokenInfo } from "../utils"; +import { BackendRequestType } from "../types"; +import { Role, MessageType, CopilotChatEntry } from "@wso2/mi-core"; import Attachments from "./Attachments"; +interface AIChatFooterProps { + isUsageExceeded?: boolean; +} + /** * Footer component containing chat input and controls */ -const AIChatFooter: React.FC = () => { +const AIChatFooter: React.FC = ({ isUsageExceeded = false }) => { const { rpcClient, messages, @@ -59,6 +63,8 @@ const AIChatFooter: React.FC = () => { const isStopButtonClicked = useRef(false); const isResponseReceived = useRef(false); const textAreaRef = useRef(null); + const abortedRef = useRef(false); + const lastUserPromptRef = useRef(""); const [isFocused, setIsFocused] = useState(false); const isDarkMode = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; @@ -83,71 +89,81 @@ const AIChatFooter: React.FC = () => { // Handle code generation streaming events from extension const handleCodeGenerationEvent = (event: any) => { + // Ignore all events if generation was aborted + if (abortedRef.current) { + return; + } + switch (event.type) { case "code_generation_start": - // Start of code generation - could show loading indicator - console.log("Code generation started"); + // Start of code generation setAssistantResponse(""); break; - + case "content_block": // Handle streaming content blocks if (event.content) { - const response = JSON.parse(event.content); - if (response.content !== null && response.content !== undefined) { - const content = response.content; - const usage = response.usage; - - if (usage.max_usage == -1) { - setRemainingTokenPercentage(-1); - } else { - const remainingTokens = Number(usage.remaining_tokens); - const maxTokens = Number(usage.max_usage); - let percentage = Math.round((remainingTokens / maxTokens) * 100); - if (percentage < 0) percentage = 0; - setRemainingTokenPercentage(percentage); + const content = event.content; + + // Update assistant response state + setAssistantResponse(prev => prev + content); + + // Update the last copilot message in real-time + setMessages((prevMessages) => { + const newMessages = [...prevMessages]; + if (newMessages.length > 0) { + newMessages[newMessages.length - 1].content += content; } - - // Update assistant response state - setAssistantResponse(prev => prev + content); - - // Update the last copilot message in real-time - setMessages((prevMessages) => { - const newMessages = [...prevMessages]; - if (newMessages.length > 0) { - newMessages[newMessages.length - 1].content += content; - } - return newMessages; - }); - } - else if (response.questions && response.questions.length > 0) { - setQuestions((prevQuestions) => [...prevQuestions, ...response.questions]); - } - } + return newMessages; + }); + } break; case "code_generation_end": // Final content replacement - console.log("Code generation completed"); if (event.content) { setAssistantResponse(event.content); handleCodeGenerationComplete(event.content); } + // Fetch and update usage after code generation + rpcClient?.getMiAiPanelRpcClient().fetchUsage().then((usage) => { + if (usage) { + rpcClient?.getAIVisualizerState().then((machineView) => { + const { remainingTokenPercentage } = updateTokenInfo(machineView); + setRemainingTokenPercentage(remainingTokenPercentage); + }); + } + }).catch((error) => { + console.error("Error fetching usage after code generation:", error); + }); + + // If diagnostics won't run, generate suggestions immediately + // Otherwise, wait for code_diagnostic_end event + if (!event.willRunDiagnostics) { + // Clear old suggestions immediately before generating new ones + setQuestions([]); + const suggestionController = new AbortController(); + generateSuggestions(copilotChat, rpcClient, suggestionController).then((response) => { + if (response && response.length > 0) { + setQuestions(response); + } + }).catch((error) => { + console.error("Error generating suggestions after code generation:", error); + }); + } break; case "code_diagnostic_start": - console.log("Code diagnostics started"); setIsValidating(true); break; case "code_diagnostic_end": - console.log("Code diagnostics completed"); setIsValidating(false); - + // Handle corrected codes if available if (event.correctedCodes && event.correctedCodes.length > 0) { const fileCorrections = new Map(); - + event.correctedCodes.forEach((item: any) => { if (item.name && (item.configuration || item.code)) { const correctedCode = item.configuration || item.code; @@ -161,28 +177,38 @@ const AIChatFooter: React.FC = () => { setMessages((prevMessages) => { const newMessages = [...prevMessages]; const lastMessage = newMessages[newMessages.length - 1]; - + if (lastMessage && lastMessage.role === Role.MICopilot) { let updatedContent = lastMessage.content; - + fileCorrections.forEach((correctedCode, fileName) => { updatedContent = replaceCodeBlock(updatedContent, fileName, correctedCode); }); - + newMessages[newMessages.length - 1] = { ...lastMessage, content: updatedContent }; } - + return newMessages; }); } } + + // Clear old suggestions immediately before generating new ones + setQuestions([]); + // Generate fresh suggestions after code generation completes + generateSuggestions(copilotChat, rpcClient, new AbortController()).then((response) => { + if (response && response.length > 0) { + setQuestions(response); + } + }).catch((error) => { + console.error("Error generating suggestions after code generation:", error); + }); break; case "error": - console.error("Code generation error:", event.error); setMessages((prevMessages) => [...prevMessages, { id: generateId(), role: Role.MICopilot, @@ -192,14 +218,18 @@ const AIChatFooter: React.FC = () => { setBackendRequestTriggered(false); setIsValidating(false); break; - + case "stop": // Code generation completed - console.log("Code generation completed"); break; - + + case "aborted": + // Abort acknowledged by extension - all streaming has stopped + setBackendRequestTriggered(false); + break; + default: - console.log("Unknown event type:", event.type); + break; } }; @@ -227,6 +257,8 @@ const AIChatFooter: React.FC = () => { // Handle stopping the response generation const handleStop = async () => { isStopButtonClicked.current = true; + // Set abort flag BEFORE making any async calls to prevent race conditions + abortedRef.current = true; try { // Request the extension to abort code generation @@ -243,7 +275,10 @@ const AIChatFooter: React.FC = () => { setIsValidating(false); } - // Remove the last user and copilot messages + // Clear assistant response state + setAssistantResponse(""); + + // Remove the last user and copilot messages from UI state setMessages((prevMessages) => { const newMessages = [...prevMessages]; newMessages.pop(); // Remove the last copilot message @@ -251,23 +286,39 @@ const AIChatFooter: React.FC = () => { return newMessages; }); - // Generate suggestions based on chat history - await generateSuggestions(copilotChat, rpcClient, new AbortController()).then((response) => { - setQuestions((prevMessages) => [...prevMessages, ...response]); + // IMPORTANT: Also remove the last user message from copilotChat + // to prevent partial conversations from persisting in localStorage + setCopilotChat((prevChat) => { + const newChat = [...prevChat]; + if (newChat.length > 0) { + newChat.pop(); // Remove the last user message + } + return newChat; }); - // Explicitly adjust the textarea height after suggestion generation + // Restore the original user prompt to the input box + setCurrentUserprompt(lastUserPromptRef.current); + + // Explicitly adjust the textarea height if (textAreaRef.current) { setTimeout(() => { textAreaRef.current.style.height = "auto"; textAreaRef.current.style.height = `${textAreaRef.current.scrollHeight}px`; }, 0); } + + // Reset abort flag after a delay to ensure all buffered events are ignored + setTimeout(() => { + abortedRef.current = false; + }, 200); } catch (error) { console.error("Error stopping code generation:", error); + // Reset abort flag on error as well + abortedRef.current = false; } finally { - isStopButtonClicked.current = false; - + // Don't reset isStopButtonClicked here - keep it true so handleSend's finally + // block won't clear the restored prompt. It will be reset on next send. + // Reset backend request triggered state setBackendRequestTriggered(false); } @@ -285,6 +336,9 @@ const AIChatFooter: React.FC = () => { }; async function handleSend(requestType: BackendRequestType = BackendRequestType.UserPrompt, prompt?: string | "") { + // Reset stop button flag at the start of a new send + isStopButtonClicked.current = false; + // Block empty user inputs and avoid state conflicts if (currentUserPrompt === "" && !Object.values(BackendRequestType).includes(requestType)) { return; @@ -308,6 +362,9 @@ const AIChatFooter: React.FC = () => { setCurrentChatId(chatId); const updateChats = (userPrompt: string, userMessageType?: MessageType) => { + // Store the user prompt for potential abort restoration + lastUserPromptRef.current = userPrompt; + // Append labels to the user prompt setMessages((prevMessages) => [ ...prevMessages, @@ -343,7 +400,7 @@ const AIChatFooter: React.FC = () => { break; } - const { backendUrl, view } = await getBackendUrlAndView(rpcClient); + const view = await getView(rpcClient); try { // Call the RPC method for streaming code generation @@ -501,10 +558,11 @@ const AIChatFooter: React.FC = () => { setIsFocused(false); }} onKeyDown={handleTextKeydown} - placeholder={placeholder} + placeholder={isUsageExceeded ? "Usage quota exceeded. Please logout and log in with your own API key to continue." : placeholder} + disabled={isUsageExceeded} style={{ flex: 1, - overflowY: "auto", + overflowY: "auto", padding: "5px 15px 5px 10px", borderRadius: "4px", border: "none", @@ -516,6 +574,8 @@ const AIChatFooter: React.FC = () => { : "var(--vscode-editorHoverWidget-background)", color: "var(--vscode-input-foreground)", position: "relative", + opacity: isUsageExceeded ? 0.5 : 1, + cursor: isUsageExceeded ? "not-allowed" : "text" }} rows={2} /> @@ -564,10 +624,9 @@ const AIChatFooter: React.FC = () => { color: isDarkMode ? "var(--vscode-input-foreground)" : "var(--vscode-editor-foreground)", - opacity: backendRequestTriggered || isValidating ? 0.5 : 1, - cursor: backendRequestTriggered || isValidating ? "not-allowed" : "pointer" + opacity: (backendRequestTriggered || isValidating || isUsageExceeded) ? 0.5 : 1, + cursor: (backendRequestTriggered || isValidating || isUsageExceeded) ? "not-allowed" : "pointer" }} - disabled={backendRequestTriggered || isValidating} > @@ -611,7 +670,7 @@ const AIChatFooter: React.FC = () => { onChange={(e: React.ChangeEvent) => handleFileAttach(e, files, setFiles, images, setImages, setFileUploadStatus) } - disabled={backendRequestTriggered || isValidating} + disabled={backendRequestTriggered || isValidating || isUsageExceeded} /> { style={{ width: "30px", color: isDarkMode ? "var(--vscode-input-foreground)" : "var(--vscode-editor-foreground)", + opacity: isUsageExceeded ? 0.5 : 1, + cursor: isUsageExceeded ? "not-allowed" : "pointer" }} - disabled={(currentUserPrompt.trim() === "" && !backendRequestTriggered && !isValidating)} + disabled={(currentUserPrompt.trim() === "" && !backendRequestTriggered && !isValidating) || isUsageExceeded} > { await rpcClient?.getMiDiagramRpcClient().logoutFromMIAccount(); }; - const handleSetApiKey = async () => { - await rpcClient?.getMiAiPanelRpcClient().setAnthropicApiKey(); - // Check again after setting the API key - checkApiKey(); - }; - const checkApiKey = async () => { const hasApiKey = await rpcClient?.getMiAiPanelRpcClient().hasAnthropicApiKey(); setHasApiKey(hasApiKey); @@ -90,15 +84,6 @@ const AIChatHeader: React.FC = () => {   Clear - - - {/* Sign-in confirmation dialog */} - ); } diff --git a/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/IdpUtills.tsx b/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/IdpUtills.tsx index 57bc7f2db01..09c17b1de96 100644 --- a/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/IdpUtills.tsx +++ b/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/IdpUtills.tsx @@ -16,7 +16,6 @@ * under the License. */ -import { RpcClient } from "@wso2/mi-rpc-client"; export interface FieldItem { name: string; type: string; @@ -84,63 +83,6 @@ function getStatusText(status: number) { } } -export const fetchWithCopilot = async ({ - rpcClient, - body, - controllerRef, - retryCount = 0, - maxRetries = 2, -}: { - rpcClient: RpcClient - body: any; - controllerRef: React.MutableRefObject; - retryCount?: number; - maxRetries?: number; -}) => { - let token: any; - try { - token = await rpcClient.getMiDiagramRpcClient().getUserAccessToken(); - } catch (error) { - rpcClient.getMiDiagramRpcClient().executeCommand({ commands: ["MI.openAiPanel"] }).catch(console.error); - throw new Error("No access token."); - } - const backendRootUri = (await rpcClient.getMiDiagramRpcClient().getBackendRootUrl()).url; - const endpoint = `${backendRootUri}/idp-connector/generate`; - controllerRef.current = new AbortController(); - const fetchWithRetry = async (): Promise => { - let response = await fetch(endpoint, { - signal: controllerRef.current?.signal, - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token.token}`, - }, - body: JSON.stringify(body), - }); - if (response.status === 401) { - await rpcClient.getMiDiagramRpcClient().refreshAccessToken(); - token = await rpcClient.getMiDiagramRpcClient().getUserAccessToken(); - response = await fetch(endpoint, { - signal: controllerRef.current?.signal, - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token.token}`, - }, - body: JSON.stringify(body), - }); - } else if (response.status === 404 && retryCount < maxRetries) { - retryCount++; - const delay = Math.pow(2, retryCount) * 1000; - await new Promise((resolve) => setTimeout(resolve, delay)); - return fetchWithRetry(); - } - if (!response.ok) throw response; - return response; - }; - return fetchWithRetry(); -}; - export function handleFetchError(response: Response) { const statusText = getStatusText(response.status); if (statusText) { diff --git a/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/SchemaEditorView.tsx b/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/SchemaEditorView.tsx index 49f0b993317..915c065b292 100644 --- a/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/SchemaEditorView.tsx +++ b/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/SchemaEditorView.tsx @@ -26,7 +26,6 @@ import { parameterConfigForFields, parameterConfigForTables, tableConflictCheck, - fetchWithCopilot, validateJson, convertJsonSchemaToArrays, handleFetchError @@ -188,22 +187,21 @@ export function SchemaEditorView({ base64Images.push(base64String); } try { - const response = await fetchWithCopilot({ - rpcClient, - body: { - operation: "generate", - images: base64Images - }, - controllerRef: controllerRef1, + // Call new RPC method instead of backend + const result = await rpcClient.getMiAiPanelRpcClient().processIdp({ + operation: "generate", + images: base64Images, + userInput: "", + jsonSchema: "" }); - const data = await response.json(); - if (!validateJson(data.message)) { + + if (!validateJson(result.schema)) { setErrors("Invalid JSON schema"); return; } - setSchema(data.message); - handleFileWrite(data.message); - const processedSchema = convertJsonSchemaToArrays(data.message); + setSchema(result.schema); + handleFileWrite(result.schema); + const processedSchema = convertJsonSchemaToArrays(result.schema); setTables(processedSchema.arrays); setFields(processedSchema.fields); } catch (error: any) { @@ -266,25 +264,22 @@ export function SchemaEditorView({ } } try { - const response = await fetchWithCopilot({ - rpcClient: rpcClient, - body: { - operation: "finetune", - user_input: userInput, - json_schema: schema, - images: base64Images, - }, - controllerRef: controllerRef2, + // Call new RPC method instead of backend + const result = await rpcClient.getMiAiPanelRpcClient().processIdp({ + operation: "finetune", + userInput: userInput, + jsonSchema: schema, + images: base64Images, }); - const data = await response.json(); - if (!validateJson(data.message)) { + + if (!validateJson(result.schema)) { setErrors("Invalid JSON schema"); return; } - setSchema(data.message); - handleFileWrite(data.message); + setSchema(result.schema); + handleFileWrite(result.schema); setUserInput(""); - const processedSchema = convertJsonSchemaToArrays(data.message); + const processedSchema = convertJsonSchemaToArrays(result.schema); setTables(processedSchema.arrays); setFields(processedSchema.fields); } catch (error: any) { diff --git a/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/TryOutView.tsx b/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/TryOutView.tsx index c7cb444dcac..c9f6ce44697 100644 --- a/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/TryOutView.tsx +++ b/workspaces/mi/mi-visualizer/src/views/Forms/IDPConnectorForm/TryOutView.tsx @@ -20,7 +20,7 @@ import { UploadWindow } from "./UploadWindow"; import { MonacoEditor } from "./MonacoEditor"; import styled from "@emotion/styled"; import { useEffect, useState, useRef } from "react"; -import { handleFetchError, SelectedConectionObject, SYSTEM_PROMPT, USER_PROMPT } from "./IdpUtills"; +import { SelectedConectionObject } from "./IdpUtills"; import { ImgAndPdfViewer } from "./ImgAndPdfViewer"; import { Button } from "@wso2/ui-toolkit"; import { ErrorAlert } from "./ErrorAlert"; @@ -128,116 +128,46 @@ export function TryOutView({ const [errors, setErrors] = useState(null); const controllerRef3 = useRef(null); - const extractLlmResponse = (llmResponse: any) => { - try { - let content = ''; - - if (llmResponse.choices?.[0]?.message?.content) { - content = llmResponse.choices[0].message.content; - } else { - throw new Error('Invalid Json'); - } - if (!content || typeof content !== 'string') { - throw new Error('Invalid Json'); - } - content = content.replace(/```json\s*/gi, '').replace(/```\s*/g, '').trim(); - let jsonMatch = content.match(/\{[\s\S]*\}/); - if (jsonMatch) { - content = jsonMatch[0]; - } - const parsedJson = JSON.parse(content); - if (!parsedJson || typeof parsedJson !== 'object') { - throw new Error('Invalid Json'); - } - return parsedJson; - } catch (error: any) { - throw new Error("Invalid Json"); - } - }; - const fillSchema = async () => { if (!tryOutBase64String) return; setIsLoading(true); let base64Images: string[] = []; - if (tryOutBase64String.startsWith("data:application/pdf")) { - const base64 = tryOutBase64String.split(",")[1]; - base64Images = await rpcClient.getMiDiagramRpcClient().convertPdfToBase64Images(base64); - if (!base64Images || base64Images.length === 0) { - setErrors("Pdf processing failed"); - setIsLoading(false); - return; - } - } else { - base64Images.push(tryOutBase64String); - } - - if (selectedConnectionName === "") { - setErrors("Please select an IDP connection to proceed."); - setIsLoading(false); - return; - } - - const selectedConnection = idpConnections.find(conn => conn.name === selectedConnectionName); - if (!selectedConnection) { - setErrors("Selected IDP connection not found."); - setIsLoading(false); - return; - } - const { apiKey, url, model } = selectedConnection; - setErrors(null); - controllerRef3.current = new AbortController(); - try { - const userMessageContent = []; - userMessageContent.push({ type: "text", text: USER_PROMPT }); - for (const base64Image of base64Images) { - userMessageContent.push({ type: "image_url", image_url: { "url": base64Image } }); - } - - const requestBody = { - model: model, - messages: [ - { role: "system", content: SYSTEM_PROMPT }, - { role: "user", content: userMessageContent } - ], - response_format: { - type: "json_schema", - json_schema: { - name: "document_extraction_schema", - schema: JSON.parse(schema), - strict: true, - }, + // Convert PDF to images if needed + if (tryOutBase64String.startsWith("data:application/pdf")) { + const base64 = tryOutBase64String.split(",")[1]; + base64Images = await rpcClient.getMiDiagramRpcClient().convertPdfToBase64Images(base64); + if (!base64Images || base64Images.length === 0) { + setErrors("Pdf processing failed"); + setIsLoading(false); + return; } - + } else { + base64Images.push(tryOutBase64String); } - - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${apiKey}` - }, - body: JSON.stringify(requestBody), - signal: controllerRef3.current.signal + + setErrors(null); + + // Call local LLM via RPC instead of external API + const response = await rpcClient.getMiAiPanelRpcClient().fillIdpSchema({ + jsonSchema: schema, + images: base64Images }); - if (!response.ok) { - const userFriendlyError = handleFetchError(response); - setErrors(userFriendlyError); - setIsLoading(false); - return; - } - const llmResponse = await response.json(); - const extractedJson = extractLlmResponse(llmResponse); - setTryoutOutput(JSON.stringify(extractedJson, null, 2)); + // Parse and display the filled data + const parsedJson = JSON.parse(response.filledData); + setTryoutOutput(JSON.stringify(parsedJson, null, 2)); } catch (error: any) { if (error.name === 'AbortError') { + // User cancelled + } else if (error instanceof SyntaxError) { + setErrors("Invalid JSON response from AI. Please try again."); } else if (error instanceof TypeError) { setErrors("Network error occurred. Please check your connection."); } else { - setErrors("An unexpected error occurred. Please try again."); + setErrors(error.message || "An unexpected error occurred. Please try again."); } } finally { setIsLoading(false); diff --git a/workspaces/mi/mi-visualizer/src/views/Forms/Tests/TestCaseForm.tsx b/workspaces/mi/mi-visualizer/src/views/Forms/Tests/TestCaseForm.tsx index cd7709ffe54..1b20d0d9a15 100644 --- a/workspaces/mi/mi-visualizer/src/views/Forms/Tests/TestCaseForm.tsx +++ b/workspaces/mi/mi-visualizer/src/views/Forms/Tests/TestCaseForm.tsx @@ -29,7 +29,6 @@ import { ParameterManager } from "@wso2/mi-diagram"; import { compareVersions } from "@wso2/mi-diagram/lib/utils/commons"; import { getProjectRuntimeVersion } from "../../AIPanel/utils"; import { AuthenticationDialog, useAuthentication } from "../../../components/AuthenticationDialog"; -import { MI_UNIT_TEST_CASE_GENERATE_BACKEND_URL } from "../../../constants"; export enum TestSuiteType { API = "API", @@ -445,27 +444,21 @@ export function TestCaseForm(props: TestCaseFormProps) { return; } - const token = await rpcClient.getMiDiagramRpcClient().getUserAccessToken(); - const backendRootUri = (await rpcClient.getMiDiagramRpcClient().getBackendRootUrl()).url; - const url = backendRootUri + MI_UNIT_TEST_CASE_GENERATE_BACKEND_URL; - // Read the current test suite file directly - const testSuiteContent = await rpcClient.getMiDiagramRpcClient().handleFileWithFS({ - filePath: props.filePath!, + const testSuiteContent = await rpcClient.getMiDiagramRpcClient().handleFileWithFS({ + filePath: props.filePath!, fileName: path.basename(props.filePath!), - operation: 'read' + operation: 'read' }); - + // Extract test suite name from the file path const testSuiteFileName = path.basename(props.filePath!, '.xml') || 'test-suite'; - + // Use the artifact path for getting context const artifactPath = props.artifactPath || props.filePath!; const contextResponse = await rpcClient.getMiDiagramRpcClient().getSelectiveArtifacts({ path: artifactPath }); - const context = [contextResponse]; const fullContextResponse = await rpcClient.getMiDiagramRpcClient().getWorkspaceContext(); - const full_context = [fullContextResponse]; const pom_file_content = await rpcClient.getMiDiagramRpcClient().getPomFileContent(); @@ -473,113 +466,69 @@ export function TestCaseForm(props: TestCaseFormProps) { const mock_service_details = await rpcClient.getMiDiagramRpcClient().getMockServices(); - let retryCount = 0; - const maxRetries = 2; - const fetchTestCaseGeneration = async (): Promise => { - const requestBody = JSON.stringify({ - context: context[0]?.artifacts || [], - test_file_name: testSuiteFileName, - test_suite_file: testSuiteContent?.content || '', - test_case_description: currentPrompt, - existing_mock_services: mock_service_details?.mockServices || [], - existing_mock_service_names: mock_service_details?.mockServiceNames || [], - num_suggestions: 1, - full_context: full_context[0]?.context || [], - pom_file: pom_file_content?.content || '', - external_connectors: external_connector_details?.connectors || [] - }); + // Call the new RPC method for test case generation + const response = await rpcClient.getMiAiPanelRpcClient().generateUnitTestCase({ + context: contextResponse?.artifacts || [], + testFileName: testSuiteFileName, + testSuiteFile: testSuiteContent?.content || '', + testCaseDescription: currentPrompt, + existingMockServices: mock_service_details?.mockServices || [], + existingMockServiceNames: mock_service_details?.mockServiceNames || [], + fullContext: fullContextResponse?.context || [], + pomFile: pom_file_content?.content || '', + externalConnectors: external_connector_details?.connectors || [] + }); + + // Parse the markdown response + const markdownResponse = response.response; - let response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token.token}` - }, - body: requestBody, + // Extract main unit test XML (first code block is the complete updated test file) + const mainXmlMatch = markdownResponse.match(/```xml\s*([\s\S]*?)```/); + if (!mainXmlMatch) { + throw new Error('No updated unit test XML found in response'); + } + const updatedUnitTestXml = mainXmlMatch[1].trim(); + + // Extract new mock services (if any) + const mockServicePattern = /###\s+([^\n]+\.xml)\s*```xml\s*([\s\S]*?)```/g; + const newMockServices: string[] = []; + const newMockServiceNames: string[] = []; + let mockMatch; + while ((mockMatch = mockServicePattern.exec(markdownResponse)) !== null) { + newMockServiceNames.push(mockMatch[1].trim()); + newMockServices.push(mockMatch[2].trim()); + } + + // Update the test suite file with the complete updated content + const artifact = props.artifactPath || props.filePath!; + await rpcClient.getMiDiagramRpcClient().updateTestSuite({ + path: props.filePath, + content: updatedUnitTestXml, + name: testSuiteFileName, + artifact + }); + + // Write new mock services if any were generated + if (newMockServices.length > 0) { + await rpcClient.getMiDiagramRpcClient().writeMockServices({ + content: newMockServices, + fileNames: newMockServiceNames }); + } - if (response.status === 401) { - // Retrieve a new token - await rpcClient.getMiDiagramRpcClient().refreshAccessToken(); - const newToken = await rpcClient.getMiDiagramRpcClient().getUserAccessToken(); - - // Make the request again with the new token - response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${newToken.token}` - }, - body: requestBody, - }); - } else if (response.status === 403) { - openSignInView({ aiPrompt: currentPrompt }); - return response; - } else if (response.status === 404) { - if (retryCount < maxRetries) { - retryCount++; - const delay = Math.pow(2, retryCount) * 1000; // Exponential backoff - await new Promise(resolve => setTimeout(resolve, delay)); - return fetchTestCaseGeneration(); // Retry the request - } else { - openUpdateExtensionView(); - return response; // Exit the function if maximum retries reached - } - } + // Close the dialog + setShowAIDialog(false); + setAiPrompt(""); - if (!response.ok) { - throw new Error('Failed to generate test case'); - } - return response; - }; - - const response = await fetchTestCaseGeneration(); - const responseBody = await response.clone().json(); - const data = await response.json() as UnitTestCaseApiResponse; - - if (data.event === "test_generation_success") { - // Update the test suite with the new content - if (data.updated_test_file) { - // Extract XML from the response (remove markdown code blocks if present) - let xmlContent = data.updated_test_file; - const xmlMatch = xmlContent.match(/```xml\n([\s\S]*?)\n```/); - if (xmlMatch) { - xmlContent = xmlMatch[1]; - } - - const artifact = props.artifactPath || props.filePath!; - rpcClient.getMiDiagramRpcClient().updateTestSuite({ - path: props.filePath, - content: xmlContent, - name: testSuiteFileName, - artifact - }).then(() => { - // Close the dialog and open the updated test suite file - setShowAIDialog(false); - setAiPrompt(""); - - // Open the updated test suite file instead of going back to overview - rpcClient.getMiVisualizerRpcClient().openView({ - type: EVENT_TYPE.OPEN_VIEW, - location: { - view: MACHINE_VIEW.TestSuite, - documentUri: props.filePath - } - }); - }); + // Open the updated test suite file + rpcClient.getMiVisualizerRpcClient().openView({ + type: EVENT_TYPE.OPEN_VIEW, + location: { + view: MACHINE_VIEW.TestSuite, + documentUri: props.filePath } + }); - // Handle mock services if they are provided - if (data.mock_services && data.mock_services.length > 0) { - rpcClient.getMiDiagramRpcClient().writeMockServices({ - content: data.mock_services, - fileNames: data.mock_service_names || [] - }); - } - } else { - throw new Error("Failed to generate test case: " + (data.error || "Unknown error")); - } - setIsLoaded(true); } catch (error) { diff --git a/workspaces/mi/mi-visualizer/src/views/Forms/Tests/TestSuiteForm.tsx b/workspaces/mi/mi-visualizer/src/views/Forms/Tests/TestSuiteForm.tsx index 7c505114cd9..27275d3ec13 100644 --- a/workspaces/mi/mi-visualizer/src/views/Forms/Tests/TestSuiteForm.tsx +++ b/workspaces/mi/mi-visualizer/src/views/Forms/Tests/TestSuiteForm.tsx @@ -30,7 +30,6 @@ import { UnitTest, TestCase, STNode } from "@wso2/mi-syntax-tree/lib/src"; import path from "path"; import { getTestSuiteXML } from "../../../utils/template-engine/mustache-templates/TestSuite"; import { SelectMockService } from "./MockServices/SelectMockService"; -import { MI_UNIT_TEST_GENERATION_BACKEND_URL } from "../../../constants"; import { getParamManagerFromValues, getParamManagerValues, ParamConfig, ParamField, ParamManager, ParamValue } from "@wso2/mi-diagram"; import { normalize } from "upath"; import { compareVersions } from "@wso2/mi-diagram/lib/utils/commons"; @@ -205,11 +204,9 @@ export function TestSuiteForm(props: TestSuiteFormProps) { return; } - const token = await rpcClient.getMiDiagramRpcClient().getUserAccessToken(); - const backendRootUri = (await rpcClient.getMiDiagramRpcClient().getBackendRootUrl()).url; - const url = backendRootUri + MI_UNIT_TEST_GENERATION_BACKEND_URL; const artifact = isWindows ? path.win32.join(projectUri, values.artifact) : path.join(projectUri, values.artifact); + // Gather context for unit test generation var context: GetSelectiveArtifactsResponse[] = []; await rpcClient?.getMiDiagramRpcClient()?.getSelectiveArtifacts({ path: artifact }).then((response: GetSelectiveArtifactsResponse) => { context.push(response); @@ -235,97 +232,67 @@ export function TestSuiteForm(props: TestSuiteFormProps) { mock_service_details = response; }); - let retryCount = 0; - const maxRetries = 2; - const fetchUnitTests = async (): Promise => { - const requestBody = JSON.stringify({ - context: context[0].artifacts, - test_file_name: values.name, - num_suggestions: 1, - type: "generate_unit_tests" , - full_context: full_context[0].context, - pom_file: pom_file_content.content, - external_connectors: external_connector_details.connectors - }); - - let response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token.token}` - }, - body: requestBody, - }); + // Call the new RPC method for unit test generation + const response = await rpcClient.getMiAiPanelRpcClient().generateUnitTest({ + context: context[0].artifacts, + testFileName: values.name, + fullContext: full_context[0].context, + pomFile: pom_file_content.content, + externalConnectors: external_connector_details.connectors + }); - if (response.status === 401) { - // Retrieve a new token - await rpcClient.getMiDiagramRpcClient().refreshAccessToken(); - const token = await rpcClient.getMiDiagramRpcClient().getUserAccessToken(); - - // Make the request again with the new token - response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token.token}` - }, - body: JSON.stringify({ context: context[0].artifacts, testFileName: values.name, num_suggestions: 1, type: "generate_unit_tests" }), - }); - } else if (response.status === 403) { - openSignInView(values); - return response; - } else if (response.status === 404) { - if (retryCount < maxRetries) { - retryCount++; - const delay = Math.pow(2, retryCount) * 1000; // Exponential backoff - await new Promise(resolve => setTimeout(resolve, delay)); - return fetchUnitTests(); // Retry the request - } else { - openUpdateExtensionView(); - return response; // Exit the function if maximum retries reached - } - } + // Parse the markdown response + const markdownResponse = response.response; - if (!response.ok) { - throw new Error('Failed to create unit tests'); - } - return response; + // Extract main unit test XML + const mainXmlMatch = markdownResponse.match(/```xml\s*([\s\S]*?)```/); + if (!mainXmlMatch) { + throw new Error('No unit test XML found in response'); + } + const unitTestXml = mainXmlMatch[1].trim(); + + // Extract mock services (if any) + const mockServicePattern = /###\s+([^\n]+\.xml)\s*```xml\s*([\s\S]*?)```/g; + const mockServices: string[] = []; + const mockServiceNames: string[] = []; + let mockMatch; + while ((mockMatch = mockServicePattern.exec(markdownResponse)) !== null) { + mockServiceNames.push(mockMatch[1].trim()); + mockServices.push(mockMatch[2].trim()); } - const response = await fetchUnitTests(); - const data = await response.json() as UnitTestApiResponse; - if (data.event === "test_generation_success") { - // Extract xml from the response - const xml = data.tests.match(/```xml\n([\s\S]*?)\n```/gs); - if (xml) { - // Remove the Markdown code block delimiters - const cleanedXml = xml.map(xml => xml.replace(/```xml\n|```/g, '')); - rpcClient.getMiDiagramRpcClient().updateTestSuite({ path: props.filePath, content: cleanedXml[0], name: values.name, artifact }).then(() => { - // Calculate the file path for the newly created test suite - let testSuiteFilePath = props.filePath; - if (!testSuiteFilePath) { - const testDir = path.join(projectUri, 'src', 'test', "wso2mi"); - testSuiteFilePath = path.join(testDir, `${values.name}.xml`); - } - - // Open the newly created/updated test suite file - rpcClient.getMiVisualizerRpcClient().openView({ - type: EVENT_TYPE.OPEN_VIEW, - location: { - view: MACHINE_VIEW.TestSuite, - documentUri: testSuiteFilePath - } - }); - }); - if(data.mock_services && data.mock_services.length > 0) { - rpcClient.getMiDiagramRpcClient().writeMockServices({ content: data.mock_services, fileNames:data.mock_service_names }); - } - } else { - console.error('No XMLs found in the response'); - } - } else { - throw new Error("Failed to generate suggestions: " + data.error); + // Write the unit test file + await rpcClient.getMiDiagramRpcClient().updateTestSuite({ + path: props.filePath, + content: unitTestXml, + name: values.name, + artifact + }); + + // Calculate the file path for the newly created test suite + let testSuiteFilePath = props.filePath; + if (!testSuiteFilePath) { + const testDir = path.join(projectUri, 'src', 'test', "wso2mi"); + testSuiteFilePath = path.join(testDir, `${values.name}.xml`); + } + + // Write mock services if any were generated + if (mockServices.length > 0) { + await rpcClient.getMiDiagramRpcClient().writeMockServices({ + content: mockServices, + fileNames: mockServiceNames + }); } + + // Open the newly created/updated test suite file + rpcClient.getMiVisualizerRpcClient().openView({ + type: EVENT_TYPE.OPEN_VIEW, + location: { + view: MACHINE_VIEW.TestSuite, + documentUri: testSuiteFilePath + } + }); + setIsLoaded(true); } catch (error) { diff --git a/workspaces/mi/mi-visualizer/src/views/LoggedOutWindow/index.tsx b/workspaces/mi/mi-visualizer/src/views/LoggedOutWindow/index.tsx index 4fa8f2abc59..284ad692807 100644 --- a/workspaces/mi/mi-visualizer/src/views/LoggedOutWindow/index.tsx +++ b/workspaces/mi/mi-visualizer/src/views/LoggedOutWindow/index.tsx @@ -78,6 +78,34 @@ const PostLoginSection = styled.div` margin-bottom: 16px; `; +const Divider = styled.div` + display: flex; + align-items: center; + color: var(--vscode-widget-border); + font-size: 12px; + width: 100%; + &::before, + &::after { + content: ""; + flex: 1; + border-bottom: 1px solid var(--vscode-widget-border); + margin: 0 8px; + } +`; + +const TextButton = styled.button` + background: none; + border: none; + color: var(--vscode-textLink-foreground); + font-size: 13px; + cursor: pointer; + padding: 0; + margin-top: -6px; + &:hover { + text-decoration: underline; + } +`; + const LegalNotice: React.FC = () => { return ( @@ -108,6 +136,10 @@ export const SignInToCopilotMessage = (props: { showProjectHeader?: boolean }) = rpcClient.sendAIStateEvent(AI_EVENT_TYPE.LOGIN); }; + const handleAnthropicKeyClick = () => { + rpcClient.sendAIStateEvent(AI_EVENT_TYPE.AUTH_WITH_API_KEY); + }; + return ( @@ -134,6 +166,8 @@ export const SignInToCopilotMessage = (props: { showProjectHeader?: boolean }) = Login to MI Copilot + or + Enter your Anthropic API key );