From 21289f6477a83efb791a5b8d04846626eb0d7f53 Mon Sep 17 00:00:00 2001 From: madushajg Date: Thu, 9 Oct 2025 20:18:09 +0530 Subject: [PATCH 01/91] Add 'create as workspace' option in creation form --- .../src/rpc-types/bi-diagram/interfaces.ts | 2 + .../rpc-managers/bi-diagram/rpc-manager.ts | 10 +- .../ballerina-extension/src/utils/bi.ts | 117 ++++++++++++++---- .../ConfigureProjectForm.tsx | 4 + .../BI/ProjectForm/ProjectFormFields.tsx | 35 +++++- .../src/views/BI/ProjectForm/index.tsx | 6 +- 6 files changed, 143 insertions(+), 31 deletions(-) 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 1e756452a72..baf58a15516 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 @@ -26,6 +26,8 @@ export interface ProjectRequest { packageName: string; projectPath: string; createDirectory: boolean; + createAsWorkspace: boolean; + workspaceName: string; orgName?: string; version?: string; } 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 2766bc90a2c..49bb797cc19 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 @@ -168,7 +168,7 @@ import { BreakpointManager } from "../../features/debugger/breakpoint-manager"; import { 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, createBIAutomation, createBIFunction, createBIProjectPure, createBIWorkspace, openInVSCode } from "../../utils/bi"; import { writeBallerinaFileDidOpen } from "../../utils/modification"; import { updateSourceCode } from "../../utils/source-utils"; import { checkProjectDiagnostics, removeUnusedImports } from "../ai-panel/repair-utils"; @@ -576,7 +576,13 @@ 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 getWorkspaces(): Promise { diff --git a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts index 5a4610ab88f..4ebc4d42700 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts @@ -31,6 +31,18 @@ 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,15 +98,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 => { @@ -147,23 +150,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 }); } + + return resolvedPath; +} - let finalOrgName = projectRequest.orgName; - if (!finalOrgName) { - finalOrgName = getUsername(); - } +/** + * 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 +220,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 +259,6 @@ sticky = true `; - - // Create Ballerina.toml file const ballerinaTomlPath = path.join(projectRoot, 'Ballerina.toml'); writeBallerinaFileDidOpen(ballerinaTomlPath, ballerinaTomlContent); @@ -247,6 +310,10 @@ sticky = true fs.writeFileSync(gitignorePath, gitignoreContent.trim()); console.log(`BI project created successfully at ${projectRoot}`); + return projectRoot; +} + +export function openInVSCode(projectRoot: string) { commands.executeCommand('vscode.openFolder', Uri.file(path.resolve(projectRoot))); } 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/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"} From b0a10f6ff3b2b5485fc32c59dbe4582618b3755f Mon Sep 17 00:00:00 2001 From: madushajg Date: Fri, 10 Oct 2025 18:39:24 +0530 Subject: [PATCH 02/91] Fix handling of local connectors in project artifacts by ensuring a default empty array is used when no modules are returned. --- .../ballerina-extension/src/utils/project-artifacts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts index c9638370f2d..47ddfdf8527 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts @@ -443,7 +443,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, From 11553073ae1060270e296a2c28e282f65b975383 Mon Sep 17 00:00:00 2001 From: madushajg Date: Fri, 10 Oct 2025 23:57:18 +0530 Subject: [PATCH 03/91] Enable rendering package overview when opening ballerina workspace --- .../ballerina-extension/src/stateMachine.ts | 87 ++++++++++++------- .../ballerina-extension/src/utils/config.ts | 59 ++++++++++++- .../src/utils/project-artifacts.ts | 23 +++-- 3 files changed, 130 insertions(+), 39 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index dbc86d127c5..cbcdb72ba6b 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -14,7 +14,7 @@ import { extension } from './BalExtensionContext'; import { BiDiagramRpcManager } from './rpc-managers/bi-diagram/rpc-manager'; import { AIStateMachine } from './views/ai-panel/aiMachine'; import { StateMachinePopup } from './stateMachinePopup'; -import { checkIsBallerina, checkIsBI, fetchScope, getOrgPackageName, UndoRedoManager } from './utils'; +import { checkIsBallerinaPackage, checkIsBallerinaWorkspace, checkIsBI, fetchScope, getFirstWorkspacePackageName, getOrgPackageName, UndoRedoManager } from './utils'; import { buildProjectArtifactsStructure } from './utils/project-artifacts'; interface MachineContext extends VisualizerLocation { @@ -717,31 +717,30 @@ 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); } -async function handleMultipleWorkspaces(workspaceFolders: readonly WorkspaceFolder[]) { - const balProjects = workspaceFolders.filter(folder => checkIsBallerina(folder.uri)); +async function handleMultipleWorkspaceFolders(workspaceFolders: readonly WorkspaceFolder[]) { + const balProjects = workspaceFolders.filter(folder => checkIsBallerinaPackage(folder.uri)); if (balProjects.length > 1) { - 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/organize-ballerina-code/')); + } }); - - 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); @@ -753,19 +752,45 @@ async function handleMultipleWorkspaces(workspaceFolders: readonly WorkspaceFold return { isBI: false, projectPath: '' }; } -async function handleSingleWorkspace(workspaceURI: any) { - const isBallerina = checkIsBallerina(workspaceURI); - const isBI = isBallerina && checkIsBI(workspaceURI); - const scope = fetchScope(workspaceURI); - const projectPath = isBallerina ? workspaceURI.fsPath : ""; - const { orgName, packageName } = getOrgPackageName(projectPath); - - setBIContext(isBI); - if (!isBI) { - console.error("No BI enabled workspace found"); +async function handleSingleWorkspaceFolder(workspaceURI: any) { + const isBallerinaWorkspace = checkIsBallerinaWorkspace(workspaceURI); + + if (isBallerinaWorkspace) { + const firstPackage = getFirstWorkspacePackageName(workspaceURI); + + if (firstPackage) { + const packagePath = path.join(workspaceURI.fsPath, firstPackage); + const packageUri = Uri.file(packagePath); + + const isBallerinaPackage = 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, scope, orgName, packageName }; + } else { + return { isBI: false, projectPath: '' }; + } + } else { + const isBallerinaPackage = 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 }; } - - return { isBI, projectPath, scope, orgName, packageName }; } function setBIContext(isBI: boolean) { diff --git a/workspaces/ballerina/ballerina-extension/src/utils/config.ts b/workspaces/ballerina/ballerina-extension/src/utils/config.ts index 53073684086..2d23128b64f 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -126,9 +126,64 @@ export function checkIsBI(uri: Uri): boolean { return false; // Return false if isBI is not set } -export function checkIsBallerina(uri: Uri): boolean { +export function checkIsBallerinaPackage(uri: Uri): boolean { 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 { + // Read the file content and check for [package] section + const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); + const packageSectionRegex = /\[package\]/; + return packageSectionRegex.test(tomlContent); + } 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; + } +} + +export function checkIsBallerinaWorkspace(uri: Uri): boolean { + const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); + + // First check if the file exists + if (!fs.existsSync(ballerinaTomlPath)) { + return false; + } + + try { + // Read the file content and check for [workspace] section + const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); + const workspaceSectionRegex = /\[workspace\]/; + return workspaceSectionRegex.test(tomlContent); + } 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; + } +} + +export function getFirstWorkspacePackageName(uri: Uri): string | null { + const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); + + try { + // Read the file content + const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); + + // Regular expression to match packages array in [workspace] section + // This matches: packages = ["package1", "package2", ...] + const packagesRegex = /packages\s*=\s*\[\s*"([^"]+)"/; + const match = tomlContent.match(packagesRegex); + + return match?.[1] || null; + } catch (error) { + // If there's an error reading the file, return null + console.error(`Error reading workspace Ballerina.toml: ${error}`); + return null; + } } export function getOrgPackageName(projectPath: string): { orgName: string, packageName: string } { diff --git a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts index 47ddfdf8527..88a200ceabd 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts @@ -53,13 +53,24 @@ export async function buildProjectArtifactsStructure(projectDir: string, langCli } // 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; - // Get the project name from the ballerina.toml file - const commonRpcManager = new CommonRpcManager(); - const tomlValues = await commonRpcManager.getCurrentProjectTomlValues(); - if (tomlValues && tomlValues.package.title) { - projectName = tomlValues.package.title; + + let projectName = ""; + if (workspace) { + projectName = workspace.name; + + // Get the project name from the ballerina.toml file + const commonRpcManager = new CommonRpcManager(); + const tomlValues = await commonRpcManager.getCurrentProjectTomlValues(); + if (tomlValues && tomlValues.package.title) { + projectName = tomlValues.package.title; + } + } else { + // Project defined within a Ballerina workspace + projectName = path.basename(projectDir); + + // TODO: Get the project name from the package Ballerina.toml file } + result.projectName = projectName; if (isUpdate) { From 967d180d4ee66a333f83969d2d39f54a7c34c8d4 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 14 Oct 2025 13:49:36 +0530 Subject: [PATCH 04/91] Fix rendering BI project-explorer for BI workspaces --- .../ballerina-extension/src/stateMachine.ts | 42 ++++++++++++++----- .../ballerina-extension/src/utils/config.ts | 35 ++++++++++++++++ .../project-explorer-provider.ts | 28 +++++++++---- 3 files changed, 87 insertions(+), 18 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index cbcdb72ba6b..abc0170c4dd 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -14,9 +14,18 @@ import { extension } from './BalExtensionContext'; import { BiDiagramRpcManager } from './rpc-managers/bi-diagram/rpc-manager'; import { AIStateMachine } from './views/ai-panel/aiMachine'; import { StateMachinePopup } from './stateMachinePopup'; -import { checkIsBallerinaPackage, checkIsBallerinaWorkspace, checkIsBI, fetchScope, getFirstWorkspacePackageName, getOrgPackageName, UndoRedoManager } from './utils'; +import { checkIsBallerinaPackage, checkIsBallerinaWorkspace, checkIsBI, fetchScope, getFirstWorkspacePackageName, getOrgPackageName, getWorkspacePackageNames, UndoRedoManager } 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; @@ -82,6 +91,7 @@ const stateMachine = createMachine( assign({ isBI: (context, event) => event.data.isBI, projectUri: (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, @@ -105,6 +115,7 @@ const stateMachine = createMachine( actions: assign({ isBI: (context, event) => event.data.isBI, projectUri: (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, @@ -116,6 +127,7 @@ const stateMachine = createMachine( actions: assign({ isBI: (context, event) => event.data.isBI, projectUri: (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, @@ -321,8 +333,8 @@ const stateMachine = createMachine( registerProjectArtifactsStructure: (context, event) => { return new Promise(async (resolve, reject) => { try { - // 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.projectUri || context.workspacePath) { // Add a 2 second delay before registering artifacts await new Promise(resolve => setTimeout(resolve, 1000)); @@ -709,7 +721,7 @@ function getLastHistory() { return historyStack?.[historyStack?.length - 1]; } -async function checkForProjects(): Promise<{ isBI: boolean, projectPath: string, scope?: SCOPE }> { +async function checkForProjects(): Promise { const workspaceFolders = workspace.workspaceFolders; if (!workspaceFolders) { @@ -723,7 +735,7 @@ async function checkForProjects(): Promise<{ isBI: boolean, projectPath: string, return await handleSingleWorkspaceFolder(workspaceFolders[0].uri); } -async function handleMultipleWorkspaceFolders(workspaceFolders: readonly WorkspaceFolder[]) { +async function handleMultipleWorkspaceFolders(workspaceFolders: readonly WorkspaceFolder[]): Promise { const balProjects = workspaceFolders.filter(folder => checkIsBallerinaPackage(folder.uri)); if (balProjects.length > 1) { @@ -752,14 +764,24 @@ async function handleMultipleWorkspaceFolders(workspaceFolders: readonly Workspa return { isBI: false, projectPath: '' }; } -async function handleSingleWorkspaceFolder(workspaceURI: any) { +async function handleSingleWorkspaceFolder(workspaceURI: Uri): Promise { const isBallerinaWorkspace = checkIsBallerinaWorkspace(workspaceURI); if (isBallerinaWorkspace) { - const firstPackage = getFirstWorkspacePackageName(workspaceURI); + // TODO: Provide a quick pick to select the package to load the WSO2 Integrator + // if the user selects a package, then load the WSO2 Integrator for that package + // if the user cancels the selection, then load the WSO2 Integrator for the first package + const packages = getWorkspacePackageNames(workspaceURI); + const userSelectedPackage = 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 + }); + + const targetPackage = userSelectedPackage || getFirstWorkspacePackageName(workspaceURI); - if (firstPackage) { - const packagePath = path.join(workspaceURI.fsPath, firstPackage); + if (targetPackage) { + const packagePath = path.join(workspaceURI.fsPath, targetPackage); const packageUri = Uri.file(packagePath); const isBallerinaPackage = checkIsBallerinaPackage(packageUri); @@ -773,7 +795,7 @@ async function handleSingleWorkspaceFolder(workspaceURI: any) { console.error("No BI enabled workspace found"); } - return { isBI, projectPath, scope, orgName, packageName }; + return { isBI, projectPath, workspacePath: workspaceURI.fsPath, scope, orgName, packageName }; } else { return { isBI: false, projectPath: '' }; } diff --git a/workspaces/ballerina/ballerina-extension/src/utils/config.ts b/workspaces/ballerina/ballerina-extension/src/utils/config.ts index 2d23128b64f..400a2a0f605 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -166,6 +166,41 @@ export function checkIsBallerinaWorkspace(uri: Uri): boolean { } } +export function getWorkspacePackageNames(uri: Uri) { + const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); + + try { + // Read the file content + const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); + + // Regular expression to match the entire packages array in [workspace] section + // This matches: packages = ["package1", "package2", ...] + const packagesRegex = /packages\s*=\s*\[([\s\S]*?)\]/; + const match = tomlContent.match(packagesRegex); + + if (!match || !match[1]) { + return null; + } + + // Extract all package names from the array content + const arrayContent = match[1]; + const packageNameRegex = /"([^"]+)"/g; + const packageNames: string[] = []; + let packageMatch; + + while ((packageMatch = packageNameRegex.exec(arrayContent)) !== null) { + packageNames.push(packageMatch[1]); + } + + return packageNames.length > 0 ? packageNames : null; + } catch (error) { + // If there's an error reading the file, return null + console.error(`Error reading workspace Ballerina.toml: ${error}`); + return null; + } +} + + export function getFirstWorkspacePackageName(uri: Uri): string | null { const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); 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 3d38d3f2460..4f8509a685f 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,29 @@ async function getProjectStructureData(): Promise { const data: ProjectExplorerEntry[] = []; if (extension.langClient) { const stateContext: VisualizerLocation = await commands.executeCommand(SHARED_COMMANDS.GET_STATE_CONTEXT); - const workspace = vscode + const ballerinaWorkspace = stateContext?.workspacePath; + const workspaceFolderOfPackage = vscode .workspace .workspaceFolders .find(folder => folder.uri.fsPath === stateContext.projectUri); - if (!workspace) { - return []; + let packageName = workspaceFolderOfPackage?.name; + let packagePath = workspaceFolderOfPackage?.uri.fsPath; + + if (!workspaceFolderOfPackage) { + if (ballerinaWorkspace) { + packageName = path.basename(Uri.parse(stateContext.projectUri).path); + packagePath = stateContext.projectUri; + } else { + return []; + } } // 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 +168,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 ); From 4b4f419fb26f3c7b346de7653f1b78688e2a5875 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 14 Oct 2025 16:34:20 +0530 Subject: [PATCH 05/91] Add wide-chevron SVG icon to common libraries --- .../font-wso2-vscode/src/icons/wide-chevron.svg | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 workspaces/common-libs/font-wso2-vscode/src/icons/wide-chevron.svg diff --git a/workspaces/common-libs/font-wso2-vscode/src/icons/wide-chevron.svg b/workspaces/common-libs/font-wso2-vscode/src/icons/wide-chevron.svg new file mode 100644 index 00000000000..ac16ed6d2df --- /dev/null +++ b/workspaces/common-libs/font-wso2-vscode/src/icons/wide-chevron.svg @@ -0,0 +1,7 @@ + + + + + + + From f4198cbe763b0e214bf60e7410290fa19e6feffd Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 14 Oct 2025 23:11:45 +0530 Subject: [PATCH 06/91] Enhance state machine and top navigation bar to include package information in history entries --- .../src/rpc-managers/common/rpc-manager.ts | 1 + .../ballerina-extension/src/stateMachine.ts | 10 +++- .../src/components/TopNavigationBar/index.tsx | 52 +++++++++++++++---- 3 files changed, 53 insertions(+), 10 deletions(-) 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 5207d4d34a5..a5bc5e264f3 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 @@ -254,6 +254,7 @@ export class CommonRpcManager implements CommonRPCAPI { const workspaceFolderPath = workspaceFolders[0].uri.fsPath; // Check if workspaceFolderPath is a Ballerina project // Assuming a Ballerina project must contain a 'Ballerina.toml' file + // TODO: This logic needs to be updated for multi-package workspaces const ballerinaProjectFile = path.join(workspaceFolderPath, 'Ballerina.toml'); if (fs.existsSync(ballerinaProjectFile)) { return workspaceFolderPath; diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index abc0170c4dd..43ac817adc1 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -460,10 +460,17 @@ const stateMachine = createMachine( return new Promise(async (resolve, reject) => { if (!context.view && context.langClient) { if (!context.position || ("groupId" in context.position)) { - history.push({ location: { view: MACHINE_VIEW.Overview, documentUri: context.documentUri } }); + history.push({ + location: { + view: MACHINE_VIEW.Overview, + documentUri: context.documentUri, + package: context.package + } + }); return resolve(); } const view = await getView(context.documentUri, context.position, context?.projectUri); + view.location.package = context.package; history.push(view); return resolve(); } else { @@ -473,6 +480,7 @@ const stateMachine = createMachine( documentUri: context.documentUri, position: context.position, identifier: context.identifier, + package: context.package, type: context?.type, isGraphql: context?.isGraphql, addType: context?.addType, diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx index 65db8c07a93..756fa04427e 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx @@ -20,7 +20,7 @@ import React, { useEffect, useState } from "react"; import styled from "@emotion/styled"; import { Icon } from "@wso2/ui-toolkit"; import { useRpcContext } from "@wso2/ballerina-rpc-client"; -import { MACHINE_VIEW } from "@wso2/ballerina-core"; +import { HistoryEntry, MACHINE_VIEW } from "@wso2/ballerina-core"; const NavContainer = styled.div` display: flex; @@ -60,7 +60,14 @@ const IconButton = styled.div` } `; -const BreadcrumbItem = styled.span<{ clickable?: boolean }>` +const BreadcrumbItem = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 2px; +`; + +const BreadcrumbText = styled.span<{ clickable?: boolean }>` ${({ clickable }: { clickable?: boolean }) => clickable && ` @@ -71,6 +78,18 @@ const BreadcrumbItem = styled.span<{ clickable?: boolean }>` `} `; +const PackageName = styled.div` + color: var(--vscode-foreground); + background-color: var(--vscode-editor-inactiveSelectionBackground); + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding: 2px 4px; + font-size: 10px; + border-radius: 4px; +`; + interface TopNavigationBarProps { onBack?: () => void; onHome?: () => void; @@ -79,7 +98,7 @@ interface TopNavigationBarProps { export function TopNavigationBar(props: TopNavigationBarProps) { const { onBack, onHome } = props; const { rpcClient } = useRpcContext(); - const [history, setHistory] = useState([]); + const [history, setHistory] = useState([]); useEffect(() => { rpcClient @@ -126,12 +145,27 @@ export function TopNavigationBar(props: TopNavigationBarProps) { existingLabels.add(shortName); return ( - {index > 0 && /} - index < history.length - 1 && handleCrumbClick(index)} - > - {shortName} + {index > 0 && ( + + )} + + index < history.length - 1 && handleCrumbClick(index)} + > + {shortName} + + {crumb.location?.workspacePath && crumb.location.package && ( + {crumb.location.package} + )} ); From 217d8278ac97a277e07afa968c77ac100730146e Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 14 Oct 2025 23:18:57 +0530 Subject: [PATCH 07/91] Add rpc fn to check for ballerina workspaces --- .../ballerina-core/src/rpc-types/common/index.ts | 1 + .../ballerina-core/src/rpc-types/common/rpc-type.ts | 1 + .../src/rpc-managers/common/rpc-handler.ts | 2 ++ .../src/rpc-managers/common/rpc-manager.ts | 5 +++++ .../src/rpc-clients/common/rpc-client.ts | 9 +++++++-- 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/common/index.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/common/index.ts index 964b6cea6d6..f4e89f44d87 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/common/index.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/common/index.ts @@ -49,5 +49,6 @@ export interface CommonRPCAPI { getWorkspaceRoot: () => Promise; showErrorMessage: (params: ShowErrorMessageRequest) => void; getCurrentProjectTomlValues: () => Promise>; + isBallerinaWorkspace: () => Promise; } diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/common/rpc-type.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/common/rpc-type.ts index c1816ec958b..7dc50ff555f 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/common/rpc-type.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/common/rpc-type.ts @@ -51,3 +51,4 @@ export const isNPSupported: RequestType = { method: `${_preFix}/i export const getWorkspaceRoot: RequestType = { method: `${_preFix}/getWorkspaceRoot` }; export const showErrorMessage: NotificationType = { method: `${_preFix}/showErrorMessage` }; export const getCurrentProjectTomlValues: RequestType = { method: `${_preFix}/getCurrentProjectTomlValues` }; +export const isBallerinaWorkspace: RequestType = { method: `${_preFix}/isBallerinaWorkspace` }; diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/rpc-handler.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/rpc-handler.ts index 82ab5d908c4..f5a548f3523 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/rpc-handler.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/rpc-handler.ts @@ -34,6 +34,7 @@ import { getWorkspaceFiles, getWorkspaceRoot, goToSource, + isBallerinaWorkspace, isNPSupported, openExternalUrl, runBackgroundTerminalCommand, @@ -60,4 +61,5 @@ export function registerCommonRpcHandlers(messenger: Messenger) { messenger.onRequest(getWorkspaceRoot, () => rpcManger.getWorkspaceRoot()); messenger.onNotification(showErrorMessage, (args: ShowErrorMessageRequest) => rpcManger.showErrorMessage(args)); messenger.onRequest(getCurrentProjectTomlValues, () => rpcManger.getCurrentProjectTomlValues()); + messenger.onRequest(isBallerinaWorkspace, () => rpcManger.isBallerinaWorkspace()); } 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 a5bc5e264f3..c046ed868c4 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 @@ -275,4 +275,9 @@ export class CommonRpcManager implements CommonRPCAPI { } } } + + async isBallerinaWorkspace(): Promise { + // ADD YOUR IMPLEMENTATION HERE + throw new Error('Not implemented'); + } } 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 cbbcbeafe3a..438c629aa2f 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 @@ -48,7 +48,8 @@ import { getCurrentProjectTomlValues, TomlValues, selectFileOrFolderPath, - showErrorMessage + showErrorMessage, + isBallerinaWorkspace } from "@wso2/ballerina-core"; import { HOST_EXTENSION } from "vscode-messenger-common"; import { Messenger } from "vscode-messenger-webview"; @@ -111,8 +112,12 @@ 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); } + + isBallerinaWorkspace(): Promise { + return this._messenger.sendRequest(isBallerinaWorkspace, HOST_EXTENSION); + } } From e78d60d9d371ec984119b22578143a09ba776546 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 14 Oct 2025 23:42:34 +0530 Subject: [PATCH 08/91] Display package names in top nav bar only for worskapces --- .../ballerina-core/src/state-machine-types.ts | 1 + .../src/rpc-managers/common/rpc-manager.ts | 10 +++++++--- .../src/components/TopNavigationBar/index.tsx | 15 ++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/workspaces/ballerina/ballerina-core/src/state-machine-types.ts b/workspaces/ballerina/ballerina-core/src/state-machine-types.ts index f3abdb08668..7e34569825c 100644 --- a/workspaces/ballerina/ballerina-core/src/state-machine-types.ts +++ b/workspaces/ballerina/ballerina-core/src/state-machine-types.ts @@ -120,6 +120,7 @@ export interface VisualizerLocation { view?: MACHINE_VIEW | null; documentUri?: string; projectUri?: string; + workspacePath?: string; identifier?: string; parentIdentifier?: string; artifactType?: DIRECTORY_MAP; 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 c046ed868c4..1dcea2dd78d 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 @@ -46,7 +46,7 @@ import { Uri, commands, env, window, workspace, MarkdownString } from "vscode"; import { URI } from "vscode-uri"; import { extension } from "../../BalExtensionContext"; import { StateMachine } from "../../stateMachine"; -import { goToSource } from "../../utils"; +import { checkIsBallerinaWorkspace, goToSource } from "../../utils"; import { askFileOrFolderPath, askFilePath, askProjectPath, BALLERINA_INTEGRATOR_ISSUES_URL, getUpdatedSource } from "./utils"; import { parse } from 'toml'; import * as fs from 'fs'; @@ -277,7 +277,11 @@ export class CommonRpcManager implements CommonRPCAPI { } async isBallerinaWorkspace(): Promise { - // ADD YOUR IMPLEMENTATION HERE - throw new Error('Not implemented'); + const workspaceFolders = workspace.workspaceFolders; + if (!workspaceFolders) { + throw new Error("No workspaces found."); + } + const workspaceFolderPath = workspaceFolders[0].uri.fsPath; + return checkIsBallerinaWorkspace(Uri.file(workspaceFolderPath)); } } diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx index 756fa04427e..7ba6efc24bc 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx @@ -16,7 +16,7 @@ * under the License. */ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import styled from "@emotion/styled"; import { Icon } from "@wso2/ui-toolkit"; import { useRpcContext } from "@wso2/ballerina-rpc-client"; @@ -125,6 +125,11 @@ export function TopNavigationBar(props: TopNavigationBarProps) { rpcClient.getVisualizerRpcClient().goSelected(index); } }; + + const isBallerinaWorkspace = useMemo(() => { + return rpcClient.getCommonRpcClient().isBallerinaWorkspace(); + }, []); + // HACK: To remove forms from breadcrumb. Will have to fix from the state machine side const hackToSkipForms = ["overview", "automation", "service", "function", "add natural function", "data mapper", "connection"]; const existingLabels = new Set(); @@ -150,9 +155,9 @@ export function TopNavigationBar(props: TopNavigationBarProps) { name="wide-chevron" iconSx={{ color: "var(--vscode-foreground)", - fontSize: crumb.location?.workspacePath ? "20px" : "15px", - opacity: 0.5 } - } + fontSize: isBallerinaWorkspace ? "20px" : "15px", + opacity: 0.5 + }} sx={{ alignSelf: "center" }} /> )} @@ -163,7 +168,7 @@ export function TopNavigationBar(props: TopNavigationBarProps) { > {shortName} - {crumb.location?.workspacePath && crumb.location.package && ( + {isBallerinaWorkspace && crumb.location.package && ( {crumb.location.package} )} From 7cd834c66177bf2c0890b7f54ae518ff71cc7179 Mon Sep 17 00:00:00 2001 From: madushajg Date: Wed, 15 Oct 2025 22:52:41 +0530 Subject: [PATCH 09/91] Refactor TopNavigationBar to enhance package name display with icon and improved styling --- .../src/components/TopNavigationBar/index.tsx | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx index 7ba6efc24bc..e7ddb604063 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx @@ -18,7 +18,7 @@ import React, { useEffect, useMemo, useState } from "react"; import styled from "@emotion/styled"; -import { Icon } from "@wso2/ui-toolkit"; +import { Codicon, Icon } from "@wso2/ui-toolkit"; import { useRpcContext } from "@wso2/ballerina-rpc-client"; import { HistoryEntry, MACHINE_VIEW } from "@wso2/ballerina-core"; @@ -78,16 +78,24 @@ const BreadcrumbText = styled.span<{ clickable?: boolean }>` `} `; -const PackageName = styled.div` +const PackageContainer = styled.div` + display: flex; + align-items: center; + gap: 4px; color: var(--vscode-foreground); background-color: var(--vscode-editor-inactiveSelectionBackground); - max-width: 100px; + max-width: 120px; + overflow: hidden; + padding: 3px 4px; + font-size: 10px; + border-radius: 5px; + line-height: 1; +`; + +const PackageName = styled.span` overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - padding: 2px 4px; - font-size: 10px; - border-radius: 4px; `; interface TopNavigationBarProps { @@ -169,7 +177,14 @@ export function TopNavigationBar(props: TopNavigationBarProps) { {shortName} {isBallerinaWorkspace && crumb.location.package && ( - {crumb.location.package} + + + {crumb.location.package} + )} From b7c8f386f44b978f79c563ed1aa63bd40462e76d Mon Sep 17 00:00:00 2001 From: madushajg Date: Thu, 16 Oct 2025 22:57:18 +0530 Subject: [PATCH 10/91] Add 'Add Project' functionality with form and validation in BI visualizer --- .../src/rpc-types/bi-diagram/index.ts | 4 +- .../src/rpc-types/bi-diagram/interfaces.ts | 10 + .../src/rpc-types/bi-diagram/rpc-type.ts | 4 +- .../ballerina-core/src/state-machine-types.ts | 1 + .../src/features/bi/activator.ts | 5 +- .../rpc-managers/bi-diagram/rpc-manager.ts | 15 ++ .../src/rpc-clients/bi-diagram/rpc-client.ts | 6 + .../ballerina-visualizer/src/MainPanel.tsx | 6 +- .../src/components/TopNavigationBar/index.tsx | 22 +- .../views/BI/ProjectForm/AddProjectForm.tsx | 136 ++++++++++++ .../BI/ProjectForm/AddProjectFormFields.tsx | 201 ++++++++++++++++++ .../src/views/BI/ProjectForm/utils.ts | 10 + .../src/views/BI/index.tsx | 1 + 13 files changed, 404 insertions(+), 17 deletions(-) create mode 100644 workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx create mode 100644 workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx 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 3b4eff402c3..843041578d1 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 @@ -115,7 +115,8 @@ import { RecordsInWorkspaceMentions, BuildMode, DevantMetadata, - GeneratedClientSaveResponse + GeneratedClientSaveResponse, + AddProjectToWorkspaceRequest } from "./interfaces"; export interface BIDiagramAPI { @@ -134,6 +135,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 baf58a15516..169040dd0dc 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 @@ -32,6 +32,16 @@ export interface ProjectRequest { version?: string; } +export interface AddProjectToWorkspaceRequest { + projectName: string; + packageName: string; + path: string; + convertToWorkspace?: boolean; + workspaceName?: string; + orgName?: string; + version?: string; +} + export interface WorkspacesResponse { workspaces: WorkspaceFolder[]; } 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 c3c6a948a00..1d7214d7a99 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 @@ -116,7 +116,8 @@ import { RecordsInWorkspaceMentions, BuildMode, DevantMetadata, - GeneratedClientSaveResponse + GeneratedClientSaveResponse, + AddProjectToWorkspaceRequest } from "./interfaces"; import { RequestType, NotificationType } from "vscode-messenger-common"; @@ -136,6 +137,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 7e34569825c..4f2dbbf514e 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", diff --git a/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts index cc2561641cf..a9f5bd9596b 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts @@ -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, () => { 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 49bb797cc19..2a23e49254b 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 @@ -143,6 +143,7 @@ import { Item, Category, NodePosition, + AddProjectToWorkspaceRequest, } from "@wso2/ballerina-core"; import * as fs from "fs"; import * as path from 'path'; @@ -585,6 +586,20 @@ export class BiDiagramRpcManager implements BIDiagramAPI { } } + async addProjectToWorkspace(params: AddProjectToWorkspaceRequest): Promise { + if (params.convertToWorkspace) { + // Create a new direcotory using the workspace name at same level as the current project and move the project to the new directory + const newDirectory = path.join(path.dirname(StateMachine.context().projectUri), params.workspaceName); + if (!fs.existsSync(newDirectory)) { + fs.mkdirSync(newDirectory, { recursive: true }); + } + fs.renameSync(StateMachine.context().projectUri, path.join(newDirectory, path.basename(StateMachine.context().projectUri))); + openInVSCode(newDirectory); + } else { + // TODO: Just add the project to the workspace + } + } + async getWorkspaces(): Promise { return new Promise(async (resolve) => { const workspaces = workspace.workspaceFolders; 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 98ae747249f..5cb6d09514f 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 @@ -119,6 +119,7 @@ import { createComponent, createGraphqlClassType, createProject, + addProjectToWorkspace, deleteByComponentInfo, deleteConfigVariableV2, deleteFlowNode, @@ -191,6 +192,7 @@ import { VerifyTypeDeleteResponse, verifyTypeDelete, ConfigVariableRequest, + AddProjectToWorkspaceRequest, } from "@wso2/ballerina-core"; import { HOST_EXTENSION } from "vscode-messenger-common"; import { Messenger } from "vscode-messenger-webview"; @@ -262,6 +264,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-visualizer/src/MainPanel.tsx b/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx index d90084333b6..59436e3bc63 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, @@ -442,7 +443,10 @@ const MainPanel = () => { setShowHome(false); setViewComponent(); break; - + case MACHINE_VIEW.BIAddProjectForm: + setShowHome(false); + setViewComponent(); + break; case MACHINE_VIEW.BIComponentView: setViewComponent(); break; diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx index e7ddb604063..7e9a3fbaadf 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx @@ -16,7 +16,7 @@ * under the License. */ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useEffect, useState } from "react"; import styled from "@emotion/styled"; import { Codicon, Icon } from "@wso2/ui-toolkit"; import { useRpcContext } from "@wso2/ballerina-rpc-client"; @@ -107,15 +107,17 @@ export function TopNavigationBar(props: TopNavigationBarProps) { const { onBack, onHome } = props; const { rpcClient } = useRpcContext(); const [history, setHistory] = useState([]); + const [isBallerinaWorkspace, setIsBallerinaWorkspace] = useState(false); useEffect(() => { - rpcClient - .getVisualizerRpcClient() - .getHistory() - .then((history) => { - console.log(">>> history", history); - setHistory(history); - }); + Promise.all([ + rpcClient.getVisualizerRpcClient().getHistory(), + rpcClient.getCommonRpcClient().isBallerinaWorkspace() + ]).then(([history, isWorkspace]) => { + console.log(">>> history", history); + setHistory(history); + setIsBallerinaWorkspace(isWorkspace); + }); }, []); const handleBack = () => { @@ -134,10 +136,6 @@ export function TopNavigationBar(props: TopNavigationBarProps) { } }; - const isBallerinaWorkspace = useMemo(() => { - return rpcClient.getCommonRpcClient().isBallerinaWorkspace(); - }, []); - // HACK: To remove forms from breadcrumb. Will have to fix from the state machine side const hackToSkipForms = ["overview", "automation", "service", "function", "add natural function", "data mapper", "connection"]; const existingLabels = new Set(); 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..fd54855494a --- /dev/null +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx @@ -0,0 +1,136 @@ +/** + * 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 handleFormDataChange = (data: Partial) => { + setFormData(prev => ({ ...prev, ...data })); + }; + + useEffect(() => { + Promise.all([ + rpcClient.getCommonRpcClient().getWorkspaceRoot(), + rpcClient.getCommonRpcClient().isBallerinaWorkspace() + ]).then(([path, isWorkspace]) => { + setPath(path.path); + setIsInWorkspace(isWorkspace); + }); + }, []); + + const handleAddProject = () => { + 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..a45c65388b0 --- /dev/null +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx @@ -0,0 +1,201 @@ +/** + * 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" + 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/utils.ts b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/utils.ts index 8c0ab104b43..ee139208971 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/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"; From 17b09cbeca5cbddd244f6c227e7f582f575297b4 Mon Sep 17 00:00:00 2001 From: madushajg Date: Fri, 17 Oct 2025 13:55:37 +0530 Subject: [PATCH 11/91] Refactor project URI references to project path in ballerina extension and visualizer components --- .../src/interfaces/extended-lang-client.ts | 2 +- .../ballerina-core/src/state-machine-types.ts | 2 +- .../ballerina-extension/src/RPCLayer.ts | 4 +- .../ballerina-extension/src/extension.ts | 2 +- .../src/features/ai/activator.ts | 2 +- .../src/features/bi/activator.ts | 16 ++-- .../config-generator/configGenerator.ts | 22 +++-- .../src/features/debugger/config-provider.ts | 4 +- .../features/natural-programming/activator.ts | 2 +- .../src/features/natural-programming/utils.ts | 29 +++--- .../src/features/test-explorer/commands.ts | 6 +- .../src/features/test-explorer/discover.ts | 7 +- .../src/features/test-explorer/runner.ts | 6 +- .../src/rpc-managers/ai-agent/rpc-manager.ts | 32 +++---- .../src/rpc-managers/ai-panel/rpc-manager.ts | 4 +- .../rpc-managers/bi-diagram/rpc-manager.ts | 95 ++++++++++--------- .../src/rpc-managers/common/rpc-manager.ts | 4 +- .../rpc-managers/icp-service/rpc-manager.ts | 6 +- .../record-creator/rpc-manager.ts | 8 +- .../service-designer/rpc-manager.ts | 30 +++--- .../rpc-managers/visualizer/rpc-manager.ts | 2 +- .../ballerina-extension/src/stateMachine.ts | 20 ++-- .../src/stateMachinePopup.ts | 2 +- .../ballerina-extension/src/utils/bi.ts | 10 +- .../src/utils/project-artifacts.ts | 28 +++--- .../src/utils/project-utils.ts | 4 +- .../src/utils/state-machine-utils.ts | 37 ++++---- .../src/views/visualizer/activate.ts | 2 +- .../src/views/visualizer/webview.ts | 4 +- .../ballerina-visualizer/src/MainPanel.tsx | 57 +++++++---- .../ballerina-visualizer/src/PopupPanel.tsx | 9 +- .../ConnectionSelector/ConnectionConfig.tsx | 2 +- .../ConnectionSelector/ConnectionCreator.tsx | 2 +- .../ConnectionSelectionList.tsx | 2 +- .../views/AIPanel/components/AIChat/index.tsx | 2 +- .../BI/AIChatAgent/AIChatAgentWizard.tsx | 4 +- .../src/views/BI/AIChatAgent/AddMcpServer.tsx | 4 +- .../src/views/BI/AIChatAgent/NewAgent.tsx | 4 +- .../src/views/BI/AIChatAgent/NewTool.tsx | 8 +- .../src/views/BI/AIChatAgent/utils.ts | 12 +-- .../Connection/AddConnectionWizard/index.tsx | 2 +- .../Connection/EditConnectionWizard/index.tsx | 1 - .../BI/HelperPaneNew/Views/Configurables.tsx | 4 +- .../BI/HelperPaneNew/Views/Functions.tsx | 8 +- .../BI/HelperPaneNew/Views/Variables.tsx | 4 +- .../ServiceClassEditor/ServiceClassConfig.tsx | 3 +- .../src/views/DataMapper/DataMapperView.tsx | 5 +- .../src/views/DataMapper/index.tsx | 2 +- .../GraphQLDiagram/ObjectViewer/index.tsx | 3 +- .../src/views/GraphQLDiagram/index.tsx | 4 +- .../src/views/TypeDiagram/index.tsx | 3 +- .../src/TypeEditor/IdentifierField.tsx | 4 +- .../src/TypeEditor/Tabs/ImportTab.tsx | 4 +- .../src/TypeEditor/Tabs/TypeCreatorTab.tsx | 4 +- .../type-editor/src/TypeEditor/TypeField.tsx | 4 +- .../project-explorer-provider.ts | 6 +- 56 files changed, 298 insertions(+), 261 deletions(-) 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 87b6d04102c..d2abbf96b96 100644 --- a/workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts +++ b/workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts @@ -583,7 +583,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/state-machine-types.ts b/workspaces/ballerina/ballerina-core/src/state-machine-types.ts index 4f2dbbf514e..5a1901b1d96 100644 --- a/workspaces/ballerina/ballerina-core/src/state-machine-types.ts +++ b/workspaces/ballerina/ballerina-core/src/state-machine-types.ts @@ -120,7 +120,7 @@ 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; diff --git a/workspaces/ballerina/ballerina-extension/src/RPCLayer.ts b/workspaces/ballerina/ballerina-extension/src/RPCLayer.ts index 42dea359982..240404826cb 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/extension.ts b/workspaces/ballerina/ballerina-extension/src/extension.ts index d86b93b39d8..68873ed7b8e 100644 --- a/workspaces/ballerina/ballerina-extension/src/extension.ts +++ b/workspaces/ballerina/ballerina-extension/src/extension.ts @@ -112,7 +112,7 @@ export async function activate(context: ExtensionContext) { // Wait for the ballerina extension to be ready 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 49f20d0cf09..402c16dfdce 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/activator.ts @@ -42,7 +42,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/bi/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts index a9f5bd9596b..05bfc3e0230 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, () => { @@ -126,8 +126,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) { @@ -149,12 +149,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 @@ -316,7 +316,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 5c437fd2363..d72ac0761dc 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/config-generator/configGenerator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/config-generator/configGenerator.ts @@ -34,20 +34,27 @@ import * as path from "path"; 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; } @@ -55,12 +62,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; 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 dfc152e73c4..9839d621303 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts @@ -114,7 +114,7 @@ function getValueFromProgramArgs(programArgs: string[], idx: number) { async function handleMainFunctionParams(config: DebugConfiguration) { const res = await extension.ballerinaExtInstance.langClient?.getMainFunctionParams({ projectRootIdentifier: { - uri: "file://" + StateMachine.context().projectUri + uri: Uri.file(StateMachine.context().projectPath).toString() } }) as MainFunctionParamsResponse; if (res.hasMain) { @@ -520,7 +520,7 @@ async function handleBreakpointVisualization(uri: Uri, clientBreakpoint: DebugPr const newContext = StateMachine.context(); // Check if breakpoint is in a different project - if (!uri.fsPath.startsWith(newContext.projectUri)) { + if (!uri.fsPath.startsWith(newContext.projectPath)) { console.log("Breakpoint is in a different project"); window.showInformationMessage("Cannot visualize breakpoint since it belongs to a different project"); openView(EVENT_TYPE.OPEN_VIEW, newContext); 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..a434477a9cf 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/natural-programming/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/natural-programming/utils.ts @@ -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[] { 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..66edd255077 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts @@ -28,9 +28,8 @@ 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 + projectPath: StateMachine.context().projectPath }; const response: TestsDiscoveryResponse = await ballerinaExtInstance.langClient?.getProjectTestFunctions(request); if (response) { @@ -41,8 +40,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 +71,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 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/rpc-managers/ai-agent/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-agent/rpc-manager.ts index f858b7ac5b8..4b8fc158b40 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,8 +410,8 @@ export class AiAgentRpcManager implements AIAgentAPI { } async updateMCPToolKit(params: McpToolUpdateRequest): Promise { - const projectUri = StateMachine.context().projectUri; - const filePath = Utils.joinPath(URI.file(projectUri), "agents.bal").fsPath; + const projectPath = StateMachine.context().projectPath; + const filePath = Utils.joinPath(URI.file(projectPath), "agents.bal").fsPath; // Generate the variable name from the server name const variableName = params.updatedNode.properties["variable"].value; 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 72b14d15476..316fae8f5ba 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 @@ -660,8 +660,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getModuleDirectory(params: GetModuleDirParams): Promise { return new Promise((resolve) => { - const projectUri = params.filePath; - const projectFsPath = URI.parse(projectUri).fsPath; + const projectPath = params.filePath; + const projectFsPath = URI.parse(projectPath).fsPath; const moduleName = params.moduleName; const generatedPath = path.join(projectFsPath, "generated", moduleName); if (fs.existsSync(generatedPath) && fs.statSync(generatedPath).isDirectory()) { 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 2a23e49254b..9209529b9ab 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 @@ -589,11 +589,11 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async addProjectToWorkspace(params: AddProjectToWorkspaceRequest): Promise { if (params.convertToWorkspace) { // Create a new direcotory using the workspace name at same level as the current project and move the project to the new directory - const newDirectory = path.join(path.dirname(StateMachine.context().projectUri), params.workspaceName); + const newDirectory = path.join(path.dirname(StateMachine.context().projectPath), params.workspaceName); if (!fs.existsSync(newDirectory)) { fs.mkdirSync(newDirectory, { recursive: true }); } - fs.renameSync(StateMachine.context().projectUri, path.join(newDirectory, path.basename(StateMachine.context().projectUri))); + fs.renameSync(StateMachine.context().projectPath, path.join(newDirectory, path.basename(StateMachine.context().projectPath))); openInVSCode(newDirectory); } else { // TODO: Just add the project to the workspace @@ -639,7 +639,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 }); }); @@ -781,7 +781,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() @@ -803,8 +803,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: "" }); @@ -843,7 +843,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); }); @@ -867,7 +867,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 @@ -932,8 +932,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); @@ -1053,8 +1053,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); @@ -1078,15 +1078,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() @@ -1213,7 +1213,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, @@ -1223,10 +1223,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) => { @@ -1346,8 +1346,8 @@ export class BiDiagramRpcManager implements BIDiagramAPI { } async getAllImports(): 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); const imports: ImportStatements[] = []; for (const file of ballerinaFiles) { @@ -1356,7 +1356,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { imports.push(fileImports); } return { - projectPath: projectUri, + projectPath, imports, }; } @@ -1437,10 +1437,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); @@ -1457,8 +1457,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() @@ -1473,8 +1473,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() @@ -1586,8 +1586,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: "" }) @@ -1650,8 +1650,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: { @@ -1725,8 +1725,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)) { @@ -1739,10 +1739,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 }; } @@ -1800,8 +1800,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 } @@ -1844,7 +1844,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, @@ -1878,7 +1878,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 }; @@ -1893,7 +1893,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 @@ -1928,8 +1928,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); @@ -1944,8 +1944,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) { @@ -1961,8 +1961,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, @@ -1982,6 +1982,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { export function getRepoRoot(projectRoot: string): string | undefined { // traverse up the directory tree until .git directory is found + // TODO: Evaluate this with multi-project workspaces. const gitDir = path.join(projectRoot, ".git"); if (fs.existsSync(gitDir)) { return projectRoot; 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 1dcea2dd78d..c7331366fac 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 @@ -78,8 +78,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); } 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 20a886773ef..625c6cddd6e 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 }, null, '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 }, null, '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 c180b7d875a..108c927e789 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 @@ -96,8 +96,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); @@ -124,8 +124,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); @@ -161,8 +161,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); @@ -181,8 +181,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); @@ -197,8 +197,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 = []; @@ -236,8 +236,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 = []; @@ -293,9 +293,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; } @@ -391,7 +391,7 @@ export class ServiceDesignerRpcManager implements ServiceDesignerAPI { async getResourceReturnTypes(params: ResourceReturnTypesRequest): Promise { return new Promise(async (resolve) => { const context = StateMachine.context(); - params.filePath = StateMachine.context().projectUri; + params.filePath = StateMachine.context().projectPath; params.context = "HTTP_STATUS_CODE"; try { const res: ResourceReturnTypesResponse = await context.langClient.getResourceReturnTypes(params); 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 2d67a76554c..2ec5762c7e6 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 @@ -197,7 +197,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 43ac817adc1..281a225c6db 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -90,7 +90,7 @@ const stateMachine = createMachine( 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, @@ -114,7 +114,7 @@ const stateMachine = createMachine( 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, @@ -126,7 +126,7 @@ 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, @@ -334,14 +334,14 @@ const stateMachine = createMachine( return new Promise(async (resolve, reject) => { try { // If the project uri or workspace path is not set, we don't need to build the project structure - if (context.projectUri || context.workspacePath) { + if (context.projectPath || context.workspacePath) { // Add a 2 second delay before registering artifacts await new Promise(resolve => setTimeout(resolve, 1000)); // Register the event driven listener to get the artifact changes context.langClient.registerPublishArtifacts(); // 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 }); @@ -379,10 +379,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(), } }); @@ -469,7 +469,7 @@ 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 = context.package; history.push(view); return resolve(); @@ -648,7 +648,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")) { @@ -694,7 +694,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(); } 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 4ebc4d42700..a0e9db513ce 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts @@ -362,7 +362,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) { @@ -384,9 +384,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, ''); } @@ -430,9 +430,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; diff --git a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts index 88a200ceabd..75680d3111e 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts @@ -28,7 +28,11 @@ import { injectAgentCode } from "./source-utils"; 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: { @@ -45,14 +49,14 @@ 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); + const workspace = vscode.workspace.workspaceFolders?.find(folder => folder.uri.fsPath === projectPath); let projectName = ""; if (workspace) { @@ -66,7 +70,7 @@ export async function buildProjectArtifactsStructure(projectDir: string, langCli } } else { // Project defined within a Ballerina workspace - projectName = path.basename(projectDir); + projectName = path.basename(projectPath); // TODO: Get the project name from the package Ballerina.toml file } @@ -82,8 +86,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(); @@ -133,7 +139,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, @@ -214,13 +220,13 @@ async function getEntryValue(artifact: BaseArtifact, icon: string, moduleName?: // This has to be replaced once we have a proper design for AI Agent Chat Service async function injectAIAgent(serviceArtifact: BaseArtifact) { // Fetch the organization name for importing the AI package - const aiModuleOrg = await new AiAgentRpcManager().getAiModuleOrg({ projectPath: StateMachine.context().projectUri }); + const aiModuleOrg = await new AiAgentRpcManager().getAiModuleOrg({ projectPath: StateMachine.context().projectPath }); //get AgentName const agentName = serviceArtifact.name.split('-')[1].trim().replace(/\//g, ''); // Retrieve the service model - const targetFile = Utils.joinPath(URI.file(StateMachine.context().projectUri), serviceArtifact.location.fileName).fsPath; + const targetFile = Utils.joinPath(URI.file(StateMachine.context().projectPath), serviceArtifact.location.fileName).fsPath; const updatedService = await new ServiceDesignerRpcManager().getServiceModelFromCode({ filePath: targetFile, codedata: { @@ -235,7 +241,7 @@ async function injectAIAgent(serviceArtifact: BaseArtifact) { return; } const injectionPosition = updatedService.service.functions[0].codedata.lineRange.endLine; - const serviceFile = path.join(StateMachine.context().projectUri, `main.bal`); + const serviceFile = path.join(StateMachine.context().projectPath, `main.bal`); ensureFileExists(serviceFile); await injectAgentCode(agentName, serviceFile, injectionPosition, aiModuleOrg.orgName); const functionPosition: NodePosition = { diff --git a/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts b/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts index 55bf802e4b1..b41581c4821 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts @@ -22,11 +22,11 @@ import * as path from 'path'; import { isSupportedVersion, VERSION } from "./config"; import { BallerinaProject } from "@wso2/ballerina-core"; -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 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 6a7ed14191d..1df1b53fa50 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 { @@ -249,7 +249,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 @@ -259,7 +259,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; @@ -267,7 +267,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; } @@ -278,7 +278,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)) { @@ -291,7 +296,7 @@ function findViewByArtifact(dir: ProjectStructureArtifactResponse, position: Nod identifier: dir.name, documentUri: currentDocumentUri, position: position, - projectUri: projectUri, + projectPath: projectPath, artifactType: DIRECTORY_MAP.SERVICE } }; @@ -302,7 +307,7 @@ function findViewByArtifact(dir: ProjectStructureArtifactResponse, position: Nod identifier: dir.name, documentUri: currentDocumentUri, position: position, - projectUri: projectUri, + projectPath: projectPath, artifactType: DIRECTORY_MAP.SERVICE, } }; @@ -381,7 +386,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/visualizer/activate.ts b/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts index e0894d64d09..100839a44f5 100644 --- a/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts +++ b/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts @@ -86,7 +86,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 e64ae2a9c7d..8e8cc3e270e 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) { undoRedoManager.reset(); diff --git a/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx b/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx index 59436e3bc63..167871c024e 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx @@ -281,7 +281,7 @@ const MainPanel = () => { case MACHINE_VIEW.Overview: setViewComponent( ); break; @@ -321,7 +321,7 @@ const MainPanel = () => { { setViewComponent( { setViewComponent( ); } else { // To support rerendering when user click on view all btn from left side panel setViewComponent( - ); @@ -382,7 +381,7 @@ const MainPanel = () => { filePath={value.documentUri} codedata={value?.dataMapperMetadata?.codeData} name={value?.dataMapperMetadata?.name} - projectUri={value.projectUri} + projectPath={value.projectPath} position={position} reusable /> @@ -400,7 +399,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); @@ -466,14 +472,18 @@ const MainPanel = () => { ); break; case MACHINE_VIEW.BIServiceConfigView: - setViewComponent(); + setViewComponent( + ); break; case MACHINE_VIEW.BIServiceClassConfigView: setViewComponent( + /> ); break; case MACHINE_VIEW.BIListenerConfigView: @@ -482,14 +492,13 @@ const MainPanel = () => { case MACHINE_VIEW.AddConnectionWizard: setViewComponent( ); break; case MACHINE_VIEW.EditConnectionWizard: setViewComponent( ); @@ -497,16 +506,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( { 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); await fetchSelectedConnection(); setLoading(false); }; diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionCreator.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionCreator.tsx index 30a4eef7c6e..2f05944ab14 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionCreator.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/ConnectionSelector/ConnectionCreator.tsx @@ -51,7 +51,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); const currentPosition = await rpcClient.getVisualizerLocation().then((location) => location.position); targetLineRangeRef.current = { startLine: { 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/views/AIPanel/components/AIChat/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx index cd03ed6b9a9..87f2fa6a9aa 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 @@ -201,7 +201,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 19b357b08a3..107b3e10bd7 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx @@ -111,8 +111,8 @@ export function AIChatAgentWizard(props: AIChatAgentWizardProps) { aiModuleOrg.current = await getAiModuleOrg(rpcClient); // get project path - const filePath = await rpcClient.getVisualizerLocation(); - projectPath.current = filePath.projectUri; + const visualizerContext = await rpcClient.getVisualizerLocation(); + projectPath.current = visualizerContext.projectPath; // Search for agent node in the current file const agentSearchResponse = await rpcClient.getBIDiagramRpcClient().search({ 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 24063f3e594..a8d894bc0ea 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AddMcpServer.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AddMcpServer.tsx @@ -286,8 +286,8 @@ export function AddMcpServer(props: AddToolProps): JSX.Element { hasUpdatedToolsField.current = false; // Reset on panel init setLoading(true); // get project path - const filePath = await rpcClient.getVisualizerLocation(); - projectPath.current = filePath.projectUri; + const visualizerContext = await rpcClient.getVisualizerLocation(); + projectPath.current = visualizerContext.projectPath; // get agent file path agentFilePath.current = await getAgentFilePath(rpcClient); // fetch tools and agent node diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewAgent.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewAgent.tsx index 64ad91ab210..8aef3258682 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewAgent.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/NewAgent.tsx @@ -71,8 +71,8 @@ export function NewAgent(props: NewAgentProps): JSX.Element { const initPanel = async () => { setLoading(true); // get project path - const filePath = await rpcClient.getVisualizerLocation(); - projectPath.current = filePath.projectUri; + const visualizerContext = await rpcClient.getVisualizerLocation(); + projectPath.current = visualizerContext.projectPath; // get agent org aiModuleOrg.current = await getAiModuleOrg(rpcClient); // fetch agent node 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 4e34ed4c025..b46793c5fa7 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(); }; @@ -152,7 +152,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 ffd17f11bd1..f704e0ce0e3 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..ebfd323c449 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 @@ -237,7 +237,7 @@ export function AddConnectionWizard(props: AddConnectionWizardProps) { 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"; 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 b6038b78b6c..c0d1bb50ce6 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 @@ -102,8 +102,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 d079620dde8..3117950d0c7 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,14 +66,14 @@ export const FunctionsPage = ({ const [isLoading, setIsLoading] = 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) => { @@ -150,7 +150,7 @@ export const FunctionsPage = ({ const setDefaultFunctionsPath = () => { rpcClient.getVisualizerLocation().then((location) => { - setProjectUri(location?.projectUri || '') + setProjectPath(location?.projectPath || '') }) } @@ -180,7 +180,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/ServiceClassEditor/ServiceClassConfig.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceClassEditor/ServiceClassConfig.tsx index 2623e3362d1..e138477245d 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceClassEditor/ServiceClassConfig.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceClassEditor/ServiceClassConfig.tsx @@ -60,12 +60,11 @@ const LoadingContainer = styled.div` interface ServiceClassConfigProps { fileName: string; position: NodePosition; - projectUri: string; } // TODO: Need to support inclusion type configurable option export function ServiceClassConfig(props: ServiceClassConfigProps) { - const { fileName, position, projectUri } = props; + const { fileName, position } = props; const { rpcClient } = useRpcContext(); const [serviceClassModel, setServiceClassModel] = useState(null); const [serviceClassFields, setServiceClassFields] = useState([]); diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/DataMapper/DataMapperView.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/DataMapper/DataMapperView.tsx index 7909fd0644f..6aae6e3b8dd 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/DataMapper/DataMapperView.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/DataMapper/DataMapperView.tsx @@ -56,6 +56,7 @@ import { calculateExpressionOffsets, convertBalCompletion, updateLineRange } fro import { createAddSubMappingRequest } from "./utils"; import { FunctionForm } from "../BI/FunctionForm"; import { UndoRedoGroup } from "../../components/UndoRedoGroup"; +import { p } from "@tanstack/query-core/build/legacy/hydration-Cvr-9VdO"; // Types for model comparison interface ModelSignature { @@ -66,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({ @@ -642,7 +643,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 92dae494004..021b910663c 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/ObjectViewer/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/ObjectViewer/index.tsx @@ -146,12 +146,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 9809b398cd4..0d3db9aa33a 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/index.tsx @@ -80,12 +80,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); @@ -519,7 +518,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 094d595e7f0..67a58e8b2e3 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/TypeDiagram/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/TypeDiagram/index.tsx @@ -37,7 +37,6 @@ export const Title: React.FC = styled.div` interface TypeDiagramProps { selectedTypeId?: string; - projectUri?: string; addType?: boolean; } @@ -51,7 +50,7 @@ interface TypeEditorState { const MAX_TYPES_FOR_FULL_VIEW = 80; export function TypeDiagram(props: TypeDiagramProps) { - const { selectedTypeId, projectUri, addType } = props; + const { selectedTypeId, addType } = props; const { rpcClient } = useRpcContext(); const commonRpcClient = rpcClient.getCommonRpcClient(); const [visualizerLocation, setVisualizerLocation] = React.useState(); 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 c09f6538a6c..d4aa221902c 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/TypeCreatorTab.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/Tabs/TypeCreatorTab.tsx @@ -343,10 +343,10 @@ export function TypeCreatorTab(props: TypeCreatorTabProps) { 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/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/src/project-explorer/project-explorer-provider.ts b/workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts index 4f8509a685f..05411a61d06 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 @@ -138,15 +138,15 @@ async function getProjectStructureData(): Promise { const workspaceFolderOfPackage = vscode .workspace .workspaceFolders - .find(folder => folder.uri.fsPath === stateContext.projectUri); + .find(folder => folder.uri.fsPath === stateContext.projectPath); let packageName = workspaceFolderOfPackage?.name; let packagePath = workspaceFolderOfPackage?.uri.fsPath; if (!workspaceFolderOfPackage) { if (ballerinaWorkspace) { - packageName = path.basename(Uri.parse(stateContext.projectUri).path); - packagePath = stateContext.projectUri; + packageName = path.basename(Uri.parse(stateContext.projectPath).path); + packagePath = stateContext.projectPath; } else { return []; } From 1ec97e953a9d6606abc4b6fdf28ded3980a35fcb Mon Sep 17 00:00:00 2001 From: madushajg Date: Fri, 17 Oct 2025 15:13:13 +0530 Subject: [PATCH 12/91] Refactor documentation and test generation functions to use project path from StateMachine, removing dependency on getBallerinaProjectRoot --- .../ai/service/documentation/doc_generator.ts | 8 +- .../ai/service/test/function_tests.ts | 10 +-- .../src/features/ai/service/test/test_plan.ts | 10 +-- .../src/rpc-managers/ai-panel/rpc-manager.ts | 75 +++++++------------ .../src/rpc-managers/common/rpc-manager.ts | 20 +---- 5 files changed, 45 insertions(+), 78 deletions(-) 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 3cd3b3dd1a3..81ff2892c39 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 @@ -18,24 +18,24 @@ import { DocGenerationRequest } from '@wso2/ballerina-core'; import { getServiceDeclaration } from '../../testGenerator'; -import { getBallerinaProjectRoot } from '../../../../rpc-managers/ai-panel/rpc-manager'; import { generateDocumentation, DocumentationGenerationRequest } from './documentation'; import { getProjectSource, getOpenAPISpecification } from '../../utils'; +import { StateMachine } from '../../../../stateMachine'; // Main documentation generator function that handles all the logic export async function generateDocumentationForService(params: DocGenerationRequest): Promise { try { // Get the project root - const projectRoot = await getBallerinaProjectRoot(); + const projectPath = StateMachine.context().projectPath; // 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 f163a14c390..0e3460a2df1 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 @@ -17,10 +17,10 @@ import { TestGenerationTarget, TestGeneratorIntermediaryState, Command } from "@wso2/ballerina-core"; import { getErrorMessage } from "../utils"; import { generateTest, getDiagnostics } from "../../testGenerator"; -import { getBallerinaProjectRoot } from "../../../../rpc-managers/ai-panel/rpc-manager"; import { URI } from "vscode-uri"; import * as fs from "fs"; import { CopilotEventHandler, createWebviewEventHandler } from "../event"; +import { StateMachine } from "../../../../stateMachine"; // Core function test generation that emits events export async function generateFunctionTestsCore( @@ -39,8 +39,8 @@ export async function generateFunctionTestsCore( content: `\n\nGenerating tests for the function ${functionIdentifier}. This may take a moment.`, }); - const projectRoot = await getBallerinaProjectRoot(); - const response = await generateTest(projectRoot, { + const projectPath = StateMachine.context().projectPath; + const response = await generateTest(projectPath, { targetType: TestGenerationTarget.Function, targetIdentifier: functionIdentifier, testPlan: params.testPlan, @@ -63,7 +63,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 +78,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 ff65265998a..395976cd102 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 @@ -19,9 +19,9 @@ import { getAnthropicClient, ANTHROPIC_SONNET_4 } from "../connection"; import { getErrorMessage } from "../utils"; import { TestGenerationTarget, TestPlanGenerationRequest, Command } from "@wso2/ballerina-core"; import { generateTest, getDiagnostics } from "../../testGenerator"; -import { getBallerinaProjectRoot } from "../../../../rpc-managers/ai-panel/rpc-manager"; import { CopilotEventHandler, createWebviewEventHandler } from "../event"; import { AIPanelAbortController } from "../../../../../src/rpc-managers/ai-panel/utils"; +import { StateMachine } from "../../../../stateMachine"; export interface TestPlanResponse { testPlan: string; @@ -168,8 +168,8 @@ export async function generateTestPlanCore( type: "content_block", content: `\n\nGenerating tests for the ${target} service. This may take a moment.`, }); - const projectRoot = await getBallerinaProjectRoot(); - const testResp = await generateTest(projectRoot, { + const projectPath = StateMachine.context().projectPath; + const testResp = await generateTest(projectPath, { targetType: TestGenerationTarget.Service, targetIdentifier: target, testPlan: assistantResponse, @@ -178,7 +178,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 +186,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/rpc-managers/ai-panel/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts index 316fae8f5ba..25c77c7682b 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 @@ -379,9 +379,9 @@ export class AiPanelRpcManager implements AIPanelAPI { async getGeneratedTests(params: TestGenerationRequest): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = await getBallerinaProjectRoot(); + const projectPath = StateMachine.context().projectPath; - const generatedTests = await generateTest(projectRoot, params, AIPanelAbortController.getInstance()); + const generatedTests = await generateTest(projectPath, params, AIPanelAbortController.getInstance()); resolve(generatedTests); } catch (error) { reject(error); @@ -392,8 +392,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getTestDiagnostics(params: TestGenerationResponse): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = await getBallerinaProjectRoot(); - const diagnostics = await getDiagnostics(projectRoot, params); + const projectPath = StateMachine.context().projectPath; + const diagnostics = await getDiagnostics(projectPath, params); resolve(diagnostics); } catch (error) { reject(error); @@ -404,8 +404,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getServiceSourceForName(params: string): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = await getBallerinaProjectRoot(); - const { serviceDeclaration, serviceDocFilePath } = await getServiceDeclaration(projectRoot, params); + const projectPath = StateMachine.context().projectPath; + const { serviceDeclaration } = await getServiceDeclaration(projectPath, params); resolve(serviceDeclaration.source); } catch (error) { reject(error); @@ -416,8 +416,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getResourceSourceForMethodAndPath(params: string): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = await getBallerinaProjectRoot(); - const { serviceDeclaration, resourceAccessorDef, serviceDocFilePath } = await getResourceAccessorDef(projectRoot, params); + const projectPath = StateMachine.context().projectPath; + const { resourceAccessorDef } = await getResourceAccessorDef(projectPath, params); resolve(resourceAccessorDef.source); } catch (error) { reject(error); @@ -428,8 +428,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getServiceNames(): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = await getBallerinaProjectRoot(); - const serviceDeclNames = await getServiceDeclarationNames(projectRoot); + const projectPath = StateMachine.context().projectPath; + const serviceDeclNames = await getServiceDeclarationNames(projectPath); resolve({ mentions: serviceDeclNames }); @@ -442,8 +442,8 @@ export class AiPanelRpcManager implements AIPanelAPI { async getResourceMethodAndPaths(): Promise { return new Promise(async (resolve, reject) => { try { - const projectRoot = await getBallerinaProjectRoot(); - const resourceAccessorNames = await getResourceAccessorNames(projectRoot); + const projectPath = StateMachine.context().projectPath; + const resourceAccessorNames = await getResourceAccessorNames(projectPath); resolve({ mentions: resourceAccessorNames }); @@ -466,9 +466,9 @@ export class AiPanelRpcManager implements AIPanelAPI { } async applyDoOnFailBlocks(): Promise { - const projectRoot = await getBallerinaProjectRoot(); + const projectPath = StateMachine.context().projectPath; - if (!projectRoot) { + if (!projectPath) { return null; } @@ -487,7 +487,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } }; - findBalFiles(projectRoot); + findBalFiles(projectPath); for (const balFile of balFiles) { const req: BIModuleNodesRequest = { @@ -748,9 +748,9 @@ export class AiPanelRpcManager implements AIPanelAPI { } async createTempFileAndGenerateMetadata(params: CreateTempFileRequest): Promise { - const projectRoot = await getBallerinaProjectRoot(); + const projectPath = StateMachine.context().projectPath; const filePath = await createTempDataMappingFile( - projectRoot, + projectPath, params.inputs, params.output, params.functionName, @@ -1082,14 +1082,14 @@ interface BalModification { async function setupProjectEnvironment(project: ProjectSource): Promise<{ langClient: any, tempDir: string } | null> { //TODO: Move this to LS - const projectRoot = await getBallerinaProjectRoot(); - if (!projectRoot) { + const projectPath = StateMachine.context().projectPath; + 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 @@ -1161,14 +1161,14 @@ enum CodeGenerationType { } async function getCurrentProjectSource(requestType: OperationType): Promise { - const projectRoot = await getBallerinaProjectRoot(); + const projectPath = StateMachine.context().projectPath; - 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'); @@ -1188,20 +1188,20 @@ async function getCurrentProjectSource(requestType: OperationType): Promise { - - const workspaceFolders = workspace.workspaceFolders; - if (!workspaceFolders) { - throw new Error("No workspaces found."); - } - - const workspaceFolderPath = workspaceFolders[0].uri.fsPath; - // Check if workspaceFolderPath is a Ballerina project - // Assuming a Ballerina project must contain a 'Ballerina.toml' file - const ballerinaProjectFile = path.join(workspaceFolderPath, 'Ballerina.toml'); - if (fs.existsSync(ballerinaProjectFile)) { - return workspaceFolderPath; - } - return null; -} - export async function postProcess(req: PostProcessRequest): Promise { let assist_resp = req.assistant_response; 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 c7331366fac..d25fd2b8b6d 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 @@ -246,25 +246,9 @@ export class CommonRpcManager implements CommonRPCAPI { return extension.ballerinaExtInstance.isNPSupported; } - async getBallerinaProjectRoot(): Promise { - const workspaceFolders = workspace.workspaceFolders; - if (!workspaceFolders) { - throw new Error("No workspaces found."); - } - const workspaceFolderPath = workspaceFolders[0].uri.fsPath; - // Check if workspaceFolderPath is a Ballerina project - // Assuming a Ballerina project must contain a 'Ballerina.toml' file - // TODO: This logic needs to be updated for multi-package workspaces - const ballerinaProjectFile = path.join(workspaceFolderPath, 'Ballerina.toml'); - if (fs.existsSync(ballerinaProjectFile)) { - return workspaceFolderPath; - } - return null; - } - async getCurrentProjectTomlValues(): Promise { - const projectRoot = await this.getBallerinaProjectRoot(); - const ballerinaTomlPath = path.join(projectRoot, 'Ballerina.toml'); + const projectPath = StateMachine.context().projectPath; + const ballerinaTomlPath = path.join(projectPath, 'Ballerina.toml'); if (fs.existsSync(ballerinaTomlPath)) { const tomlContent = await fs.promises.readFile(ballerinaTomlPath, 'utf-8'); try { From 16ff7412b26c355d22999dcf35c2d271ffa3fc72 Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 20 Oct 2025 13:13:51 +0530 Subject: [PATCH 13/91] Enable adding new projects for existing workspaces --- .../src/rpc-types/bi-diagram/interfaces.ts | 4 +- .../src/features/test-explorer/discover.ts | 2 +- .../src/rpc-managers/ai-panel/rpc-manager.ts | 2 +- .../rpc-managers/bi-diagram/rpc-handler.ts | 5 +- .../rpc-managers/bi-diagram/rpc-manager.ts | 20 +-- .../ballerina-extension/src/stateMachine.ts | 2 + .../ballerina-extension/src/utils/bi.ts | 131 ++++++++++++------ .../views/BI/ProjectForm/AddProjectForm.tsx | 18 ++- 8 files changed, 125 insertions(+), 59 deletions(-) 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 169040dd0dc..b0fde8df477 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 @@ -26,8 +26,8 @@ export interface ProjectRequest { packageName: string; projectPath: string; createDirectory: boolean; - createAsWorkspace: boolean; - workspaceName: string; + createAsWorkspace?: boolean; + workspaceName?: string; orgName?: string; version?: string; } 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 66edd255077..ed9751c9afc 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts @@ -99,7 +99,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/rpc-managers/ai-panel/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts index 25c77c7682b..7dbb1cf6ce0 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 @@ -203,7 +203,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } async addToProject(req: AddToProjectRequest): Promise { - + // TODO: Ensure this is working for multi-project workspaces. const workspaceFolders = workspace.workspaceFolders; if (!workspaceFolders) { throw new Error("No workspaces found."); diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-handler.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-handler.ts index 0bee6a351da..c34e4d2574c 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-handler.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-handler.ts @@ -142,7 +142,9 @@ import { UpdateTypesRequest, verifyTypeDelete, VerifyTypeDeleteRequest, - VisibleTypesRequest + VisibleTypesRequest, + addProjectToWorkspace, + AddProjectToWorkspaceRequest } from "@wso2/ballerina-core"; import { Messenger } from "vscode-messenger"; import { BiDiagramRpcManager } from "./rpc-manager"; @@ -164,6 +166,7 @@ export function registerBiDiagramRpcHandlers(messenger: Messenger) { messenger.onRequest(getNodeTemplate, (args: BINodeTemplateRequest) => 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 9209529b9ab..cb7910c2920 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,10 +144,14 @@ import { Category, NodePosition, AddProjectToWorkspaceRequest, + MACHINE_VIEW, + EVENT_TYPE, + VisualizerLocation, } from "@wso2/ballerina-core"; import * as fs from "fs"; import * as path from 'path'; import * as vscode from "vscode"; +import { parse } from 'toml'; import { ICreateComponentCmdParams, IWso2PlatformExtensionAPI, CommandIds as PlatformExtCommandIds } from "@wso2/wso2-platform-core"; import { @@ -166,14 +170,16 @@ 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, createBIWorkspace, openInVSCode } 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 { checkProjectDiagnostics, removeUnusedImports } from "../ai-panel/repair-utils"; import { getView } from "../../utils/state-machine-utils"; +import { buildProjectArtifactsStructure } from "../../utils/project-artifacts"; + export class BiDiagramRpcManager implements BIDiagramAPI { OpenConfigTomlRequest: (params: OpenConfigTomlRequest) => Promise; @@ -588,15 +594,9 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async addProjectToWorkspace(params: AddProjectToWorkspaceRequest): Promise { if (params.convertToWorkspace) { - // Create a new direcotory using the workspace name at same level as the current project and move the project to the new directory - const newDirectory = path.join(path.dirname(StateMachine.context().projectPath), params.workspaceName); - if (!fs.existsSync(newDirectory)) { - fs.mkdirSync(newDirectory, { recursive: true }); - } - fs.renameSync(StateMachine.context().projectPath, path.join(newDirectory, path.basename(StateMachine.context().projectPath))); - openInVSCode(newDirectory); + convertProjectToWorkspace(params.workspaceName); } else { - // TODO: Just add the project to the workspace + await addProjectToExistingWorkspace(params); } } diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index 281a225c6db..f97909669ec 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -273,6 +273,8 @@ const stateMachine = createMachine( position: (context, event) => event.viewLocation.position, identifier: (context, event) => event.viewLocation.identifier, serviceType: (context, event) => event.viewLocation.serviceType, + projectPath: (context, event) => event.viewLocation?.projectPath || context?.projectPath, + package: (context, event) => event.viewLocation?.package, type: (context, event) => event.viewLocation?.type, isGraphql: (context, event) => event.viewLocation?.isGraphql, metadata: (context, event) => event.viewLocation?.metadata, diff --git a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts index a0e9db513ce..8de2403ac41 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts @@ -20,12 +20,14 @@ 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 } 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"; export const README_FILE = "readme.md"; export const FUNCTIONS_FILE = "functions.bal"; @@ -98,45 +100,6 @@ generated/ Config.toml `; -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; @@ -313,6 +276,94 @@ sticky = true return projectRoot; } +export function convertProjectToWorkspace(workspaceName: string) { + const currentProjectPath = StateMachine.context().projectPath; + const newDirectory = path.join(path.dirname(currentProjectPath), workspaceName); + + if (!fs.existsSync(newDirectory)) { + fs.mkdirSync(newDirectory, { recursive: true }); + } + + const newProjectPath = path.join(newDirectory, path.basename(currentProjectPath)); + fs.renameSync(currentProjectPath, newProjectPath); + 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 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 = parse(ballerinaTomlContent); + const existingPackages: string[] = tomlData.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))); } diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx index fd54855494a..6fee4906bba 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx @@ -69,6 +69,7 @@ export function AddProjectForm() { }); const [isInWorkspace, setIsInWorkspace] = useState(false); const [path, setPath] = useState(""); + const [isLoading, setIsLoading] = useState(false); const handleFormDataChange = (data: Partial) => { setFormData(prev => ({ ...prev, ...data })); @@ -85,6 +86,7 @@ export function AddProjectForm() { }, []); const handleAddProject = () => { + setIsLoading(true); rpcClient.getBIDiagramRpcClient().addProjectToWorkspace({ projectName: formData.integrationName, packageName: formData.packageName, @@ -121,13 +123,21 @@ export function AddProjectForm() { From 3b821ad11b55a5ed4121930d80be004513fce627 Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 20 Oct 2025 20:53:21 +0530 Subject: [PATCH 14/91] Enable converting exsisting projects into workspaces --- .../rpc-managers/bi-diagram/rpc-manager.ts | 3 +-- .../src/rpc-managers/common/rpc-manager.ts | 16 ++---------- .../src/rpc-managers/common/utils.ts | 19 ++++++++++++-- .../ballerina-extension/src/utils/bi.ts | 26 ++++++++++++++++--- .../BI/ProjectForm/AddProjectFormFields.tsx | 3 ++- 5 files changed, 44 insertions(+), 23 deletions(-) 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 cb7910c2920..eb1cbfdcb09 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 @@ -178,7 +178,6 @@ import { writeBallerinaFileDidOpen } from "../../utils/modification"; import { updateSourceCode } from "../../utils/source-utils"; import { checkProjectDiagnostics, removeUnusedImports } from "../ai-panel/repair-utils"; import { getView } from "../../utils/state-machine-utils"; -import { buildProjectArtifactsStructure } from "../../utils/project-artifacts"; export class BiDiagramRpcManager implements BIDiagramAPI { OpenConfigTomlRequest: (params: OpenConfigTomlRequest) => Promise; @@ -594,7 +593,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async addProjectToWorkspace(params: AddProjectToWorkspaceRequest): Promise { if (params.convertToWorkspace) { - convertProjectToWorkspace(params.workspaceName); + await convertProjectToWorkspace(params); } else { await addProjectToExistingWorkspace(params); } 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 d25fd2b8b6d..9844273b864 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 @@ -47,9 +47,7 @@ import { URI } from "vscode-uri"; import { extension } from "../../BalExtensionContext"; import { StateMachine } from "../../stateMachine"; import { checkIsBallerinaWorkspace, goToSource } from "../../utils"; -import { askFileOrFolderPath, askFilePath, askProjectPath, BALLERINA_INTEGRATOR_ISSUES_URL, getUpdatedSource } from "./utils"; -import { parse } from 'toml'; -import * as fs from 'fs'; +import { askFileOrFolderPath, askFilePath, askProjectPath, BALLERINA_INTEGRATOR_ISSUES_URL, getProjectTomlValues, getUpdatedSource } from "./utils"; import path from "path"; export class CommonRpcManager implements CommonRPCAPI { @@ -247,17 +245,7 @@ export class CommonRpcManager implements CommonRPCAPI { } async getCurrentProjectTomlValues(): Promise { - const projectPath = StateMachine.context().projectPath; - 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", error); - return; - } - } + return getProjectTomlValues(StateMachine.context().projectPath); } async isBallerinaWorkspace(): 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 73d9100ae37..2a9e03aca75 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts @@ -16,10 +16,12 @@ * under the License. */ +import * as fs from 'fs'; +import path from "path"; 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"; +import { TextEdit, TomlValues } from "@wso2/ballerina-core"; +import { parse } from "toml"; export const BALLERINA_INTEGRATOR_ISSUES_URL = "https://github.com/wso2/product-ballerina-integrator/issues"; @@ -111,3 +113,16 @@ export async function applyBallerinaTomlEdit(tomlPath: Uri, textEdit: TextEdit) } }); } + +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; + } + } +} diff --git a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts index 8de2403ac41..11bbada319f 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts @@ -28,6 +28,7 @@ import { URI } from "vscode-uri"; import { debug } from "./logger"; import { parse } from "toml"; import { buildProjectArtifactsStructure } from "./project-artifacts"; +import { getProjectTomlValues } from "../rpc-managers/common/utils"; export const README_FILE = "readme.md"; export const FUNCTIONS_FILE = "functions.bal"; @@ -276,16 +277,24 @@ sticky = true return projectRoot; } -export function convertProjectToWorkspace(workspaceName: string) { +export async function convertProjectToWorkspace(params: AddProjectToWorkspaceRequest) { const currentProjectPath = StateMachine.context().projectPath; - const newDirectory = path.join(path.dirname(currentProjectPath), workspaceName); + const { package: { name: currentPackageName } } = await getProjectTomlValues(currentProjectPath); + + const newDirectory = path.join(path.dirname(currentProjectPath), params.workspaceName); if (!fs.existsSync(newDirectory)) { fs.mkdirSync(newDirectory, { recursive: true }); } - const newProjectPath = path.join(newDirectory, path.basename(currentProjectPath)); - fs.renameSync(currentProjectPath, newProjectPath); + const updatedProjectPath = path.join(newDirectory, path.basename(currentProjectPath)); + fs.renameSync(currentProjectPath, updatedProjectPath); + + createWorkspaceToml(newDirectory, currentPackageName); + updateWorkspaceToml(newDirectory, params.packageName); + + createProjectInWorkspace(params, newDirectory); + openInVSCode(newDirectory); } @@ -299,6 +308,15 @@ export async function addProjectToExistingWorkspace(params: AddProjectToWorkspac 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'); diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx index a45c65388b0..4193218b369 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx @@ -125,6 +125,7 @@ export function AddProjectFormFields({ value={formData.workspaceName} label="Workspace Name" placeholder="Enter workspace name" + autoFocus={true} required={true} /> @@ -136,7 +137,7 @@ export function AddProjectFormFields({ value={formData.integrationName} label="Integration Name" placeholder="Enter an integration name" - autoFocus={true} + autoFocus={isInWorkspace} required={true} /> From ce6ca9e96e17a547859c534bb10acbfb10e24abb Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 21 Oct 2025 11:26:36 +0530 Subject: [PATCH 15/91] Enable switch between workspace packages --- common/config/rush/pnpm-lock.yaml | 9 +- .../src/rpc-types/common/interfaces.ts | 10 +- .../src/rpc-managers/common/rpc-manager.ts | 4 +- .../src/rpc-managers/common/utils.ts | 4 +- .../src/rpc-clients/common/rpc-client.ts | 4 +- workspaces/bi/bi-extension/package.json | 5 +- .../src/project-explorer/activate.ts | 12 +- .../bi/bi-extension/src/stateMachine.ts | 8 +- workspaces/bi/bi-extension/src/utils.ts | 169 ++++++++++++++++-- 9 files changed, 185 insertions(+), 40 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index f3b8c1ddaa9..cf08320f4e3 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -2109,6 +2109,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 @@ -24259,7 +24262,10 @@ snapshots: jest-runner: 25.5.4 jest-runtime: 25.5.4 transitivePeerDependencies: + - bufferutil + - canvas - supports-color + - utf-8-validate '@jest/test-sequencer@29.7.0': dependencies: @@ -39867,10 +39873,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: diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/common/interfaces.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/common/interfaces.ts index 6ccd232ffcc..6f21d545c3e 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/common/interfaces.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/common/interfaces.ts @@ -91,6 +91,10 @@ export interface ShowErrorMessageRequest { message: string; } +export interface TomlWorkspace { + packages: string[]; +} + export interface TomlPackage { org: string; name: string; @@ -98,6 +102,10 @@ export interface TomlPackage { title: string; } -export interface TomlValues { +export interface WorkspaceTomlValues { + workspace: TomlWorkspace; +} + +export interface PackageTomlValues { package: TomlPackage; } 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 9844273b864..6825fdf84df 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 @@ -35,7 +35,7 @@ import { RunExternalCommandResponse, ShowErrorMessageRequest, SyntaxTree, - TomlValues, + PackageTomlValues, TypeResponse, WorkspaceFileRequest, WorkspaceRootResponse, @@ -244,7 +244,7 @@ export class CommonRpcManager implements CommonRPCAPI { return extension.ballerinaExtInstance.isNPSupported; } - async getCurrentProjectTomlValues(): Promise { + async getCurrentProjectTomlValues(): Promise { return getProjectTomlValues(StateMachine.context().projectPath); } 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 2a9e03aca75..e8a8388dffe 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts @@ -20,7 +20,7 @@ import * as fs from 'fs'; import path from "path"; import { NodePosition } from "@wso2/syntax-tree"; import { Position, Range, Uri, window, workspace, WorkspaceEdit } from "vscode"; -import { TextEdit, TomlValues } from "@wso2/ballerina-core"; +import { TextEdit, PackageTomlValues } from "@wso2/ballerina-core"; import { parse } from "toml"; export const BALLERINA_INTEGRATOR_ISSUES_URL = "https://github.com/wso2/product-ballerina-integrator/issues"; @@ -114,7 +114,7 @@ export async function applyBallerinaTomlEdit(tomlPath: Uri, textEdit: TextEdit) }); } -export async function getProjectTomlValues(projectPath: string): Promise { +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'); 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 438c629aa2f..6a08500f333 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 @@ -46,7 +46,7 @@ import { runBackgroundTerminalCommand, selectFileOrDirPath, getCurrentProjectTomlValues, - TomlValues, + PackageTomlValues, selectFileOrFolderPath, showErrorMessage, isBallerinaWorkspace @@ -113,7 +113,7 @@ export class CommonRpcClient implements CommonRPCAPI { return this._messenger.sendNotification(showErrorMessage, HOST_EXTENSION, params); } - getCurrentProjectTomlValues(): Promise { + getCurrentProjectTomlValues(): Promise { return this._messenger.sendRequest(getCurrentProjectTomlValues, HOST_EXTENSION); } diff --git a/workspaces/bi/bi-extension/package.json b/workspaces/bi/bi-extension/package.json index 0a57bdc8251..acc1840c837 100644 --- a/workspaces/bi/bi-extension/package.json +++ b/workspaces/bi/bi-extension/package.json @@ -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/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 01bb21b115c..f6d746249f6 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 { 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,26 +36,64 @@ export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList)); } -export function fetchProjectInfo(): ProjectInfo { - const workspaceUris = workspace.workspaceFolders ? workspace.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++; +/** + * 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; + + if (!workspaceFolders || workspaceFolders.length > 1) { + return { isBI: false, isBallerina: false, isBalWorkspace: false }; + } + + const workspaceUri = workspaceFolders[0].uri; + const isBallerinaWorkspace = checkIsBallerinaWorkspace(workspaceUri); + + if (isBallerinaWorkspace) { + const workspaceTomlValues = await getWorkspaceTomlValues(workspaceUri.fsPath); + const packagePaths = workspaceTomlValues.workspace.packages; + + const filteredPackagePaths = 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 (pkgPath.startsWith('/')) { + packagePath = path.resolve(pkgPath); + } + const isBallerina = 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: checkIsBallerinaPackage(workspaceUri), + isBalWorkspace: false }; } @@ -73,7 +113,100 @@ 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 function checkIsBallerinaPackage(uri: Uri): boolean { 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 { + // Read the file content and check for [package] section + const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); + const packageSectionRegex = /\[package\]/; + return packageSectionRegex.test(tomlContent); + } 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 function checkIsBallerinaWorkspace(uri: Uri): boolean { + const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); + + // First check if the file exists + if (!fs.existsSync(ballerinaTomlPath)) { + return false; + } + + try { + // Read the file content and check for [workspace] section + const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); + const workspaceSectionRegex = /\[workspace\]/; + return workspaceSectionRegex.test(tomlContent); + } 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 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 + */ +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; + } + } +} + +/** + * Filters package paths to only include valid paths. + * - Keeps all relative paths (as they are implicitly within the workspace) + * - For absolute paths, only keeps them if they exist AND are within the workspace + * + * @param packagePaths Array of package paths (relative or absolute) + * @param workspacePath Absolute path to the workspace root + * @returns Filtered array of valid package paths + */ +function filterPackagePaths(packagePaths: string[], workspacePath: string): string[] { + return packagePaths.filter(pkgPath => { + if (pkgPath.startsWith('/')) { + // Check if the absolute path exists AND is within the workspace + const resolvedPath = path.resolve(pkgPath); + const resolvedWorkspacePath = path.resolve(workspacePath); + return fs.existsSync(resolvedPath) && resolvedPath.startsWith(resolvedWorkspacePath); + } + // Keep relative paths as they are + return true; + }); } From 6d6b59d6af2cb77a7d707f7629534a7c9b88bc20 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 21 Oct 2025 11:57:26 +0530 Subject: [PATCH 16/91] Add missing import --- workspaces/ballerina/ballerina-extension/package.json | 8 ++++---- .../ballerina-extension/src/rpc-managers/common/utils.ts | 1 + workspaces/choreo/choreo-extension/package.json | 2 +- workspaces/mi/mi-extension/package.json | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/package.json b/workspaces/ballerina/ballerina-extension/package.json index 13f53d827ba..e72a01a8013 100644 --- a/workspaces/ballerina/ballerina-extension/package.json +++ b/workspaces/ballerina/ballerina-extension/package.json @@ -965,21 +965,21 @@ "description": "design-view", "default": { "fontPath": "./resources/font-wso2-vscode/dist/wso2-vscode.woff", - "fontCharacter": "\\f180" + "fontCharacter": "\\f182" } }, "distro-start": { "description": "start", "default": { "fontPath": "./resources/font-wso2-vscode/dist/wso2-vscode.woff", - "fontCharacter": "\\f1f5" + "fontCharacter": "\\f1f7" } }, "distro-debug": { "description": "debug", "default": { "fontPath": "./resources/font-wso2-vscode/dist/wso2-vscode.woff", - "fontCharacter": "\\f17b" + "fontCharacter": "\\f17d" } }, "distro-source-view": { @@ -993,7 +993,7 @@ "description": "persist-diagram", "default": { "fontPath": "./resources/font-wso2-vscode/dist/wso2-vscode.woff", - "fontCharacter": "\\f1d0" + "fontCharacter": "\\f1d2" } }, "distro-cached-rounded": { 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 e8a8388dffe..81c1d326d1f 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts @@ -16,6 +16,7 @@ * under the License. */ +import * as os from 'os'; import * as fs from 'fs'; import path from "path"; import { NodePosition } from "@wso2/syntax-tree"; diff --git a/workspaces/choreo/choreo-extension/package.json b/workspaces/choreo/choreo-extension/package.json index 7ebb0206ea2..122bdbcae0e 100644 --- a/workspaces/choreo/choreo-extension/package.json +++ b/workspaces/choreo/choreo-extension/package.json @@ -158,7 +158,7 @@ "description": "choreo-2", "default": { "fontPath": "./resources/font-wso2-vscode/dist/wso2-vscode.woff", - "fontCharacter": "\\f166" + "fontCharacter": "\\f168" } } } diff --git a/workspaces/mi/mi-extension/package.json b/workspaces/mi/mi-extension/package.json index 346a9caf352..3630bc3a074 100644 --- a/workspaces/mi/mi-extension/package.json +++ b/workspaces/mi/mi-extension/package.json @@ -931,14 +931,14 @@ "description": "design-view", "default": { "fontPath": "./resources/font-wso2-vscode/dist/wso2-vscode.woff", - "fontCharacter": "\\f180" + "fontCharacter": "\\f182" } }, "distro-build-package": { "description": "build-package", "default": { "fontPath": "./resources/font-wso2-vscode/dist/wso2-vscode.woff", - "fontCharacter": "\\f15b" + "fontCharacter": "\\f15d" } } } From 4e3c335560ba6988bb18b986ea66083c3544b937 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 21 Oct 2025 22:38:37 +0530 Subject: [PATCH 17/91] Update package filtering logic to ensure only valid Ballerina packages are included --- .../src/rpc-managers/common/utils.ts | 15 +++- .../ballerina-extension/src/stateMachine.ts | 23 ++++-- .../ballerina-extension/src/utils/config.ts | 77 +++++++------------ workspaces/bi/bi-extension/src/utils.ts | 30 +++++--- 4 files changed, 74 insertions(+), 71 deletions(-) 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 81c1d326d1f..c17da93d6f2 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts @@ -21,7 +21,7 @@ import * as fs from 'fs'; import path from "path"; import { NodePosition } from "@wso2/syntax-tree"; import { Position, Range, Uri, window, workspace, WorkspaceEdit } from "vscode"; -import { TextEdit, PackageTomlValues } from "@wso2/ballerina-core"; +import { TextEdit, PackageTomlValues, WorkspaceTomlValues } from "@wso2/ballerina-core"; import { parse } from "toml"; export const BALLERINA_INTEGRATOR_ISSUES_URL = "https://github.com/wso2/product-ballerina-integrator/issues"; @@ -127,3 +127,16 @@ export async function getProjectTomlValues(projectPath: 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/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index f97909669ec..40957571e28 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -9,13 +9,12 @@ import { commands, extensions, ShellExecution, Task, TaskDefinition, tasks, Uri, import { notifyCurrentWebview, RPCLayer } from './RPCLayer'; import { generateUid, getComponentIdentifier, getNodeByIndex, getNodeByName, getNodeByUid, getView } from './utils/state-machine-utils'; import * as path from 'path'; -import * as fs from 'fs'; import { extension } from './BalExtensionContext'; -import { BiDiagramRpcManager } from './rpc-managers/bi-diagram/rpc-manager'; import { AIStateMachine } from './views/ai-panel/aiMachine'; import { StateMachinePopup } from './stateMachinePopup'; -import { checkIsBallerinaPackage, checkIsBallerinaWorkspace, checkIsBI, fetchScope, getFirstWorkspacePackageName, getOrgPackageName, getWorkspacePackageNames, UndoRedoManager } from './utils'; +import { checkIsBallerinaPackage, checkIsBallerinaWorkspace, checkIsBI, fetchScope, filterPackagePaths, getOrgPackageName, UndoRedoManager } from './utils'; import { buildProjectArtifactsStructure } from './utils/project-artifacts'; +import { getWorkspaceTomlValues } from './rpc-managers/common/utils'; export interface ProjectMetadata { readonly isBI: boolean; @@ -778,17 +777,25 @@ async function handleSingleWorkspaceFolder(workspaceURI: Uri): Promise { + if (path.isAbsolute(pkgPath)) { + const resolvedPath = path.resolve(pkgPath); + const resolvedWorkspacePath = path.resolve(workspacePath); + if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(resolvedWorkspacePath)) { + return checkIsBallerinaPackage(Uri.file(resolvedPath)); + } } - - // Extract all package names from the array content - const arrayContent = match[1]; - const packageNameRegex = /"([^"]+)"/g; - const packageNames: string[] = []; - let packageMatch; - - while ((packageMatch = packageNameRegex.exec(arrayContent)) !== null) { - packageNames.push(packageMatch[1]); + const resolvedPath = path.resolve(workspacePath, pkgPath); + if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(workspacePath)) { + return checkIsBallerinaPackage(Uri.file(resolvedPath)); } - - return packageNames.length > 0 ? packageNames : null; - } catch (error) { - // If there's an error reading the file, return null - console.error(`Error reading workspace Ballerina.toml: ${error}`); - return null; - } -} - - -export function getFirstWorkspacePackageName(uri: Uri): string | null { - const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); - - try { - // Read the file content - const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); - - // Regular expression to match packages array in [workspace] section - // This matches: packages = ["package1", "package2", ...] - const packagesRegex = /packages\s*=\s*\[\s*"([^"]+)"/; - const match = tomlContent.match(packagesRegex); - - return match?.[1] || null; - } catch (error) { - // If there's an error reading the file, return null - console.error(`Error reading workspace Ballerina.toml: ${error}`); - return null; - } + return false; + }); } export function getOrgPackageName(projectPath: string): { orgName: string, packageName: string } { diff --git a/workspaces/bi/bi-extension/src/utils.ts b/workspaces/bi/bi-extension/src/utils.ts index f6d746249f6..ac8f5786feb 100644 --- a/workspaces/bi/bi-extension/src/utils.ts +++ b/workspaces/bi/bi-extension/src/utils.ts @@ -71,7 +71,7 @@ export async function fetchProjectInfo(): Promise { for (const pkgPath of filteredPackagePaths) { let packagePath = path.join(workspaceUri.fsPath, pkgPath); - if (pkgPath.startsWith('/')) { + if (path.isAbsolute(pkgPath)) { packagePath = path.resolve(pkgPath); } const isBallerina = checkIsBallerinaPackage(Uri.file(packagePath)); @@ -190,23 +190,31 @@ export async function getWorkspaceTomlValues(workspacePath: string): Promise { - if (pkgPath.startsWith('/')) { - // Check if the absolute path exists AND is within the workspace + if (path.isAbsolute(pkgPath)) { const resolvedPath = path.resolve(pkgPath); const resolvedWorkspacePath = path.resolve(workspacePath); - return fs.existsSync(resolvedPath) && resolvedPath.startsWith(resolvedWorkspacePath); + if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(resolvedWorkspacePath)) { + return checkIsBallerinaPackage(Uri.file(resolvedPath)); + } } - // Keep relative paths as they are - return true; + const resolvedPath = path.resolve(workspacePath, pkgPath); + if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(workspacePath)) { + return checkIsBallerinaPackage(Uri.file(resolvedPath)); + } + return false; }); } From 32e217b149e7476bf732ed6ab7260c8094ac4cdc Mon Sep 17 00:00:00 2001 From: madushajg Date: Wed, 22 Oct 2025 16:20:17 +0530 Subject: [PATCH 18/91] Refactor project path handling in AI panel RPC manager and state machine. Update package selection logic to accommodate multi-project workspaces. --- .../src/features/bi/activator.ts | 1 + .../src/rpc-managers/ai-panel/rpc-manager.ts | 56 +++++-------------- .../rpc-managers/bi-diagram/rpc-manager.ts | 1 - .../src/rpc-managers/common/rpc-manager.ts | 4 +- .../src/rpc-managers/common/utils.ts | 26 --------- .../ballerina-extension/src/stateMachine.ts | 32 ++++++----- .../ballerina-extension/src/utils/bi.ts | 2 +- .../ballerina-extension/src/utils/config.ts | 29 +++++++++- 8 files changed, 66 insertions(+), 85 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts index 05bfc3e0230..33f4afddde6 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts @@ -103,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); }); 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 cb758502aa5..7d197f31ca1 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 @@ -187,21 +187,15 @@ export class AiPanelRpcManager implements AIPanelAPI { } async addToProject(req: AddToProjectRequest): Promise { - // TODO: Ensure this is working for multi-project workspaces. - const workspaceFolders = workspace.workspaceFolders; - if (!workspaceFolders) { - throw new Error("No workspaces found."); - } - - const workspaceFolderPath = workspaceFolders[0].uri.fsPath; + const projectPath = StateMachine.context().projectPath; // Check if workspaceFolderPath is a Ballerina project // Assuming a Ballerina project must contain a 'Ballerina.toml' file - const ballerinaProjectFile = path.join(workspaceFolderPath, 'Ballerina.toml'); + const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); } - let balFilePath = path.join(workspaceFolderPath, req.filePath); + let balFilePath = path.join(projectPath, req.filePath); const directory = path.dirname(balFilePath); if (!fs.existsSync(directory)) { @@ -217,36 +211,26 @@ export class AiPanelRpcManager implements AIPanelAPI { async getFromFile(req: GetFromFileRequest): Promise { return new Promise(async (resolve) => { - const workspaceFolders = workspace.workspaceFolders; - if (!workspaceFolders) { - throw new Error("No workspaces found."); - } - - const workspaceFolderPath = workspaceFolders[0].uri.fsPath; - const ballerinaProjectFile = path.join(workspaceFolderPath, 'Ballerina.toml'); + const projectPath = StateMachine.context().projectPath; + const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); } - const balFilePath = path.join(workspaceFolderPath, req.filePath); + const balFilePath = path.join(projectPath, req.filePath); const content = fs.promises.readFile(balFilePath, 'utf-8'); resolve(content); }); } async deleteFromProject(req: DeleteFromProjectRequest): Promise { - const workspaceFolders = workspace.workspaceFolders; - if (!workspaceFolders) { - throw new Error("No workspaces found."); - } - - const workspaceFolderPath = workspaceFolders[0].uri.fsPath; - const ballerinaProjectFile = path.join(workspaceFolderPath, 'Ballerina.toml'); + const projectPath = StateMachine.context().projectPath; + const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); } - const balFilePath = path.join(workspaceFolderPath, req.filePath); + const balFilePath = path.join(projectPath, req.filePath); if (fs.existsSync(balFilePath)) { try { fs.unlinkSync(balFilePath); @@ -262,18 +246,13 @@ export class AiPanelRpcManager implements AIPanelAPI { } async getFileExists(req: GetFromFileRequest): Promise { - const workspaceFolders = workspace.workspaceFolders; - if (!workspaceFolders) { - throw new Error("No workspaces found."); - } - - const workspaceFolderPath = workspaceFolders[0].uri.fsPath; - const ballerinaProjectFile = path.join(workspaceFolderPath, 'Ballerina.toml'); + const projectPath = StateMachine.context().projectPath; + const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); } - const balFilePath = path.join(workspaceFolderPath, req.filePath); + const balFilePath = path.join(projectPath, req.filePath); if (fs.existsSync(balFilePath)) { return true; } @@ -675,18 +654,13 @@ export class AiPanelRpcManager implements AIPanelAPI { async addFilesToProject(params: AddFilesToProjectRequest): Promise { try { - const workspaceFolders = workspace.workspaceFolders; - if (!workspaceFolders) { - throw new Error("No workspaces found."); - } - - const workspaceFolderPath = workspaceFolders[0].uri.fsPath; + const projectPath = StateMachine.context().projectPath; - const ballerinaProjectFile = path.join(workspaceFolderPath, "Ballerina.toml"); + const ballerinaProjectFile = path.join(projectPath, "Ballerina.toml"); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); } - await addToIntegration(workspaceFolderPath, params.fileChanges); + await addToIntegration(projectPath, params.fileChanges); updateView(); return true; } catch (error) { 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 3341518eb2d..3de77d0e98e 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 @@ -1957,7 +1957,6 @@ export class BiDiagramRpcManager implements BIDiagramAPI { export function getRepoRoot(projectRoot: string): string | undefined { // traverse up the directory tree until .git directory is found - // TODO: Evaluate this with multi-project workspaces. const gitDir = path.join(projectRoot, ".git"); if (fs.existsSync(gitDir)) { return projectRoot; 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 6825fdf84df..91a55e75f65 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 @@ -46,8 +46,8 @@ import { Uri, commands, env, window, workspace, MarkdownString } from "vscode"; import { URI } from "vscode-uri"; import { extension } from "../../BalExtensionContext"; import { StateMachine } from "../../stateMachine"; -import { checkIsBallerinaWorkspace, goToSource } from "../../utils"; -import { askFileOrFolderPath, askFilePath, askProjectPath, BALLERINA_INTEGRATOR_ISSUES_URL, getProjectTomlValues, getUpdatedSource } from "./utils"; +import { checkIsBallerinaWorkspace, getProjectTomlValues, goToSource } from "../../utils"; +import { askFileOrFolderPath, askFilePath, askProjectPath, BALLERINA_INTEGRATOR_ISSUES_URL, getUpdatedSource } from "./utils"; import path from "path"; export class CommonRpcManager implements CommonRPCAPI { 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 c17da93d6f2..3080f926014 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts @@ -114,29 +114,3 @@ export async function applyBallerinaTomlEdit(tomlPath: Uri, textEdit: TextEdit) } }); } - -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/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index 40957571e28..c22c72aa6a6 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -12,9 +12,8 @@ import * as path from 'path'; import { extension } from './BalExtensionContext'; import { AIStateMachine } from './views/ai-panel/aiMachine'; import { StateMachinePopup } from './stateMachinePopup'; -import { checkIsBallerinaPackage, checkIsBallerinaWorkspace, checkIsBI, fetchScope, filterPackagePaths, getOrgPackageName, UndoRedoManager } from './utils'; +import { checkIsBallerinaPackage, checkIsBallerinaWorkspace, checkIsBI, fetchScope, filterPackagePaths, getOrgPackageName, UndoRedoManager, getProjectTomlValues, getWorkspaceTomlValues } from './utils'; import { buildProjectArtifactsStructure } from './utils/project-artifacts'; -import { getWorkspaceTomlValues } from './rpc-managers/common/utils'; export interface ProjectMetadata { readonly isBI: boolean; @@ -459,19 +458,21 @@ const stateMachine = createMachine( }, findView(context, event): Promise { return new Promise(async (resolve, reject) => { + const projectTomlValues = await getProjectTomlValues(context.projectPath); + const packageName = projectTomlValues?.package?.name; if (!context.view && context.langClient) { if (!context.position || ("groupId" in context.position)) { history.push({ location: { view: MACHINE_VIEW.Overview, documentUri: context.documentUri, - package: context.package + package: packageName || context.package } }); return resolve(); } const view = await getView(context.documentUri, context.position, context?.projectPath); - view.location.package = context.package; + view.location.package = packageName || context.package; history.push(view); return resolve(); } else { @@ -481,7 +482,7 @@ const stateMachine = createMachine( documentUri: context.documentUri, position: context.position, identifier: context.identifier, - package: context.package, + package: packageName || context.package, type: context?.type, isGraphql: context?.isGraphql, addType: context?.addType, @@ -788,17 +789,22 @@ async function handleSingleWorkspaceFolder(workspaceURI: Uri): Promise 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 (packages.length === 1) { + targetPackage = packages[0]; + } if (targetPackage) { - const packagePath = path.join(workspaceURI.fsPath, targetPackage); + const packagePath = path.isAbsolute(targetPackage) + ? targetPackage + : path.join(workspaceURI.fsPath, targetPackage); const packageUri = Uri.file(packagePath); const isBallerinaPackage = checkIsBallerinaPackage(packageUri); diff --git a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts index b2e5cc88d62..8d0bcdb3714 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts @@ -28,7 +28,7 @@ import { URI } from "vscode-uri"; import { debug } from "./logger"; import { parse } from "toml"; import { buildProjectArtifactsStructure } from "./project-artifacts"; -import { getProjectTomlValues } from "../rpc-managers/common/utils"; +import { getProjectTomlValues } from "./config"; export const README_FILE = "readme.md"; export const FUNCTIONS_FILE = "functions.bal"; diff --git a/workspaces/ballerina/ballerina-extension/src/utils/config.ts b/workspaces/ballerina/ballerina-extension/src/utils/config.ts index 3767f87e0cc..3f13725d2e5 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -16,11 +16,12 @@ * under the License. */ -import { SCOPE } from '@wso2/ballerina-core'; +import { PackageTomlValues, SCOPE, WorkspaceTomlValues } from '@wso2/ballerina-core'; import { BallerinaExtension } from '../core'; import { WorkspaceConfiguration, workspace, Uri } from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; +import { parse } from 'toml'; export enum VERSION { BETA = 'beta', @@ -221,6 +222,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'); From e9d7a38104fd1d2fa611ad235d7915bf9e5a68d6 Mon Sep 17 00:00:00 2001 From: NipunaRanasinghe Date: Wed, 22 Oct 2025 21:05:37 +0530 Subject: [PATCH 19/91] Fix fast-run to work with workspace projects --- .../src/features/debugger/config-provider.ts | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) 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 9839d621303..1ed99c5de9c 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts @@ -484,7 +484,7 @@ class BallerinaDebugAdapterTrackerFactory implements DebugAdapterTrackerFactory notifyBreakpointChange(); // restart the fast-run - getCurrentRoot().then(async (root) => { + getCurrentProjectRoot().then(async (root) => { const didStop = await stopRunFast(root); if (didStop) { runFast(root, msg.body); @@ -570,7 +570,7 @@ class BallerinaDebugAdapterDescriptorFactory implements DebugAdapterDescriptorFa async createDebugAdapterDescriptor(session: DebugSession, executable: DebugAdapterExecutable | undefined): Promise { // Check if the project contains errors(and fix the possible ones) before starting the debug session const langClient = extension.ballerinaExtInstance.langClient; - const projectRoot = await getCurrentRoot(); + const projectRoot = await getCurrentProjectRoot(); await cleanAndValidateProject(langClient, projectRoot); // Check if config generation is required before starting the debug session @@ -665,7 +665,7 @@ class FastRunDebugAdapter extends LoggingDebugSession { }); this.notificationHandler = notificationHandler; this.programArgs = (args as any).programArgs; - getCurrentRoot().then((root) => { + getCurrentProjectRoot().then((root) => { this.root = root; runFast(root, { programArgs: this.programArgs }).then((didRan) => { response.success = didRan; @@ -790,7 +790,14 @@ async function stopRunFast(root: string): Promise { }); } -async function getCurrentRoot(): Promise { +async function getCurrentProjectRoot(): Promise { + // 1. Check if the project path is already set in the state machine context + let currentProjectRoot = StateMachine.context().projectPath; + if (currentProjectRoot) { + return currentProjectRoot; + } + + // 2. Try to get the any open Ballerina files in the editor and determine the project root from there let file: string | undefined; try { file = getCurrentBallerinaFile(); @@ -798,19 +805,19 @@ async function getCurrentRoot(): Promise { // ignore } - // If no Ballerina files are open, safe to assume that the workspace root is same as the package root in BI mode. - if (!file) { - const workspaceRoot = getWorkspaceRoot(); - if (!workspaceRoot && StateMachine.context().isBI) { - throw new Error("Unable to determine the current workspace root."); - } - if (isBallerinaProject(workspaceRoot)) { - return workspaceRoot; - } + if (file) { + const currentProject = await getCurrentBallerinaProject(file); + return (currentProject.kind !== PROJECT_TYPE.SINGLE_FILE) ? currentProject.path! : file; } - const currentProject = await getCurrentBallerinaProject(file); - return (currentProject.kind !== PROJECT_TYPE.SINGLE_FILE) ? currentProject.path! : file; + // 3. Fallback to workspace root + const workspaceRoot = getWorkspaceRoot(); + if (!workspaceRoot && StateMachine.context().isBI) { + throw new Error("Unable to determine the current workspace root."); + } + if (isBallerinaProject(workspaceRoot)) { + return workspaceRoot; + } } function getJavaCommand(): string { From 5634d8267fe4617666a2690c0d89b5a1db9ec106 Mon Sep 17 00:00:00 2001 From: madushajg Date: Thu, 23 Oct 2025 19:14:55 +0530 Subject: [PATCH 20/91] Refactor Ballerina workspace handling to use async functions for checking workspace and package validity. Update related logic in state machine and AI utilities for improved performance and accuracy. --- .../ballerina-extension/src/core/extension.ts | 16 ++-- .../src/features/ai/testGenerator.ts | 39 ++-------- .../src/features/ai/utils.ts | 37 +--------- .../src/features/natural-programming/utils.ts | 19 +---- .../src/features/project/cmds/cmd-runner.ts | 1 - .../src/rpc-managers/common/rpc-manager.ts | 2 +- .../ballerina-extension/src/stateMachine.ts | 4 +- .../ballerina-extension/src/utils/bi.ts | 74 ------------------- .../ballerina-extension/src/utils/config.ts | 24 +++--- .../src/utils/file-utils.ts | 25 +++++-- .../src/utils/project-artifacts.ts | 15 ++-- .../views/persist-layer-diagram/activator.ts | 23 +++--- 12 files changed, 63 insertions(+), 216 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/core/extension.ts b/workspaces/ballerina/ballerina-extension/src/core/extension.ts index e8de14a22a0..1ef2d9d86e0 100644 --- a/workspaces/ballerina/ballerina-extension/src/core/extension.ts +++ b/workspaces/ballerina/ballerina-extension/src/core/extension.ts @@ -2346,13 +2346,15 @@ 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 => { + if (isPersistModelFile) { + this.isPersist = true; + commands.executeCommand('setContext', 'isPersistModelActive', true); + return; + } else { + this.isPersist = false; + } + }); } commands.executeCommand('setContext', 'isPersistModelActive', false); } diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts index 059ee5c5a12..89e5f6af3dd 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'; @@ -28,6 +28,7 @@ import * as os from 'os'; import { writeBallerinaFileDidOpenTemp } from '../../utils/modification'; import { closeAllBallerinaFiles } from './utils'; import { generateTestFromLLM, TestGenerationRequest1 } from './service/test/test'; +import { findBallerinaPackageRoot } from '../../utils'; const TEST_GEN_REQUEST_TIMEOUT = 100000; @@ -222,7 +223,7 @@ export async function getDiagnostics( projectRoot: string, generatedTestSource: TestGenerationResponse ): Promise { - const ballerinaProjectRoot = await findBallerinaProjectRoot(projectRoot); + const ballerinaProjectRoot = await findBallerinaPackageRoot(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'); @@ -245,7 +246,7 @@ export async function getDiagnostics( } async function getProjectSource(dirPath: string): Promise { - const projectRoot = await findBallerinaProjectRoot(dirPath); + const projectRoot = await findBallerinaPackageRoot(dirPath); if (!projectRoot) { return null; @@ -298,7 +299,7 @@ async function getProjectSource(dirPath: string): Promise } async function getProjectSourceWithTests(dirPath: string): Promise { - const projectRoot = await findBallerinaProjectRoot(dirPath); + const projectRoot = await findBallerinaPackageRoot(dirPath); if (!projectRoot) { return null; @@ -445,36 +446,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..618df1917a2 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts @@ -278,44 +278,13 @@ 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; -} +import { findBallerinaPackageRoot } from '../../utils'; /** * Gets the project source including all .bal files and modules */ export async function getProjectSource(dirPath: string): Promise { - const projectRoot = await findBallerinaProjectRoot(dirPath); + const projectRoot = await findBallerinaPackageRoot(dirPath); if (!projectRoot) { return null; @@ -371,7 +340,7 @@ export async function getProjectSource(dirPath: string): Promise { - const projectRoot = await findBallerinaProjectRoot(dirPath); + const projectRoot = await findBallerinaPackageRoot(dirPath); if (!projectRoot) { return null; 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 a434477a9cf..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'; @@ -723,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 8dcdc8a3c69..9fd0c8cef8a 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 @@ -90,7 +90,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/rpc-managers/common/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/rpc-manager.ts index 91a55e75f65..7563ca5731e 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 @@ -254,6 +254,6 @@ export class CommonRpcManager implements CommonRPCAPI { throw new Error("No workspaces found."); } const workspaceFolderPath = workspaceFolders[0].uri.fsPath; - return checkIsBallerinaWorkspace(Uri.file(workspaceFolderPath)); + return await checkIsBallerinaWorkspace(Uri.file(workspaceFolderPath)); } } diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index c22c72aa6a6..a3d1b34b987 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -775,7 +775,7 @@ async function handleMultipleWorkspaceFolders(workspaceFolders: readonly Workspa } async function handleSingleWorkspaceFolder(workspaceURI: Uri): Promise { - const isBallerinaWorkspace = checkIsBallerinaWorkspace(workspaceURI); + const isBallerinaWorkspace = await checkIsBallerinaWorkspace(workspaceURI); if (isBallerinaWorkspace) { // A workaround for supporting multiple packages in a workspace @@ -788,7 +788,7 @@ async function handleSingleWorkspaceFolder(workspaceURI: Uri): Promise 1) { diff --git a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts index 8d0bcdb3714..b50cdd0a9d0 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts @@ -560,77 +560,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 3f13725d2e5..78e96f55120 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -127,7 +127,7 @@ export function checkIsBI(uri: Uri): boolean { return false; // Return false if isBI is not set } -export function checkIsBallerinaPackage(uri: Uri): boolean { +export async function checkIsBallerinaPackage(uri: Uri): Promise { const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); // First check if the file exists @@ -136,10 +136,8 @@ export function checkIsBallerinaPackage(uri: Uri): boolean { } try { - // Read the file content and check for [package] section - const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); - const packageSectionRegex = /\[package\]/; - return packageSectionRegex.test(tomlContent); + 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}`); @@ -147,7 +145,7 @@ export function checkIsBallerinaPackage(uri: Uri): boolean { } } -export function checkIsBallerinaWorkspace(uri: Uri): boolean { +export async function checkIsBallerinaWorkspace(uri: Uri): Promise { const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); // First check if the file exists @@ -156,10 +154,8 @@ export function checkIsBallerinaWorkspace(uri: Uri): boolean { } try { - // Read the file content and check for [workspace] section - const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); - const workspaceSectionRegex = /\[workspace\]/; - return workspaceSectionRegex.test(tomlContent); + 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}`); @@ -180,18 +176,18 @@ export function checkIsBallerinaWorkspace(uri: Uri): boolean { * @param workspacePath Absolute path to the workspace root * @returns Filtered array of valid Ballerina package paths that exist within the workspace */ -export function filterPackagePaths(packagePaths: string[], workspacePath: string): string[] { - return packagePaths.filter(pkgPath => { +export async function filterPackagePaths(packagePaths: string[], workspacePath: string): Promise { + return packagePaths.filter(async pkgPath => { if (path.isAbsolute(pkgPath)) { const resolvedPath = path.resolve(pkgPath); const resolvedWorkspacePath = path.resolve(workspacePath); if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(resolvedWorkspacePath)) { - return checkIsBallerinaPackage(Uri.file(resolvedPath)); + return await checkIsBallerinaPackage(Uri.file(resolvedPath)); } } const resolvedPath = path.resolve(workspacePath, pkgPath); if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(workspacePath)) { - return checkIsBallerinaPackage(Uri.file(resolvedPath)); + return await checkIsBallerinaPackage(Uri.file(resolvedPath)); } return false; }); diff --git a/workspaces/ballerina/ballerina-extension/src/utils/file-utils.ts b/workspaces/ballerina/ballerina-extension/src/utils/file-utils.ts index 9bc57e70ffa..bcaa0befb9d 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 e96afdbc0e0..531e04f2f99 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts @@ -57,18 +57,15 @@ export async function buildProjectArtifactsStructure( let projectName = ""; if (workspace) { projectName = workspace.name; - - // Get the project name from the ballerina.toml file - const commonRpcManager = new CommonRpcManager(); - const tomlValues = await commonRpcManager.getCurrentProjectTomlValues(); - if (tomlValues && tomlValues.package.title) { - projectName = tomlValues.package.title; - } } else { // Project defined within a Ballerina workspace projectName = path.basename(projectPath); - - // TODO: Get the project name from the package Ballerina.toml file + } + // Get the project name from the ballerina.toml file + const commonRpcManager = new CommonRpcManager(); + const tomlValues = await commonRpcManager.getCurrentProjectTomlValues(); + if (tomlValues && tomlValues.package.title) { + projectName = tomlValues.package.title; } result.projectName = projectName; 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; } From 2f15ea3ca7e363fb0ba90196f56fcbeda483959c Mon Sep 17 00:00:00 2001 From: madushajg Date: Thu, 23 Oct 2025 21:04:30 +0530 Subject: [PATCH 21/91] Refactor project URI handling to use projectPath instead of projectUri --- .../src/rpc-managers/service-designer/rpc-manager.ts | 4 ++-- workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx | 2 +- .../src/components/ContextBasedFormTypeEditor/index.tsx | 5 +++-- .../TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx | 4 ++-- .../src/TypeEditor/ContextBasedTypeEditor/EditTypeView.tsx | 4 ++-- .../TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) 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 00f1eafdc76..3f7d71f8c93 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 @@ -423,7 +423,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; @@ -439,7 +439,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-visualizer/src/MainPanel.tsx b/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx index ed544d86cbe..2120e127498 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx @@ -564,7 +564,7 @@ const MainPanel = () => { ); break; diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/ContextBasedFormTypeEditor/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/ContextBasedFormTypeEditor/index.tsx index 053799a42c2..e3796055f30 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/ContextBasedFormTypeEditor/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/ContextBasedFormTypeEditor/index.tsx @@ -24,6 +24,7 @@ import DynamicModal from '../Modal'; import { FormTypeEditor } from '../../views/BI/TypeEditor'; import { useRpcContext } from '@wso2/ballerina-rpc-client'; import { ProgressRing } from '@wso2/ui-toolkit'; +import { URI, Utils } from 'vscode-uri'; const BreadcrumbContainer = styled.div` display: flex; @@ -209,8 +210,8 @@ export const ContextBasedFormTypeEditor: React.FC res.projectUri); - const filePath = `${projectUri}/types.bal`; + const projectPath = await rpcClient.getVisualizerLocation().then((res) => res.projectPath); + const filePath = Utils.joinPath(URI.file(projectPath), 'types.bal').fsPath; // Fetch all types from the file const typesResponse = await rpcClient.getBIDiagramRpcClient().getTypes({ diff --git a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx index b4daea98a56..15c46a0b50d 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx @@ -334,10 +334,10 @@ export function ContextTypeCreatorTab(props: ContextTypeCreatorProps) { 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/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 fc5bbe74941..111b0139b69 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx @@ -194,10 +194,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({ From 91e1195dfa52371c911d4284abe85d58bcd4b253 Mon Sep 17 00:00:00 2001 From: NipunaRanasinghe Date: Mon, 27 Oct 2025 23:50:11 +0530 Subject: [PATCH 22/91] Fix error messages shown when stepping into other packages within the workspace --- .../src/features/debugger/config-provider.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 1ed99c5de9c..97b640fc6fe 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts @@ -467,7 +467,7 @@ class BallerinaDebugAdapterTrackerFactory implements DebugAdapterTrackerFactory BreakpointManager.getInstance().setActiveBreakpoint(clientBreakpoint); if (isWebviewPresent) { - await handleBreakpointVisualization(uri, clientBreakpoint); + await handleDebugHitVisualization(uri, clientBreakpoint); } } else if (msg.command === "continue" || msg.command === "next" || msg.command === "stepIn" || msg.command === "stepOut") { // clear the active breakpoint @@ -516,13 +516,13 @@ class BallerinaDebugAdapterTrackerFactory implements DebugAdapterTrackerFactory } } -async function handleBreakpointVisualization(uri: Uri, clientBreakpoint: DebugProtocol.StackFrame) { +async function handleDebugHitVisualization(uri: Uri, clientBreakpoint: DebugProtocol.StackFrame) { const newContext = StateMachine.context(); - // Check if breakpoint is in a different project + // Check if breakpoint is in a different package if (!uri.fsPath.startsWith(newContext.projectPath)) { - console.log("Breakpoint is in a different project"); - window.showInformationMessage("Cannot visualize breakpoint since it belongs to a different project"); + 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); notifyBreakpointChange(); return; @@ -540,7 +540,7 @@ async function handleBreakpointVisualization(uri: Uri, clientBreakpoint: DebugPr const res = await StateMachine.langClient().getEnclosedFunctionDef(req); if (!res?.startLine || !res?.endLine) { - window.showInformationMessage("Failed to open the respective view for the breakpoint. Please manually navigate to the respective view."); + window.showInformationMessage("Failed to open the respective view for the debug hit. Please manually navigate to the respective view."); notifyBreakpointChange(); return; } From a9ca37033b348d1aa028924f53655887d2dd5a40 Mon Sep 17 00:00:00 2001 From: NipunaRanasinghe Date: Tue, 28 Oct 2025 09:02:15 +0530 Subject: [PATCH 23/91] Fix project selection and debug restart flows in BI mode --- .../src/features/debugger/config-provider.ts | 90 ++++--------------- .../src/utils/project-utils.ts | 63 ++++++++++++- 2 files changed, 80 insertions(+), 73 deletions(-) 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 97b640fc6fe..a421fce0cc8 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts @@ -26,8 +26,7 @@ import { TaskExecution, DebugAdapterTrackerFactory, DebugAdapterTracker, - ViewColumn, - TabInputText + ViewColumn } from 'vscode'; import * as child_process from "child_process"; import { getPortPromise } from 'portfinder'; @@ -36,7 +35,6 @@ import { BallerinaExtension, LANGUAGE, OLD_BALLERINA_VERSION_DEBUGGER_RUNINTERMINAL, UNSUPPORTED_DEBUGGER_RUNINTERMINAL_KIND, INVALID_DEBUGGER_RUNINTERMINAL_KIND } from '../../core'; -import { ExtendedLangClient } from '../../core/extended-language-client'; import { TM_EVENT_START_DEBUG_SESSION, CMP_DEBUGGER, sendTelemetryEvent, sendTelemetryException, CMP_NOTEBOOK, TM_EVENT_START_NOTEBOOK_DEBUG @@ -45,15 +43,14 @@ import { log, debug as debugLog, isSupportedSLVersion, isWindows } from "../../u import { decimal, ExecutableOptions } from 'vscode-languageclient/node'; import { BAL_NOTEBOOK, getTempFile, NOTEBOOK_CELL_SCHEME } from '../../views/notebook'; import fileUriToPath from 'file-uri-to-path'; -import { existsSync, readFileSync } from 'fs'; -import { dirname, join, sep } from 'path'; -import { parseTomlToConfig } from '../config-generator/utils'; +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 { Disposable } from 'monaco-languageclient'; -import { getCurrentBallerinaFile, getCurrentBallerinaProject } from '../../utils/project-utils'; -import { BallerinaProject, BallerinaProjectComponents, BIGetEnclosedFunctionRequest, EVENT_TYPE, MainFunctionParamsResponse } from '@wso2/ballerina-core'; +import { getCurrentBallerinaFile, getCurrentBallerinaProject, 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'; import { BreakpointManager } from './breakpoint-manager'; @@ -67,8 +64,6 @@ import { findHighestVersionJdk } from '../../utils/server/server'; const BALLERINA_COMMAND = "ballerina.command"; const EXTENDED_CLIENT_CAPABILITIES = "capabilities"; -const BALLERINA_TOML_REGEX = `**${sep}Ballerina.toml`; -const BALLERINA_FILE_REGEX = `**${sep}*.bal`; const BALLERINA_TOML = `Ballerina.toml`; export enum DEBUG_REQUEST { @@ -80,17 +75,6 @@ export enum DEBUG_CONFIG { TEST_DEBUG_NAME = 'Ballerina Test' } -export interface BALLERINA_TOML { - package: PACKAGE; - "build-options": any; -} - -export interface PACKAGE { - org: string; - name: string; - version: string; - distribution: string; -} class DebugConfigProvider implements DebugConfigurationProvider { async resolveDebugConfiguration(_folder: WorkspaceFolder, config: DebugConfiguration) @@ -253,6 +237,12 @@ async function getModifiedConfigs(workspaceFolder: WorkspaceFolder, config: Debu config.noDebug = Boolean(config.noDebug); + // Notify debug server that the debug session is started in low-code mode + const isWebviewPresent = isVisualizerWebviewActive(); + if (isWebviewPresent && StateMachine.context().isBI) { + config.lowCodeMode = true; + } + const activeTextEditor = window.activeTextEditor; if (activeTextEditor && activeTextEditor.document.fileName.endsWith(BAL_NOTEBOOK)) { @@ -267,52 +257,11 @@ async function getModifiedConfigs(workspaceFolder: WorkspaceFolder, config: Debu } if (!config.script) { - const tomls = await workspace.findFiles(workspaceFolder ? new RelativePattern(workspaceFolder, BALLERINA_TOML_REGEX) : BALLERINA_TOML_REGEX); - const projects: { project: BallerinaProject; balFile: Uri; relativePath: string }[] = []; - for (const toml of tomls) { - const projectRoot = dirname(toml.fsPath); - const balFiles = await workspace.findFiles(new RelativePattern(projectRoot, BALLERINA_FILE_REGEX), undefined, 1); - if (balFiles.length > 0) { - - const tomlContent: string = readFileSync(toml.fsPath, 'utf8'); - const tomlObj: BALLERINA_TOML = parseTomlToConfig(tomlContent) as BALLERINA_TOML; - const relativePath = workspace.asRelativePath(projectRoot); - projects.push({ project: { packageName: tomlObj.package.name }, balFile: balFiles[0], relativePath }); - } - } - - if (projects.length > 0) { - if (projects.length === 1) { - config.script = projects[0].balFile.fsPath; - } else { - const selectedProject = await window.showQuickPick(projects.map((project) => { - return { - label: project.project.packageName, - description: project.relativePath - }; - }), { placeHolder: "Select a Ballerina project to debug", canPickMany: false }); - if (selectedProject) { - config.script = projects[projects.indexOf(projects.find((project) => { - return project.project.packageName === selectedProject.label; - }))].balFile.fsPath; - } else { - return Promise.reject(); - } - } + // 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) { + config.script = StateMachine.context().projectPath; } else { - extension.ballerinaExtInstance.showMessageInvalidProject(); - return Promise.reject(); - } - - let langClient = extension.ballerinaExtInstance.langClient; - if (langClient.initializeResult) { - const { experimental } = langClient.initializeResult!.capabilities; - if (experimental && experimental.introspection && experimental.introspection.port > 0) { - config.networkLogsPort = experimental.introspection.port; - if (config.networkLogs === undefined) { - config.networkLogs = false; - } - } + config.script = await selectBallerinaProjectForDebugging(workspaceFolder); } } @@ -338,14 +287,13 @@ async function getModifiedConfigs(workspaceFolder: WorkspaceFolder, config: Debu config.debugServer = debugServerPort.toString(); } - // Notify debug server that the debug session is started in low-code mode - const isWebviewPresent = VisualizerWebview.currentPanel !== undefined; - if (isWebviewPresent && StateMachine.context().isBI) { - config.lowCodeMode = true; - } return config; } +function isVisualizerWebviewActive() { + return VisualizerWebview.currentPanel !== undefined; +} + export async function constructDebugConfig(uri: Uri, testDebug: boolean, args?: any): Promise { const launchConfig: WorkspaceConfiguration = workspace.getConfiguration('launch').length > 0 ? workspace.getConfiguration('launch') : diff --git a/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts b/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts index b41581c4821..af40ac2f531 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts @@ -17,10 +17,28 @@ */ import { extension } from "../BalExtensionContext"; -import { Uri, window, workspace } from "vscode"; +import { Uri, window, workspace, RelativePattern, WorkspaceFolder } from "vscode"; import * as path from 'path'; import { 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'; + +const BALLERINA_TOML_REGEX = `**${sep}Ballerina.toml`; +const BALLERINA_FILE_REGEX = `**${sep}*.bal`; + +export interface BALLERINA_TOML { + package: PACKAGE; + "build-options": any; +} + +export interface PACKAGE { + org: string; + name: string; + version: string; + distribution: string; +} function getCurrentBallerinaProject(projectPath?: string): Promise { return new Promise((resolve, reject) => { @@ -78,4 +96,45 @@ function addToWorkspace(url: string) { workspace.updateWorkspaceFolders(workspace.workspaceFolders ? workspace.workspaceFolders.length : 0, null, { uri: Uri.parse(url) }); } -export { addToWorkspace, getCurrentBallerinaProject, getCurrentBallerinaFile, getCurrenDirectoryPath }; +async function selectBallerinaProjectForDebugging(workspaceFolder?: WorkspaceFolder): Promise { + const tomls = await workspace.findFiles(workspaceFolder ? new RelativePattern(workspaceFolder, BALLERINA_TOML_REGEX) : BALLERINA_TOML_REGEX); + const projects: { project: BallerinaProject; balFile: Uri; relativePath: string }[] = []; + + for (const toml of tomls) { + const projectRoot = dirname(toml.fsPath); + const balFiles = await workspace.findFiles(new RelativePattern(projectRoot, BALLERINA_FILE_REGEX), undefined, 1); + if (balFiles.length > 0) { + const tomlContent: string = readFileSync(toml.fsPath, 'utf8'); + const tomlObj: BALLERINA_TOML = parseTomlToConfig(tomlContent) as BALLERINA_TOML; + const relativePath = workspace.asRelativePath(projectRoot); + // Add only if package name is present in Ballerina.toml (this is to exclude workspace projects) + if (tomlObj.package && tomlObj.package.name) { + projects.push({ project: { packageName: tomlObj.package.name }, balFile: balFiles[0], relativePath }); + } + } + } + + if (projects.length === 1) { + return projects[0].balFile.fsPath; + } else if (projects.length > 1) { + const selectedProject = await window.showQuickPick(projects.map((project) => { + return { + label: project.project.packageName, + description: project.relativePath + }; + }), { placeHolder: "Detected multiple Ballerina projects within the workspace. Select one to debug.", canPickMany: false }); + + if (selectedProject) { + const foundProject = projects.find((project) => project.project.packageName === selectedProject.label); + if (foundProject) { + return foundProject.balFile.fsPath; + } + } + throw new Error("Project selection cancelled"); + } else { + extension.ballerinaExtInstance.showMessageInvalidProject(); + throw new Error("No valid Ballerina projects found"); + } +} + +export { addToWorkspace, getCurrentBallerinaProject, getCurrentBallerinaFile, getCurrenDirectoryPath, selectBallerinaProjectForDebugging }; From be7b4f56e6f9638072fbe0c77f3800d016bffa8d Mon Sep 17 00:00:00 2001 From: Nipuna Ransinghe Date: Tue, 28 Oct 2025 13:28:40 +0530 Subject: [PATCH 24/91] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/features/debugger/config-provider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a421fce0cc8..6f910e7df42 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts @@ -257,7 +257,7 @@ 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 webview is present and in BI mode, use the project path from the state machine (focused project in BI) if (StateMachine.context().isBI && isWebviewPresent) { config.script = StateMachine.context().projectPath; } else { @@ -745,7 +745,7 @@ async function getCurrentProjectRoot(): Promise { return currentProjectRoot; } - // 2. Try to get the any open Ballerina files in the editor and determine the project root from there + // 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(); From 98ed3135dc6a2f0074a7f9353fa18e2a06aa7b97 Mon Sep 17 00:00:00 2001 From: NipunaRanasinghe Date: Tue, 28 Oct 2025 15:19:04 +0530 Subject: [PATCH 25/91] Address more review suggestions --- .../src/features/config-generator/configGenerator.ts | 10 ++++++---- .../src/features/debugger/config-provider.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) 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 d72ac0761dc..2a28d03619d 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/config-generator/configGenerator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/config-generator/configGenerator.ts @@ -29,7 +29,7 @@ import { TextDocumentEdit } from "vscode-languageserver-types"; import { modifyFileContent } from "../../utils/modification"; import { fileURLToPath } from "url"; import { startDebugging } from "../editor-support/activator"; -import { openView } from "../../stateMachine"; +import { openView, StateMachine } from "../../stateMachine"; import * as path from "path"; const UNUSED_IMPORT_ERR_CODE = "BCE2002"; @@ -150,9 +150,11 @@ export async function getCurrentBallerinaProjectFromContext(ballerinaExtInstance } export async function getCurrentBIProject(projectPath: string): Promise { - let currentProject: BallerinaProject = {}; - currentProject = await getCurrentBallerinaProject(projectPath); - return currentProject; + if (StateMachine.context().projectPath) { + projectPath = StateMachine.context().projectPath; + } + + return await getCurrentBallerinaProject(projectPath); } export async function handleOnUnSetValues(packageName: string, configFile: string, ignoreFile: string, ballerinaExtInstance: BallerinaExtension, isCommand: boolean, isBi: boolean): Promise { 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 6f910e7df42..463ef865017 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts @@ -258,7 +258,7 @@ 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) { + if (StateMachine.context().isBI && isWebviewPresent && StateMachine.context().projectPath) { config.script = StateMachine.context().projectPath; } else { config.script = await selectBallerinaProjectForDebugging(workspaceFolder); @@ -755,7 +755,11 @@ async function getCurrentProjectRoot(): Promise { if (file) { const currentProject = await getCurrentBallerinaProject(file); - return (currentProject.kind !== PROJECT_TYPE.SINGLE_FILE) ? currentProject.path! : file; + if (currentProject.kind === PROJECT_TYPE.SINGLE_FILE) { + return file; + } else if (currentProject.path) { + return currentProject.path; + } } // 3. Fallback to workspace root From 766afdb71a68a32bb6cd14e5651c7b7bc0003972 Mon Sep 17 00:00:00 2001 From: madushajg Date: Fri, 31 Oct 2025 23:14:39 +0530 Subject: [PATCH 26/91] Refactor filterPackagePaths to use Promise.all for improved async handling and streamline package validation logic. --- .../ballerina-extension/src/utils/config.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/utils/config.ts b/workspaces/ballerina/ballerina-extension/src/utils/config.ts index 78e96f55120..1eb016d40b4 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -177,20 +177,23 @@ export async function checkIsBallerinaWorkspace(uri: Uri): Promise { * @returns Filtered array of valid Ballerina package paths that exist within the workspace */ export async function filterPackagePaths(packagePaths: string[], workspacePath: string): Promise { - return packagePaths.filter(async pkgPath => { - if (path.isAbsolute(pkgPath)) { - const resolvedPath = path.resolve(pkgPath); - const resolvedWorkspacePath = path.resolve(workspacePath); - if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(resolvedWorkspacePath)) { + 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) && resolvedPath.startsWith(resolvedWorkspacePath)) { + return await checkIsBallerinaPackage(Uri.file(resolvedPath)); + } + } + const resolvedPath = path.resolve(workspacePath, pkgPath); + if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(workspacePath)) { return await checkIsBallerinaPackage(Uri.file(resolvedPath)); } - } - const resolvedPath = path.resolve(workspacePath, pkgPath); - if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(workspacePath)) { - return await checkIsBallerinaPackage(Uri.file(resolvedPath)); - } - return false; - }); + return false; + }) + ); + return packagePaths.filter((_, index) => results[index]); } export function getOrgPackageName(projectPath: string): { orgName: string, packageName: string } { From 2969adae2c61f87b1982bb8d227119ab429ff31b Mon Sep 17 00:00:00 2001 From: gigara Date: Tue, 4 Nov 2025 11:45:52 +0530 Subject: [PATCH 27/91] Remove redundant commands --- rush.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rush.json b/rush.json index c182811138b..564262c5974 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 From 1ef57622f84783e88b4272529c15533b23436406 Mon Sep 17 00:00:00 2001 From: gigara Date: Tue, 4 Nov 2025 11:46:17 +0530 Subject: [PATCH 28/91] Fix test results cache key id --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 268d8489b0110e14f72cf88033d4683d6f252089 Mon Sep 17 00:00:00 2001 From: gigara Date: Tue, 4 Nov 2025 12:22:21 +0530 Subject: [PATCH 29/91] Remove console log for redirect location in downloadFile function --- workspaces/ballerina/ballerina-extension/scripts/download-ls.js | 1 - 1 file changed, 1 deletion(-) 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; } From d6b2588197775f5985c5aad6dbcd6c0bac6cf9e7 Mon Sep 17 00:00:00 2001 From: tharindulak Date: Thu, 6 Nov 2025 12:20:31 +0530 Subject: [PATCH 30/91] Increase wait time for pom.xml update during project version change --- .../overviewPageTests/projectSettingPage.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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..1fd73ba06ee 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 @@ -51,8 +51,8 @@ export default function createTests() { 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'); }); From a10178066faf06ef43391ba545eabe164ca1441a Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Tue, 14 Oct 2025 18:28:28 +0530 Subject: [PATCH 31/91] Implement new login flow --- MI_COPILOT_REFACTOR_SUMMARY.md | 210 +++++++ .../mi/mi-core/src/state-machine-types.ts | 76 ++- workspaces/mi/mi-extension/src/RPCLayer.ts | 11 +- .../mi/mi-extension/src/ai-panel/aiMachine.ts | 515 ++++++++++-------- .../mi/mi-extension/src/ai-panel/auth.ts | 40 +- .../src/ai-panel/copilot/connection.ts | 125 +++++ .../ai-panel/copilot/suggestions/prompt.md | 35 ++ .../copilot/suggestions/suggestions.ts | 185 +++++++ .../ai-panel/copilot/suggestions/system.md | 22 + .../mi/mi-extension/src/ai-panel/utils.ts | 130 +++++ .../mi-extension/src/ai-panel/utils/auth.ts | 189 +++++++ .../src/rpc-managers/ai-panel/rpc-manager.ts | 18 +- workspaces/mi/mi-rpc-client/src/RpcClient.ts | 4 +- .../views/AIPanel/component/AIChatHeader.tsx | 61 +-- .../AIPanel/component/MICopilotContext.tsx | 2 +- .../component/WaitingForLoginSection.tsx | 291 ++++++++++ .../mi-visualizer/src/views/AIPanel/index.tsx | 48 +- .../mi-visualizer/src/views/AIPanel/utils.ts | 9 + .../src/views/LoggedOutWindow/index.tsx | 34 ++ 19 files changed, 1680 insertions(+), 325 deletions(-) create mode 100644 MI_COPILOT_REFACTOR_SUMMARY.md create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/prompt.md create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/system.md create mode 100644 workspaces/mi/mi-extension/src/ai-panel/utils.ts create mode 100644 workspaces/mi/mi-extension/src/ai-panel/utils/auth.ts create mode 100644 workspaces/mi/mi-visualizer/src/views/AIPanel/component/WaitingForLoginSection.tsx diff --git a/MI_COPILOT_REFACTOR_SUMMARY.md b/MI_COPILOT_REFACTOR_SUMMARY.md new file mode 100644 index 00000000000..7b0e64b0ffc --- /dev/null +++ b/MI_COPILOT_REFACTOR_SUMMARY.md @@ -0,0 +1,210 @@ +# MI Copilot Refactoring Summary + +## Overview +This refactoring moves MI Copilot logic (prompts, AI service calls) from the backend to the VSCode extension, following the pattern established by Ballerina (BI) Copilot. The changes enable support for two authentication methods: MI Intel (SSO) and Anthropic API Key. AWS Bedrock support can be added in a future iteration once the core functionality is stable. + +## Key Changes + +### 1. State Machine Types (`workspaces/mi/mi-core/src/state-machine-types.ts`) + +#### Added New Types: +- **`AIMachineStateValue`**: Updated to support hierarchical authentication states + - `Initialize`: Checking authentication status + - `Unauthenticated`: Show login window + - `Authenticating`: Hierarchical state with substates: + - `determineFlow`: Route to appropriate auth flow + - `ssoFlow`: MI Intel SSO authentication + - `apiKeyFlow`: Anthropic API key input + - `validatingApiKey`: Validating API key + - `Authenticated`: Ready state with active session + - `Disabled`: Extension disabled + - `NotSupported`: Multi-root workspace not supported + +- **`AI_EVENT_TYPE`**: Extended with new authentication events + - `CHECK_AUTH`, `AUTH_WITH_API_KEY`, `SUBMIT_API_KEY` + - `COMPLETE_AUTH`, `CANCEL_LOGIN`, `SILENT_LOGOUT` + +- **`LoginMethod` enum**: + - `MI_INTEL`: WSO2 SSO authentication + - `ANTHROPIC_KEY`: Direct Anthropic API key + +- **`AuthCredentials` type**: Discriminated union for storing credentials + - Different secret structures for each login method + - Type-safe credential management + +- **`AIMachineContext`**: State machine context + - `loginMethod`: Current authentication method + - `userToken`: Active token information + - `errorMessage`: Error state tracking + +- **`AIMachineEventMap` and `AIMachineSendableEvent`**: Type-safe event handling + +### 2. Connection Service (`workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts`) + +New file providing AI service connections: + +- **`getAnthropicClient(model)`**: Returns appropriate Anthropic client based on login method + - MI Intel: Uses backend proxy with token auth + - Anthropic Key: Direct API connection + +- **`fetchWithAuth()`**: Handles authenticated requests with automatic token refresh + +- **`getProviderCacheControl()`**: Cache control settings for Anthropic + +### 3. Authentication Utilities (`workspaces/mi/mi-extension/src/ai-panel/utils/auth.ts`) + +New authentication management utilities: + +- **Credential Storage**: + - `storeAuthCredentials()`: Store credentials securely + - `getAuthCredentials()`: Retrieve stored credentials + - `clearAuthCredentials()`: Clear credentials on logout + +- **Token Management**: + - `getAccessToken()`: Get current access token with auto-refresh + - `getRefreshedAccessToken()`: Refresh MI Intel tokens + - `getLoginMethod()`: Get current login method + +- **Legacy Migration**: + - `cleanupLegacyTokens()`: Remove old token storage format + +### 4. State Machine Services (`workspaces/mi/mi-extension/src/ai-panel/utils.ts`) + +New file with state machine service implementations: + +- **`checkToken()`**: Verify existing authentication +- **`logout(isUserLogout)`**: Handle logout (with/without SSO redirect) +- **`initiateInbuiltAuth()`**: Start SSO authentication flow +- **`validateApiKey(apiKey, loginMethod)`**: Validate Anthropic API key + +### 5. Updated AI State Machine (`workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts`) + +Complete rewrite following Ballerina's pattern: + +- **Hierarchical State Structure**: + - Proper state transitions for each auth method + - Error handling with automatic retry/logout + - Workspace validation before authentication + +- **Service Integration**: + - `checkWorkspaceAndToken`: Combined workspace and token check + - `validateApiKey`: API key validation service + - `getTokenAfterAuth`: Retrieve token after successful auth + +- **Type-Safe Event Handling**: + - Support for events with payloads (API key, AWS credentials) + - Discriminated union for event types + +### 6. Updated Login Screen (`workspaces/mi/mi-visualizer/src/views/LoggedOutWindow/index.tsx`) + +Enhanced login UI with multiple authentication options: + +- **New UI Elements**: + - "Login to MI Copilot" button (SSO) + - "Enter your Anthropic API key" link + - Divider between options + +- **Event Handlers**: + - `handleAnthropicKeyClick()`: Trigger API key flow + +### 7. Simplified Chat Header (`workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatHeader.tsx`) + +Removed custom API key functionality: + +- **Removed**: + - API key setup button + - API key status badge + - `hasApiKey` state management + - `checkApiKey()` function + - `handleSetApiKey()` function + +- **Kept**: + - Clear chat button + - Logout button + +### 8. Updated Auth Module (`workspaces/mi/mi-extension/src/ai-panel/auth.ts`) + +Updated to use new credential structure: + +- Added `getLogoutUrl()` function +- Updated `exchangeAuthCode()` to use new `AuthCredentials` format +- Stores credentials via `storeAuthCredentials()` + +### 9. Package Dependencies (`workspaces/mi/mi-extension/package.json`) + +Added new dependencies: + +```json +"@ai-sdk/anthropic": "^1.2.12", +"ai": "^4.3.16", +"jwt-decode": "^4.0.0" +``` + +## Migration Path + +### For Users: + +1. **Existing MI Intel Users**: + - Tokens will be migrated to new format on first launch + - No action required + +2. **API Key Users**: + - Previous custom API key functionality removed from header + - Must set API key via login screen + - Re-authenticate on next login + +### For Developers: + +1. **Install Dependencies**: Run `rush update` or package manager to install new dependencies +2. **Build**: Rebuild the extension +3. **Test**: Verify all authentication flows work correctly + +## Benefits + +1. **Consistency**: Matches Ballerina's proven authentication architecture +2. **Flexibility**: Support for multiple AI providers (API key and SSO) +3. **Type Safety**: Full TypeScript type coverage for auth flows +4. **Future-Ready**: Easy to add new authentication methods (AWS Bedrock can be added later) +5. **Better UX**: Unified login experience +6. **Simplified Implementation**: Focus on core functionality first +7. **Local Processing**: AI logic runs in extension (future implementation) + +## Future Work + +1. Implement AI prompt logic in extension (currently in backend) +2. Add input form for API key in visualizer +3. Migrate existing backend AI logic to extension +4. Add settings panel similar to Ballerina for managing auth +5. Token usage tracking for custom API keys +6. Support for custom model selection +7. Add AWS Bedrock support (deferred to future release) + +## Files Modified + +### Core Types: +- `workspaces/mi/mi-core/src/state-machine-types.ts` + +### Extension Backend: +- `workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts` (complete rewrite) +- `workspaces/mi/mi-extension/src/ai-panel/auth.ts` (updated) +- `workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts` (new) +- `workspaces/mi/mi-extension/src/ai-panel/utils.ts` (new) +- `workspaces/mi/mi-extension/src/ai-panel/utils/auth.ts` (new) +- `workspaces/mi/mi-extension/package.json` (dependencies added) + +### Visualizer Frontend: +- `workspaces/mi/mi-visualizer/src/views/LoggedOutWindow/index.tsx` (updated) +- `workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatHeader.tsx` (simplified) + +## Testing Checklist + +- [ ] MI Intel SSO login flow +- [ ] Anthropic API key login flow +- [ ] Token refresh for MI Intel +- [ ] Logout from each auth method +- [ ] Multi-root workspace handling +- [ ] Error handling for invalid credentials +- [ ] Legacy token migration +- [ ] State persistence across extension reloads +- [ ] API key validation with valid/invalid keys + diff --git a/workspaces/mi/mi-core/src/state-machine-types.ts b/workspaces/mi/mi-core/src/state-machine-types.ts index b08086bc72c..d7dccbdda0f 100644 --- a/workspaces/mi/mi-core/src/state-machine-types.ts +++ b/workspaces/mi/mi-core/src/state-machine-types.ts @@ -109,24 +109,94 @@ 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) + | '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", } +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; +}; + +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; + errorMessage?: string; +} + export enum EVENT_TYPE { OPEN_VIEW = "OPEN_VIEW", REPLACE_VIEW = "REPLACE_VIEW", @@ -201,7 +271,9 @@ export interface AIVisualizerLocation { view?: AI_MACHINE_VIEW | null; initialPrompt?: PromptObject; state?: AIMachineStateValue; - userTokens?: AIUserTokens; + loginMethod?: LoginMethod; + userToken?: AIUserToken; + errorMessage?: string; } export interface AIUserTokens { diff --git a/workspaces/mi/mi-extension/src/RPCLayer.ts b/workspaces/mi/mi-extension/src/RPCLayer.ts index e154bf23d31..dd885fb8f19 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)); @@ -122,7 +122,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({ + view: context.view, + initialPrompt: extension.initialPrompt, + state: StateMachineAI.state(), + loginMethod: context.loginMethod, + userToken: context.userToken, + 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..ace34cae10f 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts @@ -19,305 +19,394 @@ /* 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, AIUserToken, 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 } from './utils/auth'; +import { checkToken, initiateInbuiltAuth, logout, validateApiKey } from './utils'; import { PromptObject } from '@wso2/mi-core'; -interface ChatEntry { - role: string; - content: string; - errorCode?: string; -} +export const USER_CHECK_BACKEND_URL = '/user/usage'; -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, errorMessage: undefined, }, on: { DISPOSE: { - target: "checkWorkspace", + target: 'Initialize', + actions: assign({ + loginMethod: (_ctx) => undefined, + userToken: (_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, + 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, + 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, + errorMessage: (_ctx) => undefined, + }) + ] }, { - target: 'disabled', - actions: - assign({ - errorCode: (context, event) => event.data + target: 'Disabled', + actions: assign({ + loginMethod: (_ctx) => undefined, + userToken: (_ctx) => undefined, + errorMessage: (_ctx, event) => event.data?.message || 'Unknown error' }) } ] } }, - - loggedOut: { + Unauthenticated: { on: { - LOGIN: { - target: "WaitingForLogin", - } - } - }, - - Ready: { - invoke: { - src: 'getSuggestions', - onDone: { - target: "Ready" + [AI_EVENT_TYPE.LOGIN]: { + target: 'Authenticating', + actions: assign({ + loginMethod: (_ctx) => LoginMethod.MI_INTEL + }) }, - onError: { - target: "Ready", + [AI_EVENT_TYPE.AUTH_WITH_API_KEY]: { + target: 'Authenticating', actions: assign({ - errorCode: (context, event) => event.data + loginMethod: (_ctx) => LoginMethod.ANTHROPIC_KEY }) } - }, - on: { - LOGOUT: "loggedOut", - EXECUTE: "Executing", - CLEAR: { - target: "Ready", - } } }, - - disabled: { - invoke: { - src: 'disableExtension' - }, - on: { - RETRY: { - target: "initialize", + 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, + }) + } + } }, - LOGOUT: "loggedOut", + 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' + }) + } + } + } } }, - - WaitingForLogin: { + Authenticated: { invoke: { - src: 'openLogin', + id: 'getTokenAfterAuth', + src: 'getTokenAfterAuth', + onDone: { + actions: assign({ + userToken: (_ctx, event) => ({ token: event.data.token }), + loginMethod: (_ctx, event) => event.data.loginMethod, + errorMessage: (_ctx) => undefined, + }) + }, onError: { - target: "loggedOut", + target: 'Unauthenticated', actions: assign({ - errorCode: (context, event) => event.data + userToken: (_ctx) => undefined, + loginMethod: (_ctx) => undefined, + errorMessage: (_ctx, event) => event.data?.message || 'Failed to retrieve authentication credentials', }) } }, on: { - SIGN_IN_SUCCESS: "Ready", - CANCEL: "loggedOut", - FAILIER: "loggedOut" + [AI_EVENT_TYPE.LOGOUT]: { + target: 'Unauthenticated', + actions: [ + 'logout', + assign({ + loginMethod: (_) => undefined, + userToken: (_) => undefined, + errorMessage: (_) => undefined, + }) + ] + }, + [AI_EVENT_TYPE.SILENT_LOGOUT]: { + target: 'Unauthenticated', + actions: [ + 'silentLogout', + assign({ + loginMethod: (_) => undefined, + userToken: (_) => undefined, + errorMessage: (_) => undefined, + }) + ] + } } }, - - Executing: { + Disabled: { on: { - COMPLETE: "Ready", - ERROR: "Ready", - STOP: "Ready", - LOGEDOUT: "loggedOut" + [AI_EVENT_TYPE.RETRY]: { + target: 'Initialize', + actions: assign({ + userToken: (_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, + loginMethod: (_ctx) => undefined, + errorMessage: (_ctx) => undefined, + }) + }, + [AI_EVENT_TYPE.LOGOUT]: { + target: 'Unauthenticated', + actions: [ + 'logout', + assign({ + loginMethod: (_) => undefined, + userToken: (_) => 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); - } - }); -} - -async function checkToken(context, event): Promise { +const checkWorkspaceAndToken = async (): Promise<{ workspaceSupported: boolean; tokenData?: { token: string; loginMethod: LoginMethod } }> => { 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 }); + // Check workspace support first + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) { + resolve({ workspaceSupported: false }); + return; } - } catch (error: any) { - log(error.toString()); + + // Then check token + const tokenData = await checkToken(); + resolve({ workspaceSupported: true, tokenData }); + } catch (error) { reject(error); } }); -} +}; -async function openLogin(context, event) { +const openLogin = async () => { return new Promise(async (resolve, reject) => { try { - initiateInbuiltAuth(); + const status = await initiateInbuiltAuth(); + if (!status) { + aiStateService.send({ type: AI_EVENT_TYPE.CANCEL_LOGIN }); + } + resolve(status); } catch (error) { reject(error); } }); -} +}; + +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); +}; + +const getTokenAfterAuth = async () => { + const result = await getAccessToken(); + const loginMethod = await getLoginMethod(); + if (!result || !loginMethod) { + throw new Error('No authentication credentials found'); + } + return { token: result, loginMethod: loginMethod }; +}; -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 aiStateService = interpret(aiMachine.withConfig({ + services: { + checkWorkspaceAndToken: checkWorkspaceAndToken, + openLogin: openLogin, + validateApiKey: validateApiKeyService, + getTokenAfterAuth: getTokenAfterAuth, + }, + actions: { + logout: () => { + logout(); + }, + silentLogout: () => { + logout(false); + }, + } +})); -// Create a service to interpret the machine -export const aiStateService = interpret(aiStateMachine); +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..725c47c7241 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/auth.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/auth.ts @@ -18,11 +18,12 @@ import axios from 'axios'; import { StateMachineAI } from './aiMachine'; -import { AI_EVENT_TYPE, AIUserTokens } from '@wso2/mi-core'; +import { AI_EVENT_TYPE, AIUserTokens, 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'; +import { storeAuthCredentials } from './utils/auth'; export interface AccessToken { accessToken: string; @@ -46,10 +47,14 @@ export async function getAuthUrl(callbackUri: string): Promise { // return `${this._config.loginUrl}?profile=vs-code&client_id=${this._config.clientId}` // + `&state=${stateBase64}&code_challenge=${this._challenge.code_challenge}`; - const state = encodeURIComponent(btoa(JSON.stringify({ callbackUri: 'vscode://wso2.micro-integrator/signin' }))); + const state = encodeURIComponent(btoa(JSON.stringify({ callbackUri }))); 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}`; } +export function getLogoutUrl(): string { + return `https://api.asgardeo.io/t/${AUTH_ORG}/oidc/logout`; +} + export async function exchangeAuthCodeNew(authCode: string): Promise { const params = new URLSearchParams({ client_id: AUTH_CLIENT_ID, @@ -84,29 +89,16 @@ export async function exchangeAuthCode(authCode: string) { 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}`); - } + // Store credentials in new format + const credentials: AuthCredentials = { + loginMethod: LoginMethod.MI_INTEL, + secrets: { + accessToken: response.accessToken, + refreshToken: response.refreshToken ?? '' + } + }; + await storeAuthCredentials(credentials); StateMachineAI.sendEvent(AI_EVENT_TYPE.SIGN_IN_SUCCESS); } catch (error: any) { 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..8af83f47475 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.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. + +import { createAnthropic } from "@ai-sdk/anthropic"; +import { getAccessToken, getLoginMethod, getRefreshedAccessToken } from "../utils/auth"; +import { StateMachineAI } from "../aiMachine"; +import { AI_EVENT_TYPE, LoginMethod } from "@wso2/mi-core"; + +export const ANTHROPIC_HAIKU_3_5 = "claude-3-5-haiku-20241022"; +export const ANTHROPIC_SONNET_4_5 = "claude-sonnet-4-5-20250929"; + +type AnthropicModel = + | typeof ANTHROPIC_HAIKU_3_5 + | typeof ANTHROPIC_SONNET_4_5; + +let cachedAnthropic: ReturnType | null = null; +let cachedAuthMethod: LoginMethod | null = null; + +/** + * Get the backend URL for MI Copilot + */ +const getBackendUrl = (): string => { + return process.env.MI_COPILOT_BACKEND_V2 as string; +}; + +/** + * 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); + console.log("Response status: ", response.status); + + // Handle token expiration + if (response.status === 401) { + console.log("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); + return; + } + } + + return response; + } catch (error: any) { + if (error?.message === "TOKEN_EXPIRED") { + StateMachineAI.sendEvent(AI_EVENT_TYPE.LOGOUT); + } 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 = getBackendUrl(); + cachedAnthropic = createAnthropic({ + baseURL: backendUrl + "/intelligence-api/v1.0/claude", + apiKey: "xx", // dummy value; real auth is via fetchWithAuth + fetch: fetchWithAuth, + }); + } else if (loginMethod === LoginMethod.ANTHROPIC_KEY) { + const apiKey = await getAccessToken(); + cachedAnthropic = createAnthropic({ + baseURL: "https://api.anthropic.com/v1", + apiKey: apiKey, + }); + } else { + throw new Error(`Unsupported login method: ${loginMethod}`); + } + + cachedAuthMethod = loginMethod; + } + + 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/suggestions/prompt.md b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/prompt.md new file mode 100644 index 00000000000..9878953fcba --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/prompt.md @@ -0,0 +1,35 @@ +{{!-- + Copyright (c) 2025 WSO2 LLC. (http://www.wso2.org) 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. +--}} +{{#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}} + + +Use 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 help you solve. +{{/if}} 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..af39df720cd --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts @@ -0,0 +1,185 @@ +/** + * 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, CoreMessage } from "ai"; +import * as Handlebars from "handlebars"; +import * as path from "path"; +import * as fs from "fs"; +import { getAnthropicClient, ANTHROPIC_SONNET_4, getProviderCacheControl } from "../connection"; +import { GenerateSuggestionsRequest, GenerateSuggestionsResponse } from "@wso2/mi-core"; +import { CopilotEventHandler } from "../../../rpc-managers/ai-panel/event-handler"; + +/** + * Read and render a template file using Handlebars + */ +function renderTemplate(templateFile: string, context: Record): string { + const templatesDir = path.join(__dirname, "."); + const templatePath = path.join(templatesDir, templateFile); + + if (!fs.existsSync(templatePath)) { + throw new Error(`Template file not found: ${templatePath}`); + } + + const templateContent = fs.readFileSync(templatePath, "utf-8"); + 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"); +} + +/** + * Get the project context (placeholder for now - will be implemented with actual project analysis) + */ +function getProjectContext(): string[] { + // TODO: Implement actual project context extraction + // This should analyze the current MI project and return relevant information + // about APIs, sequences, endpoints, etc. + return []; +} + +/** + * Core suggestion generation function with streaming + */ +export async function generateSuggestionsCore( + params: GenerateSuggestionsRequest, + eventHandler: CopilotEventHandler +): Promise { + const chatHistory = params.chatHistory || []; + const formattedChatHistory = formatChatHistory(chatHistory); + const projectContext = getProjectContext(); + + // Render system prompt + const systemPrompt = renderTemplate("system.md", {}); + + // Render user prompt + const userPrompt = renderTemplate("prompt.md", { + chat_history: formattedChatHistory, + context: projectContext, + }); + + const cacheOptions = await getProviderCacheControl(); + + const messages: CoreMessage[] = [ + { + role: "system", + content: systemPrompt, + providerOptions: cacheOptions, + }, + { + role: "user", + content: userPrompt, + providerOptions: cacheOptions, + }, + ]; + + try { + const { fullStream } = streamText({ + model: await getAnthropicClient(ANTHROPIC_SONNET_4), + maxTokens: 4096, + temperature: 0.7, // Slightly higher temperature for suggestions + messages, + }); + + eventHandler.handleStart(); + let assistantResponse: string = ""; + + for await (const part of fullStream) { + switch (part.type) { + case "text-delta": { + const textPart = part.textDelta; + assistantResponse += textPart; + eventHandler.handleContentBlock(textPart); + break; + } + case "error": { + const error = part.error; + console.error("Error during suggestion generation:", error); + const errorMessage = error instanceof Error ? error.message : String(error); + eventHandler.handleError(errorMessage); + break; + } + case "finish": { + const finishReason = part.finishReason; + console.log("Suggestion generation finish reason:", finishReason); + + if (finishReason === "error") { + // Error already handled above + break; + } + + // Send the complete response + eventHandler.handleEnd(assistantResponse); + eventHandler.handleStop("suggestions"); + break; + } + } + } + } catch (error) { + console.error("Error generating suggestions:", error); + const errorMessage = error instanceof Error ? error.message : String(error); + eventHandler.handleError(errorMessage); + eventHandler.handleStop("suggestions"); + } +} + +/** + * Main public function that generates suggestions + * This is the entry point called by the RPC handler + */ +export async function generateSuggestions( + params: GenerateSuggestionsRequest, + projectUri: string +): Promise { + const eventHandler = new CopilotEventHandler(projectUri); + + try { + await generateSuggestionsCore(params, eventHandler); + + // Return success response + return { + response: "Suggestions generated successfully", + files: [], + images: [], + }; + } catch (error) { + console.error("Error in generateSuggestions:", error); + const errorMessage = error instanceof Error ? error.message : String(error); + eventHandler.handleError(errorMessage); + + return { + response: `Error: ${errorMessage}`, + files: [], + images: [], + }; + } +} + diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/system.md b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/system.md new file mode 100644 index 00000000000..1afffcea57f --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/system.md @@ -0,0 +1,22 @@ +{{!-- + Copyright (c) WSO2 LLC. (http://www.wso2.org) 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. +--}} +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/utils.ts b/workspaces/mi/mi-extension/src/ai-panel/utils.ts new file mode 100644 index 00000000000..7cfc4f801a2 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/utils.ts @@ -0,0 +1,130 @@ +/** + * 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 vscode from 'vscode'; +import { AIUserToken, LoginMethod, AuthCredentials } from '@wso2/mi-core'; +import { createAnthropic } from '@ai-sdk/anthropic'; +import { generateText } from 'ai'; +import { getAuthUrl, getLogoutUrl } from './auth'; +import { extension } from '../MIExtensionContext'; +import { getAccessToken, clearAuthCredentials, storeAuthCredentials, getLoginMethod, cleanupLegacyTokens } from './utils/auth'; + +export const checkToken = async (): Promise<{ token: string; loginMethod: LoginMethod } | undefined> => { + return new Promise(async (resolve, reject) => { + try { + // Clean up any legacy tokens on initialization + await cleanupLegacyTokens(); + + const token = await getAccessToken(); + const loginMethod = await getLoginMethod(); + if (!token || !loginMethod) { + resolve(undefined); + return; + } + resolve({ token, loginMethod }); + } catch (error) { + reject(error); + } + }); +}; + +export const logout = async (isUserLogout: boolean = true) => { + // For user-initiated logout, invalidate the session on the server + if (isUserLogout) { + try { + 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 + console.log('Logout request to server failed (non-critical):', err); + }); + } + } catch (error) { + // Ignore errors during token check + console.log('Error during logout token check (non-critical):', error); + } + } + + // Always clear stored credentials locally + await clearAuthCredentials(); +}; + +export 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)); +} + +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.'); + } + + if (!apiKey || !apiKey.startsWith('sk-') || apiKey.length < 20) { + throw new Error('Please enter a valid Anthropic API key.'); + } + + try { + const directAnthropic = createAnthropic({ + apiKey: apiKey, + baseURL: 'https://api.anthropic.com/v1' + }); + + await generateText({ + model: directAnthropic('claude-3-haiku-20240307'), + maxTokens: 1, + messages: [{ role: 'user', content: 'Hi' }] + }); + + // Store credentials + const credentials: AuthCredentials = { + loginMethod: LoginMethod.ANTHROPIC_KEY, + secrets: { + apiKey: apiKey + } + }; + await storeAuthCredentials(credentials); + + return { token: apiKey }; + + } catch (error) { + console.error('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.'); + } + throw new Error('Connection failed. Please check your internet connection and ensure your API key is valid.'); + } + throw new Error('Validation failed. Please try again.'); + } +}; + + diff --git a/workspaces/mi/mi-extension/src/ai-panel/utils/auth.ts b/workspaces/mi/mi-extension/src/ai-panel/utils/auth.ts new file mode 100644 index 00000000000..dda44b890c4 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/utils/auth.ts @@ -0,0 +1,189 @@ +/** + * 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 vscode from 'vscode'; +import { extension } from "../../MIExtensionContext"; +import axios from 'axios'; +import { jwtDecode, JwtPayload } from 'jwt-decode'; +import { AuthCredentials, LoginMethod } from '@wso2/mi-core'; + +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; + +export const REFRESH_TOKEN_NOT_AVAILABLE_ERROR_MESSAGE = "Refresh token is not available."; +export const TOKEN_REFRESH_ONLY_SUPPORTED_FOR_MI_INTEL = "Token refresh is only supported for MI Intelligence authentication"; +export const AUTH_CREDENTIALS_SECRET_KEY = 'MIAuthCredentials'; + +// Legacy keys for migration +const LEGACY_ACCESS_TOKEN_SECRET_KEY = 'MIAIUser'; +const LEGACY_REFRESH_TOKEN_SECRET_KEY = 'MIAIRefreshToken'; + +// ================================== +// Structured Auth Credentials Utils +// ================================== +export const storeAuthCredentials = async (credentials: AuthCredentials): Promise => { + const credentialsJson = JSON.stringify(credentials); + await extension.context.secrets.store(AUTH_CREDENTIALS_SECRET_KEY, credentialsJson); +}; + +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) { + console.error('Error parsing auth credentials:', error); + return undefined; + } +}; + +export const clearAuthCredentials = async (): Promise => { + await extension.context.secrets.delete(AUTH_CREDENTIALS_SECRET_KEY); +}; + +// ================================== +// MI Copilot Auth Utils +// ================================== +export const getLoginMethod = async (): Promise => { + const credentials = await getAuthCredentials(); + if (credentials) { + return credentials.loginMethod; + } + return undefined; +}; + +export const getAccessToken = async (): Promise => { + return new Promise(async (resolve, reject) => { + try { + const credentials = await getAuthCredentials(); + + if (credentials) { + switch (credentials.loginMethod) { + case LoginMethod.MI_INTEL: + try { + const { accessToken } = credentials.secrets; + let finalToken = accessToken; + + // Decode token and check expiration + const decoded = jwtDecode(accessToken); + const now = Math.floor(Date.now() / 1000); + if (decoded.exp && decoded.exp < now) { + finalToken = await getRefreshedAccessToken(); + } + resolve(finalToken); + return; + } catch (err) { + if (axios.isAxiosError(err)) { + const status = err.response?.status; + if (status === 400) { + reject(new Error("TOKEN_EXPIRED")); + return; + } + } + reject(err); + return; + } + + case LoginMethod.ANTHROPIC_KEY: + resolve(credentials.secrets.apiKey); + return; + + default: + const { loginMethod }: AuthCredentials = credentials; + reject(new Error(`Unsupported login method: ${loginMethod}`)); + return; + + } + } + resolve(undefined); + } catch (error: any) { + reject(error); + } + }); +}; + +export const getRefreshedAccessToken = async (): Promise => { + return new Promise(async (resolve, reject) => { + const CommonReqHeaders = { + 'Content-Type': 'application/x-www-form-urlencoded; charset=utf8', + 'Accept': 'application/json' + }; + + try { + const credentials = await getAuthCredentials(); + if (!credentials || credentials.loginMethod !== LoginMethod.MI_INTEL) { + throw new Error(TOKEN_REFRESH_ONLY_SUPPORTED_FOR_MI_INTEL); + } + + const { refreshToken } = credentials.secrets; + if (!refreshToken) { + reject(new Error(REFRESH_TOKEN_NOT_AVAILABLE_ERROR_MESSAGE)); + return; + } + + const params = new URLSearchParams({ + client_id: AUTH_CLIENT_ID, + 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; + + // Update stored credentials + const updatedCredentials: AuthCredentials = { + ...credentials, + secrets: { + accessToken: newAccessToken, + refreshToken: newRefreshToken + } + }; + await storeAuthCredentials(updatedCredentials); + + resolve(newAccessToken); + } catch (error: any) { + reject(error); + } + }); +}; + +/** + * Cleanup legacy tokens from old authentication system + */ +export const cleanupLegacyTokens = async (): Promise => { + try { + 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); + console.log('Legacy tokens cleaned up successfully.'); + } + } catch (error) { + console.error('Error cleaning up legacy tokens:', error); + } +}; + 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..1bf305c4abf 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 @@ -40,6 +40,7 @@ import { } from "./utils"; import { CopilotEventHandler } from "./event-handler"; import { MiDiagramRpcManager } from "../mi-diagram/rpc-manager"; +import { generateSuggestions as generateSuggestionsFromLLM } from "../../ai-panel/copilot/suggestions/suggestions"; export class MIAIPanelRpcManager implements MIAIPanelAPI { private eventHandler: CopilotEventHandler; @@ -64,21 +65,8 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { 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 - ); - - // 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 - }; + // Use the new LLM-based suggestion generation + return await generateSuggestionsFromLLM(request, this.projectUri); } catch (error) { console.error('Error generating suggestions:', error); throw new Error(`Failed to generate suggestions: ${error instanceof Error ? error.message : 'Unknown error'}`); 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-visualizer/src/views/AIPanel/component/AIChatHeader.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatHeader.tsx index ee5393e484c..711f700188a 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatHeader.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatHeader.tsx @@ -16,70 +16,26 @@ * under the License. */ -import React, { useState, useEffect } from "react"; +import React from "react"; import { Button, Codicon } from "@wso2/ui-toolkit"; -import { Badge, Header, HeaderButtons, ResetsInBadge } from '../styles'; +import { Header, HeaderButtons } from '../styles'; import { useMICopilotContext } from "./MICopilotContext"; /** * Header component for the chat interface - * Shows token information and action buttons + * Shows action buttons */ const AIChatHeader: React.FC = () => { - const { rpcClient, setChatClearEventTriggered, tokenInfo, chatClearEventTriggered, backendRequestTriggered} = useMICopilotContext(); - const [hasApiKey, setHasApiKey] = useState(false); + const { rpcClient, setChatClearEventTriggered, chatClearEventTriggered, backendRequestTriggered} = useMICopilotContext(); const handleLogout = async () => { 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); - }; - - // Check for API key on component mount - useEffect(() => { - checkApiKey(); - }, [rpcClient]); - const isLoading = chatClearEventTriggered || backendRequestTriggered; return (
- - {hasApiKey ? ( -
-
- - Copilot is using your API Key -
- Logout to clear the API key -
- ) : ( - <> - Remaining Free Usage:{" "} - {tokenInfo.remainingPercentage === -1 - ? "Unlimited" - : tokenInfo.isLessThanOne - ? "<1%" - : `${tokenInfo.remainingPercentage}%`} -
- - {tokenInfo.remainingPercentage !== -1 && - `Resets in: ${ - tokenInfo.timeToReset < 1 ? "< 1 day" : `${Math.round(tokenInfo.timeToReset)} days` - }`} - - - )} -
- - - {/* Sign-in confirmation dialog */} - ); } From efffc5f4412df8197ad307ee5bb5ad6d7cfc5b50 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 30 Oct 2025 16:19:49 +0530 Subject: [PATCH 49/91] Implement auto fill --- MI_COPILOT_REFACTOR_SUMMARY.md | 210 ------------------ .../mi-core/src/rpc-types/ai-panel/index.ts | 15 +- .../src/rpc-types/ai-panel/rpc-type.ts | 6 +- .../mi-core/src/rpc-types/ai-panel/types.ts | 19 ++ .../src/components/Form/FormGenerator.tsx | 75 ++++--- .../src/ai-panel/copilot/auto-fill/fill.ts | 206 +++++++++++++++++ .../src/ai-panel/copilot/auto-fill/prompt.ts | 182 +++++++++++++++ .../src/ai-panel/copilot/auto-fill/system.ts | 175 +++++++++++++++ .../src/ai-panel/copilot/connectors/prompt.ts | 2 +- .../src/rpc-managers/ai-panel/rpc-handler.ts | 9 +- .../src/rpc-managers/ai-panel/rpc-manager.ts | 37 ++- .../src/rpc-clients/ai-panel/rpc-client.ts | 12 +- 12 files changed, 694 insertions(+), 254 deletions(-) delete mode 100644 MI_COPILOT_REFACTOR_SUMMARY.md create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/fill.ts create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/prompt.ts create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/system.ts diff --git a/MI_COPILOT_REFACTOR_SUMMARY.md b/MI_COPILOT_REFACTOR_SUMMARY.md deleted file mode 100644 index 7b0e64b0ffc..00000000000 --- a/MI_COPILOT_REFACTOR_SUMMARY.md +++ /dev/null @@ -1,210 +0,0 @@ -# MI Copilot Refactoring Summary - -## Overview -This refactoring moves MI Copilot logic (prompts, AI service calls) from the backend to the VSCode extension, following the pattern established by Ballerina (BI) Copilot. The changes enable support for two authentication methods: MI Intel (SSO) and Anthropic API Key. AWS Bedrock support can be added in a future iteration once the core functionality is stable. - -## Key Changes - -### 1. State Machine Types (`workspaces/mi/mi-core/src/state-machine-types.ts`) - -#### Added New Types: -- **`AIMachineStateValue`**: Updated to support hierarchical authentication states - - `Initialize`: Checking authentication status - - `Unauthenticated`: Show login window - - `Authenticating`: Hierarchical state with substates: - - `determineFlow`: Route to appropriate auth flow - - `ssoFlow`: MI Intel SSO authentication - - `apiKeyFlow`: Anthropic API key input - - `validatingApiKey`: Validating API key - - `Authenticated`: Ready state with active session - - `Disabled`: Extension disabled - - `NotSupported`: Multi-root workspace not supported - -- **`AI_EVENT_TYPE`**: Extended with new authentication events - - `CHECK_AUTH`, `AUTH_WITH_API_KEY`, `SUBMIT_API_KEY` - - `COMPLETE_AUTH`, `CANCEL_LOGIN`, `SILENT_LOGOUT` - -- **`LoginMethod` enum**: - - `MI_INTEL`: WSO2 SSO authentication - - `ANTHROPIC_KEY`: Direct Anthropic API key - -- **`AuthCredentials` type**: Discriminated union for storing credentials - - Different secret structures for each login method - - Type-safe credential management - -- **`AIMachineContext`**: State machine context - - `loginMethod`: Current authentication method - - `userToken`: Active token information - - `errorMessage`: Error state tracking - -- **`AIMachineEventMap` and `AIMachineSendableEvent`**: Type-safe event handling - -### 2. Connection Service (`workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts`) - -New file providing AI service connections: - -- **`getAnthropicClient(model)`**: Returns appropriate Anthropic client based on login method - - MI Intel: Uses backend proxy with token auth - - Anthropic Key: Direct API connection - -- **`fetchWithAuth()`**: Handles authenticated requests with automatic token refresh - -- **`getProviderCacheControl()`**: Cache control settings for Anthropic - -### 3. Authentication Utilities (`workspaces/mi/mi-extension/src/ai-panel/utils/auth.ts`) - -New authentication management utilities: - -- **Credential Storage**: - - `storeAuthCredentials()`: Store credentials securely - - `getAuthCredentials()`: Retrieve stored credentials - - `clearAuthCredentials()`: Clear credentials on logout - -- **Token Management**: - - `getAccessToken()`: Get current access token with auto-refresh - - `getRefreshedAccessToken()`: Refresh MI Intel tokens - - `getLoginMethod()`: Get current login method - -- **Legacy Migration**: - - `cleanupLegacyTokens()`: Remove old token storage format - -### 4. State Machine Services (`workspaces/mi/mi-extension/src/ai-panel/utils.ts`) - -New file with state machine service implementations: - -- **`checkToken()`**: Verify existing authentication -- **`logout(isUserLogout)`**: Handle logout (with/without SSO redirect) -- **`initiateInbuiltAuth()`**: Start SSO authentication flow -- **`validateApiKey(apiKey, loginMethod)`**: Validate Anthropic API key - -### 5. Updated AI State Machine (`workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts`) - -Complete rewrite following Ballerina's pattern: - -- **Hierarchical State Structure**: - - Proper state transitions for each auth method - - Error handling with automatic retry/logout - - Workspace validation before authentication - -- **Service Integration**: - - `checkWorkspaceAndToken`: Combined workspace and token check - - `validateApiKey`: API key validation service - - `getTokenAfterAuth`: Retrieve token after successful auth - -- **Type-Safe Event Handling**: - - Support for events with payloads (API key, AWS credentials) - - Discriminated union for event types - -### 6. Updated Login Screen (`workspaces/mi/mi-visualizer/src/views/LoggedOutWindow/index.tsx`) - -Enhanced login UI with multiple authentication options: - -- **New UI Elements**: - - "Login to MI Copilot" button (SSO) - - "Enter your Anthropic API key" link - - Divider between options - -- **Event Handlers**: - - `handleAnthropicKeyClick()`: Trigger API key flow - -### 7. Simplified Chat Header (`workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatHeader.tsx`) - -Removed custom API key functionality: - -- **Removed**: - - API key setup button - - API key status badge - - `hasApiKey` state management - - `checkApiKey()` function - - `handleSetApiKey()` function - -- **Kept**: - - Clear chat button - - Logout button - -### 8. Updated Auth Module (`workspaces/mi/mi-extension/src/ai-panel/auth.ts`) - -Updated to use new credential structure: - -- Added `getLogoutUrl()` function -- Updated `exchangeAuthCode()` to use new `AuthCredentials` format -- Stores credentials via `storeAuthCredentials()` - -### 9. Package Dependencies (`workspaces/mi/mi-extension/package.json`) - -Added new dependencies: - -```json -"@ai-sdk/anthropic": "^1.2.12", -"ai": "^4.3.16", -"jwt-decode": "^4.0.0" -``` - -## Migration Path - -### For Users: - -1. **Existing MI Intel Users**: - - Tokens will be migrated to new format on first launch - - No action required - -2. **API Key Users**: - - Previous custom API key functionality removed from header - - Must set API key via login screen - - Re-authenticate on next login - -### For Developers: - -1. **Install Dependencies**: Run `rush update` or package manager to install new dependencies -2. **Build**: Rebuild the extension -3. **Test**: Verify all authentication flows work correctly - -## Benefits - -1. **Consistency**: Matches Ballerina's proven authentication architecture -2. **Flexibility**: Support for multiple AI providers (API key and SSO) -3. **Type Safety**: Full TypeScript type coverage for auth flows -4. **Future-Ready**: Easy to add new authentication methods (AWS Bedrock can be added later) -5. **Better UX**: Unified login experience -6. **Simplified Implementation**: Focus on core functionality first -7. **Local Processing**: AI logic runs in extension (future implementation) - -## Future Work - -1. Implement AI prompt logic in extension (currently in backend) -2. Add input form for API key in visualizer -3. Migrate existing backend AI logic to extension -4. Add settings panel similar to Ballerina for managing auth -5. Token usage tracking for custom API keys -6. Support for custom model selection -7. Add AWS Bedrock support (deferred to future release) - -## Files Modified - -### Core Types: -- `workspaces/mi/mi-core/src/state-machine-types.ts` - -### Extension Backend: -- `workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts` (complete rewrite) -- `workspaces/mi/mi-extension/src/ai-panel/auth.ts` (updated) -- `workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts` (new) -- `workspaces/mi/mi-extension/src/ai-panel/utils.ts` (new) -- `workspaces/mi/mi-extension/src/ai-panel/utils/auth.ts` (new) -- `workspaces/mi/mi-extension/package.json` (dependencies added) - -### Visualizer Frontend: -- `workspaces/mi/mi-visualizer/src/views/LoggedOutWindow/index.tsx` (updated) -- `workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatHeader.tsx` (simplified) - -## Testing Checklist - -- [ ] MI Intel SSO login flow -- [ ] Anthropic API key login flow -- [ ] Token refresh for MI Intel -- [ ] Logout from each auth method -- [ ] Multi-root workspace handling -- [ ] Error handling for invalid credentials -- [ ] Legacy token migration -- [ ] State persistence across extension reloads -- [ ] API key validation with valid/invalid keys - 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 84f71e22ee4..d42eef53fd9 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 @@ -24,7 +24,8 @@ import { GenerateUnitTestRequest, GenerateUnitTestResponse, GenerateUnitTestCaseRequest, GenerateUnitTestCaseResponse, ProcessIdpRequest, ProcessIdpResponse, - DmcToTsRequest, DmcToTsResponse + DmcToTsRequest, DmcToTsResponse, + AutoFillFormRequest, AutoFillFormResponse } from "./types"; // Export types for external use @@ -44,7 +45,9 @@ export type { ProcessIdpRequest, ProcessIdpResponse, DmcToTsRequest, - DmcToTsResponse + DmcToTsResponse, + AutoFillFormRequest, + AutoFillFormResponse } from './types'; export interface MIAIPanelAPI { @@ -81,6 +84,11 @@ export interface MIAIPanelAPI { // DMC to TypeScript Conversion // ================================== dmcToTs: (request: DmcToTsRequest) => Promise + + // ================================== + // Auto-Fill Form + // ================================== + autoFillForm: (request: AutoFillFormRequest) => Promise } // Export RPC type definitions @@ -95,5 +103,6 @@ export { generateUnitTest, generateUnitTestCase, processIdp, - dmcToTs + 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 0c9ca6b191d..e1fbaa9eb6c 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 @@ -26,7 +26,8 @@ import { GenerateUnitTestRequest, GenerateUnitTestResponse, GenerateUnitTestCaseRequest, GenerateUnitTestCaseResponse, ProcessIdpRequest, ProcessIdpResponse, - DmcToTsRequest, DmcToTsResponse + DmcToTsRequest, DmcToTsResponse, + AutoFillFormRequest, AutoFillFormResponse } from "./types"; const _prefix = "mi-ai-panel"; @@ -48,5 +49,8 @@ export const processIdp: RequestType = { // 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 cad6cdefa52..f20112db04f 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 @@ -150,3 +150,22 @@ export interface DmcToTsRequest { 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-diagram/src/components/Form/FormGenerator.tsx b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx index ff6290981a5..d137bcf8b47 100644 --- a/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx +++ b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx @@ -404,13 +404,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 +462,47 @@ 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 + // Convert payload format: insertText → insert_text + const convertedPayloadsStr = JSON.stringify(payloads).replaceAll("insertText", "insert_text"); + const convertedVariablesStr = JSON.stringify(variables); + const convertedParamsStr = JSON.stringify(params); + const convertedPropertiesStr = JSON.stringify(properties); + const convertedHeadersStr = JSON.stringify(headers); + const convertedConfigsStr = JSON.stringify(configs); + + // Convert current values format: configKey → config_key, isExpression → is_expression + const convertedValues = JSON.parse( + JSON.stringify(values) + .replaceAll("configKey", "config_key") + .replaceAll("isExpression", "is_expression") + ); + + // Flatten connectionNames object to array of all connection names + const allConnectionNames = Object.values(connectionNames).flat(); + + // Call RPC method instead of backend API + const response = await rpcClient.getMiAiPanelRpcClient().autoFillForm({ + payloads: [convertedPayloadsStr], + variables: [convertedVariablesStr], + params: [convertedParamsStr], + properties: [convertedPropertiesStr], + headers: [convertedHeadersStr], + configs: [convertedConfigsStr], + 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"); + + // Convert response back: is_expression → isExpression + if (response.filled_values) { + const result = JSON.stringify(response.filled_values).replaceAll("is_expression", "isExpression"); setGeneratedFormDetails(JSON.parse(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/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..7f0a218478a --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/fill.ts @@ -0,0 +1,206 @@ +/** + * 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"; + +// Register Handlebars partials +Handlebars.registerPartial("synapse_guide", SYNAPSE_GUIDE); +Handlebars.registerPartial("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 (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) + }); + + console.log('[AutoFill] 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); + + console.log('[AutoFill] Successfully generated form suggestions'); + + return { + filled_values: filledValues, + }; + } catch (error) { + console.error('[AutoFill] 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..af76afa40c0 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/auto-fill/prompt.ts @@ -0,0 +1,182 @@ +/** + * 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}}} + +{{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/connectors/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts index 732eea0c1fc..c9c7b2ea529 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts @@ -56,4 +56,4 @@ Task Instructions: - 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. -`; \ No newline at end of file +`; 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 9d62cc31e01..4cdc03f146c 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 @@ -34,7 +34,9 @@ import { processIdp, ProcessIdpRequest, dmcToTs, - DmcToTsRequest + DmcToTsRequest, + autoFillForm, + AutoFillFormRequest } from "@wso2/mi-core"; export function registerMIAiPanelRpcHandlers(messenger: MessengerAPI, projectUri: string) { @@ -69,4 +71,9 @@ export function registerMIAiPanelRpcHandlers(messenger: MessengerAPI, projectUri // 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 b9af22c8670..fc14f1c3d57 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 @@ -28,7 +28,9 @@ import { ProcessIdpRequest, ProcessIdpResponse, DmcToTsRequest, - DmcToTsResponse + DmcToTsResponse, + AutoFillFormRequest, + AutoFillFormResponse } from '@wso2/mi-core'; import {RUNTIME_VERSION_440} from "../../constants"; import {compareVersions, getMIVersionFromPom} from "../../util/onboardingUtils"; @@ -629,4 +631,37 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { 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 { + console.log('[autoFillForm] Starting form auto-fill'); + console.log('[autoFillForm] Form fields count:', Object.keys(request.current_values || {}).length); + console.log('[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 + }); + + console.log('[autoFillForm] Form auto-fill completed successfully'); + return result; + } catch (error) { + console.error('[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-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 eaa6c15a0a2..f939fdfbf59 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 @@ -41,7 +41,10 @@ import { processIdp, DmcToTsRequest, DmcToTsResponse, - dmcToTs + dmcToTs, + AutoFillFormRequest, + AutoFillFormResponse, + autoFillForm } from "@wso2/mi-core"; import { HOST_EXTENSION } from "vscode-messenger-common"; import { Messenger } from "vscode-messenger-webview"; @@ -113,4 +116,11 @@ export class MiAiPanelRpcClient implements MIAIPanelAPI { 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); + } } From fff7e2f6a009c68d09a7dc2909930b66fd008aa3 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 30 Oct 2025 18:40:35 +0530 Subject: [PATCH 50/91] Implement fill IDP schema --- .../mi-core/src/rpc-types/ai-panel/index.ts | 5 + .../src/rpc-types/ai-panel/rpc-type.ts | 4 + .../mi-core/src/rpc-types/ai-panel/types.ts | 10 ++ .../src/ai-panel/copilot/idp/fill_schema.ts | 164 ++++++++++++++++++ .../src/rpc-managers/ai-panel/rpc-handler.ts | 3 + .../src/rpc-managers/ai-panel/rpc-manager.ts | 24 +++ .../src/rpc-clients/ai-panel/rpc-client.ts | 7 + .../Forms/IDPConnectorForm/TryOutView.tsx | 120 +++---------- 8 files changed, 242 insertions(+), 95 deletions(-) create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/idp/fill_schema.ts 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 d42eef53fd9..23dfcde3ac5 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 @@ -24,6 +24,7 @@ import { GenerateUnitTestRequest, GenerateUnitTestResponse, GenerateUnitTestCaseRequest, GenerateUnitTestCaseResponse, ProcessIdpRequest, ProcessIdpResponse, + FillIdpSchemaRequest, FillIdpSchemaResponse, DmcToTsRequest, DmcToTsResponse, AutoFillFormRequest, AutoFillFormResponse } from "./types"; @@ -44,6 +45,8 @@ export type { GenerateUnitTestCaseResponse, ProcessIdpRequest, ProcessIdpResponse, + FillIdpSchemaRequest, + FillIdpSchemaResponse, DmcToTsRequest, DmcToTsResponse, AutoFillFormRequest, @@ -79,6 +82,7 @@ export interface MIAIPanelAPI { // IDP (Intelligent Document Processor) // ================================== processIdp: (request: ProcessIdpRequest) => Promise + fillIdpSchema: (request: FillIdpSchemaRequest) => Promise // ================================== // DMC to TypeScript Conversion @@ -103,6 +107,7 @@ export { 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 e1fbaa9eb6c..1005ae4d636 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 @@ -26,6 +26,7 @@ import { GenerateUnitTestRequest, GenerateUnitTestResponse, GenerateUnitTestCaseRequest, GenerateUnitTestCaseResponse, ProcessIdpRequest, ProcessIdpResponse, + FillIdpSchemaRequest, FillIdpSchemaResponse, DmcToTsRequest, DmcToTsResponse, AutoFillFormRequest, AutoFillFormResponse } from "./types"; @@ -46,6 +47,9 @@ export const generateUnitTestCase: 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` }; 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 f20112db04f..f2929603f38 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 @@ -141,6 +141,16 @@ 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 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..bf81f06c5cd --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/fill_schema.ts @@ -0,0 +1,164 @@ +/** + * 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"; + +// 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); + + // 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); + } + + // Handle primitive types + switch (type) { + case 'string': + return z.string(); + case 'number': + case 'integer': + return z.number(); + case 'boolean': + return z.boolean(); + case 'null': + return z.null(); + default: + // Fallback for unknown types + return z.any(); + } +} + +/** + * 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 { + console.log('[fillIdpSchema] Starting schema filling'); + console.log('[fillIdpSchema] Schema length:', params.jsonSchema?.length || 0); + console.log('[fillIdpSchema] 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) + }); + } + + console.log('[fillIdpSchema] 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 + }); + + console.log('[fillIdpSchema] Schema filling completed successfully'); + + // Return the structured object as JSON string + return { + filledData: JSON.stringify(result.object, null, 2) + }; + } catch (error) { + console.error('[fillIdpSchema] Error filling schema:', error); + throw 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 4cdc03f146c..e8763a165dc 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 @@ -33,6 +33,8 @@ import { GenerateUnitTestCaseRequest, processIdp, ProcessIdpRequest, + fillIdpSchema, + FillIdpSchemaRequest, dmcToTs, DmcToTsRequest, autoFillForm, @@ -66,6 +68,7 @@ export function registerMIAiPanelRpcHandlers(messenger: MessengerAPI, projectUri // IDP (Intelligent Document Processor) // ================================== messenger.onRequest(processIdp, (request: ProcessIdpRequest) => rpcManager.processIdp(request)); + messenger.onRequest(fillIdpSchema, (request: FillIdpSchemaRequest) => rpcManager.fillIdpSchema(request)); // ================================== // DMC to TypeScript Conversion 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 fc14f1c3d57..6c65f8de7ad 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 @@ -27,6 +27,8 @@ import { CopilotChatEntry, ProcessIdpRequest, ProcessIdpResponse, + FillIdpSchemaRequest, + FillIdpSchemaResponse, DmcToTsRequest, DmcToTsResponse, AutoFillFormRequest, @@ -48,6 +50,7 @@ import { 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 { getLoginMethod } from '../../ai-panel/auth'; import { LoginMethod } from '@wso2/mi-core'; @@ -608,6 +611,27 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { } } + /** + * Fills an IDP schema with data extracted from images + */ + async fillIdpSchema(request: FillIdpSchemaRequest): Promise { + try { + console.log('[fillIdpSchema] Starting schema filling'); + console.log('[fillIdpSchema] Images count:', request.images?.length || 0); + + const result = await fillIdpSchema({ + jsonSchema: request.jsonSchema, + images: request.images + }); + + console.log('[fillIdpSchema] Schema filling completed successfully'); + return result; + } catch (error) { + console.error('[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 */ 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 f939fdfbf59..7a320f1335b 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 @@ -39,6 +39,9 @@ import { ProcessIdpRequest, ProcessIdpResponse, processIdp, + FillIdpSchemaRequest, + FillIdpSchemaResponse, + fillIdpSchema, DmcToTsRequest, DmcToTsResponse, dmcToTs, @@ -110,6 +113,10 @@ export class MiAiPanelRpcClient implements MIAIPanelAPI { return this._messenger.sendRequest(processIdp, HOST_EXTENSION, request); } + fillIdpSchema(request: FillIdpSchemaRequest): Promise { + return this._messenger.sendRequest(fillIdpSchema, HOST_EXTENSION, request); + } + // ================================== // DMC to TypeScript Conversion // ================================== 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); From f5045c9126e48664ff9f87645da7a5e1b6857e1f Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 30 Oct 2025 19:10:21 +0530 Subject: [PATCH 51/91] Cleanup dead code --- .../mi-core/src/rpc-types/mi-diagram/index.ts | 2 - .../src/rpc-types/mi-diagram/rpc-type.ts | 2 - workspaces/mi/mi-extension/.env.example | 3 - workspaces/mi/mi-extension/src/RPCLayer.ts | 3 - .../mi/mi-extension/src/constants/index.ts | 1 - .../src/rpc-managers/ai-panel/rpc-manager.ts | 50 ++--- .../src/rpc-managers/ai-panel/utils.ts | 190 ------------------ .../rpc-managers/mi-diagram/rpc-handler.ts | 2 - .../rpc-managers/mi-diagram/rpc-manager.ts | 26 --- .../src/rpc-clients/mi-diagram/rpc-client.ts | 6 - .../mi/mi-visualizer/src/constants/index.ts | 8 - .../views/AIPanel/component/AIChatFooter.tsx | 4 +- .../AIPanel/component/MICopilotContext.tsx | 1 - .../src/views/AIPanel/constants.ts | 8 - .../mi-visualizer/src/views/AIPanel/utils.ts | 164 +-------------- .../Forms/IDPConnectorForm/IdpUtills.tsx | 58 ------ .../IDPConnectorForm/SchemaEditorView.tsx | 1 - 17 files changed, 27 insertions(+), 502 deletions(-) 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-extension/.env.example b/workspaces/mi/mi-extension/.env.example index 92129b5242d..419d87991dd 100644 --- a/workspaces/mi/mi-extension/.env.example +++ b/workspaces/mi/mi-extension/.env.example @@ -1,8 +1,5 @@ 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/v1.0 diff --git a/workspaces/mi/mi-extension/src/RPCLayer.ts b/workspaces/mi/mi-extension/src/RPCLayer.ts index 157a4ea7cbe..10727240ead 100644 --- a/workspaces/mi/mi-extension/src/RPCLayer.ts +++ b/workspaces/mi/mi-extension/src/RPCLayer.ts @@ -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 || '', 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/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts index 6c65f8de7ad..8f9087a6111 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 @@ -37,12 +37,7 @@ import { 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, getWorkspaceContext @@ -51,6 +46,7 @@ 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'; @@ -176,17 +172,22 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { } /** - * 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); throw new Error(`Failed to analyze diagnostics: ${error instanceof Error ? error.message : 'Unknown error'}`); @@ -262,14 +263,9 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { // 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, @@ -417,15 +413,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)) { 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 8659bd9d4af..b1f9c91f4ee 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 @@ -29,12 +29,6 @@ import { getConnectors } from "../../ai-panel/copilot/connectors/connectors"; import { codeDiagnostics } from "../../ai-panel/copilot/diagnostics/diagnostics"; import { openAIWebview, StateMachineAI } from "../../ai-panel/aiMachine"; -// 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`; - // Error messages export const COPILOT_ERROR_MESSAGES = { BAD_REQUEST: "Bad request. Please check your input and try again.", @@ -72,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 */ @@ -174,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 */ @@ -294,68 +166,6 @@ 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 []; - } -} - /** * Generates code using local LLM (ditching backend) */ 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..a1d25837866 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, @@ -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 4d5fe8328b1..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 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..3ed36758059 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, @@ -243,7 +242,6 @@ import { editOpenAPISpec, executeCommand, exportProject, - getAIResponse, getAPIDirectory, getAddressEndpoint, getAllArtifacts, @@ -721,10 +719,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); } 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 5280640cbb3..ffda3a06d64 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx @@ -23,7 +23,7 @@ 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, updateTokenInfo } from "../utils"; +import { generateSuggestions, generateId, getView, fetchCodeGenerationsWithRetry, replaceCodeBlock, setupCodeGenerationEventListener, updateTokenInfo } from "../utils"; import { BackendRequestType, FixedConfigItem, CorrectedCodeItem, } from "../types"; import { Role, MessageType, CopilotChatEntry, ChatMessage } from "@wso2/mi-core"; import Attachments from "./Attachments"; @@ -363,7 +363,7 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) break; } - const { backendUrl, view } = await getBackendUrlAndView(rpcClient); + const view = await getView(rpcClient); try { // Call the RPC method for streaming code generation diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx index 6bd53c88c8e..6941770e39c 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx @@ -32,7 +32,6 @@ import { FileHistoryEntry, } from "../types"; import { - fetchBackendUrl, getProjectRuntimeVersion, getProjectUUID, compareVersions, diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts b/workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts index fab48c81bee..f24fc69d557 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts @@ -16,14 +16,6 @@ * under the License. */ -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_DIAGNOSTICS_RESPONSE_BACKEND_URL = `/synapse/bug-fix`; - // MI Copilot Error Messages export const COPILOT_ERROR_MESSAGES = { BAD_REQUEST: 'Bad Request', diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts b/workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts index ce1fba7e0e6..82145c3631c 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts @@ -21,10 +21,6 @@ import { CopilotChatEntry, Role, MessageType, ChatMessage } from "@wso2/mi-core" import { GetWorkspaceContextResponse, MACHINE_VIEW, EVENT_TYPE, FileObject, ImageObject} from "@wso2/mi-core"; import { - MI_ARTIFACT_EDIT_BACKEND_URL, - MI_ARTIFACT_GENERATION_BACKEND_URL, - MI_SUGGESTIVE_QUESTIONS_BACKEND_URL, - MI_DIAGNOSTICS_RESPONSE_BACKEND_URL, COPILOT_ERROR_MESSAGES, MAX_FILE_SIZE, VALID_FILE_TYPES, } from "./constants"; @@ -40,15 +36,6 @@ export async function getProjectRuntimeVersion(rpcClient: RpcClientType): Promis } } -export async function fetchBackendUrl(rpcClient:RpcClientType): Promise { - try { - return (await rpcClient.getMiAiPanelRpcClient().getBackendRootUrl()).url; - } catch (error) { - console.error('Failed to fetch backend URL:', error); - return undefined; - } - } - export async function getProjectUUID(rpcClient: RpcClientType): Promise { try { return (await rpcClient.getMiDiagramRpcClient().getProjectUuid()).uuid; @@ -326,14 +313,14 @@ export function updateTokenInfo(machineView: any) { return { timeToReset, remainingTokenPercentage, remaingTokenLessThanOne }; } -export async function getBackendUrlAndView(rpcClient: RpcClientType): Promise<{ backendUrl: string; view: string }> { +export async function getView(rpcClient: RpcClientType): Promise { const machineView = await rpcClient?.getVisualizerState(); switch (machineView?.view) { case MACHINE_VIEW.Overview: case MACHINE_VIEW.ADD_ARTIFACT: - return { backendUrl: MI_ARTIFACT_GENERATION_BACKEND_URL, view: "Overview" }; + return "Overview"; default: - return { backendUrl: MI_ARTIFACT_EDIT_BACKEND_URL, view: "Artifact" }; + return "Artifact"; } } @@ -421,90 +408,6 @@ export async function fetchCodeGenerationsWithRetry( } } -export async function fetchWithRetry( - type: BackendRequestType, - url: string, - body: {}, - rpcClient: RpcClientType, - controller: AbortController, - chatHistory?: CopilotChatEntry[], - thinking?: boolean -): Promise { - let retryCount = 0; - const maxRetries = 2; - const token = await rpcClient?.getMiDiagramRpcClient().getUserAccessToken(); - - const bodyWithThinking = { - ...body, - thinking: thinking || false - }; - - let response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token.token}`, - }, - body: JSON.stringify(bodyWithThinking), - signal: controller.signal, - }); - - const handleFetchError = (response: Response) => { - const newMessages = [...chatHistory]; - const statusText = getStatusText(response.status); - let error = `Failed to fetch response. Status: ${statusText}`; - - if (response.status == 429) { - response.json().then((body) => { - error += body.detail; - }); - } else if (response.status == 422) { - error = getStatusText(422); - } - - newMessages[newMessages.length - 1].content += error; - newMessages[newMessages.length - 1].type = MessageType.Error; - return newMessages; - }; - - if (response.status == 401) { - await rpcClient?.getMiDiagramRpcClient().refreshAccessToken(); - const newToken = await rpcClient?.getMiDiagramRpcClient().getUserAccessToken(); - - response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${newToken.token}`, - }, - body: JSON.stringify(bodyWithThinking), - signal: controller.signal, - }); - } 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, rpcClient, controller, chatHistory, thinking); // Retry the request with all parameters - } else { - openUpdateExtensionView(rpcClient); - throw new Error("Resource not found : Check backend URL"); - } - } else if (!response.ok) { - switch (type) { - case BackendRequestType.Suggestions: - openUpdateExtensionView(rpcClient); - throw new Error("Failed to fetch initial questions"); - case BackendRequestType.UserPrompt: - handleFetchError(response); - throw new Error("Failed to fetch code genrations"); - default: - } - } else { - return response; - } -} - // Utilities for file handling export const handleFileAttach = (e: any, existingFiles: FileObject[], setFiles: Function, existingImages: ImageObject[], setImages: Function, setFileUploadStatus: Function) => { const files = e.target.files; @@ -615,66 +518,7 @@ export const isDarkMode = (): boolean => { return window.matchMedia("(prefers-color-scheme: dark)").matches; } - return false; -} - -/** - * Function to send diagnostics to the LLM backend and get a response - * @param diagnostics The diagnostics response received from the code diagnostics - * @param xmlCodes The XML code content for each file - * @param rpcClient The RPC client instance - * @param controller The abort controller for the fetch request - * @returns Promise with the response from the LLM backend - */ -export async function getDiagnosticsReponseFromLlm( - diagnostics: any, - xmlCodes: any, - rpcClient: RpcClientType, - controller: AbortController -): Promise { - try { - // Get the backend URL - const backendRootUri = await fetchBackendUrl(rpcClient); - if (!backendRootUri) { - throw new Error("Failed to fetch backend URL"); - } - - // Construct the full URL - const url = backendRootUri + MI_DIAGNOSTICS_RESPONSE_BACKEND_URL; - - // Get the context - const context = await getContext(rpcClient); - - // Prepare the request body - const requestBody = { - diagnostics: diagnostics.diagnostics, - xmlCodes: xmlCodes, - context: context[0].context - }; - - // Send the request to the backend - return fetchWithRetry( - BackendRequestType.UserPrompt, - url, - requestBody, - rpcClient, - controller, - undefined, - false // Not in thinking mode for diagnostics - ); - } catch (error) { - console.error("Error sending diagnostics to LLM:", error); - - const errorMessage = error instanceof Error - ? error.message - : "Unknown error occurred when analyzing diagnostics"; - - return Promise.reject({ - status: "error", - message: `Failed to analyze diagnostics: ${errorMessage}`, - originalError: error - }); - } + return false; } /** 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 545d98dcb61..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 From 51ee052f0c8836c9a4c988124c494f9c47db844f Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 30 Oct 2025 19:22:33 +0530 Subject: [PATCH 52/91] Add AI module guide --- .../src/ai-panel/copilot/context/ai_module.ts | 245 ++++++++++++++++++ .../copilot/generation/generations.ts | 3 +- 2 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/context/ai_module.ts 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..1f1640a6695 --- /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: 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 + + +\`\`\`xml + +#### Memory Connection +\`\`\`xml + + + FILE_MEMORY + FILE_MEMORY_CONN + + +\`\`\`xml + +### 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 + +\`\`\`xml + +## 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 + + +\`\`\`xml + +### 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 + +\`\`\`xml + +## 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 + +\`\`\`xml + +## Retrieving data from vector store + +\`\`\`xml + + + OPENAI_CONN + KB_CONN + + {\${payload.content}} + true + text-embedding-3-small + 5 + 0.75 + ai_getFromKnowledge_1 + true + +\`\`\`xml + +## 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 + +\`\`\`xml + +Example: Knowledge retrieval tool +\`\`\`xml + +\`\`\`xml + +Example: API call tool +\`\`\`xml + +\`\`\`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 + + + + + + +\`\`\`xml +`; 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 index d6d95dfadd8..edee32a135d 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts @@ -24,6 +24,7 @@ import { PROMPT_TEMPLATE } from "./prompt_v2"; import { SYNAPSE_GUIDE } from "../context/synapse_guide"; 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"; // Register Handlebars helpers Handlebars.registerHelper("upper", (str: string) => { @@ -38,7 +39,7 @@ Handlebars.registerHelper("eq", (a: any, b: any) => { Handlebars.registerPartial("synapse_guide", SYNAPSE_GUIDE); Handlebars.registerPartial("synapse_expression_guide", SYNAPSE_EXPRESSION_GUIDE); Handlebars.registerPartial("synapse_expression_examples", SYNAPSE_EXPRESSION_EXAMPLES); -Handlebars.registerPartial("ai_module", ""); // Placeholder for AI module documentation +Handlebars.registerPartial("ai_module", AI_MODULE_GUIDE); /** * Render a template using Handlebars From 097cc5ed4f2bdf8f81e78e3094fb828b097b4e8c Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 30 Oct 2025 19:25:44 +0530 Subject: [PATCH 53/91] Improve prompts for older MI versions --- .../src/ai-panel/copilot/context/ai_module.ts | 2 +- .../ai-panel/copilot/context/synapse_guide.ts | 3 +- .../copilot/context/synapse_guide_v1.ts | 142 +++++++++++++ .../ai-panel/copilot/generation/prompt_v1.ts | 193 +++++++++++++----- .../ai-panel/copilot/generation/system_v1.ts | 92 +++++---- .../AIPanel/component/WelcomeMessage.tsx | 2 +- 6 files changed, 340 insertions(+), 94 deletions(-) create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/context/synapse_guide_v1.ts 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 index 1f1640a6695..3f98b6ed67f 100644 --- 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 @@ -16,7 +16,7 @@ * under the License. */ -export const AI_MODULE = ` +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. 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 index 732f7bc6507..9cd3e89c263 100644 --- 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 @@ -16,7 +16,8 @@ * under the License. */ -export const SYNAPSE_GUIDE = `### Latest Synapse integration development guidelines and best practices +export const SYNAPSE_GUIDE = ` +### Latest Synapse integration development guidelines and best practices #### Steps for developing integration solutions: - Make necessary assumptions to complete the solution. 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/generation/prompt_v1.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/prompt_v1.ts index cd37b54368e..162152964d5 100644 --- 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 @@ -17,64 +17,57 @@ */ export const PROMPT_TEMPLATE = ` -{{#if context}} -I give you access to my whole project below. - - +{{#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 files}} -I provide you following files for additional reference. - - - -{{#each files}} - - -{{this}} - - -{{/each}} - +{{#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}} -You may need to use WSO2 Connectors. WSO2 EI connectors are components that enable integration between WSO2 EI and various third-party systems, services, and APIs. You can always use WSO2 Connectors whenever possible instead of directly calling third party APIs. -Following are the JSON signatures of the connectors you may need. + - +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}} - -Please follow these rules when using connectors: - -1. ONLY use operations specified in the JSON signatures of the connectors. -2. There are two types of connectors: those with connectionBasedSupport and those without. -3. If the connectionBasedSupport parameter is set to true in the JSON signature of the connector, create a local entry with the init operation and pass the local entry name as the configKey parameter in the connector operation. -Example: If the local entry key is CONNECTION_1, it should be passed into the operation as -4. Always add name paramter to the init operation. -5. If the availableConnections parameter contains available connections, you MUST select one connection type and add it as a parameter in the init method as connectionType. -6. If an init operation is added as a local entry for a connector, DO NOT initialize the connector again anywhere else. -7. For connectors that require initialization, create a local entry first before using any operations. -8. Never use in connectors. instead use connector -9. Always implement a complete solution. Do not leave placeholder comments for the user to implement. Ensure that all required connector operation parameters are explicitly specified. - -Following is an example of how to define a local entry for a connector: - +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 @@ -90,14 +83,118 @@ Following is an example of how to define a local entry for a connector: \`\`\`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. -If you are generating an integration use following order. -1. Define local entries if you are using connectors. Create seperate file for each local entry. -2. Then define rest of the artifacts +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}} -{{#if images}}I have attached some images for your reference. {{/if}}Now first take your time to think through STEP BY STEP to get the right answer strictly adhering to given guidlines and then reply to the following user query accordingly. - +Now, analyze the following user query: + + {{question}} - + + +Before you create a response: +{{#if thinking_enabled}}- 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. +- 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}} +{{#if connectors}} +STEPS TO FOLLOW: +{{#if 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}} +- 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}} +{{/if}} +{{/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 index d3e792a1c8f..932e18dbf02 100644 --- 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 @@ -17,57 +17,63 @@ */ export const SYSTEM_TEMPLATE = ` -You are the WSO2 MI Copilot, a highly specialized AI assistant focused on developing WSO2 Synapse integrations for WSO2 Micro Integrator. Your expertise lies in understanding and implementing complex integration scenarios using WSO2 technologies. As a WSO2 employee, you always represent WSO2 and focus on providing sample integration solutions for users to refer to for their integration problems. +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. -When presented with a user request, follow these steps: +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. -1. Analyze the user's QUERY. If it is a normal question answer casually, if it is a request for a solution, provide a solution as per the guidelines. +### When processing a query, follow these steps: -2. If the request is related to WSO2, Micro Integrator, or Synapse Integrations, proceed to develop a sample integration solution. If not, politely decline to answer and explain that you can only assist with WSO2, Micro Integrator, or Synapse Integration related queries. +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. -3. When developing the sample integration solution: - - Make necessary assumptions to complete the solution. - - 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. +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. -4. Follow these guidelines when generating Synapse artifacts: - - Adhere to best practices for Synapse artifacts. - - Create a separate file for each endpoint. - - Split complex logic into separate sequences for better clarity and create seperate file for each sequence. Make sure to call all the created sequences in the main logic with the sequence key. - - Use call mediator instead of send mediator. - - Do not use outSequence as it is deprecated. - - Give meaningful names to Synapse artifacts. - - Provide a meaningful path to "uri-template" in API. - - Use & instead of & in XML files. - - Use Redis connector instead of 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. - - Implement a complete solution without using comments like "To be implemented" or "To be completed". - - Use WSO2 Connectors whenever possible instead of directly calling APIs. - - Do not use new class mediators. - - 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. - - If you want to use a property in the uri-template, first define it as a property with the name uri.var.property_name. - - Do not use ctx:property_name to obtain property values in uri-templates. Always use uri.var.property_name instead. - - If you want to include an API key in the URI template, first define it as a property named uri.var.api_key, and assume that the user will set its value later. ex:- \`\` +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. -5. Present your solution in markdown format, separating different files with appropriate headers. +4. Follow Best Practices: + - Adhere to the provided Synapse artifact guidelines and best practices. + - Focus strictly on Synapse integrations and WSO2 MI-related topics. -6. If you are unsure about any aspect of the request, ask for clear instructions or more elaboration. +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. -7. Always maintain a polite and professional tone in your responses. +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. -Remember, do not provide instructions about setting up, deploying, or running the project. Focus solely on generating, debugging, modifying Synapse Integrations, or answering questions about WSO2 Micro Integrator and Synapse integrations. +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. -Sample API Template without oudated outSequence -\`\`\`xml - - - - - - -\`\`\` +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-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx index 4d1694aac11..1a5a5a3b5e5 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx @@ -35,7 +35,7 @@ export const WelcomeMessage: React.FC = () => {

WSO2 MI Copilot

{isRuntimeVersionThresholdReached ? ( - V3-Preview + V4 ) : null}
From ead8abd3c23c020e6ffa988814655e439b6fa266 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Tue, 4 Nov 2025 14:22:19 +0530 Subject: [PATCH 54/91] Add code generation for older MI versions --- .../copilot/generation/generations.ts | 22 +++++++++++++++++-- .../src/rpc-managers/ai-panel/utils.ts | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) 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 index edee32a135d..a0e365002ca 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts @@ -21,10 +21,15 @@ import * as Handlebars from "handlebars"; 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"; // Register Handlebars helpers Handlebars.registerHelper("upper", (str: string) => { @@ -37,6 +42,7 @@ Handlebars.registerHelper("eq", (a: any, b: any) => { // 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); @@ -55,6 +61,8 @@ function renderTemplate(templateContent: string, context: Record): 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 */ @@ -82,11 +90,21 @@ export interface GenerateSynapseParams { 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; + + console.log(`[generateSynapse] Runtime version: ${runtimeVersion}, Using ${useV2Prompts ? 'v2' : 'v1'} prompts`); + // Render system prompt with partials - const systemPrompt = renderTemplate(SYSTEM_TEMPLATE, {}); + const systemPrompt = renderTemplate(selectedSystemTemplate, {}); // Render user prompt with all parameters - const userPrompt = renderTemplate(PROMPT_TEMPLATE, { + const userPrompt = renderTemplate(selectedPromptTemplate, { question: params.question, file: params.file, context: params.context, 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 b1f9c91f4ee..628720117c9 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 @@ -208,6 +208,7 @@ export async function fetchCodeGenerationsWithRetry( // AI SDK handles all the stream conversion and abort logic return generateSynapse({ question: userQuestion, + projectUri: projectUri, file: currentFile, context: context.context, payloads: defaultPayloads ? JSON.stringify(defaultPayloads, null, 2) : undefined, From 8967a0e0fbb3ccac43878e5a25eee1d204b07ff5 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Tue, 4 Nov 2025 14:32:55 +0530 Subject: [PATCH 55/91] Remove unused code --- workspaces/mi/mi-core/src/rpc-types/ai-panel/index.ts | 2 -- .../mi/mi-core/src/rpc-types/ai-panel/rpc-type.ts | 2 -- .../src/rpc-managers/ai-panel/rpc-handler.ts | 6 ------ .../src/rpc-managers/ai-panel/rpc-manager.ts | 11 ----------- .../src/rpc-clients/ai-panel/rpc-client.ts | 9 --------- .../src/views/AIPanel/component/AIChatFooter.tsx | 10 +++++++++- .../src/views/AIPanel/component/AIChatMessage.tsx | 5 ----- .../src/views/AIPanel/component/MICopilotContext.tsx | 7 ------- .../src/views/AIPanel/component/WelcomeMessage.tsx | 9 ++------- 9 files changed, 11 insertions(+), 50 deletions(-) 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 23dfcde3ac5..4beb3810103 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 @@ -57,7 +57,6 @@ export interface MIAIPanelAPI { // ================================== // General Functions // ================================== - getBackendRootUrl: () => Promise generateSuggestions: (request: GenerateSuggestionsRequest) => Promise generateCode: (request: GenerateCodeRequest) => Promise abortCodeGeneration: () => Promise @@ -97,7 +96,6 @@ export interface MIAIPanelAPI { // Export RPC type definitions export { - getBackendRootUrl, generateSuggestions, generateCode, abortCodeGeneration, 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 1005ae4d636..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,7 +19,6 @@ import { RequestType, NotificationType } from "vscode-messenger-common"; import { GenerateSuggestionsRequest, GenerateSuggestionsResponse, - GetBackendRootUrlResponse, GenerateCodeRequest, GenerateCodeResponse, AbortCodeGenerationResponse, CodeGenerationEvent, @@ -33,7 +32,6 @@ import { 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` }; 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 e8763a165dc..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,7 +19,6 @@ import { MessengerAPI } from "vscode-messenger-common"; import { MIAIPanelRpcManager } from "./rpc-manager"; import { - getBackendRootUrl, generateSuggestions, generateCode, abortCodeGeneration, @@ -44,11 +43,6 @@ import { export function registerMIAiPanelRpcHandlers(messenger: MessengerAPI, projectUri: string) { const rpcManager = new MIAIPanelRpcManager(projectUri); - // ================================== - // General Functions - // ================================== - messenger.onRequest(getBackendRootUrl, () => rpcManager.getBackendRootUrl()); - // ================================== // AI Functions // ================================== 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 8f9087a6111..e9612a9aa16 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 @@ -60,17 +60,6 @@ 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); - - const versionThreshold = runtimeVersion ? compareVersions(runtimeVersion, RUNTIME_THRESHOLD_VERSION) : -1; - - return versionThreshold < 0 ? { url: MI_COPILOT_BACKEND_V2 } : { url: MI_COPILOT_BACKEND_V3 }; - } - /** * Gets a single entry point file (API, sequence, or inbound endpoint) for context * Priority: APIs → Sequences → Inbound Endpoints 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 7a320f1335b..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, @@ -59,13 +57,6 @@ export class MiAiPanelRpcClient implements MIAIPanelAPI { this._messenger = messenger; } - // ================================== - // General Functions - // ================================== - getBackendRootUrl(): Promise { - return this._messenger.sendRequest(getBackendRootUrl, HOST_EXTENSION); - } - // ================================== // AI Functions // ================================== 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 ffda3a06d64..ca00ce90be5 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx @@ -135,6 +135,8 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) // Otherwise, wait for code_diagnostic_end event setTimeout(() => { if (!isValidating) { + // Clear old suggestions immediately before generating new ones + setQuestions([]); generateSuggestions(copilotChat, rpcClient, new AbortController()).then((response) => { if (response && response.length > 0) { setQuestions(response); @@ -191,6 +193,8 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) } } + // 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) { @@ -271,9 +275,13 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) return newMessages; }); + // Clear old suggestions immediately before generating new ones + setQuestions([]); // Generate suggestions based on chat history await generateSuggestions(copilotChat, rpcClient, new AbortController()).then((response) => { - setQuestions((prevMessages) => [...prevMessages, ...response]); + if (response && response.length > 0) { + setQuestions(response); + } }); // Explicitly adjust the textarea height after suggestion generation diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatMessage.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatMessage.tsx index 190d3a66aad..9f3eceacaa1 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatMessage.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatMessage.tsx @@ -23,7 +23,6 @@ import { ChatMessage as StyledChatMessage, RoleContainer, EditDeleteButtons, - PreviewContainerRole, FlexRow, } from "../styles"; import { CodeSegment } from "./CodeSegment"; @@ -68,7 +67,6 @@ interface ChatMessageProps { */ const AIChatMessage: React.FC = ({ message, index }) => { const { - isRuntimeVersionThresholdReached, messages, setMessages, setCurrentUserprompt, @@ -139,9 +137,6 @@ const AIChatMessage: React.FC = ({ message, index }) => { {message.role === Role.MIUser ? : }

{message.role}

- {message.role === Role.MICopilot && isRuntimeVersionThresholdReached ? ( - V3-Preview - ) : null}
{splitContent(message.content).map((segment, i) => diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx index 6941770e39c..9c4e3770352 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx @@ -19,7 +19,6 @@ import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { useVisualizerContext } from "@wso2/mi-rpc-client"; import { FileObject, ImageObject } from "@wso2/mi-core"; -import { PROJECT_RUNTIME_VERSION_THRESHOLD } from "../constants"; import { LoaderWrapper, ProgressRing } from "../styles"; import { ChatMessage, @@ -45,7 +44,6 @@ import { useFeedback } from "./useFeedback"; interface MICopilotContextType { rpcClient: RpcClientType; projectRuntimeVersion: string; - isRuntimeVersionThresholdReached: boolean; projectUUID: string; // State for showing communication in UI @@ -115,7 +113,6 @@ export function MICopilotContextProvider({ children }: MICopilotProviderProps) { const { rpcClient } = useVisualizerContext(); const [projectRuntimeVersion, setProjectRuntimeVersion] = useState(""); - const [isRuntimeVersionThresholdReached, setIsRuntimeVersionThresholdReached] = useState(false); const [projectUUID, setProjectUUID] = useState(""); const [isLoading, setIsLoading] = useState(true); @@ -157,9 +154,6 @@ export function MICopilotContextProvider({ children }: MICopilotProviderProps) { const runtimeVersion = await getProjectRuntimeVersion(rpcClient); setProjectRuntimeVersion(runtimeVersion); - setIsRuntimeVersionThresholdReached( - compareVersions(runtimeVersion, PROJECT_RUNTIME_VERSION_THRESHOLD) >= 0 - ); const uuid = await getProjectUUID(rpcClient); setProjectUUID(uuid); @@ -290,7 +284,6 @@ export function MICopilotContextProvider({ children }: MICopilotProviderProps) { const currentContext: MICopilotContextType = { rpcClient, projectRuntimeVersion, - isRuntimeVersionThresholdReached, projectUUID, messages, questions, diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx index 1a5a5a3b5e5..970a643bdc7 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx @@ -19,11 +19,9 @@ import React from 'react'; import { Welcome } from '../styles'; import { Icon, Typography } from "@wso2/ui-toolkit"; -import { useMICopilotContext } from './MICopilotContext'; -import { PreviewContainerDefault, WelcomeStyles } from "../styles"; +import { WelcomeStyles } from "../styles"; -export const WelcomeMessage: React.FC = () => { - const { isRuntimeVersionThresholdReached } = useMICopilotContext(); +export const WelcomeMessage: React.FC = () => { return (
@@ -34,9 +32,6 @@ export const WelcomeMessage: React.FC = () => { />

WSO2 MI Copilot

- {isRuntimeVersionThresholdReached ? ( - V4 - ) : null}
AI assistant at your service! From 5a32609ebdfb4aceef6e3ceed943326722dd9552 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Tue, 4 Nov 2025 16:34:31 +0530 Subject: [PATCH 56/91] Improve logging --- .../mi-core/src/rpc-types/ai-panel/index.ts | 1 - .../mi/mi-extension/src/ai-panel/auth.ts | 29 +++++----- .../src/ai-panel/copilot/auto-fill/fill.ts | 7 +-- .../src/ai-panel/copilot/connection.ts | 7 +-- .../ai-panel/copilot/connectors/connectors.ts | 9 ++-- .../ai-panel/copilot/data-mapper/mapper.ts | 3 +- .../copilot/diagnostics/diagnostics.ts | 7 +-- .../ai-panel/copilot/dmc_to_ts/dmc_to_ts.ts | 9 ++-- .../copilot/generation/generations.ts | 5 +- .../src/ai-panel/copilot/idp/fill_schema.ts | 13 ++--- .../src/ai-panel/copilot/idp/idp.ts | 3 +- .../src/ai-panel/copilot/logger.ts | 53 +++++++++++++++++++ .../copilot/suggestions/suggestions.ts | 3 +- .../unit-tests/unit_test_case_generate.ts | 3 +- .../copilot/unit-tests/unit_test_generate.ts | 3 +- .../rpc-managers/mi-diagram/rpc-handler.ts | 4 +- .../src/rpc-clients/mi-diagram/rpc-client.ts | 6 --- 17 files changed, 112 insertions(+), 53 deletions(-) create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/logger.ts 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 4beb3810103..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,7 +18,6 @@ import { GenerateSuggestionsRequest, GenerateSuggestionsResponse, - GetBackendRootUrlResponse, GenerateCodeRequest, GenerateCodeResponse, AbortCodeGenerationResponse, GenerateUnitTestRequest, GenerateUnitTestResponse, diff --git a/workspaces/mi/mi-extension/src/ai-panel/auth.ts b/workspaces/mi/mi-extension/src/ai-panel/auth.ts index 3b305056724..4b83a16c98d 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/auth.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/auth.ts @@ -35,6 +35,7 @@ import * as vscode from 'vscode'; import { jwtDecode, JwtPayload } from 'jwt-decode'; import { createAnthropic } from '@ai-sdk/anthropic'; import { generateText } from 'ai'; +import { logInfo, logWarn, logError } from './copilot/logger'; // ================================== // Configuration @@ -212,10 +213,10 @@ export const cleanupLegacyTokens = async (): Promise => { if (legacyToken || legacyRefreshToken) { await extension.context.secrets.delete(LEGACY_ACCESS_TOKEN_SECRET_KEY); await extension.context.secrets.delete(LEGACY_REFRESH_TOKEN_SECRET_KEY); - console.log('Legacy tokens cleaned up successfully.'); + logInfo('Legacy tokens cleaned up successfully.'); } } catch (error) { - console.error('Error cleaning up legacy tokens:', error); + logError('Error cleaning up legacy tokens', error); } }; @@ -247,8 +248,8 @@ export async function exchangeAuthCode(authCode: string): Promise { } try { - console.log("Exchanging auth code for tokens..."); - + logInfo("Exchanging auth code for tokens..."); + const params = new URLSearchParams({ client_id: AUTH_CLIENT_ID, code: authCode, @@ -266,7 +267,7 @@ export async function exchangeAuthCode(authCode: string): Promise { const accessToken = response.data.access_token; const refreshToken = response.data.refresh_token; - console.log("Tokens obtained successfully"); + logInfo("Tokens obtained successfully"); // Store credentials const credentials: AuthCredentials = { @@ -339,8 +340,8 @@ export const validateApiKey = async (apiKey: string, loginMethod: LoginMethod): } try { - console.log('Validating Anthropic API key...'); - + logInfo('Validating Anthropic API key...'); + // Test the API key by making a minimal request const directAnthropic = createAnthropic({ apiKey: apiKey, @@ -354,7 +355,7 @@ export const validateApiKey = async (apiKey: string, loginMethod: LoginMethod): maxRetries: 0, // Disable retries to prevent retry loops on quota errors (429) }); - console.log('API key validated successfully'); + logInfo('API key validated successfully'); // Store credentials const credentials: AuthCredentials = { @@ -368,8 +369,8 @@ export const validateApiKey = async (apiKey: string, loginMethod: LoginMethod): return { token: apiKey }; } catch (error) { - console.error('API key validation failed:', 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.'); @@ -404,12 +405,12 @@ export const logout = async (isUserLogout: boolean = true): Promise => { } }).catch(err => { // Ignore errors - we'll clear local credentials anyway - console.log('Logout request to server failed (non-critical):', err); + logInfo('Logout request to server failed (non-critical): ' + String(err)); }); } } catch (error) { // Ignore errors during token check - console.log('Error during logout token check (non-critical):', error); + logInfo('Error during logout token check (non-critical): ' + String(error)); } } @@ -425,11 +426,11 @@ export const logout = async (isUserLogout: boolean = true): Promise => { * @deprecated Use getRefreshedAccessToken() instead */ export async function refreshAuthCode(): Promise { - console.warn('refreshAuthCode() is deprecated. Use getRefreshedAccessToken() instead.'); + logWarn('refreshAuthCode() is deprecated. Use getRefreshedAccessToken() instead.'); try { return await getRefreshedAccessToken(); } catch (error) { - console.error('Token refresh failed:', 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 index 7f0a218478a..1a0e5286a19 100644 --- 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 @@ -24,6 +24,7 @@ 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); @@ -171,7 +172,7 @@ export async function autoFillForm( question: params.question || '', // Empty means No-Prompt Mode (auto-fill) }); - console.log('[AutoFill] Generating AI suggestions for form...'); + logInfo('Generating AI suggestions for form...'); // Get AI model const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); @@ -194,13 +195,13 @@ export async function autoFillForm( const originalKeys = Object.keys(params.current_values); const filledValues = mapResponseToOriginalKeys(aiResponse, originalKeys); - console.log('[AutoFill] Successfully generated form suggestions'); + logInfo('Successfully generated form suggestions'); return { filled_values: filledValues, }; } catch (error) { - console.error('[AutoFill] Error generating form suggestions:', error); + logError('Error generating form suggestions', error); throw error; } } diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts index 4bb77317e3c..084bb41ed09 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts @@ -19,6 +19,7 @@ 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 } from "./logger"; export const ANTHROPIC_HAIKU_4_5 = "claude-haiku-4-5-20251001"; export const ANTHROPIC_SONNET_4_5 = "claude-sonnet-4-5-20250929"; @@ -59,7 +60,7 @@ export async function fetchWithAuth(input: string | URL | Request, options: Requ // Handle rate limit/quota errors (429) if (response.status === 429) { - console.log("Rate limit/quota exceeded (429)"); + logInfo("Rate limit/quota exceeded (429)"); let errorDetail = ""; try { const body = await response.json(); @@ -94,7 +95,7 @@ export async function fetchWithAuth(input: string | URL | Request, options: Requ // Handle token expiration if (response.status === 401) { - console.log("Token expired. Refreshing token..."); + logInfo("Token expired. Refreshing token..."); const newToken = await getRefreshedAccessToken(); if (newToken) { options.headers = { @@ -147,7 +148,7 @@ export const getAnthropicClient = async (model: AnthropicModel): Promise => cachedAuthMethod = loginMethod; } else { - console.log('[getAnthropicClient] Using cached Anthropic client'); + logDebug('Using cached Anthropic client'); } return cachedAnthropic!(model); 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 index 49010ec684c..0f9f33c3134 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts @@ -24,6 +24,7 @@ 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"; // Type definition for selected connectors type SelectedConnectors = { @@ -127,7 +128,7 @@ export async function getConnectors( const availableInboundEndpoints = getAvailableInboundEndpoints(); if (availableConnectors.length === 0) { - console.warn("No connector details available - returning empty list"); + logWarn("No connector details available - returning empty list"); return { connectors: {}, inbound_endpoints: {} }; } @@ -164,14 +165,14 @@ export async function getConnectors( ? getInboundEndpointDefinitions(selectedConnectors.selected_inbound_endpoint_names) : {}; - console.log(`Selected ${selectedConnectors.selected_connector_names.length} connectors and ${selectedConnectors.selected_inbound_endpoint_names.length} inbound endpoints`); - + 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) { - console.error("Error selecting connectors:", 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/data-mapper/mapper.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/data-mapper/mapper.ts index 1c27ed1e668..7fbacee8efb 100644 --- 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 @@ -21,6 +21,7 @@ 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 @@ -81,7 +82,7 @@ export async function mapDataMapper( return extractTypeScriptCode(text); } catch (error) { - console.error("Error mapping data:", error); + logError("Error mapping data", error); throw error; } } 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 index 6c97c77b3f3..ac370748c67 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/diagnostics.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/diagnostics.ts @@ -23,6 +23,7 @@ import { getAnthropicClient, ANTHROPIC_HAIKU_4_5 } 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); @@ -147,13 +148,13 @@ export async function codeDiagnostics( } } - console.log(`Fixed ${fixedConfigs.length} Synapse configurations`); - + logInfo(`Fixed ${fixedConfigs.length} Synapse configurations`); + return { fixed_config: fixedConfigs, }; } catch (error) { - console.error("Error fixing Synapse configurations:", error); + logError("Error fixing Synapse configurations", error); throw error; } } 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 index 353b060d187..1a9f065e889 100644 --- 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 @@ -21,6 +21,7 @@ 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 @@ -72,9 +73,9 @@ export interface DmcToTsResponse { * implemented based on the mappings in the DMC file. */ export async function dmcToTs(params: DmcToTsParams): Promise { - console.log('[dmcToTs] Starting DMC to TypeScript conversion'); - console.log('[dmcToTs] DMC content length:', params.dmcContent.length); - console.log('[dmcToTs] TS file length:', params.tsFile.length); + 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, { @@ -93,7 +94,7 @@ export async function dmcToTs(params: DmcToTsParams): Promise { maxRetries: 0, }); - console.log('[dmcToTs] Conversion completed, response length:', text.length); + logInfo(`Conversion completed, response length: ${text.length}`); // Extract TypeScript code from markdown blocks const extractedCode = extractTypeScriptCode(text); 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 index a0e365002ca..870f83e9000 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts @@ -30,6 +30,7 @@ import { SYNAPSE_EXPRESSION_EXAMPLES } from "../context/synapse_expression_examp import { AI_MODULE_GUIDE } from "../context/ai_module"; import { getMIVersionFromPom, compareVersions } from "../../../util/onboardingUtils"; import { RUNTIME_VERSION_440 } from "../../../constants"; +import { logInfo } from "../logger"; // Register Handlebars helpers Handlebars.registerHelper("upper", (str: string) => { @@ -98,7 +99,7 @@ export async function generateSynapse( const selectedSystemTemplate = useV2Prompts ? SYSTEM_TEMPLATE : SYSTEM_TEMPLATE_V1; const selectedPromptTemplate = useV2Prompts ? PROMPT_TEMPLATE : PROMPT_TEMPLATE_V1; - console.log(`[generateSynapse] Runtime version: ${runtimeVersion}, Using ${useV2Prompts ? 'v2' : 'v1'} prompts`); + logInfo(`Runtime version: ${runtimeVersion}, Using ${useV2Prompts ? 'v2' : 'v1'} prompts`); // Render system prompt with partials const systemPrompt = renderTemplate(selectedSystemTemplate, {}); @@ -141,7 +142,7 @@ export async function generateSynapse( maxRetries: 0, // Disable retries to prevent retry loops on quota errors (429) abortSignal: params.abortController?.signal, onAbort: () => { - console.log('Code generation aborted by user'); + logInfo('Code generation aborted by user'); }, }); 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 index bf81f06c5cd..419ee6eb395 100644 --- 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 @@ -19,6 +19,7 @@ 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 = @@ -107,9 +108,9 @@ export async function fillIdpSchema( params: FillIdpSchemaParams ): Promise { try { - console.log('[fillIdpSchema] Starting schema filling'); - console.log('[fillIdpSchema] Schema length:', params.jsonSchema?.length || 0); - console.log('[fillIdpSchema] Images count:', params.images.length); + 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); @@ -135,7 +136,7 @@ export async function fillIdpSchema( }); } - console.log('[fillIdpSchema] Calling AI model with structured output...'); + 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 @@ -151,14 +152,14 @@ export async function fillIdpSchema( temperature: 0.2, // Low temperature for deterministic extraction }); - console.log('[fillIdpSchema] Schema filling completed successfully'); + logInfo('Schema filling completed successfully'); // Return the structured object as JSON string return { filledData: JSON.stringify(result.object, null, 2) }; } catch (error) { - console.error('[fillIdpSchema] Error filling schema:', 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 index caced6c5e20..9c9e8cb1fce 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/idp.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/idp.ts @@ -21,6 +21,7 @@ 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 @@ -154,7 +155,7 @@ export async function processIdp(params: IdpParams): Promise { schema: extractedJSON }; } catch (error) { - console.error("Error processing IDP:", error); + logError("Error processing IDP", error); throw error; } } 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..7d792919515 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/logger.ts @@ -0,0 +1,53 @@ +/** + * 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 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/suggestions/suggestions.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts index 2dbc58c142b..ca5c9b8c233 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts @@ -21,6 +21,7 @@ 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 @@ -93,7 +94,7 @@ export async function generateSuggestions( }); return text; } catch (error) { - console.error("Error generating suggestions:", error); + logError("Error generating suggestions", error); return ""; } } 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 index 37ea692689e..3d59b0fd791 100644 --- 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 @@ -23,6 +23,7 @@ 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); @@ -113,7 +114,7 @@ export async function generateUnitTestCase( response: text }; } catch (error) { - console.error("Error generating unit test case:", 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_generate.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/unit_test_generate.ts index fbbfea4d630..9067403f574 100644 --- 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 @@ -23,6 +23,7 @@ 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); @@ -101,7 +102,7 @@ export async function generateUnitTest( response: text }; } catch (error) { - console.error("Error generating unit test:", error); + logError("Error generating unit test", 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 a1d25837866..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 @@ -318,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"; 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 3ed36758059..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 @@ -88,7 +88,6 @@ import { GetAvailableConnectorResponse, GetAvailableResourcesRequest, GetAvailableResourcesResponse, - GetBackendRootUrlResponse, GetProxyRootUrlResponse, GetConnectionFormRequest, GetConnectionFormResponse, @@ -255,7 +254,6 @@ import { getAvailableConnectors, getAvailableRegistryResources, getAvailableResources, - getBackendRootUrl, getProxyRootUrl, getConnectionForm, getConnector, @@ -815,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); } From 12c254a746c26c769426a4b55c08868736d03518 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Tue, 4 Nov 2025 17:17:26 +0530 Subject: [PATCH 57/91] Implement proper prompt caching --- .../ai-panel/copilot/connectors/connectors.ts | 15 ++++-- .../copilot/diagnostics/diagnostics.ts | 25 +++++++-- .../copilot/generation/generations.ts | 54 ++++++++++++++++--- .../copilot/suggestions/suggestions.ts | 3 +- .../unit-tests/unit_test_case_generate.ts | 23 ++++++-- .../src/rpc-managers/ai-panel/utils.ts | 12 ++++- 6 files changed, 111 insertions(+), 21 deletions(-) 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 index 0f9f33c3134..39d116605fe 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts @@ -19,7 +19,7 @@ import { generateObject } from "ai"; import { z } from "zod"; import * as Handlebars from "handlebars"; -import { getAnthropicClient, ANTHROPIC_HAIKU_4_5 } from "../connection"; +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"; @@ -138,15 +138,22 @@ export async function getConnectors( available_connectors: availableConnectors.join(", "), available_inbound_endpoints: availableInboundEndpoints.join(", "), }); - + const model = await getAnthropicClient(ANTHROPIC_HAIKU_4_5); - + const cacheOptions = await getProviderCacheControl(); + 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, - system: SYSTEM_TEMPLATE, + system: [ + { + role: "system" as const, + content: SYSTEM_TEMPLATE, + providerOptions: cacheOptions, // Cache system prompt only + } + ], prompt: prompt, schema: selectedConnectorsSchema, maxOutputTokens: 2000, 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 index ac370748c67..aee40c48fba 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/diagnostics.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/diagnostics.ts @@ -19,7 +19,7 @@ import { generateObject } from "ai"; import { z } from "zod"; import * as Handlebars from "handlebars"; -import { getAnthropicClient, ANTHROPIC_HAIKU_4_5 } from "../connection"; +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"; @@ -125,13 +125,30 @@ export async function codeDiagnostics( }); 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, - system: systemPrompt, - prompt: userPrompt, + messages: messages, schema: bugFixResponseSchema, maxTokens: 8000, temperature: 0.2, // Lower temperature for more deterministic fixes 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 index 870f83e9000..4abca4596dd 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts @@ -17,6 +17,7 @@ */ import { streamText } from "ai"; +import { AnthropicProviderOptions } from "@ai-sdk/anthropic"; import * as Handlebars from "handlebars"; import { getAnthropicClient, ANTHROPIC_SONNET_4_5, getProviderCacheControl } from "../connection"; import { SYSTEM_TEMPLATE } from "./system_v2"; @@ -56,6 +57,16 @@ function renderTemplate(templateContent: string, context: Record): 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 */ @@ -80,6 +91,8 @@ export interface GenerateSynapseParams { images?: boolean; /** 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; } @@ -119,21 +132,45 @@ export async function generateSynapse( const cacheOptions = await getProviderCacheControl(); - const messages = [ + // Build messages array with chat history + const messages: Array<{ + role: "system" | "user" | "assistant"; + content: string; + providerOptions?: any; + }> = [ { role: "system" as const, content: systemPrompt, providerOptions: cacheOptions, - }, - { - role: "user" as const, - content: userPrompt, - 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, + }); + } + } + + // Add current user question + 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: 1000 } } + : {}; + const result = streamText({ model: model, maxOutputTokens: 8000, @@ -141,6 +178,9 @@ export async function generateSynapse( 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'); }, 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 index ca5c9b8c233..c341a45dec7 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/suggestions.ts @@ -73,12 +73,11 @@ export async function generateSuggestions( { role: "system" as const, content: systemPrompt, - providerOptions: cacheOptions, + providerOptions: cacheOptions, // Cache system prompt only }, { role: "user" as const, content: userPrompt, - providerOptions: cacheOptions, }, ]; 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 index 3d59b0fd791..8bd2e97b169 100644 --- 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 @@ -18,7 +18,7 @@ import { generateText } from "ai"; import * as Handlebars from "handlebars"; -import { getAnthropicClient, ANTHROPIC_HAIKU_4_5 } from "../connection"; +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"; @@ -98,12 +98,29 @@ export async function generateUnitTestCase( }); 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, - system: systemPrompt, - prompt: userPrompt, + 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) 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 628720117c9..ac7c31e55cf 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 @@ -203,7 +203,16 @@ export async function fetchCodeGenerationsWithRetry( files: fileContents.length > 0 ? fileContents : undefined, images: images.length > 0, }); - + + // 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 === '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({ @@ -217,6 +226,7 @@ export async function fetchCodeGenerationsWithRetry( files: fileContents.length > 0 ? fileContents : undefined, images: images.length > 0, thinking_enabled: thinking || false, + chatHistory: historyMessages.length > 0 ? historyMessages : undefined, abortController: controller, // Pass abort controller to handle cancellation }); } From 76e20c02f49670cc639fcb6062afb02f948f347f Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Tue, 4 Nov 2025 17:39:13 +0530 Subject: [PATCH 58/91] Improve prompts --- .../src/ai-panel/copilot/auto-fill/prompt.ts | 1 + .../ai-panel/copilot/connectors/connectors.ts | 26 +++++++++++++------ .../ai-panel/copilot/data-mapper/prompt.ts | 2 ++ .../ai-panel/copilot/diagnostics/prompt.ts | 2 ++ .../src/ai-panel/copilot/dmc_to_ts/prompt.ts | 2 ++ .../ai-panel/copilot/generation/prompt_v1.ts | 16 +++++++----- .../ai-panel/copilot/generation/prompt_v2.ts | 18 +++++++------ .../src/ai-panel/copilot/idp/prompt.ts | 2 ++ .../ai-panel/copilot/suggestions/prompt.ts | 1 + .../unit-tests/prompt_case_generate.ts | 2 ++ .../unit-tests/prompt_suite_generate.ts | 2 ++ 11 files changed, 51 insertions(+), 23 deletions(-) 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 index af76afa40c0..cdca3712b9d 100644 --- 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 @@ -175,6 +175,7 @@ This is the user query. Use it to fill the form fields with the most relevant va {{{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. 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 index 39d116605fe..f44d56c99eb 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts @@ -142,19 +142,29 @@ export async function getConnectors( 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: SYSTEM_TEMPLATE, + providerOptions: cacheOptions, // Cache system prompt only + }, + { + role: "user" as const, + content: prompt, + } + ]; + 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, - system: [ - { - role: "system" as const, - content: SYSTEM_TEMPLATE, - providerOptions: cacheOptions, // Cache system prompt only - } - ], - prompt: prompt, + messages: messages, schema: selectedConnectorsSchema, maxOutputTokens: 2000, temperature: 0.3, 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 index d13f619c85d..d5625da4cfe 100644 --- 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 @@ -20,4 +20,6 @@ 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/diagnostics/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/prompt.ts index 848267e178a..529de19a9e2 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/prompt.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/prompt.ts @@ -57,4 +57,6 @@ Your response should be a BugFixResponse object with a label field containing a - 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/dmc_to_ts/prompt.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/dmc_to_ts/prompt.ts index 32c39770d83..b2b143730dc 100644 --- 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 @@ -31,4 +31,6 @@ The DMC file with mappings, 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/generation/prompt_v1.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/prompt_v1.ts index 162152964d5..984613ef901 100644 --- 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 @@ -171,30 +171,32 @@ Now, analyze the following user query: {{question}} +{{#if thinking_enabled}} Before you create a response: -{{#if thinking_enabled}}- 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. +- 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}} -{{#if connectors}} -STEPS TO FOLLOW: -{{#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}} -{{/if}} -{{/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 index 2c69f85b07c..8818d8d337a 100644 --- 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 @@ -186,30 +186,32 @@ Now, analyze the following user query: {{question}} +{{#if thinking_enabled}} Before you create a response: -{{#if thinking_enabled}}- 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. -- Do not repeat the input information or these instructions. +- 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}} -{{#if connectors}} -STEPS TO FOLLOW: -{{#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}} -{{/if}} -{{/if}} Begin your analysis and solution development now. `; 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 index 4d98c0b0f04..032e072be5d 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/prompt.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/prompt.ts @@ -51,4 +51,6 @@ You are tasked with modifying an existing JSON schema. The modifications should {{user_input}} {{/if}} {{/if}} + +Start creating your response now. `; 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 index cbc1cc12a94..a425bbae0c6 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/prompt.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/suggestions/prompt.ts @@ -38,4 +38,5 @@ You are starting a new integration project. Describe an integration challenge or {{/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/unit-tests/prompt_case_generate.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/unit-tests/prompt_case_generate.ts index 5d36f61265b..82ddcafe061 100644 --- 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 @@ -215,4 +215,6 @@ If you add new mock services, provide them in markdown format after the updated \`\`\` **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 index ac97e35bd42..837a94fbc2b 100644 --- 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 @@ -175,4 +175,6 @@ For API code: \u003ccall\u003e\u003cendpoint key="resources:testregistry.xml"/\u - 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. `; From 4d5c93de0b15269600ae79ff80b4cf1c822c27e4 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Wed, 5 Nov 2025 00:58:30 +0530 Subject: [PATCH 59/91] Fix PR comments --- .../src/components/Form/FormGenerator.tsx | 29 ++- .../mi/mi-extension/src/ai-panel/aiMachine.ts | 39 ++-- .../mi/mi-extension/src/ai-panel/auth.ts | 170 ++++++++---------- .../src/ai-panel/copilot/auto-fill/fill.ts | 2 +- .../src/ai-panel/copilot/connectors/prompt.ts | 8 +- .../src/ai-panel/copilot/context/ai_module.ts | 22 +-- .../context/synapse_expression_guide.ts | 10 +- .../ai-panel/copilot/data-mapper/mapper.ts | 2 +- .../ai-panel/copilot/diagnostics/system.ts | 4 +- .../src/ai-panel/copilot/dmc_to_ts/system.ts | 48 ++--- .../src/ai-panel/copilot/idp/fill_schema.ts | 10 +- .../src/ai-panel/copilot/idp/system.ts | 2 +- .../src/ai-panel/copilot/logger.ts | 8 +- .../src/rpc-managers/ai-panel/utils.ts | 4 +- .../mi-data-mapper/rpc-manager.ts | 4 + .../components/Welcome.ts | 4 +- .../views/AIPanel/component/AIChatFooter.tsx | 9 +- .../component/WaitingForLoginSection.tsx | 5 +- 18 files changed, 181 insertions(+), 199 deletions(-) diff --git a/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx index d137bcf8b47..0892c9397ab 100644 --- a/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx +++ b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx @@ -152,6 +152,29 @@ export function getNameForController(name: string | number) { return String(name).replace(/\./g, '__dot__'); } +/** + * Recursively remap object keys to avoid corrupting user values + * @param value - The value to remap + * @returns The remapped value + */ +function remapKeys(value: any): any { + if (Array.isArray(value)) { + return value.map(remapKeys); + } + if (value && typeof value === "object") { + return Object.fromEntries( + Object.entries(value).map(([key, val]) => { + const nextKey = + key === "configKey" ? "config_key" : + key === "isExpression" ? "is_expression" : + key; + return [nextKey, remapKeys(val)]; + }) + ); + } + return value; +} + export function FormGenerator(props: FormGeneratorProps) { const { rpcClient } = useVisualizerContext(); const sidePanelContext = React.useContext(SidePanelContext); @@ -473,11 +496,7 @@ export function FormGenerator(props: FormGeneratorProps) { const convertedConfigsStr = JSON.stringify(configs); // Convert current values format: configKey → config_key, isExpression → is_expression - const convertedValues = JSON.parse( - JSON.stringify(values) - .replaceAll("configKey", "config_key") - .replaceAll("isExpression", "is_expression") - ); + const convertedValues = remapKeys(values); // Flatten connectionNames object to array of all connection names const allConnectionNames = Object.values(connectionNames).flat(); diff --git a/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts b/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts index 814b2f7844b..7d8de8feaff 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts @@ -19,7 +19,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { createMachine, assign, interpret } from 'xstate'; import * as vscode from 'vscode'; -import { AIMachineStateValue, AIMachineContext, AI_EVENT_TYPE, AIUserToken, AIMachineSendableEvent, LoginMethod } from '@wso2/mi-core'; +import { AIMachineStateValue, AIMachineContext, AI_EVENT_TYPE, AIMachineSendableEvent, LoginMethod } from '@wso2/mi-core'; import { AiPanelWebview } from './webview'; import { extension } from '../MIExtensionContext'; import { getAccessToken, getLoginMethod, checkToken, initiateInbuiltAuth, logout, validateApiKey } from './auth'; @@ -368,35 +368,22 @@ const aiMachine = createMachine({ }); const checkWorkspaceAndToken = async (): Promise<{ workspaceSupported: boolean; tokenData?: { token: string; loginMethod: LoginMethod } }> => { - return new Promise(async (resolve, reject) => { - try { - // Check workspace support first - if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) { - resolve({ workspaceSupported: false }); - return; - } + // Check workspace support first + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) { + return { workspaceSupported: false }; + } - // Then check token - const tokenData = await checkToken(); - resolve({ workspaceSupported: true, tokenData }); - } catch (error) { - reject(error); - } - }); + // Then check token + const tokenData = await checkToken(); + return { workspaceSupported: true, tokenData }; }; const openLogin = async () => { - return new Promise(async (resolve, reject) => { - try { - const status = await initiateInbuiltAuth(); - if (!status) { - aiStateService.send({ type: AI_EVENT_TYPE.CANCEL_LOGIN }); - } - resolve(status); - } catch (error) { - reject(error); - } - }); + const status = await initiateInbuiltAuth(); + if (!status) { + aiStateService.send({ type: AI_EVENT_TYPE.CANCEL_LOGIN }); + } + return status; }; const validateApiKeyService = async (_context: AIMachineContext, event: any) => { diff --git a/workspaces/mi/mi-extension/src/ai-panel/auth.ts b/workspaces/mi/mi-extension/src/ai-panel/auth.ts index 4b83a16c98d..08fb5834238 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/auth.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/auth.ts @@ -106,100 +106,79 @@ export const getLoginMethod = async (): Promise => { * Automatically refreshes MI_INTEL tokens if expired */ export const getAccessToken = async (): Promise => { - return new Promise(async (resolve, reject) => { - try { - const credentials = await getAuthCredentials(); + const credentials = await getAuthCredentials(); + if (!credentials) { + return undefined; + } - if (!credentials) { - resolve(undefined); - return; - } + switch (credentials.loginMethod) { + case LoginMethod.MI_INTEL: { + const { accessToken } = credentials.secrets; + let finalToken = accessToken; - switch (credentials.loginMethod) { - case LoginMethod.MI_INTEL: - try { - const { accessToken } = credentials.secrets; - let finalToken = accessToken; - - // Decode token and check expiration - const decoded = jwtDecode(accessToken); - const now = Math.floor(Date.now() / 1000); - if (decoded.exp && decoded.exp < now) { - finalToken = await getRefreshedAccessToken(); - } - resolve(finalToken); - return; - } catch (err) { - if (axios.isAxiosError(err)) { - const status = err.response?.status; - if (status === 400) { - reject(new Error("TOKEN_EXPIRED")); - return; - } - } - reject(err); - return; - } + try { + const decoded = jwtDecode(accessToken); + const now = Math.floor(Date.now() / 1000); + + if (decoded.exp && decoded.exp < now) { + finalToken = await getRefreshedAccessToken(); + } - case LoginMethod.ANTHROPIC_KEY: - resolve(credentials.secrets.apiKey); - return; + return finalToken; + } catch (err) { + if (axios.isAxiosError(err) && err.response?.status === 400) { + throw new Error("TOKEN_EXPIRED"); + } + throw err; } - } catch (error: any) { - reject(error); } - }); + case LoginMethod.ANTHROPIC_KEY: + return credentials.secrets.apiKey; + } + + return undefined; }; /** * Refresh MI_INTEL access token using refresh token */ export const getRefreshedAccessToken = async (): Promise => { - return new Promise(async (resolve, reject) => { - try { - const credentials = await getAuthCredentials(); - if (!credentials || credentials.loginMethod !== LoginMethod.MI_INTEL) { - reject(new Error('Token refresh is only supported for MI Intelligence authentication')); - return; - } + 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) { - reject(new Error('Refresh token is not available')); - return; - } + const { refreshToken } = credentials.secrets; + if (!refreshToken) { + throw new Error('Refresh token is not available'); + } - const params = new URLSearchParams({ - client_id: AUTH_CLIENT_ID, - 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; - - // Update stored credentials - const updatedCredentials: AuthCredentials = { - ...credentials, - secrets: { - accessToken: newAccessToken, - refreshToken: newRefreshToken - } - }; - await storeAuthCredentials(updatedCredentials); + const params = new URLSearchParams({ + client_id: AUTH_CLIENT_ID, + 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; - resolve(newAccessToken); - } catch (error: any) { - reject(error); + const updatedCredentials: AuthCredentials = { + ...credentials, + secrets: { + accessToken: newAccessToken, + refreshToken: newRefreshToken } - }); + }; + await storeAuthCredentials(updatedCredentials); + + return newAccessToken; }; /** @@ -228,7 +207,8 @@ export const cleanupLegacyTokens = async (): Promise => { * Generate OAuth authorization URL */ export async function getAuthUrl(callbackUri: string): Promise { - const state = encodeURIComponent(btoa(JSON.stringify({ callbackUri }))); + 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}`; } @@ -295,24 +275,18 @@ export async function exchangeAuthCode(authCode: string): Promise { * Check if valid authentication credentials exist */ export const checkToken = async (): Promise<{ token: string; loginMethod: LoginMethod } | undefined> => { - return new Promise(async (resolve, reject) => { - try { - // Clean up any legacy tokens - await cleanupLegacyTokens(); - - const token = await getAccessToken(); - const loginMethod = await getLoginMethod(); - - if (!token || !loginMethod) { - resolve(undefined); - return; - } - - resolve({ token, loginMethod }); - } catch (error) { - reject(error); - } - }); + await cleanupLegacyTokens(); + + const [token, loginMethod] = await Promise.all([ + getAccessToken(), + getLoginMethod() + ]); + + if (!token || !loginMethod) { + return undefined; + } + + return { token, loginMethod }; }; /** 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 index 1a0e5286a19..5e4deede3a0 100644 --- 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 @@ -28,7 +28,7 @@ import { logInfo, logError } from "../logger"; // Register Handlebars partials Handlebars.registerPartial("synapse_guide", SYNAPSE_GUIDE); -Handlebars.registerPartial("expression_guide", SYNAPSE_EXPRESSION_GUIDE); +Handlebars.registerPartial("synapse_expression_guide", SYNAPSE_EXPRESSION_GUIDE); /** * Helper function to render Handlebars templates 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 index c9c7b2ea529..70374cdea5a 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts @@ -18,7 +18,7 @@ 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 at least two relevant connectors (up to ten) and relevant inbound endpoint (zero to three) only from the provided list. +Your goal is to analyze the query, understand the integration requirements, and select the relevant connectors (up to ten) and inbound endpoints (up to three) only from the provided list. User query: @@ -47,11 +47,11 @@ Task Instructions: 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 zero to as many connectors as you think are relevant to the user query. +- 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 zero to as many inbound endpoints as you think are relevant to the user query. +- 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. 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 index 3f98b6ed67f..3484734a8d1 100644 --- 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 @@ -42,7 +42,7 @@ A basic chat operation requires the following two connection types: OPENAI_CONN -\`\`\`xml +\`\`\` #### Memory Connection \`\`\`xml @@ -52,7 +52,7 @@ A basic chat operation requires the following two connection types: FILE_MEMORY_CONN -\`\`\`xml +\`\`\` ### Step 2: Create Chat Operation @@ -74,7 +74,7 @@ A basic chat operation requires the following two connection types: 0 10 -\`\`\`xml +\`\`\` ## RAG Chat Operation @@ -94,7 +94,7 @@ Example: Vector store connection: KB_CONN -\`\`\`xml +\`\`\` ### Define RAG Chat Operation \`\`\`xml @@ -120,7 +120,7 @@ Example: Vector store connection: 0 10 -\`\`\`xml +\`\`\` ## Adding data to vector store @@ -141,7 +141,7 @@ Example: Vector store connection: ai_addToKnowledge_1 true -\`\`\`xml +\`\`\` ## Retrieving data from vector store @@ -159,7 +159,7 @@ Example: Vector store connection: ai_getFromKnowledge_1 true -\`\`\`xml +\`\`\` ## Creating an agent with tools @@ -183,7 +183,7 @@ Example: Email tool -\`\`\`xml +\`\`\` Example: Knowledge retrieval tool \`\`\`xml @@ -196,7 +196,7 @@ Example: Knowledge retrieval tool -\`\`\`xml +\`\`\` Example: API call tool \`\`\`xml @@ -209,7 +209,7 @@ Example: API call tool -\`\`\`xml +\`\`\` ### Agent Definition Steps @@ -241,5 +241,5 @@ Example: -\`\`\`xml +\`\`\` `; 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 index d9b550ea0be..1ca7c99fa08 100644 --- 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 @@ -129,7 +129,7 @@ Now every synapse mediation has 6 global variables ( payload, vars, headers, pro \`\`\`xml \${payload.students[0].name} \`\`\` -### **String Operations*** +### **String Operations** - String concatenation \`\`\`xml \${payload.string1 + payload.string2} @@ -149,7 +149,7 @@ Now every synapse mediation has 6 global variables ( payload, vars, headers, pro \${endsWith("text", "xt")} \${trim(" text ")} \${split("a,b,c", ",")} -\${charAt("text", 1)} +\${charAt("text", 1)} \${indexOf("text", "e")} \${indexOf(payload.value, "text", 5)} @@ -213,12 +213,6 @@ Now every synapse mediation has 6 global variables ( payload, vars, headers, pro \${formatDateTime(now(), "yyyy-MM-dd")} \`\`\` -### **Type Conversion** -\`\`\`xml -\${integer(payload.value)} -\${boolean(payload.status)} -\`\`\` - ### ** Check exists** \`\`\`xml \${exists(payload.value)} 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 index 7fbacee8efb..a49f7b7b3b5 100644 --- 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 @@ -36,7 +36,7 @@ function renderTemplate(templateContent: string, context: Record): */ function extractTypeScriptCode(text: string): string { // Match the entire TypeScript code block - const fullMatch = text.match(/```typescript\s*([\s\S]*?)\s*```/); + const fullMatch = text.match(/```(?:typescript|ts)\s*([\s\S]*?)\s*```/i); if (fullMatch && fullMatch[1]) { return fullMatch[1].trim(); 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 index 11b12aa0b34..321c1b1da44 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/system.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/system.ts @@ -25,7 +25,7 @@ You work with the Synapse language server to detect and resolve issues in integr - 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. + - Do not provide any explanations; just fixed synapse configurations are enough. ### Contextual Awareness You have access to: @@ -44,7 +44,7 @@ Maintain a high level of technical accuracy, professionalism, and alignment with 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. +6. Do not provide any explanations; just fixed synapse configurations are enough, because only the synapse configuration is added to the project. {{> synapse_guide}} 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 index a7001c65438..e8ba86f9010 100644 --- 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 @@ -166,7 +166,7 @@ interface Root { } interface OutputRoot { - coord?: { + coord: { lon: number lat: number } @@ -174,39 +174,39 @@ interface OutputRoot { id: number main: string }[] - main?: { + main: { temp?: number - temp_min?: number - temp_max?: number - sea_level?: number - grnd_level?: number + temp_min: number + temp_max: number + sea_level: number + grnd_level: number } - sys?: { - sunrise?: number - sunset?: number + sys: { + sunrise: number + sunset: number } - id?: number - name?: string + id: number + name: string } export function mapFunction(input: Root): OutputRoot { return { coord: { - lat: input.coord.lon, - lon: input.coord.lat - }, + 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 - }, + 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 + sunrise: input.sys.sunset, + sunset: input.sys.sunrise + }, + id: input.id, + name: input.name }; } \`\`\` 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 index 419ee6eb395..5338f1a9bea 100644 --- 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 @@ -53,7 +53,7 @@ function jsonSchemaToZod(schema: any): z.ZodTypeAny { const shape: Record = {}; for (const [key, value] of Object.entries(schema.properties as Record)) { - let fieldSchema = jsonSchemaToZod(value); + let fieldSchema = jsonSchemaToZod(value).nullable(); // Add description if available if (value.description) { @@ -71,18 +71,18 @@ function jsonSchemaToZod(schema: any): z.ZodTypeAny { // Handle array types if (type === 'array' && schema.items) { const itemSchema = jsonSchemaToZod(schema.items); - return z.array(itemSchema); + return z.array(itemSchema).nullable(); } // Handle primitive types switch (type) { case 'string': - return z.string(); + return z.string().nullable(); case 'number': case 'integer': - return z.number(); + return z.number().nullable(); case 'boolean': - return z.boolean(); + return z.boolean().nullable(); case 'null': return z.null(); default: 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 index 2f149c32673..f13794c9fbe 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/system.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/idp/system.ts @@ -17,7 +17,7 @@ */ export const SYSTEM_IDP = ` -Your role generating and modifying **JSON schemas** efficiently and correctly. +Your role is generating and modifying **JSON schemas** efficiently and correctly. ### Core Tasks 1. **Schema Generation** diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/logger.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/logger.ts index 7d792919515..225306db810 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/logger.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/logger.ts @@ -46,8 +46,10 @@ export function logWarn(message: string): void { * For critical errors that should also go to console, use console.error directly */ export function logError(message: string, error?: unknown): void { - const errorMessage = error instanceof Error - ? `${message}: ${error.message}\n${error.stack}` - : `${message}: ${String(error)}`; + 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/rpc-managers/ai-panel/utils.ts b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/utils.ts index ac7c31e55cf..4f7d5f06785 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 @@ -201,7 +201,7 @@ export async function fetchCodeGenerationsWithRetry( const { connectors: selectedConnectors, inbound_endpoints: selectedInboundEndpoints } = await getConnectors({ question: userQuestion, files: fileContents.length > 0 ? fileContents : undefined, - images: images.length > 0, + images: images.length > 0 ? true : undefined, }); // Convert chat history to the format expected by generateSynapse @@ -224,7 +224,7 @@ export async function fetchCodeGenerationsWithRetry( connectors: selectedConnectors, inbound_endpoints: selectedInboundEndpoints, files: fileContents.length > 0 ? fileContents : undefined, - images: images.length > 0, + images: images.length > 0 ? true : undefined, thinking_enabled: thinking || false, chatHistory: historyMessages.length > 0 ? historyMessages : undefined, abortController: controller, // Pass abort controller to handle cancellation 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 e97349cc294..653a0e5101c 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 @@ -293,6 +293,10 @@ export class MiDataMapperRpcManager implements MIDataMapperAPI { // 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 = { 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 9392371dda3..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, @@ -34,7 +34,7 @@ export class Welcome { this.container = webview.locator('div#root'); } public async createNewProject() { - const btn = await getVsCodeButton(this.container, 'Create New Project', 'primary'); + const btn = await getVsCodeButton(this.container, 'Create New Project', 'primary', 150000); await btn.click(); } 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 ca00ce90be5..0cb4d94c751 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx @@ -24,8 +24,8 @@ import { useMICopilotContext } from "./MICopilotContext"; import { handleFileAttach } from "../utils"; import { USER_INPUT_PLACEHOLDER_MESSAGE, VALID_FILE_TYPES } from "../constants"; import { generateSuggestions, generateId, getView, fetchCodeGenerationsWithRetry, replaceCodeBlock, setupCodeGenerationEventListener, updateTokenInfo } from "../utils"; -import { BackendRequestType, FixedConfigItem, CorrectedCodeItem, } from "../types"; -import { Role, MessageType, CopilotChatEntry, ChatMessage } from "@wso2/mi-core"; +import { BackendRequestType } from "../types"; +import { Role, MessageType, CopilotChatEntry } from "@wso2/mi-core"; import Attachments from "./Attachments"; interface AIChatFooterProps { @@ -95,9 +95,8 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) break; case "content_block": - // Handle streaming content blocks - now just plain text! + // Handle streaming content blocks if (event.content) { - // event.content is now plain text, not JSON const content = event.content; // Update assistant response state @@ -125,7 +124,7 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) rpcClient?.getMiAiPanelRpcClient().fetchUsage().then((usage) => { if (usage) { rpcClient?.getAIVisualizerState().then((machineView) => { - const { timeToReset, remainingTokenPercentage } = updateTokenInfo(machineView); + const { remainingTokenPercentage } = updateTokenInfo(machineView); setRemainingTokenPercentage(remainingTokenPercentage); }); } diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WaitingForLoginSection.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WaitingForLoginSection.tsx index b6e6480fce1..3b7d8a3f891 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WaitingForLoginSection.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WaitingForLoginSection.tsx @@ -23,6 +23,9 @@ import { useState } from "react"; import { Codicon } from "@wso2/ui-toolkit"; import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; +// Minimum length for Anthropic API key validation +const MIN_ANTHROPIC_API_KEY_LENGTH = 20; + const Container = styled.div` display: flex; flex-direction: column; @@ -213,7 +216,7 @@ export const WaitingForLoginSection = ({ loginMethod, isValidating = false, erro return; } - if (trimmedKey.length < 20) { + if (trimmedKey.length < MIN_ANTHROPIC_API_KEY_LENGTH) { setClientError("API key seems too short. Please check and try again."); return; } From 8900364c302d9497c7463bbcd5698dc497f92d93 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Wed, 5 Nov 2025 01:43:42 +0530 Subject: [PATCH 60/91] Fix code generation abort --- .../mi-core/src/rpc-types/ai-panel/types.ts | 5 +- .../rpc-managers/ai-panel/event-handler.ts | 4 ++ .../src/rpc-managers/ai-panel/rpc-manager.ts | 7 +- .../views/AIPanel/component/AIChatFooter.tsx | 69 +++++++++++++++---- .../AIPanel/component/MICopilotContext.tsx | 12 +++- 5 files changed, 76 insertions(+), 21 deletions(-) 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 f2929603f38..162897aace0 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; 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..b304187490c 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 @@ -55,6 +55,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", 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 e9612a9aa16..830852541a1 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 @@ -230,6 +230,8 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { if (this.currentController) { console.log('Aborting code generation...'); this.currentController.abort(); + // Send explicit abort acknowledgment to UI + this.eventHandler.handleAborted(); this.currentController = null; return { success: true }; } @@ -299,10 +301,11 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { this.eventHandler.handleEnd(assistantResponse); // Run code diagnostics on the generated response only for runtime versions > 4.4.0 + // Also check if the generation wasn't aborted before starting diagnostics const runtimeVersion = await getMIVersionFromPom(this.projectUri); const shouldRunDiagnostics = runtimeVersion ? compareVersions(runtimeVersion, RUNTIME_VERSION_440) > 0 : false; - - if (shouldRunDiagnostics) { + + if (shouldRunDiagnostics && !this.currentController?.signal.aborted) { await this.handleCodeDiagnostics(assistantResponse); } 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 0cb4d94c751..85d5b798344 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx @@ -63,6 +63,8 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) 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; @@ -87,21 +89,27 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) // Handle code generation streaming events from extension const handleCodeGenerationEvent = (event: any) => { + // Ignore all events if generation was aborted + if (abortedRef.current) { + console.log('Ignoring event - generation was aborted', event.type); + return; + } + switch (event.type) { case "code_generation_start": // Start of code generation - could show loading indicator console.log("Code generation started"); setAssistantResponse(""); break; - + case "content_block": // Handle streaming content blocks if (event.content) { 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]; @@ -110,7 +118,7 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) } return newMessages; }); - } + } break; case "code_generation_end": @@ -220,7 +228,13 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) // Code generation completed console.log("Code generation completed"); break; - + + case "aborted": + // Abort acknowledged by extension - all streaming has stopped + console.log("Abort acknowledged by extension"); + setBackendRequestTriggered(false); + break; + default: console.log("Unknown event type:", event.type); } @@ -250,6 +264,8 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) // 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 @@ -266,7 +282,10 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) 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 @@ -274,15 +293,23 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) return newMessages; }); - // Clear old suggestions immediately before generating new ones - setQuestions([]); - // Generate suggestions based on chat history - await generateSuggestions(copilotChat, rpcClient, new AbortController()).then((response) => { - if (response && response.length > 0) { - setQuestions(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; }); + // Restore the original user prompt to the input box + setCurrentUserprompt(lastUserPromptRef.current); + + // Don't generate suggestions after abort - user wants to stop all AI activity + // Just clear existing suggestions + setQuestions([]); + // Explicitly adjust the textarea height after suggestion generation if (textAreaRef.current) { setTimeout(() => { @@ -290,11 +317,19 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) 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); } @@ -312,6 +347,9 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) }; 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; @@ -335,6 +373,9 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) 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, diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx index 9c4e3770352..dd307acd752 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx @@ -268,12 +268,18 @@ export function MICopilotContextProvider({ children }: MICopilotProviderProps) { }, [chatClearEventTriggered]); // Update local storage whenever backend call finishes + // Add debounce to prevent saving during abort cleanup useMemo(() => { if (!isLoading && !backendRequestTriggered) { - localStorage.setItem(localStorageKeys.chatFile, JSON.stringify(copilotChat)); - localStorage.setItem(localStorageKeys.questionFile, questions[questions.length - 1] || ""); + // Debounce localStorage writes to allow abort cleanup to complete + const timeoutId = setTimeout(() => { + localStorage.setItem(localStorageKeys.chatFile, JSON.stringify(copilotChat)); + localStorage.setItem(localStorageKeys.questionFile, questions[questions.length - 1] || ""); + }, 300); // 300ms delay ensures abort cleanup completes (200ms abort flag reset + buffer) + + return () => clearTimeout(timeoutId); } - }, [isLoading, backendRequestTriggered]); + }, [isLoading, backendRequestTriggered, copilotChat, questions]); useMemo(() => { if (!isLoading) { From fb82d1ae810cbd3aec9f879729ef598a5f719597 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 12:26:55 +0530 Subject: [PATCH 61/91] Fix coderabbit PR comments --- .../mi-core/src/rpc-types/ai-panel/types.ts | 1 + .../mi/mi-core/src/state-machine-types.ts | 2 ++ .../DataMapper/Header/AIMapButton.tsx | 7 ++-- .../DataMapper/Header/DataMapperHeader.tsx | 9 ++++- .../src/components/Form/FormGenerator.tsx | 31 +++++++++++++---- .../mi/mi-extension/src/ai-panel/aiMachine.ts | 10 ++++++ .../src/ai-panel/copilot/auto-fill/fill.ts | 33 ++++++++++++++++++ .../src/ai-panel/copilot/connectors/prompt.ts | 2 +- .../context/synapse_expression_guide.ts | 4 +-- .../src/ai-panel/copilot/idp/fill_schema.ts | 2 +- .../rpc-managers/ai-panel/event-handler.ts | 4 +-- .../src/rpc-managers/ai-panel/rpc-manager.ts | 34 +++++++++++-------- .../src/rpc-managers/ai-panel/utils.ts | 11 ++++-- .../mi-data-mapper/rpc-manager.ts | 3 ++ .../views/AIPanel/component/AIChatFooter.tsx | 33 ++++++++---------- .../AIPanel/component/MICopilotContext.tsx | 10 ++++-- 16 files changed, 141 insertions(+), 55 deletions(-) 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 162897aace0..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 @@ -77,6 +77,7 @@ export interface CodeGenerationEvent { command?: string; xmlCodes?: XmlCodeEntry[]; correctedCodes?: CorrectedCodeItem[]; + willRunDiagnostics?: boolean; } // Diagnostics types diff --git a/workspaces/mi/mi-core/src/state-machine-types.ts b/workspaces/mi/mi-core/src/state-machine-types.ts index 8538c031771..4873e81c436 100644 --- a/workspaces/mi/mi-core/src/state-machine-types.ts +++ b/workspaces/mi/mi-core/src/state-machine-types.ts @@ -140,6 +140,7 @@ export enum AI_EVENT_TYPE { RETRY = "RETRY", USAGE_EXCEEDED = "USAGE_EXCEEDED", USAGE_RESET = "USAGE_RESET", + UPDATE_USAGE = "UPDATE_USAGE", } export type AIMachineEventMap = { @@ -160,6 +161,7 @@ export type AIMachineEventMap = { [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 = 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..bf7f0a883ba 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,7 +48,7 @@ const StyledButton = styled(Button) <{ isLoading: boolean }>` min-width: 80px; `; -const AIMapButton: React.FC = ({ onClick, isLoading }) => { +const AIMapButton: React.FC = ({ onClick, isLoading, disabled = false }) => { var [remaingTokenLessThanOne, setRemainingTokenLessThanOne] = useState(false); var [remainingTokenPercentage, setRemainingTokenPercentage] = useState(""); @@ -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 e0305d9a8de..2c2d1c50626 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,13 +54,18 @@ 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("hasValidSchemas", hasValidSchemas); + if (!mappingExist || choice && hasValidSchemas) { props.setIsLoading(true); props.setIsMapping(true); await rpcClient.getMiDataMapperRpcClient().getMappingFromAI(); @@ -98,6 +104,7 @@ export function DataMapperHeader(props: DataMapperHeaderProps) { { + const nextKey = + key === "config_key" ? "configKey" : + key === "is_expression" ? "isExpression" : + key; + return [nextKey, remapKeysReverse(val)]; + }) + ); + } + return value; +} + export function FormGenerator(props: FormGeneratorProps) { const { rpcClient } = useVisualizerContext(); const sidePanelContext = React.useContext(SidePanelContext); @@ -486,9 +505,9 @@ export function FormGenerator(props: FormGeneratorProps) { }; const values = getValues(); - // Convert helper pane data to JSON strings - // Convert payload format: insertText → insert_text - const convertedPayloadsStr = JSON.stringify(payloads).replaceAll("insertText", "insert_text"); + // Convert helper pane data to JSON strings with structural key remapping + // Convert payload format: insertText → insert_text (structurally, not via string replacement) + const convertedPayloadsStr = JSON.stringify(remapKeys(payloads)); const convertedVariablesStr = JSON.stringify(variables); const convertedParamsStr = JSON.stringify(params); const convertedPropertiesStr = JSON.stringify(properties); @@ -516,10 +535,10 @@ export function FormGenerator(props: FormGeneratorProps) { field_descriptions: fieldDescriptions, }); - // Convert response back: is_expression → isExpression + // Convert response back: is_expression → isExpression, config_key → configKey if (response.filled_values) { - const result = JSON.stringify(response.filled_values).replaceAll("is_expression", "isExpression"); - setGeneratedFormDetails(JSON.parse(result)); + const result = remapKeysReverse(response.filled_values); + setGeneratedFormDetails(result); } else { throw new Error("No filled values returned from auto-fill"); } diff --git a/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts b/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts index 7d8de8feaff..800340758b0 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/aiMachine.ts @@ -294,6 +294,11 @@ const aiMachine = createMachine({ 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, + }) } } }, @@ -323,6 +328,11 @@ const aiMachine = createMachine({ errorMessage: (_) => undefined, }) ] + }, + [AI_EVENT_TYPE.UPDATE_USAGE]: { + actions: assign({ + usage: (_ctx, event) => event.payload?.usage, + }) } } }, 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 index 5e4deede3a0..41dd355f0aa 100644 --- 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 @@ -82,6 +82,39 @@ function createDynamicZodSchema( 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) { 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 index 70374cdea5a..e0b31abfbf6 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/prompt.ts @@ -18,7 +18,7 @@ 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 ten) and inbound endpoints (up to three) only from the provided list. +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: 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 index 1ca7c99fa08..4e68382415c 100644 --- 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 @@ -148,8 +148,8 @@ Now every synapse mediation has 6 global variables ( payload, vars, headers, pro \${startsWith("text", "te")} \${endsWith("text", "xt")} \${trim(" text ")} -\${split("a,b,c", ",")} -\${charAt("text", 1)} +\${split("a,b,c", ",")} +\${charAt("text", 1)} \${indexOf("text", "e")} \${indexOf(payload.value, "text", 5)} 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 index 5338f1a9bea..7a810d1c6e5 100644 --- 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 @@ -87,7 +87,7 @@ function jsonSchemaToZod(schema: any): z.ZodTypeAny { return z.null(); default: // Fallback for unknown types - return z.any(); + return z.any().nullable(); } } 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 b304187490c..38a2b00362a 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 @@ -39,8 +39,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 { 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 830852541a1..4fc8b715514 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 @@ -297,15 +297,16 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { console.error("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 - // Also check if the generation wasn't aborted before starting diagnostics + // 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; + const willRunDiagnostics = shouldRunDiagnostics && !this.currentController?.signal.aborted; + + // Send final response with diagnostics flag + this.eventHandler.handleEnd(assistantResponse, willRunDiagnostics); - if (shouldRunDiagnostics && !this.currentController?.signal.aborted) { + // Run code diagnostics if needed + if (willRunDiagnostics) { await this.handleCodeDiagnostics(assistantResponse); } @@ -313,7 +314,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"); @@ -491,19 +493,23 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { 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 as string; + const backendUrl = process.env.MI_COPILOT_ANTHROPIC_PROXY_URL; + if (!backendUrl) { + console.warn('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); + const response = await fetchWithAuth(`${backendUrl}${USER_CHECK_BACKEND_URL}`); if (response.ok) { const usage = await response.json(); - // Update state machine context - const context = StateMachineAI.context(); + // Get current state before updating const currentState = StateMachineAI.state(); - // Update context with usage data FIRST before any state transitions - if (context && (currentState === 'Authenticated' || currentState === 'UsageExceeded')) { - Object.assign(context, { usage: usage }); + // 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 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 4f7d5f06785..98d444d0fb2 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,7 +21,7 @@ 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, AI_EVENT_TYPE } 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"; import { generateSynapse } from "../../ai-panel/copilot/generation/generations"; @@ -185,7 +185,10 @@ export async function fetchCodeGenerationsWithRetry( const defaultPayloads = await miDiagramRpcManager.getAllInputDefaultPayloads(); // Extract the user's question from chat history (last user message) - const lastUserMessage = [...chatHistory].reverse().find(entry => entry.role === 'user'); + // 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 @@ -209,7 +212,9 @@ export async function fetchCodeGenerationsWithRetry( const historyMessages = chatHistory .slice(-7, -1) // Take last 7 messages and exclude the last one (current question) = 6 messages .map(entry => ({ - role: entry.role === 'user' ? 'user' as const : 'assistant' as const, + role: entry.role === Role.CopilotUser || entry.role === 'user' + ? 'user' as const + : 'assistant' as const, content: entry.content })); 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 653a0e5101c..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 @@ -310,6 +310,9 @@ export class MiDataMapperRpcManager implements MIDataMapperAPI { } 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-visualizer/src/views/AIPanel/component/AIChatFooter.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx index 85d5b798344..913441f3af3 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx @@ -138,21 +138,20 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) } }); - // If diagnostics won't run (runtime < 4.4.0), generate suggestions now + // If diagnostics won't run, generate suggestions immediately // Otherwise, wait for code_diagnostic_end event - setTimeout(() => { - if (!isValidating) { - // Clear old suggestions immediately before generating new ones - setQuestions([]); - 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); - }); - } - }, 100); // Small delay to let isValidating update + 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": @@ -306,11 +305,7 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) // Restore the original user prompt to the input box setCurrentUserprompt(lastUserPromptRef.current); - // Don't generate suggestions after abort - user wants to stop all AI activity - // Just clear existing suggestions - setQuestions([]); - - // Explicitly adjust the textarea height after suggestion generation + // Explicitly adjust the textarea height if (textAreaRef.current) { setTimeout(() => { textAreaRef.current.style.height = "auto"; diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx index dd307acd752..2ddd4dbe41a 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx @@ -173,8 +173,12 @@ export function MICopilotContextProvider({ children }: MICopilotProviderProps) { const { timeToReset, remainingTokenPercentage } = updateTokenInfo(updatedMachineView); setRemainingTokenPercentage(remainingTokenPercentage); setTimeToReset(timeToReset); + }).catch((error) => { + console.error('Failed to update token information:', error); }); } + }).catch((error) => { + console.error('Failed to fetch usage information:', error); }); // Initial token info from current state @@ -269,7 +273,7 @@ export function MICopilotContextProvider({ children }: MICopilotProviderProps) { // Update local storage whenever backend call finishes // Add debounce to prevent saving during abort cleanup - useMemo(() => { + useEffect(() => { if (!isLoading && !backendRequestTriggered) { // Debounce localStorage writes to allow abort cleanup to complete const timeoutId = setTimeout(() => { @@ -281,11 +285,11 @@ export function MICopilotContextProvider({ children }: MICopilotProviderProps) { } }, [isLoading, backendRequestTriggered, copilotChat, questions]); - useMemo(() => { + useEffect(() => { if (!isLoading) { localStorage.setItem(localStorageKeys.fileHistory, JSON.stringify(FileHistory)); } - }, [FileHistory]); + }, [isLoading, FileHistory]); const currentContext: MICopilotContextType = { rpcClient, From 1f3269a8a836dfae673ace5489e8d1f98bffe1d4 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 13:54:49 +0530 Subject: [PATCH 62/91] Fix code rabbit PR commits --- .../src/components/DataMapper/Header/AIMapButton.tsx | 4 ++-- .../src/components/DataMapper/Header/DataMapperHeader.tsx | 4 ++-- .../mi/mi-diagram/src/components/Form/FormGenerator.tsx | 1 + .../src/ai-panel/copilot/context/synapse_expression_guide.ts | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) 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 bf7f0a883ba..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 @@ -49,7 +49,7 @@ const StyledButton = styled(Button) <{ isLoading: boolean }>` `; const AIMapButton: React.FC = ({ onClick, isLoading, disabled = false }) => { - var [remaingTokenLessThanOne, setRemainingTokenLessThanOne] = useState(false); + var [remainingTokenLessThanOne, setRemainingTokenLessThanOne] = useState(false); var [remainingTokenPercentage, setRemainingTokenPercentage] = useState(""); const { rpcClient } = useVisualizerContext(); @@ -83,7 +83,7 @@ const AIMapButton: React.FC = ({ onClick, isLoading, disabled }); }, []); - var tokenUsageText = remainingTokenPercentage === 'Unlimited' ? remainingTokenPercentage : (remaingTokenLessThanOne ? '<1%' : `${remainingTokenPercentage}%`); + var tokenUsageText = remainingTokenPercentage === 'Unlimited' ? remainingTokenPercentage : (remainingTokenLessThanOne ? '<1%' : `${remainingTokenPercentage}%`); return ( 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 2c2d1c50626..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 @@ -64,8 +64,8 @@ export function DataMapperHeader(props: DataMapperHeaderProps) { if (mappingExist) { choice = await rpcClient.getMiDataMapperRpcClient().confirmMappingAction(); } - console.log("hasValidSchemas", hasValidSchemas); - if (!mappingExist || choice && hasValidSchemas) { + console.log("valid schemas:", hasValidSchemas); + if ((!mappingExist || choice) && hasValidSchemas) { props.setIsLoading(true); props.setIsMapping(true); await rpcClient.getMiDataMapperRpcClient().getMappingFromAI(); diff --git a/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx index 8931c255fc6..48c132364b3 100644 --- a/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx +++ b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx @@ -186,6 +186,7 @@ function remapKeysReverse(value: any): any { const nextKey = key === "config_key" ? "configKey" : key === "is_expression" ? "isExpression" : + key === "insertText" ? "insert_text" : key; return [nextKey, remapKeysReverse(val)]; }) 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 index 4e68382415c..4caba98429f 100644 --- 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 @@ -213,12 +213,12 @@ Now every synapse mediation has 6 global variables ( payload, vars, headers, pro \${formatDateTime(now(), "yyyy-MM-dd")} \`\`\` -### ** Check exists** +### **Check exists** \`\`\`xml \${exists(payload.value)} \`\`\` -### ** Accessing Secrets** +### **Accessing Secrets** \`\`\`xml \${registry("gov:/config/service")} \${wso2-vault("mysqlpassword")} From 03f0c118575a0b5edb0203396cafa032aa720482 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 14:32:30 +0530 Subject: [PATCH 63/91] Clear console logs from ai panel RPC manager --- .../src/components/Form/FormGenerator.tsx | 2 +- .../src/rpc-managers/ai-panel/rpc-manager.ts | 81 ++++++++++--------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx index 48c132364b3..41cd5040f2e 100644 --- a/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx +++ b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx @@ -186,7 +186,7 @@ function remapKeysReverse(value: any): any { const nextKey = key === "config_key" ? "configKey" : key === "is_expression" ? "isExpression" : - key === "insertText" ? "insert_text" : + key === "insert_text" ? "insertText" : key; return [nextKey, remapKeysReverse(val)]; }) 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 4fc8b715514..f70668aba00 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 @@ -49,6 +49,7 @@ 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; @@ -81,12 +82,12 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { if (files.length > 0) { const firstFile = require('path').join(folderPath, files[0]); const content = fs.readFileSync(firstFile, 'utf-8'); - console.log(`[generateSuggestions] Using entry point: ${folder}/${files[0]}`); + logDebug(`[generateSuggestions] Using entry point: ${folder}/${files[0]}`); return [content]; } } - console.log('[generateSuggestions] No entry points found, using empty context'); + logDebug('[generateSuggestions] No entry points found, using empty context'); return []; } @@ -101,10 +102,10 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { if (currentFile && fs.existsSync(currentFile) && currentFile.toLowerCase().endsWith('.xml')) { try { const content = fs.readFileSync(currentFile, 'utf-8'); - console.log(`[generateSuggestions] Using currently editing file: ${currentFile}`); + logDebug(`[generateSuggestions] Using currently editing file: ${currentFile}`); return content; } catch (error) { - console.warn(`[generateSuggestions] Could not read current file: ${currentFile}`, error); + logWarn(`[generateSuggestions] Could not read current file: ${currentFile}`); } } return null; @@ -119,7 +120,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { // Decision tree for context selection: if (chatHistory.length === 0) { // Case 1: Empty chat - Use currently editing file OR entry point - console.log('[generateSuggestions] Empty chat history'); + logDebug('[generateSuggestions] Empty chat history'); const currentFile = await this.getCurrentlyEditingFile(); if (currentFile) { context = [currentFile]; @@ -128,11 +129,11 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { } } else if (lastMessage?.role === 'assistant') { // Case 2: Last message from AI (user hasn't interrupted) - Use chat history only - console.log('[generateSuggestions] Following AI response, using 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 - console.log('[generateSuggestions] User interrupted or new topic'); + logDebug('[generateSuggestions] User interrupted or new topic'); const currentFile = await this.getCurrentlyEditingFile(); if (currentFile) { context = [currentFile]; @@ -141,7 +142,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { } } - console.log(`[generateSuggestions] Context size: ${context.length} files, Chat history: ${chatHistory.length} messages`); + logDebug(`[generateSuggestions] Context size: ${context.length} files, Chat history: ${chatHistory.length} messages`); // Use the new LLM-based suggestion generation const suggestionText = await generateSuggestionsFromLLM( @@ -155,7 +156,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { 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'}`); } } @@ -178,7 +179,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { 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'}`); } } @@ -203,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; } } @@ -216,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'}`); } @@ -228,17 +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 }; } } @@ -280,7 +281,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { 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; } @@ -294,7 +295,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { assistantResponse += textChunk; } } catch (error) { - console.error("Error reading code generation stream:", error); + logError("Error reading code generation stream", error); } // Determine if diagnostics will run before sending the end event @@ -323,10 +324,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; } @@ -434,7 +435,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(); } @@ -495,7 +496,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { const backendUrl = process.env.MI_COPILOT_ANTHROPIC_PROXY_URL; if (!backendUrl) { - console.warn('MI_COPILOT_ANTHROPIC_PROXY_URL is not configured; skipping usage fetch.'); + logWarn('MI_COPILOT_ANTHROPIC_PROXY_URL is not configured; skipping usage fetch.'); return undefined; } @@ -514,20 +515,20 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { // Check if quota is exceeded and transition to UsageExceeded state if (usage.remaining_tokens <= 0 && currentState === 'Authenticated') { - console.log('Quota exceeded. Transitioning to UsageExceeded state.'); + logInfo('Quota exceeded. Transitioning to UsageExceeded state.'); StateMachineAI.sendEvent(AI_EVENT_TYPE.USAGE_EXCEEDED); } // Check if we're in UsageExceeded state and if usage has reset if (currentState === 'UsageExceeded' && usage.remaining_tokens > 0) { - console.log('Usage has reset. Transitioning back to Authenticated state.'); + logInfo('Usage has reset. Transitioning back to Authenticated state.'); StateMachineAI.sendEvent(AI_EVENT_TYPE.USAGE_RESET); } return usage; } } catch (error) { - console.error('Failed to fetch usage:', error); + logError('Failed to fetch usage', error); } return undefined; @@ -550,7 +551,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { return result; } catch (error) { - console.error('Error generating unit test:', error); + logError('Error generating unit test', error); throw new Error(`Failed to generate unit test: ${error instanceof Error ? error.message : 'Unknown error'}`); } } @@ -576,7 +577,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { return result; } catch (error) { - console.error('Error generating unit test case:', error); + logError('Error generating unit test case', error); throw new Error(`Failed to generate unit test case: ${error instanceof Error ? error.message : 'Unknown error'}`); } } @@ -606,18 +607,18 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { */ async fillIdpSchema(request: FillIdpSchemaRequest): Promise { try { - console.log('[fillIdpSchema] Starting schema filling'); - console.log('[fillIdpSchema] Images count:', request.images?.length || 0); + logDebug('[fillIdpSchema] Starting schema filling'); + logDebug(`[fillIdpSchema] Images count: ${request.images?.length || 0}`); const result = await fillIdpSchema({ jsonSchema: request.jsonSchema, images: request.images }); - console.log('[fillIdpSchema] Schema filling completed successfully'); + logDebug('[fillIdpSchema] Schema filling completed successfully'); return result; } catch (error) { - console.error('[fillIdpSchema] Error filling schema:', error); + logError('[fillIdpSchema] Error filling schema', error); throw new Error(`Failed to fill IDP schema: ${error instanceof Error ? error.message : 'Unknown error'}`); } } @@ -627,9 +628,9 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { */ async dmcToTs(request: DmcToTsRequest): Promise { try { - console.log('[dmcToTs] Starting DMC to TypeScript conversion'); - console.log('[dmcToTs] DMC content length:', request.dmcContent?.length || 0); - console.log('[dmcToTs] TS file length:', request.tsFile?.length || 0); + 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'); @@ -638,10 +639,10 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { tsFile: request.tsFile }); - console.log('[dmcToTs] Conversion completed successfully'); + logDebug('[dmcToTs] Conversion completed successfully'); return result; } catch (error) { - console.error('[dmcToTs] Error converting DMC to TS:', 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'}`); } } @@ -651,9 +652,9 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { */ async autoFillForm(request: AutoFillFormRequest): Promise { try { - console.log('[autoFillForm] Starting form auto-fill'); - console.log('[autoFillForm] Form fields count:', Object.keys(request.current_values || {}).length); - console.log('[autoFillForm] Has user question:', !!request.question); + 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'); @@ -671,10 +672,10 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { question: request.question }); - console.log('[autoFillForm] Form auto-fill completed successfully'); + logDebug('[autoFillForm] Form auto-fill completed successfully'); return result; } catch (error) { - console.error('[autoFillForm] Error auto-filling form:', error); + logError('[autoFillForm] Error auto-filling form', error); throw new Error(`Failed to auto-fill form: ${error instanceof Error ? error.message : 'Unknown error'}`); } } From 019c9cf7630112ddb892b828e2a0675304abc7bf Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 15:09:49 +0530 Subject: [PATCH 64/91] Fix image, pdf and file support --- .../ai-panel/copilot/connectors/connectors.ts | 31 +++-- .../copilot/generation/generations.ts | 44 +++--- .../src/ai-panel/copilot/message-utils.ts | 128 ++++++++++++++++++ .../src/rpc-managers/ai-panel/utils.ts | 20 +-- 4 files changed, 184 insertions(+), 39 deletions(-) create mode 100644 workspaces/mi/mi-extension/src/ai-panel/copilot/message-utils.ts 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 index f44d56c99eb..57d14fe79d9 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts @@ -19,12 +19,14 @@ 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 = { @@ -100,10 +102,10 @@ function getInboundEndpointDefinitions(inboundNames: string[]): Record 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`); + userMessageContent = buildMessageContent(prompt, params.files, params.images); + } else { + userMessageContent = prompt; + } + // Build messages array with cache control on system message - const messages: Array<{ - role: "system" | "user"; - content: string; - providerOptions?: any; - }> = [ + const messages: any[] = [ { role: "system" as const, content: SYSTEM_TEMPLATE, @@ -155,7 +166,7 @@ export async function getConnectors( }, { role: "user" as const, - content: prompt, + content: userMessageContent, } ]; 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 index 4abca4596dd..d052665aa96 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts @@ -19,6 +19,7 @@ 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"; @@ -32,6 +33,7 @@ import { AI_MODULE_GUIDE } from "../context/ai_module"; import { getMIVersionFromPom, compareVersions } from "../../../util/onboardingUtils"; import { RUNTIME_VERSION_440 } from "../../../constants"; import { logInfo } from "../logger"; +import { buildMessageContent } from "../message-utils"; // Register Handlebars helpers Handlebars.registerHelper("upper", (str: string) => { @@ -85,10 +87,10 @@ export interface GenerateSynapseParams { connectors?: Record; /** Available inbound endpoints with their JSON signatures (optional) */ inbound_endpoints?: Record; - /** Additional files attached by the user (optional) */ - files?: string[]; - /** Whether images are attached (optional) */ - images?: boolean; + /** 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) */ @@ -125,19 +127,13 @@ export async function generateSynapse( payloads: params.payloads, connectors: params.connectors, inbound_endpoints: params.inbound_endpoints, - files: params.files, - images: params.images, thinking_enabled: params.thinking_enabled || false, }); const cacheOptions = await getProviderCacheControl(); // Build messages array with chat history - const messages: Array<{ - role: "system" | "user" | "assistant"; - content: string; - providerOptions?: any; - }> = [ + const messages: any[] = [ { role: "system" as const, content: systemPrompt, @@ -158,11 +154,27 @@ export async function generateSynapse( } } - // Add current user question - messages.push({ - role: "user" as const, - content: userPrompt - }); + // 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 + 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); 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..1e32e396625 --- /dev/null +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/message-utils.ts @@ -0,0 +1,128 @@ +/** + * 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"; + +/** + * 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}} +`; + +/** + * Filters files into text files and PDF files based on mimetype + */ +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 { + textFiles.push(file); + } + } + + 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 + * + * @param prompt - The main user prompt text + * @param files - Array of file objects (text files and PDFs) + * @param images - Array of image objects with base64 encoded data + * @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 + 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 + 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 + 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 (data:image/png;base64,...) + }); + } + } + + // 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); +} 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 98d444d0fb2..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 @@ -183,28 +183,22 @@ export async function fetchCodeGenerationsWithRetry( const context = await getWorkspaceContext(projectUri, selective); const miDiagramRpcManager = new MiDiagramRpcManager(projectUri); const defaultPayloads = await miDiagramRpcManager.getAllInputDefaultPayloads(); - + // 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 + + // Get currently editing file content if available (first file only) const currentFile = files.length > 0 ? files[0]?.content : undefined; - - // Prepare file contents from attached files - const fileContents = files.map((file: any) => - `File: ${file.name}\n${file.content}` - ); // Get relevant connectors and inbound endpoints for the user's query - console.log("Fetching relevant connectors for query:", userQuestion); const { connectors: selectedConnectors, inbound_endpoints: selectedInboundEndpoints } = await getConnectors({ question: userQuestion, - files: fileContents.length > 0 ? fileContents : undefined, - images: images.length > 0 ? true : undefined, + files: files.length > 0 ? files : undefined, + images: images.length > 0 ? images : undefined, }); // Convert chat history to the format expected by generateSynapse @@ -228,8 +222,8 @@ export async function fetchCodeGenerationsWithRetry( payloads: defaultPayloads ? JSON.stringify(defaultPayloads, null, 2) : undefined, connectors: selectedConnectors, inbound_endpoints: selectedInboundEndpoints, - files: fileContents.length > 0 ? fileContents : undefined, - images: images.length > 0 ? true : undefined, + 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 From 054f3ccfe840c58c2ff7d57f469536d253c6f39b Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 15:49:39 +0530 Subject: [PATCH 65/91] Improve file upload --- .../src/components/Form/FormGenerator.tsx | 26 ++-- .../ai-panel/copilot/connectors/connectors.ts | 1 + .../copilot/generation/generations.ts | 1 + .../src/ai-panel/copilot/message-utils.ts | 147 +++++++++++++++++- .../src/rpc-managers/ai-panel/rpc-manager.ts | 16 +- .../src/views/AIPanel/constants.ts | 43 ++++- 6 files changed, 213 insertions(+), 21 deletions(-) diff --git a/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx index 41cd5040f2e..18dd84e6268 100644 --- a/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx +++ b/workspaces/mi/mi-diagram/src/components/Form/FormGenerator.tsx @@ -508,12 +508,13 @@ export function FormGenerator(props: FormGeneratorProps) { // Convert helper pane data to JSON strings with structural key remapping // Convert payload format: insertText → insert_text (structurally, not via string replacement) - const convertedPayloadsStr = JSON.stringify(remapKeys(payloads)); - const convertedVariablesStr = JSON.stringify(variables); - const convertedParamsStr = JSON.stringify(params); - const convertedPropertiesStr = JSON.stringify(properties); - const convertedHeadersStr = JSON.stringify(headers); - const convertedConfigsStr = JSON.stringify(configs); + // 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); @@ -522,13 +523,14 @@ export function FormGenerator(props: FormGeneratorProps) { 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], - variables: [convertedVariablesStr], - params: [convertedParamsStr], - properties: [convertedPropertiesStr], - headers: [convertedHeadersStr], - configs: [convertedConfigsStr], + 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, 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 index 57d14fe79d9..153c6d34dec 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connectors/connectors.ts @@ -152,6 +152,7 @@ export async function getConnectors( 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; 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 index d052665aa96..7f6e73e1e61 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts @@ -162,6 +162,7 @@ export async function generateSynapse( 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({ 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 index 1e32e396625..627e3d8b28b 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/message-utils.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/message-utils.ts @@ -18,6 +18,7 @@ import * as Handlebars from "handlebars"; import { FileObject, ImageObject } from "@wso2/mi-core"; +import { logWarn } from "./logger"; /** * Template for text files content @@ -35,8 +36,100 @@ File: {{this.name}} {{/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[] = []; @@ -45,9 +138,10 @@ export function filterFiles(files: FileObject[]): { textFiles: FileObject[]; pdf for (const file of files) { if (file.mimetype === "application/pdf") { pdfFiles.push(file); - } else { + } 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 }; @@ -57,9 +151,11 @@ export function filterFiles(files: FileObject[]): { textFiles: FileObject[]; pdf * 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) - * @param images - Array of image objects with base64 encoded data + * @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( @@ -69,7 +165,7 @@ export function buildMessageContent( ): any[] { const content: any[] = []; - // Add files if provided + // Add files if provided (assumes pre-validation) if (files && files.length > 0) { const { textFiles, pdfFiles } = filterFiles(files); @@ -77,7 +173,7 @@ export function buildMessageContent( for (const pdfFile of pdfFiles) { content.push({ type: "file", - data: pdfFile.content, // Base64 encoded content + data: pdfFile.content, // Base64 encoded content (pre-validated) mediaType: "application/pdf" }); } @@ -94,7 +190,7 @@ export function buildMessageContent( } } - // Add images if provided + // Add images if provided (assumes pre-validation) if (images && images.length > 0) { content.push({ type: "text", @@ -106,7 +202,7 @@ export function buildMessageContent( // The AI SDK will convert this to the provider's format internally content.push({ type: "image", - image: image.imageBase64 // Use the full data URI (data:image/png;base64,...) + image: image.imageBase64 // Use the full data URI (pre-validated) }); } } @@ -126,3 +222,40 @@ export function buildMessageContent( 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/rpc-managers/ai-panel/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts index f70668aba00..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 @@ -248,8 +248,22 @@ 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 diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts b/workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts index f24fc69d557..03a20a1b37d 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts @@ -34,6 +34,47 @@ export const USER_INPUT_PLACEHOLDER_MESSAGE = "Ask MI Copilot"; export const PROJECT_RUNTIME_VERSION_THRESHOLD = "4.4.0"; export const VALID_FILE_TYPES = { - files: ["text/plain", "application/json", "application/x-yaml", "application/xml", "application/pdf", "text/xml"], + files: [ + // 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", + + // RTF + "text/rtf", + "application/rtf", + + // PDF (binary but supported) + "application/pdf" + ], images: ["image/jpeg", "image/png", "image/gif", "image/svg+xml"], }; From 0f8db56ff5fe59209d79a8f463c6440d37126dc9 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 18:03:31 +0530 Subject: [PATCH 66/91] Improve error logging --- .../mi/mi-extension/src/ai-panel/auth.ts | 2 +- .../src/ai-panel/copilot/connection.ts | 4 +-- .../copilot/generation/generations.ts | 6 +++- .../rpc-managers/ai-panel/event-handler.ts | 9 ++--- .../views/AIPanel/component/AIChatFooter.tsx | 15 ++------ .../views/AIPanel/component/CodeSegment.tsx | 2 -- .../AIPanel/component/WelcomeMessage.tsx | 34 +++++++++++++++++-- .../mi-visualizer/src/views/AIPanel/utils.ts | 3 +- 8 files changed, 49 insertions(+), 26 deletions(-) diff --git a/workspaces/mi/mi-extension/src/ai-panel/auth.ts b/workspaces/mi/mi-extension/src/ai-panel/auth.ts index 08fb5834238..00134e86f1a 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/auth.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/auth.ts @@ -81,7 +81,7 @@ export const getAuthCredentials = async (): Promise try { return JSON.parse(credentialsJson) as AuthCredentials; } catch (error) { - console.error('Error parsing auth credentials:', error); + logError('Error parsing auth credentials', error); return undefined; } }; diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts index 084bb41ed09..a18b182ffb2 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts @@ -19,7 +19,7 @@ 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 } from "./logger"; +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"; @@ -66,7 +66,7 @@ export async function fetchWithAuth(input: string | URL | Request, options: Requ const body = await response.json(); errorDetail = body.detail || ""; } catch (e) { - console.error("Failed to parse 429 response body:", e); + logError("Failed to parse 429 response body", e); } // Transition to UsageExceeded state 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 index 7f6e73e1e61..1a3f589bd15 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts @@ -32,7 +32,7 @@ import { SYNAPSE_EXPRESSION_EXAMPLES } from "../context/synapse_expression_examp import { AI_MODULE_GUIDE } from "../context/ai_module"; import { getMIVersionFromPom, compareVersions } from "../../../util/onboardingUtils"; import { RUNTIME_VERSION_440 } from "../../../constants"; -import { logInfo } from "../logger"; +import { logInfo, logError } from "../logger"; import { buildMessageContent } from "../message-utils"; // Register Handlebars helpers @@ -197,6 +197,10 @@ export async function generateSynapse( onAbort: () => { logInfo('Code generation aborted by user'); }, + onError: (error) => { + logError('AI SDK error during code generation', error); + // Error will be caught by caller's try-catch and shown to user + }, }); // Use AI SDK's built-in method to convert to Response with streaming 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 38a2b00362a..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 { @@ -78,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-visualizer/src/views/AIPanel/component/AIChatFooter.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx index 913441f3af3..4e62e879790 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx @@ -91,14 +91,12 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) const handleCodeGenerationEvent = (event: any) => { // Ignore all events if generation was aborted if (abortedRef.current) { - console.log('Ignoring event - generation was aborted', event.type); 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; @@ -123,7 +121,6 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) case "code_generation_end": // Final content replacement - console.log("Code generation completed"); if (event.content) { setAssistantResponse(event.content); handleCodeGenerationComplete(event.content); @@ -155,12 +152,10 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) 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 @@ -212,7 +207,6 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) break; case "error": - console.error("Code generation error:", event.error); setMessages((prevMessages) => [...prevMessages, { id: generateId(), role: Role.MICopilot, @@ -222,20 +216,18 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) 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 - console.log("Abort acknowledged by extension"); setBackendRequestTriggered(false); break; default: - console.log("Unknown event type:", event.type); + break; } }; @@ -633,7 +625,6 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) opacity: (backendRequestTriggered || isValidating || isUsageExceeded) ? 0.5 : 1, cursor: (backendRequestTriggered || isValidating || isUsageExceeded) ? "not-allowed" : "pointer" }} - disabled={backendRequestTriggered || isValidating || isUsageExceeded} > diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/CodeSegment.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/CodeSegment.tsx index 89277345004..b9517be5ea8 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/CodeSegment.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/CodeSegment.tsx @@ -101,7 +101,6 @@ export const CodeSegment: React.FC = ({ segmentText, loading, }; const handleAddToWorkspace = async (e: React.MouseEvent) => { - console.log(name, " - Adding to workspace : ", name); e.stopPropagation(); await fetchFileInfo(); @@ -154,7 +153,6 @@ export const CodeSegment: React.FC = ({ segmentText, loading, }; const handleRevertToLastCheckpoint = async (e: React.MouseEvent) => { - console.log(name, " - Reverting to last checkpoint"); e.stopPropagation(); await fetchFileInfo(); diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx index 970a643bdc7..21181356994 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/WelcomeMessage.tsx @@ -21,6 +21,32 @@ import { Welcome } from '../styles'; import { Icon, Typography } from "@wso2/ui-toolkit"; import { WelcomeStyles } from "../styles"; +// CSS Toggle Icon Component +const ToggleIcon: React.FC = () => { + return ( +
+
+
+ ); +}; + export const WelcomeMessage: React.FC = () => { return ( @@ -34,14 +60,18 @@ export const WelcomeMessage: React.FC = () => {

WSO2 MI Copilot

- AI assistant at your service! + The AI Integration Engineer is at your service!
- Please review generated code before adding to your integration. + Please review the generated code before adding it to your integration.
to attach context
+
+ + to toggle thinking mode +
);}; diff --git a/workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts b/workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts index 82145c3631c..5082864bdea 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts @@ -380,8 +380,7 @@ export async function fetchCodeGenerationsWithRetry( thinking?: boolean ): Promise { // Use RPC call to extension for streaming code generation - console.log("Generating code with streaming: visualizer -> extension"); - try { + try { const response = await rpcClient.getMiAiPanelRpcClient().generateCode({ chatHistory: chatHistory, files: files, From dab9088578f24719434dcb700ebd389fe9a169a4 Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 21:02:04 +0530 Subject: [PATCH 67/91] Improve error handling in code generation --- .../copilot/generation/generations.ts | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) 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 index 1a3f589bd15..6368d83d00c 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/generation/generations.ts @@ -34,6 +34,7 @@ import { getMIVersionFromPom, compareVersions } from "../../../util/onboardingUt 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) => { @@ -181,7 +182,7 @@ export async function generateSynapse( // Configure provider options for thinking mode if enabled const anthropicOptions: AnthropicProviderOptions = params.thinking_enabled - ? { thinking: { type: 'enabled', budgetTokens: 1000 } } + ? { thinking: { type: 'enabled', budgetTokens: 1024 } } : {}; const result = streamText({ @@ -199,7 +200,31 @@ export async function generateSynapse( }, onError: (error) => { logError('AI SDK error during code generation', error); - // Error will be caught by caller's try-catch and shown to user + + // 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 + }); }, }); From 78dfe815b6389f0b7dca9aa30e4ec871e44ee6ec Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 21:09:18 +0530 Subject: [PATCH 68/91] Fix code rabbit PR commits --- .../mi/mi-extension/src/ai-panel/copilot/connection.ts | 6 +++++- .../src/views/AIPanel/component/AIChatFooter.tsx | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts index a18b182ffb2..87a883d43be 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts @@ -35,7 +35,11 @@ let cachedAuthMethod: LoginMethod | null = null; * Get the backend URL for MI Copilot */ const getAnthropicProxyUrl = (): string => { - return process.env.MI_COPILOT_ANTHROPIC_PROXY_URL + '/proxy/anthropic/v1' as 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`; }; /** 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 4e62e879790..450f6578335 100644 --- a/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx +++ b/workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx @@ -133,6 +133,8 @@ const AIChatFooter: React.FC = ({ isUsageExceeded = false }) setRemainingTokenPercentage(remainingTokenPercentage); }); } + }).catch((error) => { + console.error("Error fetching usage after code generation:", error); }); // If diagnostics won't run, generate suggestions immediately From da4ee8b7f0cbe1761197f5eb7b42ea82b97a7c9c Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 21:22:56 +0530 Subject: [PATCH 69/91] Handle undefined api key error --- workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts index 87a883d43be..1dba6ec04ae 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/connection.ts @@ -142,6 +142,9 @@ export const getAnthropicClient = async (model: AnthropicModel): Promise => }); } 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, From 3e37f5dc02f92f9c3427477b9c82a06db69b5d7c Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Thu, 6 Nov 2025 22:14:21 +0530 Subject: [PATCH 70/91] Fix an issue in diagnostics prompt --- workspaces/mi/mi-extension/package.json | 5 +++-- .../mi-extension/src/ai-panel/copilot/diagnostics/prompt.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/workspaces/mi/mi-extension/package.json b/workspaces/mi/mi-extension/package.json index b69844b864e..b75e950fbd1 100644 --- a/workspaces/mi/mi-extension/package.json +++ b/workspaces/mi/mi-extension/package.json @@ -967,7 +967,7 @@ "postbuild": "pnpm run copyVSIX" }, "devDependencies": { - "@playwright/test": "~1.52.0", + "@playwright/test": "~1.55.1", "@types/mocha": "^10.0.1", "@types/node": "22.15.21", "@types/vscode": "^1.100.0", @@ -983,7 +983,7 @@ "eslint": "^9.27.0", "glob": "^11.0.2", "mocha": "^11.4.0", - "playwright-core": "~1.52.0", + "playwright-core": "~1.55.1", "rimraf": "~6.0.1", "ts-loader": "^9.5.2", "ts-morph": "^26.0.0", @@ -1025,6 +1025,7 @@ "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", "node-fetch": "~3.3.2", 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 index 529de19a9e2..7597b3f4306 100644 --- a/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/prompt.ts +++ b/workspaces/mi/mi-extension/src/ai-panel/copilot/diagnostics/prompt.ts @@ -51,7 +51,7 @@ Analyze the diagnostics information, identify the root causes, and correct the S Analyze the issues carefully and provide the complete fixed configuration for ALL files. -Your response should be a BugFixResponse object with a label field containing a list of SynapseConfiguration objects, one for each file, with each object containing: +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) From 938713c17987b646a2332a4e00a0a2b20c339218 Mon Sep 17 00:00:00 2001 From: tharindulak Date: Thu, 6 Nov 2025 15:16:45 +0530 Subject: [PATCH 71/91] Increase wait times for dependency loading and pom.xml updates --- .../components/Overview.ts | 3 ++- .../projectSettingPage.spec.ts | 24 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) 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/overviewPageTests/projectSettingPage.spec.ts b/workspaces/mi/mi-extension/src/test/e2e-playwright-tests/overviewPageTests/projectSettingPage.spec.ts index 1fd73ba06ee..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,9 +44,9 @@ 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'); @@ -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(); From 0016864305f283b8cc143aac0d69fc1a4c2c280d Mon Sep 17 00:00:00 2001 From: Isuru Wijesiri Date: Fri, 7 Nov 2025 12:42:44 +0530 Subject: [PATCH 72/91] Update pnpm-lock --- common/config/rush/pnpm-lock.yaml | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 4b5dff04ef9..bc628496b39 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -3646,6 +3646,9 @@ importers: ../../workspaces/mi/mi-extension: dependencies: + '@ai-sdk/anthropic': + specifier: ^2.0.35 + version: 2.0.41(zod@3.25.76) '@apidevtools/json-schema-ref-parser': specifier: 12.0.2 version: 12.0.2 @@ -3661,6 +3664,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 +3706,9 @@ importers: adm-zip: specifier: ~0.5.16 version: 0.5.16 + ai: + specifier: ^5.0.76 + version: 5.0.87(zod@3.25.76) axios: specifier: ~1.12.0 version: 1.12.2 @@ -3721,6 +3730,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 +3742,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 @@ -3793,6 +3808,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 @@ -22915,12 +22933,25 @@ snapshots: aws4fetch: 1.0.20 zod: 4.1.11 + '@ai-sdk/anthropic@2.0.41(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.41(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@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.6(zod@4.1.11)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -22928,6 +22959,13 @@ snapshots: '@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 @@ -37380,6 +37418,14 @@ snapshots: clean-stack: 4.2.0 indent-string: 5.0.0 + ai@5.0.87(zod@3.25.76): + dependencies: + '@ai-sdk/gateway': 2.0.6(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.87(zod@4.1.11): dependencies: '@ai-sdk/gateway': 2.0.6(zod@4.1.11) From f4342fcfff0ed7256925a6fe02a052696a0ccd1b Mon Sep 17 00:00:00 2001 From: Senith Uthsara Date: Fri, 7 Nov 2025 13:25:20 +0530 Subject: [PATCH 73/91] fix create connection new variable pop form not saving --- .../BI/Connection/AddConnectionWizard/index.tsx | 17 +++++++++++++++-- .../Connection/ConnectionConfigView/index.tsx | 6 ++++-- 2 files changed, 19 insertions(+), 4 deletions(-) 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..f51a7f5afa8 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,7 +234,7 @@ 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); @@ -258,15 +260,26 @@ 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) { + if (options?.postUpdateCallBack) { + options.postUpdateCallBack(); + } + return; + }; if (response.artifacts.length > 0) { // clear memory selectedNodeRef.current = undefined; 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} /> )} From f0e908b0e450850983c5687535c4327a5dcef2a6 Mon Sep 17 00:00:00 2001 From: madushajg Date: Fri, 7 Nov 2025 16:27:28 +0530 Subject: [PATCH 74/91] Enhance Ballerina project handling and debugging features - Added asynchronous checks for Ballerina package validity across multiple files. - Improved project root resolution logic in the debugger and test explorer. - Introduced error handling for OpenAPI definition generation. - Refactored utility functions for better clarity and performance. - Updated state machine interactions to open views upon project artifact builds. --- .../src/features/debugger/config-provider.ts | 82 +++++++++++++------ .../src/features/test-explorer/discover.ts | 12 ++- .../src/features/tryit/activator.ts | 27 +++--- .../src/rpc-managers/common/utils.ts | 5 +- .../ballerina-extension/src/stateMachine.ts | 5 +- workspaces/bi/bi-extension/src/utils.ts | 79 +++++++++++------- 6 files changed, 132 insertions(+), 78 deletions(-) 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 2d8f41b6e94..8bda7140e66 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'; @@ -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' @@ -765,36 +764,72 @@ 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().projectPath; - if (currentProjectRoot) { - return currentProjectRoot; +/** + * 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; } +} - // 2. Try to get any open Ballerina files in the editor and determine the project root from there - let file: string | 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 { - file = getCurrentBallerinaFile(); - } catch (error) { - // ignore + const project = await getCurrentBallerinaProject(filePath); + + if (project.kind === PROJECT_TYPE.SINGLE_FILE) { + return filePath; + } + + return project.path; + } catch { + return undefined; } +} + +/** + * 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 + */ +export async function getCurrentProjectRoot(): Promise { + const currentFilePath = tryGetCurrentBallerinaFile(); + const contextProjectRoot = StateMachine.context()?.projectPath; - if (file) { - const currentProject = await getCurrentBallerinaProject(file); - if (currentProject.kind === PROJECT_TYPE.SINGLE_FILE) { - return file; - } else if (currentProject.path) { - return currentProject.path; + // 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; } } - // 3. Fallback to workspace root + // Fallback to workspace root if it's a valid Ballerina package const workspaceRoot = getWorkspaceRoot(); - if (!workspaceRoot ) { + if (!workspaceRoot) { throw new Error("Unable to determine the current workspace root."); } - if (isBallerinaProject(workspaceRoot)) { + + if (await checkIsBallerinaPackage(Uri.file(workspaceRoot))) { return workspaceRoot; } @@ -839,8 +874,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/test-explorer/discover.ts b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts index ed9751c9afc..006ba517e1a 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts @@ -22,15 +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 "../debugger"; let groups: string[] = []; export async function discoverTestFunctionsInProject(ballerinaExtInstance: BallerinaExtension, testController: TestController) { groups.push(testController.id); - const request: TestsDiscoveryRequest = { - projectPath: StateMachine.context().projectPath - }; + + const projectPath = await getCurrentProjectRoot(); + + if (!projectPath) { + return; + } + + const request: TestsDiscoveryRequest = { projectPath }; const response: TestsDiscoveryResponse = await ballerinaExtInstance.langClient?.getProjectTestFunctions(request); if (response) { createTests(response, testController); diff --git a/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts index f40991b4b87..a48dff64385 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts @@ -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().projectPath; - 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/common/utils.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts index 5181b6d59b9..872a3843427 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/common/utils.ts @@ -17,12 +17,9 @@ */ import * as os from 'os'; -import * as fs from 'fs'; -import path from "path"; import { NodePosition } from "@wso2/syntax-tree"; import { Position, Range, Uri, window, workspace, WorkspaceEdit } from "vscode"; -import { TextEdit, PackageTomlValues, WorkspaceTomlValues } from "@wso2/ballerina-core"; -import { parse } from "toml"; +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/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index 4a4abfb76b4..e7b2e4e0831 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -120,6 +120,7 @@ const stateMachine = createMachine( }), async (context, event) => { await buildProjectArtifactsStructure(event.data.projectPath, StateMachine.langClient(), true); + openView(EVENT_TYPE.OPEN_VIEW, { view: MACHINE_VIEW.Overview }); notifyCurrentWebview(); } ] @@ -880,7 +881,7 @@ async function handleSingleWorkspaceFolder(workspaceURI: Uri): Promise { } const workspaceUri = workspaceFolders[0].uri; - const isBallerinaWorkspace = checkIsBallerinaWorkspace(workspaceUri); + const isBallerinaWorkspace = await checkIsBallerinaWorkspace(workspaceUri); if (isBallerinaWorkspace) { const workspaceTomlValues = await getWorkspaceTomlValues(workspaceUri.fsPath); const packagePaths = workspaceTomlValues.workspace.packages; - const filteredPackagePaths = filterPackagePaths(packagePaths, workspaceUri.fsPath); + 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 @@ -74,7 +74,7 @@ export async function fetchProjectInfo(): Promise { if (path.isAbsolute(pkgPath)) { packagePath = path.resolve(pkgPath); } - const isBallerina = checkIsBallerinaPackage(Uri.file(packagePath)); + const isBallerina = await checkIsBallerinaPackage(Uri.file(packagePath)); if (isBallerina) { isBalCount++; if (checkIsBI(Uri.file(packagePath))) { @@ -92,7 +92,7 @@ export async function fetchProjectInfo(): Promise { return { isBI: checkIsBI(workspaceUri), - isBallerina: checkIsBallerinaPackage(workspaceUri), + isBallerina: await checkIsBallerinaPackage(workspaceUri), isBalWorkspace: false }; } @@ -121,7 +121,7 @@ export function checkIsBI(uri: Uri): boolean { * @param uri - The URI of the directory to check * @returns true if the directory is a valid Ballerina package, false otherwise */ -export function checkIsBallerinaPackage(uri: Uri): boolean { +export async function checkIsBallerinaPackage(uri: Uri): Promise { const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); // First check if the file exists @@ -130,10 +130,8 @@ export function checkIsBallerinaPackage(uri: Uri): boolean { } try { - // Read the file content and check for [package] section - const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); - const packageSectionRegex = /\[package\]/; - return packageSectionRegex.test(tomlContent); + 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}`); @@ -149,7 +147,7 @@ export function checkIsBallerinaPackage(uri: Uri): boolean { * @param uri - The URI of the directory to check * @returns true if the directory is a valid Ballerina workspace, false otherwise */ -export function checkIsBallerinaWorkspace(uri: Uri): boolean { +export async function checkIsBallerinaWorkspace(uri: Uri): Promise { const ballerinaTomlPath = path.join(uri.fsPath, 'Ballerina.toml'); // First check if the file exists @@ -158,10 +156,8 @@ export function checkIsBallerinaWorkspace(uri: Uri): boolean { } try { - // Read the file content and check for [workspace] section - const tomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); - const workspaceSectionRegex = /\[workspace\]/; - return workspaceSectionRegex.test(tomlContent); + 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}`); @@ -169,6 +165,26 @@ export function checkIsBallerinaWorkspace(uri: Uri): boolean { } } +/** + * 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. * @@ -176,7 +192,7 @@ export function checkIsBallerinaWorkspace(uri: Uri): boolean { * @returns A Promise that resolves to the parsed TOML values if successful, * or undefined if the file doesn't exist or parsing fails */ -export async function getWorkspaceTomlValues(workspacePath: string): Promise { +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'); @@ -202,19 +218,22 @@ export async function getWorkspaceTomlValues(workspacePath: string): Promise { - if (path.isAbsolute(pkgPath)) { - const resolvedPath = path.resolve(pkgPath); - const resolvedWorkspacePath = path.resolve(workspacePath); - if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(resolvedWorkspacePath)) { - return checkIsBallerinaPackage(Uri.file(resolvedPath)); +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) && resolvedPath.startsWith(resolvedWorkspacePath)) { + return await checkIsBallerinaPackage(Uri.file(resolvedPath)); + } } - } - const resolvedPath = path.resolve(workspacePath, pkgPath); - if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(workspacePath)) { - return checkIsBallerinaPackage(Uri.file(resolvedPath)); - } - return false; - }); + const resolvedPath = path.resolve(workspacePath, pkgPath); + if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(workspacePath)) { + return await checkIsBallerinaPackage(Uri.file(resolvedPath)); + } + return false; + }) + ); + return packagePaths.filter((_, index) => results[index]); } From 1e8d2fa3c10ad6ff947fe4dc48d380dbc5471303 Mon Sep 17 00:00:00 2001 From: tharindulak Date: Sat, 8 Nov 2025 10:03:14 +0530 Subject: [PATCH 75/91] Fix package vulverability --- common/config/rush/.pnpmfile.cjs | 6 + common/config/rush/pnpm-lock.yaml | 1958 +++++++++++++++-------------- 2 files changed, 1036 insertions(+), 928 deletions(-) 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 bc628496b39..2de699ac7a9 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) @@ -2540,7 +2540,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 +2686,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 +2698,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 +2776,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 +3068,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 +3125,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 +3159,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 +3174,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 +3302,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 +3567,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 +3612,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 +3627,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 @@ -3648,7 +3648,7 @@ importers: dependencies: '@ai-sdk/anthropic': specifier: ^2.0.35 - version: 2.0.41(zod@3.25.76) + version: 2.0.42(zod@3.25.76) '@apidevtools/json-schema-ref-parser': specifier: 12.0.2 version: 12.0.2 @@ -3708,7 +3708,7 @@ importers: version: 0.5.16 ai: specifier: ^5.0.76 - version: 5.0.87(zod@3.25.76) + version: 5.0.89(zod@3.25.76) axios: specifier: ~1.12.0 version: 1.12.2 @@ -3789,7 +3789,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 @@ -3853,7 +3853,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 @@ -3956,7 +3956,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) @@ -4134,7 +4134,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) @@ -4214,7 +4214,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 @@ -4320,10 +4320,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) @@ -4332,7 +4332,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) @@ -4407,7 +4407,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 @@ -4474,7 +4474,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) @@ -4496,20 +4496,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 @@ -4567,44 +4567,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': @@ -4615,8 +4615,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': @@ -4635,32 +4635,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': @@ -4682,8 +4682,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' @@ -4717,8 +4717,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': @@ -4737,8 +4737,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': @@ -5663,8 +5663,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==} @@ -6614,8 +6614,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 @@ -7204,8 +7204,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': '*' @@ -7334,6 +7334,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: @@ -7404,6 +7417,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: @@ -7656,6 +7678,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==} @@ -7809,113 +7844,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] @@ -8048,8 +8083,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': @@ -8208,8 +8243,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': @@ -9202,95 +9237,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==} @@ -9300,68 +9335,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' @@ -9397,8 +9432,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==} @@ -10387,8 +10422,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': @@ -10810,8 +10845,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 @@ -10893,8 +10928,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: @@ -11655,8 +11690,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: @@ -11699,8 +11734,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: @@ -12017,11 +12052,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==} @@ -13348,8 +13383,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==} @@ -14263,8 +14298,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: @@ -17357,8 +17392,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==} @@ -17475,8 +17510,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 @@ -17543,8 +17578,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==} @@ -18228,8 +18263,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: @@ -19481,6 +19516,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: @@ -19642,6 +19683,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'} @@ -19986,8 +20030,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 @@ -20121,8 +20165,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==} @@ -21013,8 +21057,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' @@ -21061,8 +21105,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==} @@ -21149,8 +21193,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 @@ -22923,9 +22967,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 @@ -22933,26 +22977,26 @@ snapshots: aws4fetch: 1.0.20 zod: 4.1.11 - '@ai-sdk/anthropic@2.0.41(zod@3.25.76)': + '@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.41(zod@4.1.11)': + '@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@3.25.76)': + '@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.6(zod@4.1.11)': + '@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) @@ -23047,31 +23091,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 @@ -23097,7 +23141,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 @@ -23109,21 +23153,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 @@ -23143,7 +23187,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 @@ -23152,7 +23196,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 @@ -23168,17 +23212,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 @@ -23189,15 +23233,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 @@ -23207,14 +23251,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 @@ -23224,20 +23268,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 @@ -23246,10 +23290,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 @@ -23275,12 +23319,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 @@ -23318,9 +23362,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 @@ -23341,9 +23385,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 @@ -23351,21 +23395,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 @@ -23385,7 +23429,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 @@ -23394,27 +23438,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 @@ -23451,9 +23495,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 @@ -23489,7 +23533,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 @@ -23497,14 +23541,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 @@ -23516,7 +23560,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 @@ -23526,11 +23570,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 @@ -23539,12 +23583,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 @@ -25535,7 +25579,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 @@ -26087,7 +26131,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 @@ -26270,7 +26314,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 @@ -26284,7 +26328,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 @@ -26933,7 +26977,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) @@ -27090,7 +27134,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: @@ -27099,14 +27143,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) @@ -27132,7 +27176,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 @@ -27145,18 +27189,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' @@ -27170,7 +27214,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) @@ -27387,7 +27431,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 @@ -27397,14 +27441,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 @@ -27414,11 +27458,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)': @@ -28158,9 +28202,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: @@ -28420,6 +28464,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 @@ -28644,6 +28706,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 @@ -29096,6 +29172,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 @@ -29204,9 +29289,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) @@ -29214,28 +29299,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: @@ -29265,78 +29350,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': {} @@ -29499,7 +29584,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 @@ -29757,9 +29842,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 @@ -29998,13 +30083,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) @@ -30112,7 +30197,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) @@ -30121,7 +30206,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) @@ -30299,29 +30384,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' @@ -30973,7 +31058,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) @@ -30983,7 +31068,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) @@ -30995,33 +31080,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: @@ -31035,7 +31120,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) @@ -31045,7 +31130,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) @@ -31081,7 +31166,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 @@ -31345,7 +31430,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) @@ -31355,7 +31440,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) @@ -31380,10 +31465,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 @@ -31434,7 +31519,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) @@ -31472,7 +31557,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) @@ -31491,13 +31576,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 @@ -31533,33 +31618,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: @@ -31575,7 +31660,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 @@ -31583,23 +31668,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: @@ -31629,7 +31714,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 @@ -31936,7 +32021,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 @@ -31960,7 +32045,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 @@ -31988,7 +32073,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 @@ -32025,7 +32110,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) @@ -32053,7 +32138,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 @@ -32061,7 +32146,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 @@ -32078,7 +32163,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: @@ -32090,7 +32175,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) @@ -32143,7 +32228,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: @@ -32485,20 +32570,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 @@ -32531,12 +32616,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' @@ -32617,20 +32702,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 @@ -32663,7 +32748,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: @@ -32942,16 +33027,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' @@ -32990,13 +33075,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: @@ -33291,29 +33376,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 @@ -33321,14 +33406,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 @@ -33342,14 +33427,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) @@ -33378,7 +33463,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: @@ -33597,14 +33682,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) @@ -33627,10 +33712,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: @@ -33676,7 +33761,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) @@ -33808,11 +33893,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 @@ -33823,7 +33908,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: @@ -33960,7 +34045,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 @@ -33970,7 +34055,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 @@ -33984,11 +34069,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 @@ -33998,7 +34083,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 @@ -34056,10 +34141,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 @@ -34133,10 +34218,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) @@ -34171,15 +34256,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 @@ -34210,11 +34295,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' @@ -34299,19 +34384,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 @@ -34338,7 +34423,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 @@ -34750,10 +34835,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 @@ -34777,10 +34862,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 @@ -35045,20 +35130,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 @@ -35066,213 +35151,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) @@ -35281,78 +35366,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 @@ -35361,11 +35446,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 @@ -35373,26 +35458,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 @@ -35404,51 +35489,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': {} @@ -35475,7 +35560,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: @@ -36172,11 +36257,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 @@ -36184,11 +36269,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 @@ -36859,7 +36944,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 @@ -37289,7 +37374,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) @@ -37418,17 +37503,17 @@ snapshots: clean-stack: 4.2.0 indent-string: 5.0.0 - ai@5.0.87(zod@3.25.76): + ai@5.0.89(zod@3.25.76): dependencies: - '@ai-sdk/gateway': 2.0.6(zod@3.25.76) + '@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.87(zod@4.1.11): + 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 @@ -37525,7 +37610,7 @@ snapshots: dependencies: type-fest: 0.21.3 - ansi-escapes@7.1.1: + ansi-escapes@7.2.0: dependencies: environment: 1.1.0 @@ -37816,7 +37901,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 @@ -37826,7 +37911,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 @@ -37835,7 +37920,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 @@ -37844,7 +37929,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 @@ -38066,7 +38151,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: @@ -38074,7 +38159,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: @@ -38084,14 +38169,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: @@ -38100,14 +38185,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: @@ -38652,13 +38737,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: @@ -38674,11 +38759,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 @@ -38693,7 +38778,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: @@ -38846,19 +38931,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) @@ -39098,20 +39183,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: @@ -39454,7 +39539,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: @@ -39706,7 +39791,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: @@ -39827,13 +39912,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: @@ -39919,7 +40004,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 @@ -39934,7 +40019,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: @@ -39951,7 +40036,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: @@ -39967,7 +40052,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 @@ -39978,7 +40063,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: @@ -40004,7 +40089,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: @@ -40645,7 +40730,7 @@ snapshots: dependencies: jake: 10.9.4 - electron-to-chromium@1.5.244: {} + electron-to-chromium@1.5.249: {} email-addresses@5.0.0: {} @@ -41282,7 +41367,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 @@ -41443,7 +41528,7 @@ snapshots: events-universal@1.0.1: dependencies: - bare-events: 2.8.1 + bare-events: 2.8.2 transitivePeerDependencies: - bare-abort-controller @@ -41685,7 +41770,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: @@ -41857,19 +41942,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: @@ -42042,7 +42127,7 @@ snapshots: flatten@1.0.3: {} - flow-parser@0.289.0: {} + flow-parser@0.290.0: {} flush-write-stream@1.1.1: dependencies: @@ -42088,7 +42173,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: @@ -42140,7 +42225,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 @@ -42156,7 +42241,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) @@ -42176,11 +42261,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 @@ -42195,7 +42280,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: @@ -42229,7 +42314,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: {} @@ -42354,7 +42439,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: @@ -42576,7 +42661,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: @@ -42633,7 +42718,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: @@ -43113,7 +43198,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: @@ -43145,9 +43230,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 @@ -43158,7 +43243,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: @@ -43171,9 +43256,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 @@ -43181,7 +43266,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: @@ -43191,7 +43276,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: @@ -44131,16 +44216,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 @@ -44204,7 +44289,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 @@ -44230,7 +44315,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 @@ -44992,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 @@ -45060,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 @@ -45087,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 @@ -45120,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 @@ -45210,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 @@ -45612,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 @@ -46621,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 @@ -46765,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 @@ -46855,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: {} @@ -47590,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 @@ -47878,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: @@ -47911,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 @@ -47919,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: @@ -47929,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: @@ -48591,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: @@ -49095,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 @@ -49131,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 @@ -49254,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 @@ -49477,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 @@ -49844,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 @@ -49862,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 @@ -49907,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 @@ -49932,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: @@ -50069,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: @@ -50081,7 +50183,7 @@ snapshots: sax@1.2.4: {} - sax@1.4.2: {} + sax@1.4.3: {} saxes@3.1.11: dependencies: @@ -50942,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: @@ -50958,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: @@ -50970,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: {} @@ -51111,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: @@ -51138,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: @@ -51146,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 @@ -51178,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: @@ -51202,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 @@ -51262,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 @@ -51288,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 @@ -51304,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) @@ -51381,7 +51483,7 @@ snapshots: - tsx - yaml - tailwindcss@4.1.16: {} + tailwindcss@4.1.17: {} tapable@0.2.9: {} @@ -51478,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 @@ -51487,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): @@ -51500,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: @@ -51532,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 @@ -51810,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 @@ -51859,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 @@ -51877,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 @@ -51897,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): @@ -52121,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 @@ -52278,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 @@ -52564,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) @@ -52855,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) @@ -52870,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 @@ -53120,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) @@ -53142,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): @@ -53157,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): @@ -53168,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 @@ -53178,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: @@ -53190,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 @@ -53199,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): @@ -53323,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 @@ -53331,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 @@ -53359,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 @@ -53408,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: @@ -53431,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: @@ -53473,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 @@ -53497,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: @@ -53505,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 @@ -53529,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: @@ -53539,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 @@ -53563,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: @@ -53597,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: @@ -53631,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: @@ -53665,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: @@ -53930,7 +54032,7 @@ snapshots: xml-js@1.6.11: dependencies: - sax: 1.4.2 + sax: 1.4.3 xml-name-validator@2.0.1: {} @@ -53940,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: {} From 87b077d2a7a575d5dce419c3606eb4857a885d98 Mon Sep 17 00:00:00 2001 From: Senith Uthsara Date: Mon, 10 Nov 2025 10:08:59 +0530 Subject: [PATCH 76/91] fix PR issues --- .../Connection/AddConnectionWizard/index.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) 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 f51a7f5afa8..4126a04bb0f 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 @@ -234,7 +234,7 @@ export function AddConnectionWizard(props: AddConnectionWizardProps) { setCurrentStep(WizardStep.GENERATE_CONNECTOR); }; - const handleOnFormSubmit = async (node?: FlowNode, _dataMapperMode?: DataMapperDisplayMode, options?: FormSubmitOptions) => { + const handleOnFormSubmit = async (node: FlowNode, _dataMapperMode?: DataMapperDisplayMode, options?: FormSubmitOptions) => { console.log(">>> on form submit", node); if (selectedNodeRef.current) { setSavingFormStatus(SavingFormStatus.SAVING); @@ -275,7 +275,9 @@ export function AddConnectionWizard(props: AddConnectionWizardProps) { .then((response) => { console.log(">>> Updated source code", response); if (!isConnector) { - if (options?.postUpdateCallBack) { + setSavingFormStatus(SavingFormStatus.SUCCESS); + selectedNodeRef.current = undefined; + if (options?.postUpdateCallBack) { options.postUpdateCallBack(); } return; @@ -308,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 ) ); From 69afcb1829c7ac45cd2b7b078be13e30631695cf Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 10 Nov 2025 14:35:05 +0530 Subject: [PATCH 77/91] Update webview content to conditionally display loading title based on BI extension availability --- .../ballerina-extension/src/views/visualizer/webview.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workspaces/ballerina/ballerina-extension/src/views/visualizer/webview.ts b/workspaces/ballerina/ballerina-extension/src/views/visualizer/webview.ts index 2fb44ae0489..a7ed431ac61 100644 --- a/workspaces/ballerina/ballerina-extension/src/views/visualizer/webview.ts +++ b/workspaces/ballerina/ballerina-extension/src/views/visualizer/webview.ts @@ -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 From 4b5793626d722064ba1cbb7aa6230a73d3e0679e Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 10 Nov 2025 14:35:21 +0530 Subject: [PATCH 78/91] Refactor project checking logic in state machine to support switching between projects --- .../ballerina-extension/src/stateMachine.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index 86f8626f9e0..17a9f44dda3 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -105,7 +105,7 @@ const stateMachine = createMachine( states: { switch_project: { invoke: { - src: checkForProjects, + src: (context, event) => checkForProjects(true), onDone: [ { target: "viewActive.viewReady", @@ -130,7 +130,7 @@ const stateMachine = createMachine( }, initialize: { invoke: { - src: checkForProjects, + src: (context, event) => checkForProjects(false), onDone: [ { target: "renderInitialView", @@ -797,7 +797,7 @@ function getLastHistory() { return historyStack?.[historyStack?.length - 1]; } -async function checkForProjects(): Promise { +async function checkForProjects(isSwitching: boolean = false): Promise { const workspaceFolders = workspace.workspaceFolders; if (!workspaceFolders) { @@ -808,7 +808,7 @@ async function checkForProjects(): Promise { return await handleMultipleWorkspaceFolders(workspaceFolders); } - return await handleSingleWorkspaceFolder(workspaceFolders[0].uri); + return await handleSingleWorkspaceFolder(workspaceFolders[0].uri, isSwitching); } async function handleMultipleWorkspaceFolders(workspaceFolders: readonly WorkspaceFolder[]): Promise { @@ -848,7 +848,7 @@ async function handleMultipleWorkspaceFolders(workspaceFolders: readonly Workspa return { isBI: false, projectPath: '' }; } -async function handleSingleWorkspaceFolder(workspaceURI: Uri): Promise { +async function handleSingleWorkspaceFolder(workspaceURI: Uri, isSwitching: boolean = false): Promise { const isBallerinaWorkspace = await checkIsBallerinaWorkspace(workspaceURI); if (isBallerinaWorkspace) { @@ -862,16 +862,20 @@ async function handleSingleWorkspaceFolder(workspaceURI: Uri): Promise 1) { + 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 (packages.length === 1) { + } else if (!shouldShowBIQuickPick || packages.length === 1) { targetPackage = packages[0]; } From 1e663903716c5620ea5b441416ad42b7c3cf3f52 Mon Sep 17 00:00:00 2001 From: sachiniSam Date: Mon, 10 Nov 2025 14:37:03 +0530 Subject: [PATCH 79/91] Ignore exact name check in saving the form --- .../src/RecordFromJson/RecordFromJson.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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."); From 4ab43fefca9b088781302cbfd8971332a6093eb8 Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 10 Nov 2025 15:04:48 +0530 Subject: [PATCH 80/91] Refactor project root retrieval logic to improve consistency and remove unused functions in debugger and related modules --- .../src/features/debugger/config-provider.ts | 80 +--------------- .../src/features/test-explorer/discover.ts | 2 +- .../src/features/tryit/activator.ts | 2 +- .../src/rpc-managers/ai-panel/rpc-manager.ts | 29 +++--- .../src/utils/project-utils.ts | 95 ++++++++++++++++++- 5 files changed, 112 insertions(+), 96 deletions(-) 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 8bda7140e66..ce7e26801af 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/debugger/config-provider.ts @@ -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'; @@ -764,78 +764,6 @@ async function stopRunFast(root: string): Promise { }); } -/** - * 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; - } -} - -/** - * 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 - */ -export 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`); -} - 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 @@ -862,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 }); } 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 006ba517e1a..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,7 +22,7 @@ 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 "../debugger"; +import { getCurrentProjectRoot } from "../../utils/project-utils"; let groups: string[] = []; diff --git a/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts index a48dff64385..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 = { 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 d190be90afb..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().projectPath; + 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'); @@ -208,7 +209,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } async getFromFile(req: GetFromFileRequest): Promise { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); @@ -224,7 +225,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } async deleteFromProject(req: DeleteFromProjectRequest): Promise { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); @@ -246,7 +247,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } async getFileExists(req: GetFromFileRequest): Promise { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const ballerinaProjectFile = path.join(projectPath, 'Ballerina.toml'); if (!fs.existsSync(ballerinaProjectFile)) { throw new Error("Not a Ballerina project."); @@ -309,7 +310,7 @@ export class AiPanelRpcManager implements AIPanelAPI { async getGeneratedTests(params: TestGenerationRequest): Promise { return new Promise(async (resolve, reject) => { try { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const generatedTests = await generateTest(projectPath, params, AIPanelAbortController.getInstance()); resolve(generatedTests); @@ -322,7 +323,7 @@ export class AiPanelRpcManager implements AIPanelAPI { async getTestDiagnostics(params: TestGenerationResponse): Promise { return new Promise(async (resolve, reject) => { try { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const diagnostics = await getDiagnostics(projectPath, params); resolve(diagnostics); } catch (error) { @@ -334,7 +335,7 @@ export class AiPanelRpcManager implements AIPanelAPI { async getServiceSourceForName(params: string): Promise { return new Promise(async (resolve, reject) => { try { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const { serviceDeclaration } = await getServiceDeclaration(projectPath, params); resolve(serviceDeclaration.source); } catch (error) { @@ -346,7 +347,7 @@ export class AiPanelRpcManager implements AIPanelAPI { async getResourceSourceForMethodAndPath(params: string): Promise { return new Promise(async (resolve, reject) => { try { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const { resourceAccessorDef } = await getResourceAccessorDef(projectPath, params); resolve(resourceAccessorDef.source); } catch (error) { @@ -358,7 +359,7 @@ export class AiPanelRpcManager implements AIPanelAPI { async getServiceNames(): Promise { return new Promise(async (resolve, reject) => { try { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const serviceDeclNames = await getServiceDeclarationNames(projectPath); resolve({ mentions: serviceDeclNames @@ -372,7 +373,7 @@ export class AiPanelRpcManager implements AIPanelAPI { async getResourceMethodAndPaths(): Promise { return new Promise(async (resolve, reject) => { try { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const resourceAccessorNames = await getResourceAccessorNames(projectPath); resolve({ mentions: resourceAccessorNames @@ -392,7 +393,7 @@ export class AiPanelRpcManager implements AIPanelAPI { } async applyDoOnFailBlocks(): Promise { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); if (!projectPath) { return null; @@ -654,7 +655,7 @@ export class AiPanelRpcManager implements AIPanelAPI { async addFilesToProject(params: AddFilesToProjectRequest): Promise { try { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); const ballerinaProjectFile = path.join(projectPath, "Ballerina.toml"); if (!fs.existsSync(ballerinaProjectFile)) { @@ -740,7 +741,7 @@ interface BalModification { async function setupProjectEnvironment(project: ProjectSource): Promise<{ langClient: ExtendedLangClient, tempDir: string } | null> { //TODO: Move this to LS - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); if (!projectPath) { return null; } @@ -814,7 +815,7 @@ enum CodeGenerationType { } async function getCurrentProjectSource(requestType: OperationType): Promise { - const projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); if (!projectPath) { return null; diff --git a/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts b/workspaces/ballerina/ballerina-extension/src/utils/project-utils.ts index af40ac2f531..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`; @@ -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 +}; From 1013b271a7a039802ea5fcfc20f111b62fdd0029 Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 10 Nov 2025 16:00:40 +0530 Subject: [PATCH 81/91] Update Ballerina workspace documentation link in state machine --- workspaces/ballerina/ballerina-extension/src/stateMachine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index 17a9f44dda3..90961870ef4 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -831,7 +831,7 @@ async function handleMultipleWorkspaceFolders(workspaceFolders: readonly Workspa 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/organize-ballerina-code/')); + commands.executeCommand('vscode.open', Uri.parse('https://ballerina.io/learn/workspaces')); } }); From 3d22f1ff26b0b45818001c7463ef7615481bb4fd Mon Sep 17 00:00:00 2001 From: Dan Niles Date: Mon, 10 Nov 2025 18:47:25 +0530 Subject: [PATCH 82/91] Update chat agent creation to generate agent at module level for U12 --- .../BI/AIChatAgent/AIChatAgentWizard.tsx | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) 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..820b778f62e 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx @@ -171,6 +171,55 @@ 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 + const response = await rpcClient.getLangClientRpcClient().getBallerinaVersion(); + const ballerinaVersion = response.version; + + // 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 || parts[0] !== '2201') { + return true; // Can't parse properly or unexpected format, execute for safety + } + const minorVersion = parseInt(parts[1], 10); + if (isNaN(minorVersion)) { + return true; // Can't parse minor version, execute for safety + } + 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; From ceefcd8722cabc56a2db477427e24cddbbaa8981 Mon Sep 17 00:00:00 2001 From: Dan Niles Date: Mon, 10 Nov 2025 19:13:37 +0530 Subject: [PATCH 83/91] Improve legacy version check for AIChatAgentWizard to handle version parsing more robustly --- .../views/BI/AIChatAgent/AIChatAgentWizard.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) 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 820b778f62e..0332d47c350 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx @@ -178,12 +178,20 @@ export function AIChatAgentWizard(props: AIChatAgentWizardProps) { // 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 || parts[0] !== '2201') { - return true; // Can't parse properly or unexpected format, execute for safety + 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(minorVersion)) { - return true; // Can't parse minor version, execute for safety + 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; })(); From 7c2f88716e4f2edf23be49217054fe4de60d7de1 Mon Sep 17 00:00:00 2001 From: Dan Niles Date: Mon, 10 Nov 2025 19:29:31 +0530 Subject: [PATCH 84/91] Add error handling for Ballerina version retrieval in AIChatAgentWizard --- .../src/views/BI/AIChatAgent/AIChatAgentWizard.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 0332d47c350..bcb47f9834d 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/AIChatAgent/AIChatAgentWizard.tsx @@ -172,8 +172,13 @@ export function AIChatAgentWizard(props: AIChatAgentWizardProps) { await rpcClient.getBIDiagramRpcClient().getSourceCode({ filePath: projectPath.current, flowNode: modelNodeTemplate }); // hack: Generate agent at module level for Ballerina versions under 2201.13.0 - const response = await rpcClient.getLangClientRpcClient().getBallerinaVersion(); - const ballerinaVersion = response.version; + 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 || (() => { From 8a361bef944a78dd4c32934a2b080967709de9eb Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 10 Nov 2025 18:45:07 +0530 Subject: [PATCH 85/91] Address review suggestions --- .../ballerina-extension/src/core/extension.ts | 10 +++------- .../src/rpc-managers/bi-diagram/rpc-manager.ts | 12 ++++++++++-- .../ballerina/ballerina-extension/src/utils/bi.ts | 12 ++++++++---- .../ballerina-extension/src/utils/config.ts | 10 ++++++++-- .../src/views/BI/ProjectForm/utils.ts | 2 +- .../project-explorer/project-explorer-provider.ts | 13 ++++++++++--- workspaces/bi/bi-extension/src/utils.ts | 7 +++++-- 7 files changed, 45 insertions(+), 21 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/core/extension.ts b/workspaces/ballerina/ballerina-extension/src/core/extension.ts index 1ef2d9d86e0..c27824327eb 100644 --- a/workspaces/ballerina/ballerina-extension/src/core/extension.ts +++ b/workspaces/ballerina/ballerina-extension/src/core/extension.ts @@ -2347,14 +2347,10 @@ export class BallerinaExtension { if (textEditor?.document) { const fileUri: Uri = textEditor.document.uri; checkIsPersistModelFile(fileUri).then(isPersistModelFile => { - if (isPersistModelFile) { - this.isPersist = true; - commands.executeCommand('setContext', 'isPersistModelActive', true); - return; - } else { - this.isPersist = false; - } + this.isPersist = isPersistModelFile; + commands.executeCommand('setContext', 'isPersistModelActive', isPersistModelFile); }); + return; } commands.executeCommand('setContext', 'isPersistModelActive', false); } 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 c288a9acca9..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 @@ -598,9 +598,17 @@ export class BiDiagramRpcManager implements BIDiagramAPI { async addProjectToWorkspace(params: AddProjectToWorkspaceRequest): Promise { if (params.convertToWorkspace) { - await convertProjectToWorkspace(params); + try { + await convertProjectToWorkspace(params); + } catch (error) { + window.showErrorMessage("Error converting project to workspace"); + } } else { - await addProjectToExistingWorkspace(params); + try { + await addProjectToExistingWorkspace(params); + } catch (error) { + window.showErrorMessage("Error adding project to existing workspace"); + } } } diff --git a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts index b50cdd0a9d0..cd1de8524b8 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/bi.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/bi.ts @@ -20,7 +20,7 @@ import { exec } from "child_process"; import { window, commands, workspace, Uri } from "vscode"; import * as fs from 'fs'; import path from "path"; -import { AddProjectToWorkspaceRequest, BallerinaProjectComponents, ComponentRequest, CreateComponentResponse, createFunctionSignature, EVENT_TYPE, MACHINE_VIEW, MigrateRequest, NodePosition, ProjectRequest, STModification, SyntaxTreeResponse, VisualizerLocation } 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"; @@ -283,7 +283,11 @@ sticky = true export async function convertProjectToWorkspace(params: AddProjectToWorkspaceRequest) { const currentProjectPath = StateMachine.context().projectPath; - const { package: { name: currentPackageName } } = await getProjectTomlValues(currentProjectPath); + 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); @@ -330,8 +334,8 @@ function updateWorkspaceToml(workspacePath: string, packageName: string) { try { const ballerinaTomlContent = fs.readFileSync(ballerinaTomlPath, 'utf8'); - const tomlData = parse(ballerinaTomlContent); - const existingPackages: string[] = tomlData.packages || []; + const tomlData: WorkspaceTomlValues = parse(ballerinaTomlContent); + const existingPackages: string[] = tomlData.workspace?.packages || []; if (existingPackages.includes(packageName)) { return; // Package already exists diff --git a/workspaces/ballerina/ballerina-extension/src/utils/config.ts b/workspaces/ballerina/ballerina-extension/src/utils/config.ts index f42e18dc478..6f53c889563 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -223,12 +223,13 @@ export async function filterPackagePaths(packagePaths: string[], workspacePath: if (path.isAbsolute(pkgPath)) { const resolvedPath = path.resolve(pkgPath); const resolvedWorkspacePath = path.resolve(workspacePath); - if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(resolvedWorkspacePath)) { + if (fs.existsSync(resolvedPath) && isPathInside(resolvedPath, resolvedWorkspacePath)) { return await checkIsBallerinaPackage(Uri.file(resolvedPath)); } } const resolvedPath = path.resolve(workspacePath, pkgPath); - if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(workspacePath)) { + const resolvedWorkspacePath = path.resolve(workspacePath); + if (fs.existsSync(resolvedPath) && isPathInside(resolvedPath, resolvedWorkspacePath)) { return await checkIsBallerinaPackage(Uri.file(resolvedPath)); } return false; @@ -237,6 +238,11 @@ export async function filterPackagePaths(packagePaths: string[], workspacePath: 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'); 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 ee139208971..3d210daa161 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/utils.ts +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/utils.ts @@ -69,7 +69,7 @@ export const isFormValidAddProject = (formData: AddProjectFormData, isInWorkspac return ( formData.integrationName.length >= 2 && formData.packageName.length >= 2 && - (isInWorkspace || (!isInWorkspace && formData.workspaceName.length >= 1)) && + (isInWorkspace || (!isInWorkspace && formData.workspaceName?.length >= 1)) && validatePackageName(formData.packageName, formData.integrationName) === null ); }; 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 043d98d5efd..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,14 +134,18 @@ async function getProjectStructureData(): Promise { const data: ProjectExplorerEntry[] = []; if (extension.langClient) { const stateContext: VisualizerLocation = await commands.executeCommand(SHARED_COMMANDS.GET_STATE_CONTEXT); - const ballerinaWorkspace = stateContext?.workspacePath; + if (!stateContext) { + return []; + } + + const ballerinaWorkspace = stateContext.workspacePath; const workspaceFolderOfPackage = vscode .workspace .workspaceFolders .find(folder => folder.uri.fsPath === stateContext.projectPath); - let packageName = workspaceFolderOfPackage?.name; - let packagePath = workspaceFolderOfPackage?.uri.fsPath; + let packageName: string; + let packagePath: string; if (!workspaceFolderOfPackage) { if (ballerinaWorkspace) { @@ -150,6 +154,9 @@ async function getProjectStructureData(): Promise { } 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 diff --git a/workspaces/bi/bi-extension/src/utils.ts b/workspaces/bi/bi-extension/src/utils.ts index b3fb8c7f73f..cab706691e1 100644 --- a/workspaces/bi/bi-extension/src/utils.ts +++ b/workspaces/bi/bi-extension/src/utils.ts @@ -62,6 +62,9 @@ export async function fetchProjectInfo(): Promise { 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); @@ -172,7 +175,7 @@ export async function checkIsBallerinaWorkspace(uri: Uri): Promise { * @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 { +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'); @@ -192,7 +195,7 @@ async function getProjectTomlValues(projectPath: string): Promise { +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'); From a06efd7a539d8a1966aaf7ef0fdcd624d929ab54 Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 10 Nov 2025 20:36:12 +0530 Subject: [PATCH 86/91] Address review suggestions --- .../ballerina/ballerina-extension/src/utils/config.ts | 4 ++-- workspaces/bi/bi-extension/src/utils.ts | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/utils/config.ts b/workspaces/ballerina/ballerina-extension/src/utils/config.ts index 6f53c889563..b5f4f8cfebf 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -268,7 +268,7 @@ export function getOrgPackageName(projectPath: string): { orgName: string, packa } } -export async function getProjectTomlValues(projectPath: string): Promise { +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'); @@ -281,7 +281,7 @@ export async function getProjectTomlValues(projectPath: string): Promise { +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'); diff --git a/workspaces/bi/bi-extension/src/utils.ts b/workspaces/bi/bi-extension/src/utils.ts index cab706691e1..47ae99e5da4 100644 --- a/workspaces/bi/bi-extension/src/utils.ts +++ b/workspaces/bi/bi-extension/src/utils.ts @@ -227,12 +227,13 @@ export async function filterPackagePaths(packagePaths: string[], workspacePath: if (path.isAbsolute(pkgPath)) { const resolvedPath = path.resolve(pkgPath); const resolvedWorkspacePath = path.resolve(workspacePath); - if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(resolvedWorkspacePath)) { + if (fs.existsSync(resolvedPath) && isPathInside(resolvedPath, resolvedWorkspacePath)) { return await checkIsBallerinaPackage(Uri.file(resolvedPath)); } } const resolvedPath = path.resolve(workspacePath, pkgPath); - if (fs.existsSync(resolvedPath) && resolvedPath.startsWith(workspacePath)) { + const resolvedWorkspacePath = path.resolve(workspacePath); + if (fs.existsSync(resolvedPath) && isPathInside(resolvedPath, resolvedWorkspacePath)) { return await checkIsBallerinaPackage(Uri.file(resolvedPath)); } return false; @@ -240,3 +241,8 @@ export async function filterPackagePaths(packagePaths: string[], workspacePath: ); 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); +} From a12771ea2c39b6e91199d9aeac3f2f36ac6f97f8 Mon Sep 17 00:00:00 2001 From: Kanushka Gayan Date: Mon, 10 Nov 2025 21:14:05 +0530 Subject: [PATCH 87/91] Enhance ExpressionField and ChipExpressionBaseComponent to support onRemove functionality --- .../src/components/editors/ExpressionField.tsx | 1 + .../ChipExpressionEditor/ChipExpressionBaseComponent.tsx | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) 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 && ( + + )} ) } From 85754c404e74e7325d2a543bfef7ccc69fd40ca1 Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 10 Nov 2025 22:13:50 +0530 Subject: [PATCH 88/91] Refactor project root retrieval in test generation and documentation services to use getCurrentProjectRoot function. --- .../ai/service/documentation/doc_generator.ts | 4 ++-- .../ai/service/test/function_tests.ts | 12 +++++++++-- .../src/features/ai/service/test/test_plan.ts | 11 ++++++++-- .../src/features/ai/testGenerator.ts | 20 ++++--------------- .../src/features/ai/utils.ts | 17 +++------------- 5 files changed, 28 insertions(+), 36 deletions(-) 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 81ff2892c39..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,13 +20,13 @@ 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 projectPath = StateMachine.context().projectPath; + const projectPath = await getCurrentProjectRoot(); // Get the project source files const projectSource = await getProjectSource(projectPath); 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 0e3460a2df1..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,7 +39,15 @@ export async function generateFunctionTestsCore( content: `\n\nGenerating tests for the function ${functionIdentifier}. This may take a moment.`, }); - const projectPath = StateMachine.context().projectPath; + 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, 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 920eca1bb6c..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,7 +168,14 @@ export async function generateTestPlanCore( type: "content_block", content: `\n\nGenerating tests for the ${target} service. This may take a moment.`, }); - const projectPath = StateMachine.context().projectPath; + 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, diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts index 89e5f6af3dd..48331a10cf1 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/testGenerator.ts @@ -28,7 +28,6 @@ import * as os from 'os'; import { writeBallerinaFileDidOpenTemp } from '../../utils/modification'; import { closeAllBallerinaFiles } from './utils'; import { generateTestFromLLM, TestGenerationRequest1 } from './service/test/test'; -import { findBallerinaPackageRoot } from '../../utils'; const TEST_GEN_REQUEST_TIMEOUT = 100000; @@ -220,10 +219,9 @@ export async function getResourceAccessorDef(projectRoot: string, resourceMethod } export async function getDiagnostics( - projectRoot: string, + ballerinaProjectRoot: string, generatedTestSource: TestGenerationResponse ): Promise { - const ballerinaProjectRoot = await findBallerinaPackageRoot(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'); @@ -245,12 +243,7 @@ export async function getDiagnostics( }; } -async function getProjectSource(dirPath: string): Promise { - const projectRoot = await findBallerinaPackageRoot(dirPath); - - if (!projectRoot) { - return null; - } +async function getProjectSource(projectRoot: string): Promise { const projectSource: ProjectSource = { sourceFiles: [], @@ -298,14 +291,9 @@ async function getProjectSource(dirPath: string): Promise return projectSource; } -async function getProjectSourceWithTests(dirPath: string): Promise { - const projectRoot = await findBallerinaPackageRoot(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'); diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts index 618df1917a2..dbe39c57e62 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts @@ -278,17 +278,11 @@ async function showNoBallerinaSourceWarningMessage() { import { ProjectSource, ProjectModule, OpenAPISpec } from '@wso2/ballerina-core'; import { langClient } from './activator'; -import { findBallerinaPackageRoot } from '../../utils'; /** * Gets the project source including all .bal files and modules */ -export async function getProjectSource(dirPath: string): Promise { - const projectRoot = await findBallerinaPackageRoot(dirPath); - - if (!projectRoot) { - return null; - } +export async function getProjectSource(projectRoot: string): Promise { const projectSource: ProjectSource = { sourceFiles: [], @@ -339,14 +333,9 @@ export async function getProjectSource(dirPath: string): Promise { - const projectRoot = await findBallerinaPackageRoot(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'); From 307c61479b626ad4c684abee96ad010ebabd07eb Mon Sep 17 00:00:00 2001 From: madushajg Date: Mon, 10 Nov 2025 23:15:37 +0530 Subject: [PATCH 89/91] Update project command title in package.json and enhance package selection logic in stateMachine.ts for multi-root workspace support. --- workspaces/ballerina/ballerina-extension/package.json | 2 +- .../ballerina/ballerina-extension/src/stateMachine.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/workspaces/ballerina/ballerina-extension/package.json b/workspaces/ballerina/ballerina-extension/package.json index f36cfccdd8a..70763fa10a8 100644 --- a/workspaces/ballerina/ballerina-extension/package.json +++ b/workspaces/ballerina/ballerina-extension/package.json @@ -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/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index 90961870ef4..340671dfac4 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -879,6 +879,13 @@ async function handleSingleWorkspaceFolder(workspaceURI: Uri, isSwitching: boole 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 From ab1b220f23019d44b657e0d574551ced8b8d7263 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 11 Nov 2025 00:51:59 +0530 Subject: [PATCH 90/91] Update changelog for Ballerina and BI extensions to reflect the addition of workspace support --- workspaces/ballerina/ballerina-extension/CHANGELOG.md | 4 ++-- workspaces/bi/bi-extension/CHANGELOG.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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/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 From db64a27c25b50547c732ac673ddbac96c2596ba6 Mon Sep 17 00:00:00 2001 From: choreo-cicd Date: Mon, 10 Nov 2025 21:10:47 +0000 Subject: [PATCH 91/91] Update version to ballerina-integrator-1.5.0 --- workspaces/ballerina/ballerina-extension/package.json | 2 +- workspaces/bi/bi-extension/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/package.json b/workspaces/ballerina/ballerina-extension/package.json index 70763fa10a8..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", diff --git a/workspaces/bi/bi-extension/package.json b/workspaces/bi/bi-extension/package.json index 3df42c55a21..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": {