From 4850255a5de3f3134d49eab092af23c2c68a873b Mon Sep 17 00:00:00 2001 From: Vellummyilum Vinoth Date: Tue, 11 Nov 2025 14:43:41 +0530 Subject: [PATCH 1/8] Support multiple file uploads --- .../src/rpc-types/ai-panel/interfaces.ts | 2 +- .../src/features/ai/dataMapping.ts | 92 ++++- .../ai/service/datamapper/context_api.ts | 380 ++++++------------ .../ai/service/datamapper/datamapper.ts | 22 +- .../features/ai/service/datamapper/types.ts | 32 ++ .../src/rpc-managers/ai-panel/utils.ts | 41 +- .../data/commandTemplates.const.ts | 2 +- 7 files changed, 271 insertions(+), 300 deletions(-) diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/interfaces.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/interfaces.ts index 8742990e418..8173d0c0212 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/interfaces.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/interfaces.ts @@ -168,7 +168,7 @@ export interface DataMappingRecord { } export interface GenerateTypesFromRecordRequest { - attachment?: Attachment[] + attachment: Attachment[] } export interface GenerateTypesFromRecordResponse { diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/dataMapping.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/dataMapping.ts index ab3aa1e1876..a21ddd5a80d 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/dataMapping.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/dataMapping.ts @@ -477,11 +477,10 @@ export async function generateMappings( ): Promise { const targetFilePath = metadataWithAttachments.metadata.codeData.lineRange.fileName || context.documentUri; - const optionalMappingInstructionFile = metadataWithAttachments.attachments && metadataWithAttachments.attachments.length > 0 - ? metadataWithAttachments.attachments[0] - : undefined; - - const generatedMappings = await generateMappingExpressionsFromModel(metadataWithAttachments.metadata.mappingsModel as DMModel, optionalMappingInstructionFile); + const generatedMappings = await generateMappingExpressionsFromModel( + metadataWithAttachments.metadata.mappingsModel as DMModel, + metadataWithAttachments.attachments || [] + ); const customFunctionMappings = generatedMappings.filter(mapping => mapping.isFunctionCall); let customFunctionsFilePath: string | undefined; @@ -1123,20 +1122,47 @@ export async function generateInlineMappingsSource( // processContextTypeCreation - Functions for processing context type creation // ================================================================================================ -// Extract record types from Ballerina code -export function extractRecordTypes(typesCode: string): { name: string; code: string }[] { - const recordPattern = /\b(?:public|private)?\s*type\s+(\w+)\s+record\s+(?:{[|]?|[|]?{)[\s\S]*?;?\s*[}|]?;/g; - const matches = [...typesCode.matchAll(recordPattern)]; - return matches.map((match) => ({ - name: match[1], - code: match[0].trim(), - })); +// Extract record and enum types from syntax tree +export async function extractRecordTypesFromSyntaxTree( + langClient: ExtendedLangClient, + filePath: string +): Promise<{ records: string[]; enums: string[] }> { + const st = (await langClient.getSyntaxTree({ + documentIdentifier: { + uri: Uri.file(filePath).toString(), + }, + })) as SyntaxTree; + + if (!st.syntaxTree) { + throw new Error("Failed to retrieve syntax tree for file: " + filePath); + } + + const modulePart = st.syntaxTree as ModulePart; + const records: string[] = []; + const enums: string[] = []; + + for (const member of modulePart.members) { + if (STKindChecker.isTypeDefinition(member)) { + const typeName = member.typeName?.value; + if (typeName) { + records.push(typeName); + } + } else if (STKindChecker.isEnumDeclaration(member)) { + const enumName = member.identifier?.value; + if (enumName) { + enums.push(enumName); + } + } + } + + return { records, enums }; } // Generate Ballerina record types from context attachments and validate against existing records export async function generateTypesFromContext( sourceAttachments: Attachment[], - projectComponents: ProjectComponentsResponse + projectComponents: ProjectComponentsResponse, + langClient: ExtendedLangClient ): Promise { if (!sourceAttachments || sourceAttachments.length === 0) { throw new Error("Source attachments are required for type generation"); @@ -1163,10 +1189,14 @@ export async function generateTypesFromContext( const typeFilePath = baseFilePath + typeComponent.filePath; existingRecordTypesMap.set(typeComponent.name, { type: typeComponent.name, isArray: false, filePath: typeFilePath }); }); + moduleSummary.enums.forEach((enumComponent: ComponentInfo) => { + const enumFilePath = baseFilePath + enumComponent.filePath; + existingRecordTypesMap.set(enumComponent.name, { type: enumComponent.name, isArray: false, filePath: enumFilePath }); + }); }); }); - // Generate type definitions from attachments + // Generate type definitions from all attachments together const typeGenerationRequest: GenerateTypesFromRecordRequest = { attachment: sourceAttachments }; @@ -1174,6 +1204,29 @@ export async function generateTypesFromContext( const typeGenerationResponse = await generateTypeCreation(typeGenerationRequest); const generatedTypesCode = typeGenerationResponse.typesCode; + // Create temp directory and file to validate generated types + const tempDirectory = await createTempBallerinaDir(); + const tempTypesFilePath = path.join(tempDirectory, outputFileName); + + writeBallerinaFileDidOpenTemp(tempTypesFilePath, generatedTypesCode); + + // Extract record and enum names from syntax tree + const { records: generatedRecords, enums: generatedEnums } = await extractRecordTypesFromSyntaxTree(langClient, tempTypesFilePath); + + // Check for duplicate record names + for (const recordName of generatedRecords) { + if (existingRecordTypesMap.has(recordName)) { + throw new Error(`Record "${recordName}" already exists in the workspace`); + } + } + + // Check for duplicate enum names + for (const enumName of generatedEnums) { + if (existingRecordTypesMap.has(enumName)) { + throw new Error(`Enum "${enumName}" already exists in the workspace`); + } + } + return { typesCode: generatedTypesCode, filePath: outputFileName, @@ -1181,13 +1234,16 @@ export async function generateTypesFromContext( }; } -// Generate Ballerina record type definitions from an attachment file +// Generate Ballerina record type definitions from attachment files export async function generateTypeCreation( typeGenerationRequest: GenerateTypesFromRecordRequest ): Promise { - const sourceFile = typeGenerationRequest.attachment?.[0]; + if (typeGenerationRequest.attachment.length === 0) { + throw new Error('No attachments provided for type generation'); + } - const generatedTypeDefinitions = await extractRecordTypeDefinitionsFromFile(sourceFile); + // Process all attachments together to understand correlations + const generatedTypeDefinitions = await extractRecordTypeDefinitionsFromFile(typeGenerationRequest.attachment); if (typeof generatedTypeDefinitions !== 'string') { throw new Error(`Failed to generate types: ${JSON.stringify(generatedTypeDefinitions)}`); } diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/context_api.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/context_api.ts index 0f8a331f16a..14cf1183c78 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/context_api.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/context_api.ts @@ -17,111 +17,32 @@ import { generateText, ModelMessage } from "ai"; import { getAnthropicClient, ANTHROPIC_SONNET_4 } from "../connection"; import { AIPanelAbortController } from "../../../../../src/rpc-managers/ai-panel/utils"; +import { ContentPart, DataMapperRequest, DataMapperResponse, FileData, FileTypeHandler, ProcessType } from "./types"; -// Types -export type FileData = { - fileName: string; - content: string; -}; - -export type ProcessType = "mapping_instruction" | "records" | "requirements"; - -export type DataMapperRequest = { - file?: FileData; - text?: string; - processType: ProcessType; - isRequirementAnalysis?: boolean; //TODO: Why is this -}; - -export type DataMapperResponse = { - fileContent: string; -}; - -export type SupportedFileExtension = "pdf" | "jpg" | "jpeg" | "png" | "txt"; // Maybe have better names and types? export async function processDataMapperInput(request: DataMapperRequest): Promise { - if (request.file) { - return await processFile(request.file, request.processType, request.isRequirementAnalysis); + if (request.files.length > 0) { + return await processFiles(request.files, request.processType, request.isRequirementAnalysis); } else if (request.text) { - const message = await processText(request.text, request.processType); - const fileContent = request.isRequirementAnalysis - ? message - : extractBallerinaCode(message, request.processType); - return { fileContent }; + return await processFiles([{ fileName: 'text', content: btoa(request.text) }], request.processType, request.isRequirementAnalysis); } else { - throw new Error("No file or text provided. Please provide file data or text input."); + throw new Error("No files or text provided. Please provide file data or text input."); } } -// Process file data -async function processFile(file: FileData, processType: ProcessType, isRequirementAnalysis: boolean = false): Promise { - let message: string; - - const extension = getFileExtension(file.fileName); - +// Process files (single or multiple) +async function processFiles(files: FileData[], processType: ProcessType, isRequirementAnalysis: boolean = false): Promise { try { - //TODO: I think we should handle supported files from one place. - if (extension === "pdf") { - message = await processPdf(file.content, processType); - } else if (extension === "jpeg" || extension === "jpg" || extension === "png") { - message = await processImage(file.content, processType, extension); - } else if (extension === "txt" || extension === "csv" || !extension) { - const txtContent = atob(file.content); - message = await processText(txtContent, processType); - } else { - throw new Error(`Unsupported file type: ${extension}`); - } + const message = await processFilesWithClaude(files, processType); - const fileContent = isRequirementAnalysis + const fileContent = isRequirementAnalysis ? getRequirementsContent(message) : extractBallerinaCode(message, processType); return { fileContent }; } catch (error) { - throw new Error(`Error processing file: ${error instanceof Error ? error.message : String(error)}`); - } -} - -// Process PDF content -async function processPdf(base64Content: string, processType: ProcessType): Promise { - try { - return await extractionUsingClaude({ - pdfData: base64Content, - processType - }); - } catch (error) { - throw new Error(`PDF processing error: ${error instanceof Error ? error.message : String(error)}`); - } -} - -// Process image content -async function processImage(base64Content: string, processType: ProcessType, extension: string): Promise { - // Only process actual image extensions - if (extension !== "jpeg" && extension !== "jpg" && extension !== "png") { - throw new Error(`Unsupported image extension: ${extension}`); - } - - try { - return await imageExtractionUsingClaude({ - imgData: base64Content, - processType, - extension - }); - } catch (error) { - throw new Error(`Image processing error: ${error instanceof Error ? error.message : String(error)}`); - } -} - -// Process text content -async function processText(text: string, processType: ProcessType): Promise { - try { - return await textExtractionUsingClaude({ - textContent: text, - processType - }); - } catch (error) { - throw new Error(`Error processing text: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Error processing ${files.length === 1 ? 'file' : 'files'}: ${error instanceof Error ? error.message : String(error)}`); } } @@ -158,12 +79,73 @@ function getRequirementsContent(message: any): string { return String(message); } +// Supported file types configuration +const SUPPORTED_FILE_TYPES: Record = { + pdf: (file: FileData) => ({ + type: "file", + data: file.content, + mediaType: "application/pdf" + }), + jpeg: (file: FileData) => ({ + type: "image", + image: file.content, + mediaType: "image/jpeg" + }), + jpg: (file: FileData) => ({ + type: "image", + image: file.content, + mediaType: "image/jpeg" + }), + png: (file: FileData) => ({ + type: "image", + image: file.content, + mediaType: "image/png" + }), + txt: (file: FileData, includeFileName: boolean) => { + const txtContent = atob(file.content); + return { + type: "text", + text: includeFileName ? `File: ${file.fileName}\n\n${txtContent}` : txtContent + }; + }, + csv: (file: FileData, includeFileName: boolean) => { + const txtContent = atob(file.content); + return { + type: "text", + text: includeFileName ? `File: ${file.fileName}\n\n${txtContent}` : txtContent + }; + } +}; + // Get file extension from filename function getFileExtension(fileName: string): string { const extension = fileName.toLowerCase().split('.').pop(); return extension || ""; } +// Convert file to content part for Claude API +function convertFileToContentPart(file: FileData, includeFileName: boolean = false): ContentPart { + const extension = getFileExtension(file.fileName); + + const handler = SUPPORTED_FILE_TYPES[extension]; + + if (handler) { + return handler(file, includeFileName); + } + + // Fallback for files without extension + if (!extension) { + const txtContent = atob(file.content); + return { + type: "text", + text: includeFileName ? `File: ${file.fileName}\n\n${txtContent}` : txtContent + }; + } + + const supportedTypes = Object.keys(SUPPORTED_FILE_TYPES).join(', '); + throw new Error(`Unsupported file type: ${extension}. Supported types are: ${supportedTypes}`); +} + // Prompt generation functions function getMappingInstructionPrompt(): string { return `You are an AI assistant specialized in generating data field mappings for data integration and transformation tasks. @@ -256,96 +238,48 @@ Generate only Ballerina code with in tags based on the provided } function getRecordsPrompt(): string { - return `You are an AI assistant specializing in the Ballerina programming language. -Your task is to analyze given content and create Ballerina code for type records based on the content provided. - -IMPORTANT: - - Do not take any assumptions based on data types or records. - - Do not include any comments in the code - - Final output has only Ballerina code within tags. - - Extract as much as all possible records and fields - -Please follow these steps to create the Ballerina code: - - 1. Analyze the content: - a) If it is an image, Input records appear on the left side of the image, and output records appear on the right side. - b) All subfields of nested fields or subfields should be structured hierarchically, expanding downwards recursively within their respective parent fields. This hierarchy should reflect nested relationships. - c) Must extract all records and their all fields and their data types in the content. - d) Using and refer to all links or hyperlinks that provide additional information about records and data types in the content. - e) Quote and number specific parts of the content that mention record types and data types. - f) List all record types mentioned in the content, numbering them (e.g., 1. RecordType1, 2. RecordType2, ...). - g) For each record type, list it's all fields and their exact data types as mentioned in the content, also numbering them (e.g., 1.1 field1: SI, 1.2 field2: int, ... ). - h) Identify any nested structures and explain how they relate to the main records. - i) Summarize and use relevant comments or conditions or additional information about the records or data types in the content. - - 2. Define the record types: - Based on your analysis: - - Create a type record for each identified record with its sub-fields - - Consider all records and fields with optional and nullable feature - - Use only the exact data types you identified in step 1 for each field and record - - Apply these naming conventions: PascalCase for record names, camelCase for field names - - For nested fields, create recursive record types, stopping at simple data types - -After your analysis, provide the Ballerina code within tags. The code should include: - - Type record definitions for all identified records with its all fields - -Example output structure (generic, without specific content): - - -type RecordName1 record { - FieldDataType1 fieldName1; - FieldDataType2 fieldName2; - }; - -type RecordName2 record { - FieldDataType3 fieldName3; - RecordName1 nestedField; -}; + return `You are an AI assistant specializing in the Ballerina programming language. Your task is to analyze provided content and generate comprehensive Ballerina type record definitions based on all the information present in that content. -type RecordName3 record { - FieldDataType4 fieldName4; - RecordName4 nestedField; -}; - +Your goal is to extract every possible record type and field from this content and convert them into proper Ballerina type record definitions. You must capture all the information mentioned - leave nothing out. -Sample example for the required format: +## Code Generation Requirements -type Person record { - int? id?; - string firstName; - string? lastName; - int? age; - string country?; - College? college?; -}; +Generate Ballerina code that includes: -type College record { - Course[] courses; -}; +- Type record definitions for ALL identified records with ALL their fields +- Proper handling of optional (\`?\`) and nullable features - but ONLY when explicitly mentioned as optional or nullable in the content +- Correct Ballerina naming conventions +- No comments in the generated code +- No assumptions beyond what's explicitly stated in the content -type Course record { - string? id?; - decimal credits?; - Address? address; -}; +## Enum Declaration Format -type Student record { - string id; - string firstName; - float? age; - record { - int id; - float credits?; - Address address; - }[] courses; -}; +When you encounter enumerated types, use this specific syntax: -type A record { - Person[] person; +\`\`\`ballerina +enum EnumName { + VALUE1, + VALUE2, + VALUE3 }; +\`\`\` + +## Output Format + +Present your final code within \`\` tags. Structure your code as follows: + +- Place all enum definitions first +- Follow with type record definitions +- Use proper Ballerina syntax throughout -type B record { - Student[] student?; +Example structure (generic structure only): + +type Person record { + int? id?; + string firstName; + string? lastName; + int? age; + Address? address?; }; type Address record { @@ -354,7 +288,14 @@ type Address record { string? zipcode; }; -Generate only Ballerina code with in tags based on the provided content.`; +enum Gender { + MALE, + FEMALE, + OTHER +}; + +Generate only Ballerina code with in tags based on the provided content. +`; } function getRequirementsPrompt(): string { @@ -403,93 +344,28 @@ function getPromptForProcessType(processType: ProcessType): string { } } -// Claude API integration functions -async function extractionUsingClaude({ pdfData, processType }: { pdfData: string; processType: ProcessType }): Promise { +// Process files with Claude (handles both single and multiple files) +async function processFilesWithClaude(files: FileData[], processType: ProcessType): Promise { const promptText = getPromptForProcessType(processType); - - const messages: ModelMessage[] = [ - { - role: "user", - content: [ - { - type: "file", - data: pdfData, - mediaType: "application/pdf" - }, - { - type: "text", - text: promptText - } - ] - } - ]; - const { text } = await generateText({ - model: await getAnthropicClient(ANTHROPIC_SONNET_4), - maxOutputTokens: 8192, - temperature: 0, - messages: messages, - abortSignal: AIPanelAbortController.getInstance().signal - }); - - return text; -} + // Build content array with all files + const contentParts: Array = []; + const includeFileName = files.length > 1; -async function imageExtractionUsingClaude({ - imgData, - processType, - extension -}: { - imgData: string; - processType: ProcessType; - extension: string; -}): Promise { - const promptText = getPromptForProcessType(processType); - - // Convert extension to proper media type - const mimeType = extension === "png" ? "image/png" : "image/jpeg"; - - const messages: ModelMessage[] = [ - { - role: "user", - content: [ - { - type: "image", - image: imgData, - mediaType: mimeType - }, - { - type: "text", - text: promptText - } - ] - } - ]; + for (const file of files) { + contentParts.push(convertFileToContentPart(file, includeFileName)); + } - const { text } = await generateText({ - model: await getAnthropicClient(ANTHROPIC_SONNET_4), - maxOutputTokens: 8192, - temperature: 0, - messages: messages, - abortSignal: AIPanelAbortController.getInstance().signal + // Add the prompt at the end + contentParts.push({ + type: "text", + text: promptText }); - return text; -} - -async function textExtractionUsingClaude({ - textContent, - processType -}: { - textContent: string; - processType: ProcessType; -}): Promise { - const promptText = getPromptForProcessType(processType); - const messages: ModelMessage[] = [ { role: "user", - content: promptText + "\n\n" + textContent + content: contentParts } ]; @@ -505,21 +381,21 @@ async function textExtractionUsingClaude({ } // Utility functions for specific use cases -export async function generateMappingInstruction(input: { file?: FileData; text?: string }): Promise { +export async function generateMappingInstruction(input: { files: FileData[]; text?: string }): Promise { return await processDataMapperInput({ ...input, processType: "mapping_instruction" }); } -export async function generateRecord(input: { file?: FileData; text?: string }): Promise { +export async function generateRecord(input: { files: FileData[]; text?: string }): Promise { return await processDataMapperInput({ ...input, processType: "records" }); } -export async function extractRequirements(input: { file?: FileData; text?: string }): Promise { +export async function extractRequirements(input: { files: FileData[]; text?: string }): Promise { return await processDataMapperInput({ ...input, processType: "requirements", diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/datamapper.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/datamapper.ts index 3ca22f012bc..1ed96c7f1b8 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/datamapper.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/datamapper.ts @@ -29,7 +29,7 @@ import { getDataMappingPrompt } from "./dataMappingPrompt"; import { getBallerinaCodeRepairPrompt } from "./codeRepairPrompt"; import { CopilotEventHandler, createWebviewEventHandler } from "../event"; import { getErrorMessage } from "../utils"; -import { buildMappingFileArray, buildRecordMap, collectExistingFunctions, collectModuleInfo, createTempBallerinaDir, createTempFileAndGenerateMetadata, getFunctionDefinitionFromSyntaxTree, getUniqueFunctionFilePaths, prepareMappingContext, generateInlineMappingsSource, generateTypesFromContext, extractRecordTypes, repairCodeAndGetUpdatedContent, extractImports, generateDataMapperModel, determineCustomFunctionsPath, generateMappings, getCustomFunctionsContent } from "../../dataMapping"; +import { buildMappingFileArray, buildRecordMap, collectExistingFunctions, collectModuleInfo, createTempBallerinaDir, createTempFileAndGenerateMetadata, getFunctionDefinitionFromSyntaxTree, getUniqueFunctionFilePaths, prepareMappingContext, generateInlineMappingsSource, generateTypesFromContext, repairCodeAndGetUpdatedContent, extractImports, generateDataMapperModel, determineCustomFunctionsPath, generateMappings, getCustomFunctionsContent } from "../../dataMapping"; import { BiDiagramRpcManager, getBallerinaFiles } from "../../../../../src/rpc-managers/bi-diagram/rpc-manager"; import { updateSourceCode } from "../../../../../src/utils/source-utils"; import { StateMachine } from "../../../../stateMachine"; @@ -764,24 +764,20 @@ export async function generateContextTypesCore(typeCreationRequest: ProcessConte try { const biDiagramRpcManager = new BiDiagramRpcManager(); + const langClient = StateMachine.langClient(); const projectComponents = await biDiagramRpcManager.getProjectComponents(); - // Generate types from context - const { typesCode, filePath, recordMap } = await generateTypesFromContext( + // Generate types from context with validation + const { typesCode, filePath } = await generateTypesFromContext( typeCreationRequest.attachments, - projectComponents + projectComponents, + langClient ); - const extractedNewRecords = extractRecordTypes(typesCode); - for (const newRecord of extractedNewRecords) { - if (recordMap.has(newRecord.name)) { - throw new Error(`Record "${newRecord.name}" already exists in the workspace.`); - } - } - // Build assistant response - const sourceAttachmentName = typeCreationRequest.attachments?.[0]?.name || "attachment"; - assistantResponse = `Record types generated from the ${sourceAttachmentName} file shown below.\n`; + const sourceAttachmentNames = typeCreationRequest.attachments?.map(a => a.name).join(", ") || "attachment"; + const fileText = typeCreationRequest.attachments?.length === 1 ? "file" : "files"; + assistantResponse = `Record types generated from the ${sourceAttachmentNames} ${fileText} shown below.\n`; assistantResponse += `\n\`\`\`ballerina\n${typesCode}\n\`\`\`\n`; // Send assistant response through event handler diff --git a/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/types.ts b/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/types.ts index 34c28c33ed3..830839612a8 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/types.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/ai/service/datamapper/types.ts @@ -87,3 +87,35 @@ export interface ChatResponse { total_tokens: number; }; } + +// ============================================================================= +// CONTEXT API CONTENT TYPES +// ============================================================================= + +export type FileData = { + fileName: string; + content: string; +}; + +export type ContentPart = { + type: "file" | "image" | "text"; + data?: string; + image?: string; + text?: string; + mediaType?: string; +}; + +export type FileTypeHandler = (file: FileData, includeFileName: boolean) => ContentPart; + +export type ProcessType = "mapping_instruction" | "records" | "requirements"; + +export type DataMapperRequest = { + files: FileData[]; + text?: string; + processType: ProcessType; + isRequirementAnalysis?: boolean; //TODO: Why is this +}; + +export type DataMapperResponse = { + fileContent: string; +}; diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/utils.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/utils.ts index 904c00faf7b..f2b6341e81e 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/utils.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/utils.ts @@ -22,7 +22,8 @@ import { Position, Range, Uri, workspace, WorkspaceEdit } from 'vscode'; import path from "path"; import * as fs from 'fs'; import { AIChatError } from "./utils/errors"; -import { DataMapperRequest, DataMapperResponse, FileData, processDataMapperInput } from "../../../src/features/ai/service/datamapper/context_api"; +import { processDataMapperInput } from "../../../src/features/ai/service/datamapper/context_api"; +import { DataMapperRequest, DataMapperResponse, FileData } from "../../../src/features/ai/service/datamapper/types"; import { getAskResponse } from "../../../src/features/ai/service/ask/ask"; import { MappingFileRecord} from "./types"; import { generateAutoMappings, generateRepairCode } from "../../../src/features/ai/service/datamapper/datamapper"; @@ -150,16 +151,16 @@ async function convertAttachmentToFileData(attachment: Attachment): Promise { let dataMapperResponse: DataMapperModelResponse = { mappingsModel: dataMapperModel as DMModel }; - if (mappingInstructionFile) { - const enhancedResponse = await enrichModelWithMappingInstructions(mappingInstructionFile, dataMapperResponse); + if (mappingInstructionFiles.length > 0) { + const enhancedResponse = await enrichModelWithMappingInstructions(mappingInstructionFiles, dataMapperResponse); dataMapperResponse = enhancedResponse as DataMapperModelResponse; } @@ -172,12 +173,16 @@ export async function generateMappingExpressionsFromModel( })); } -// Processes a mapping instruction file and merges it with the existing data mapper model -export async function enrichModelWithMappingInstructions(mappingInstructionFile: Attachment, currentDataMapperResponse: DataMapperModelResponse): Promise { - if (!mappingInstructionFile) { return currentDataMapperResponse; } - const fileData = await convertAttachmentToFileData(mappingInstructionFile); +// Processes mapping instruction files and merges them with the existing data mapper model +export async function enrichModelWithMappingInstructions(mappingInstructionFiles: Attachment[], currentDataMapperResponse: DataMapperModelResponse): Promise { + if (!mappingInstructionFiles || mappingInstructionFiles.length === 0) { return currentDataMapperResponse; } + + const fileDataArray = await Promise.all( + mappingInstructionFiles.map(file => convertAttachmentToFileData(file)) + ); + const requestParams: DataMapperRequest = { - file: fileData, + files: fileDataArray, processType: "mapping_instruction" }; const response: DataMapperResponse = await processDataMapperInput(requestParams); @@ -206,12 +211,18 @@ export async function repairSourceFilesWithAI(codeRepairRequest: repairCodeReque // Type Creator related functions // Extracts type definitions from a file attachment and generates Ballerina record definitions -export async function extractRecordTypeDefinitionsFromFile(sourceFile: Attachment): Promise { - if (!sourceFile) { throw new Error("File is undefined"); } +export async function extractRecordTypeDefinitionsFromFile(sourceFiles: Attachment[]): Promise { + if (sourceFiles.length === 0) { + throw new Error("No files provided"); + } + + // Process all files together to understand correlations + const fileDataArray = await Promise.all( + sourceFiles.map(attachment => convertAttachmentToFileData(attachment)) + ); - const fileData = await convertAttachmentToFileData(sourceFile); const requestParams: DataMapperRequest = { - file: fileData, + files: fileDataArray, processType: "records" }; const response: DataMapperResponse = await processDataMapperInput(requestParams); @@ -230,7 +241,7 @@ export async function requirementsSpecification(filepath: string): Promise Date: Tue, 11 Nov 2025 17:03:43 +0530 Subject: [PATCH 2/8] Added isWorkspaceSupported property to manage workspace compatibility and updated related commands and conditions in the project explorer --- .../ballerina-extension/src/core/extension.ts | 26 ++++++++++++++----- workspaces/bi/bi-extension/package.json | 2 +- workspaces/bi/bi-extension/src/extension.ts | 1 + .../src/project-explorer/activate.ts | 3 ++- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/core/extension.ts b/workspaces/ballerina/ballerina-extension/src/core/extension.ts index c27824327eb..a137d0b0189 100644 --- a/workspaces/ballerina/ballerina-extension/src/core/extension.ts +++ b/workspaces/ballerina/ballerina-extension/src/core/extension.ts @@ -33,7 +33,18 @@ import { exec, spawnSync, execSync } from 'child_process'; import { LanguageClientOptions, State as LS_STATE, RevealOutputChannelOn, ServerOptions } from "vscode-languageclient/node"; import { getServerOptions } from '../utils/server/server'; import { ExtendedLangClient } from './extended-language-client'; -import { debug, log, getOutputChannel, outputChannel, isWindows, isWSL, isSupportedVersion, VERSION, isSupportedSLVersion } from '../utils'; +import { + debug, + log, + getOutputChannel, + outputChannel, + isWindows, + isWSL, + isSupportedVersion, + VERSION, + isSupportedSLVersion, + createVersionNumber +} from '../utils'; import { AssertionError } from "assert"; import { BALLERINA_HOME, ENABLE_ALL_CODELENS, ENABLE_TELEMETRY, ENABLE_SEMANTIC_HIGHLIGHTING, OVERRIDE_BALLERINA_HOME, @@ -142,6 +153,7 @@ export class BallerinaExtension { public ballerinaVersion: string; public biSupported: boolean; public isNPSupported: boolean; + public isWorkspaceSupported: boolean; public extension: Extension; private clientOptions: LanguageClientOptions; public langClient?: ExtendedLangClient; @@ -172,6 +184,7 @@ export class BallerinaExtension { this.ballerinaVersion = ''; this.biSupported = false; this.isNPSupported = false; + this.isWorkspaceSupported = false; this.isPersist = false; this.ballerinaUserHomeName = '.ballerina'; @@ -442,9 +455,10 @@ export class BallerinaExtension { } try { - this.biSupported = isSupportedSLVersion(this, 2201123); // Minimum supported version for BI - this.isNPSupported = isSupportedSLVersion(this, 2201130) && this.enabledExperimentalFeatures(); // Minimum supported requirements for NP - debug(`[INIT] Feature support calculated - BI: ${this.biSupported}, NP: ${this.isNPSupported}`); + this.biSupported = isSupportedSLVersion(this, createVersionNumber(2201, 12, 3)); // Minimum supported version for BI: 2201.12.3 + this.isNPSupported = isSupportedSLVersion(this, createVersionNumber(2201, 13, 0)) && this.enabledExperimentalFeatures(); // Minimum supported requirements for NP: 2201.13.0 + this.isWorkspaceSupported = isSupportedSLVersion(this, createVersionNumber(2201, 13, 0)); // Minimum supported requirements for Workspace: 2201.13.0 + debug(`[INIT] Feature support calculated - BI: ${this.biSupported}, NP: ${this.isNPSupported}, Workspace: ${this.isWorkspaceSupported}`); } catch (error) { debug(`[INIT] Error calculating feature support: ${error}`); // Don't throw here, we can continue without these features @@ -464,7 +478,7 @@ export class BallerinaExtension { debug(`[INIT] Final Ballerina Home: ${this.ballerinaHome}`); debug(`[INIT] Plugin Dev Mode: ${this.overrideBallerinaHome()}`); debug(`[INIT] Debug Mode: ${this.enableLSDebug()}`); - debug(`[INIT] Feature flags - Experimental: ${this.enabledExperimentalFeatures()}, BI: ${this.biSupported}, NP: ${this.isNPSupported}`); + debug(`[INIT] Feature flags - Experimental: ${this.enabledExperimentalFeatures()}, BI: ${this.biSupported}, NP: ${this.isNPSupported}, Workspace: ${this.isWorkspaceSupported}`); // Check version compatibility try { @@ -2283,7 +2297,7 @@ export class BallerinaExtension { } public enabledLiveReload(): boolean { - return isSupportedSLVersion(this, 2201100) && workspace.getConfiguration().get(ENABLE_LIVE_RELOAD); + return isSupportedSLVersion(this, createVersionNumber(2201, 10, 0)) && workspace.getConfiguration().get(ENABLE_LIVE_RELOAD); } public enabledPerformanceForecasting(): boolean { diff --git a/workspaces/bi/bi-extension/package.json b/workspaces/bi/bi-extension/package.json index 3df42c55a21..2ad496c503a 100644 --- a/workspaces/bi/bi-extension/package.json +++ b/workspaces/bi/bi-extension/package.json @@ -142,7 +142,7 @@ }, { "command": "BI.project-explorer.add", - "when": "view == BI.project-explorer && BI.project == true", + "when": "view == BI.project-explorer && BI.project == true && BI.isWorkspaceSupported == true", "group": "navigation" }, { diff --git a/workspaces/bi/bi-extension/src/extension.ts b/workspaces/bi/bi-extension/src/extension.ts index e7c354e0972..993ee4f845f 100644 --- a/workspaces/bi/bi-extension/src/extension.ts +++ b/workspaces/bi/bi-extension/src/extension.ts @@ -27,6 +27,7 @@ export function activate(context: vscode.ExtensionContext) { extension.langClient = ballerinaExt.exports.ballerinaExtInstance.langClient; extension.biSupported = ballerinaExt.exports.ballerinaExtInstance.biSupported; extension.isNPSupported = ballerinaExt.exports.ballerinaExtInstance.isNPSupported; + extension.isWorkspaceSupported = ballerinaExt.exports.ballerinaExtInstance?.isWorkspaceSupported; StateMachine.initialize(); return; } diff --git a/workspaces/bi/bi-extension/src/project-explorer/activate.ts b/workspaces/bi/bi-extension/src/project-explorer/activate.ts index 67abc97fe9c..f7b811b90eb 100644 --- a/workspaces/bi/bi-extension/src/project-explorer/activate.ts +++ b/workspaces/bi/bi-extension/src/project-explorer/activate.ts @@ -56,8 +56,9 @@ function createProjectTree(dataProvider: ProjectExplorerEntryProvider) { function registerBallerinaCommands(dataProvider: ProjectExplorerEntryProvider, isBI: boolean, isBalWorkspace?: boolean) { commands.registerCommand(BI_COMMANDS.REFRESH_COMMAND, () => dataProvider.refresh()); + commands.executeCommand('setContext', 'BI.isWorkspaceSupported', extension.isWorkspaceSupported); - if (isBalWorkspace) { + if (extension.isWorkspaceSupported && isBalWorkspace) { commands.executeCommand('setContext', 'BI.isBalWorkspace', true); } if (isBI) { From ec3b5187e0e27c4f4d2aff37b3fbb4bff87026ff Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 11 Nov 2025 17:04:04 +0530 Subject: [PATCH 3/8] Refactor version handling by introducing createVersionNumber utility and updating isSupportedSLVersion checks across multiple files for improved version comparison --- .../src/features/debugger/config-provider.ts | 10 ++- .../features/natural-programming/activator.ts | 4 +- .../src/features/project/cmds/build.ts | 4 +- .../src/features/project/cmds/cmd-runner.ts | 4 +- .../features/project/cmds/xml-to-record.ts | 6 +- .../ballerina-extension/src/utils/config.ts | 86 +++++++++++++++++-- .../src/utils/server/server.ts | 4 +- .../record-creator/src/CreateRecord/index.tsx | 4 +- .../components/FormComponents/Utils/index.tsx | 82 ++++++++++++++++-- .../type-editor/src/CreateRecord/index.tsx | 4 +- .../components/FormComponents/Utils/index.tsx | 82 ++++++++++++++++-- .../bi/bi-extension/src/biExtentionContext.ts | 1 + 12 files changed, 256 insertions(+), 35 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 ce7e26801af..18b6c9890b4 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,13 @@ 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, checkIsBallerinaPackage } from "../../utils"; +import { + log, + debug as debugLog, + isSupportedSLVersion, + isWindows, + createVersionNumber +} 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'; @@ -654,7 +660,7 @@ class BIRunAdapter extends LoggingDebugSession { runCommand = `${runCommand} -- ${programArgs.join(' ')}`; } - if (isSupportedSLVersion(extension.ballerinaExtInstance, 2201130) && extension.ballerinaExtInstance.enabledExperimentalFeatures()) { + if (isSupportedSLVersion(extension.ballerinaExtInstance, createVersionNumber(2201, 13, 0)) && extension.ballerinaExtInstance.enabledExperimentalFeatures()) { runCommand = `${runCommand} --experimental`; } 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 f27851bbb6b..2c8397cc9ef 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/natural-programming/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/natural-programming/activator.ts @@ -28,11 +28,11 @@ import { MONITERED_EXTENSIONS, COMMAND_SHOW_TEXT } from './constants'; -import { isSupportedSLVersion } from "../../utils"; +import { isSupportedSLVersion, createVersionNumber } from "../../utils"; import { CustomDiagnostic } from './custom-diagnostics'; let diagnosticCollection: vscode.DiagnosticCollection; -const BALLERINA_UPDATE_13 = 2201130; +const BALLERINA_UPDATE_13 = createVersionNumber(2201, 13, 0); // Version 2201.13.0 export function activate(ballerinaExtInstance: BallerinaExtension) { const backgroundDriftCheckConfig = vscode.workspace.getConfiguration().get(ENABLE_BACKGROUND_DRIFT_CHECK); diff --git a/workspaces/ballerina/ballerina-extension/src/features/project/cmds/build.ts b/workspaces/ballerina/ballerina-extension/src/features/project/cmds/build.ts index 50eb77e5998..ca08706b1d5 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/project/cmds/build.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/project/cmds/build.ts @@ -26,7 +26,7 @@ import { runCommand, BALLERINA_COMMANDS, PROJECT_TYPE, PALETTE_COMMANDS, MESSAGE from "./cmd-runner"; import { getCurrentBallerinaProject, getCurrenDirectoryPath, getCurrentBallerinaFile } from "../../../utils/project-utils"; -import { isSupportedSLVersion } from "../../../utils"; +import { isSupportedSLVersion, createVersionNumber } from "../../../utils"; export function activateBuildCommand() { // register run project build handler @@ -45,7 +45,7 @@ export function activateBuildCommand() { let balCommand = BALLERINA_COMMANDS.BUILD; - if (isSupportedSLVersion(extension.ballerinaExtInstance, 2201130) && extension.ballerinaExtInstance.enabledExperimentalFeatures()) { + if (isSupportedSLVersion(extension.ballerinaExtInstance, createVersionNumber(2201, 13, 0)) && extension.ballerinaExtInstance.enabledExperimentalFeatures()) { balCommand = BALLERINA_COMMANDS.BUILD_WITH_EXPERIMENTAL; } 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 74b52546772..13daaab78aa 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 @@ -18,7 +18,7 @@ import { BallerinaProject } from "@wso2/ballerina-core"; import { Terminal, window, workspace } from "vscode"; -import { isSupportedSLVersion, isWindows } from "../../../utils"; +import { isSupportedSLVersion, isWindows, createVersionNumber } from "../../../utils"; import { extension } from "../../../BalExtensionContext"; import { TracerMachine } from "../../../features/tracing"; @@ -197,7 +197,7 @@ export function createTerminal(path: string, env?: { [key: string]: string }): v } export function getRunCommand(): BALLERINA_COMMANDS { - if (isSupportedSLVersion(extension.ballerinaExtInstance, 2201130) && extension.ballerinaExtInstance.enabledExperimentalFeatures()) { + if (isSupportedSLVersion(extension.ballerinaExtInstance, createVersionNumber(2201, 13, 0)) && extension.ballerinaExtInstance.enabledExperimentalFeatures()) { return BALLERINA_COMMANDS.RUN_WITH_EXPERIMENTAL; } else if (extension.ballerinaExtInstance.enabledLiveReload()) { return BALLERINA_COMMANDS.RUN_WITH_WATCH; diff --git a/workspaces/ballerina/ballerina-extension/src/features/project/cmds/xml-to-record.ts b/workspaces/ballerina/ballerina-extension/src/features/project/cmds/xml-to-record.ts index 294569cef24..4b3801cf125 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/project/cmds/xml-to-record.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/project/cmds/xml-to-record.ts @@ -22,7 +22,7 @@ import { import { commands, window, env } from "vscode"; import { extension } from "../../../BalExtensionContext"; import { PALETTE_COMMANDS, MESSAGES } from "./cmd-runner"; -import { isSupportedSLVersion } from "../../../utils"; +import { isSupportedSLVersion, createVersionNumber } from "../../../utils"; import { DIAGNOSTIC_SEVERITY, XMLToRecord } from "@wso2/ballerina-core"; const MSG_NOT_SUPPORT = "Paste XML as a Ballerina record feature is not supported"; @@ -34,8 +34,8 @@ export function activatePasteXMLAsRecord() { } commands.registerCommand(PALETTE_COMMANDS.PASTE_XML_AS_RECORD, () => { - // This command is only available since Swan Lake Update 7 patch 2 - if (!isSupportedSLVersion(extension.ballerinaExtInstance, 220172)) { + // This command is only available since Swan Lake Update 7 patch 2 (2201.7.2) + if (!isSupportedSLVersion(extension.ballerinaExtInstance, createVersionNumber(2201, 7, 2))) { window.showErrorMessage(`${MSG_NOT_SUPPORT} in ${extension.ballerinaExtInstance.ballerinaVersion}`); return; } diff --git a/workspaces/ballerina/ballerina-extension/src/utils/config.ts b/workspaces/ballerina/ballerina-extension/src/utils/config.ts index b5f4f8cfebf..4d38326992a 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -98,18 +98,92 @@ export function isSupportedVersion(ballerinaExtInstance: BallerinaExtension, sup return false; } -export function isSupportedSLVersion(ballerinaExtInstance: BallerinaExtension, minSupportedVersion: number) { +/** + * Creates a version object for comparison. + * + * @param major Major version number + * @param minor Minor version number + * @param patch Patch version number + * @returns A version object with major, minor, and patch components + * + * @example + * // Version 2201.1.30 + * createVersionNumber(2201, 1, 30) + * // Version 2201.12.10 + * createVersionNumber(2201, 12, 10) + */ +export function createVersionNumber( + major: number, + minor: number, + patch: number +): { major: number; minor: number; patch: number } { + return { major, minor, patch }; +} + +/** + * Compares two versions using semantic versioning rules. + * Returns true if current version >= minimum version. + * + * @param current Current version components + * @param minimum Minimum required version components + * @returns true if current >= minimum + */ +function compareVersions( + current: { major: number; minor: number; patch: number }, + minimum: { major: number; minor: number; patch: number } +): boolean { + // Compare major version first + if (current.major !== minimum.major) { + return current.major > minimum.major; + } + + // Major versions are equal, compare minor + if (current.minor !== minimum.minor) { + return current.minor > minimum.minor; + } + + // Major and minor are equal, compare patch + return current.patch >= minimum.patch; +} + +/** + * Compares the current Ballerina version against a minimum required version. + * Only returns true for GA (non-preview/alpha/beta) versions that meet or exceed the minimum. + * + * @param ballerinaExtInstance The Ballerina extension instance + * @param minSupportedVersion Minimum version (use createVersionNumber helper to generate) + * @returns true if current version is GA and meets minimum requirement + * + * @example + * // Check if version is at least 2201.1.30 + * isSupportedSLVersion(ext, createVersionNumber(2201, 1, 30)) + */ +export function isSupportedSLVersion( + ballerinaExtInstance: BallerinaExtension, + minSupportedVersion: { major: number; minor: number; patch: number } +) { const ballerinaVersion: string = ballerinaExtInstance.ballerinaVersion.toLocaleLowerCase(); const isGA: boolean = !ballerinaVersion.includes(VERSION.ALPHA) && !ballerinaVersion.includes(VERSION.BETA) && !ballerinaVersion.includes(VERSION.PREVIEW); + if (!isGA) { + return false; + } + + // Parse current version const regex = /(\d+)\.(\d+)\.(\d+)/; const match = ballerinaVersion.match(regex); - const currentVersionNumber = match ? Number(match.slice(1).join("")) : 0; - - if (minSupportedVersion <= currentVersionNumber && isGA) { - return true; + if (!match) { + return false; } - return false; + + const currentVersion = { + major: Number(match[1]), + minor: Number(match[2]), + patch: Number(match[3]) + }; + + // Compare versions component by component + return compareVersions(currentVersion, minSupportedVersion); } export function checkIsBI(uri: Uri): boolean { diff --git a/workspaces/ballerina/ballerina-extension/src/utils/server/server.ts b/workspaces/ballerina/ballerina-extension/src/utils/server/server.ts index df95ea9b0e0..d817676ef4c 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/server/server.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/server/server.ts @@ -21,7 +21,7 @@ import { debug, log } from '../logger'; import { ServerOptions, ExecutableOptions } from 'vscode-languageclient/node'; import { isWindows } from '..'; import { BallerinaExtension } from '../../core'; -import { isSupportedSLVersion } from '../config'; +import { isSupportedSLVersion, createVersionNumber } from '../config'; import * as fs from 'fs'; import * as path from 'path'; import { orderBy } from 'lodash'; @@ -150,7 +150,7 @@ export function findHighestVersionJdk(directory: string): string | null { export function getServerOptions(extension: BallerinaExtension): ServerOptions { debug('Getting server options.'); // Check if user wants to use Ballerina CLI language server or if version requires it - const BI_SUPPORTED_MINIMUM_VERSION = 2201123; // 2201.12.3 + const BI_SUPPORTED_MINIMUM_VERSION = createVersionNumber(2201, 12, 3); // Version 2201.12.3 if (extension?.useDistributionLanguageServer() || !isSupportedSLVersion(extension, BI_SUPPORTED_MINIMUM_VERSION)) { return getServerOptionsUsingCLI(extension); } else { diff --git a/workspaces/ballerina/record-creator/src/CreateRecord/index.tsx b/workspaces/ballerina/record-creator/src/CreateRecord/index.tsx index d63249d496f..f51c66b75e5 100644 --- a/workspaces/ballerina/record-creator/src/CreateRecord/index.tsx +++ b/workspaces/ballerina/record-creator/src/CreateRecord/index.tsx @@ -23,7 +23,7 @@ import { RecordConfigTypeSelector } from "../RecordConfigTypeSelector"; import { RecordFromJson } from "../RecordFromJson"; import { RecordFromXml } from "../RecordFromXml"; import { Context } from "../Context"; -import { isSupportedSLVersion } from "../components/FormComponents/Utils"; +import { isSupportedSLVersion, createVersionNumber } from "../components/FormComponents/Utils"; import { FormContainer } from "../style"; enum ConfigState { @@ -67,7 +67,7 @@ export function CreateRecord(props: CreateRecordProps) { const checkBallerinVersion = () => { if (ballerinaVersion) { - return isSupportedSLVersion(ballerinaVersion, 220172); + return isSupportedSLVersion(ballerinaVersion, createVersionNumber(2201, 7, 2)); } return false; }; diff --git a/workspaces/ballerina/record-creator/src/components/FormComponents/Utils/index.tsx b/workspaces/ballerina/record-creator/src/components/FormComponents/Utils/index.tsx index 8ff687ef61a..dec23f10840 100644 --- a/workspaces/ballerina/record-creator/src/components/FormComponents/Utils/index.tsx +++ b/workspaces/ballerina/record-creator/src/components/FormComponents/Utils/index.tsx @@ -109,21 +109,91 @@ async function sendDidChange(docUri: string, content: string, langServerRpcClien }); } -export function isSupportedSLVersion(balVersion: string, minSupportedVersion: number) { +/** + * Creates a version object for comparison. + * + * @param major Major version number + * @param minor Minor version number + * @param patch Patch version number + * @returns A version object with major, minor, and patch components + * + * @example + * // Version 2201.1.30 + * createVersionNumber(2201, 1, 30) + * // Version 2201.12.10 + * createVersionNumber(2201, 12, 10) + */ +export function createVersionNumber(major: number, minor: number, patch: number): { major: number; minor: number; patch: number } { + return { major, minor, patch }; +} + +/** + * Compares two versions using semantic versioning rules. + * Returns true if current version >= minimum version. + * + * @param current Current version components + * @param minimum Minimum required version components + * @returns true if current >= minimum + */ +function compareVersions( + current: { major: number; minor: number; patch: number }, + minimum: { major: number; minor: number; patch: number } +): boolean { + // Compare major version first + if (current.major !== minimum.major) { + return current.major > minimum.major; + } + + // Major versions are equal, compare minor + if (current.minor !== minimum.minor) { + return current.minor > minimum.minor; + } + + // Major and minor are equal, compare patch + return current.patch >= minimum.patch; +} + +/** + * Compares the current Ballerina version against a minimum required version. + * Only returns true for GA (non-preview/alpha/beta) versions that meet or exceed the minimum. + * + * @param balVersion The current Ballerina version string + * @param minSupportedVersion Minimum version (use createVersionNumber helper to generate) + * @returns true if current version is GA and meets minimum requirement + * + * @example + * // Check if version is at least 2201.1.30 + * isSupportedSLVersion("2201.1.30", createVersionNumber(2201, 1, 30)) + */ +export function isSupportedSLVersion( + balVersion: string, + minSupportedVersion: { major: number; minor: number; patch: number } +) { const ballerinaVersion: string = balVersion.toLocaleLowerCase(); const isGA: boolean = !ballerinaVersion.includes(VERSION.ALPHA) && !ballerinaVersion.includes(VERSION.BETA) && !ballerinaVersion.includes(VERSION.PREVIEW); + if (!isGA) { + return false; + } + + // Parse current version const regex = /(\d+)\.(\d+)\.(\d+)/; const match = ballerinaVersion.match(regex); - const currentVersionNumber = match ? Number(match.slice(1).join("")) : 0; - - if (minSupportedVersion <= currentVersionNumber && isGA) { - return true; + if (!match) { + return false; } - return false; + + const currentVersion = { + major: Number(match[1]), + minor: Number(match[2]), + patch: Number(match[3]) + }; + + // Compare versions component by component + return compareVersions(currentVersion, minSupportedVersion); } export function getTooltipIconComponent(title: string): React.ReactNode { diff --git a/workspaces/ballerina/type-editor/src/CreateRecord/index.tsx b/workspaces/ballerina/type-editor/src/CreateRecord/index.tsx index d63249d496f..f51c66b75e5 100644 --- a/workspaces/ballerina/type-editor/src/CreateRecord/index.tsx +++ b/workspaces/ballerina/type-editor/src/CreateRecord/index.tsx @@ -23,7 +23,7 @@ import { RecordConfigTypeSelector } from "../RecordConfigTypeSelector"; import { RecordFromJson } from "../RecordFromJson"; import { RecordFromXml } from "../RecordFromXml"; import { Context } from "../Context"; -import { isSupportedSLVersion } from "../components/FormComponents/Utils"; +import { isSupportedSLVersion, createVersionNumber } from "../components/FormComponents/Utils"; import { FormContainer } from "../style"; enum ConfigState { @@ -67,7 +67,7 @@ export function CreateRecord(props: CreateRecordProps) { const checkBallerinVersion = () => { if (ballerinaVersion) { - return isSupportedSLVersion(ballerinaVersion, 220172); + return isSupportedSLVersion(ballerinaVersion, createVersionNumber(2201, 7, 2)); } return false; }; diff --git a/workspaces/ballerina/type-editor/src/components/FormComponents/Utils/index.tsx b/workspaces/ballerina/type-editor/src/components/FormComponents/Utils/index.tsx index 8ff687ef61a..dec23f10840 100644 --- a/workspaces/ballerina/type-editor/src/components/FormComponents/Utils/index.tsx +++ b/workspaces/ballerina/type-editor/src/components/FormComponents/Utils/index.tsx @@ -109,21 +109,91 @@ async function sendDidChange(docUri: string, content: string, langServerRpcClien }); } -export function isSupportedSLVersion(balVersion: string, minSupportedVersion: number) { +/** + * Creates a version object for comparison. + * + * @param major Major version number + * @param minor Minor version number + * @param patch Patch version number + * @returns A version object with major, minor, and patch components + * + * @example + * // Version 2201.1.30 + * createVersionNumber(2201, 1, 30) + * // Version 2201.12.10 + * createVersionNumber(2201, 12, 10) + */ +export function createVersionNumber(major: number, minor: number, patch: number): { major: number; minor: number; patch: number } { + return { major, minor, patch }; +} + +/** + * Compares two versions using semantic versioning rules. + * Returns true if current version >= minimum version. + * + * @param current Current version components + * @param minimum Minimum required version components + * @returns true if current >= minimum + */ +function compareVersions( + current: { major: number; minor: number; patch: number }, + minimum: { major: number; minor: number; patch: number } +): boolean { + // Compare major version first + if (current.major !== minimum.major) { + return current.major > minimum.major; + } + + // Major versions are equal, compare minor + if (current.minor !== minimum.minor) { + return current.minor > minimum.minor; + } + + // Major and minor are equal, compare patch + return current.patch >= minimum.patch; +} + +/** + * Compares the current Ballerina version against a minimum required version. + * Only returns true for GA (non-preview/alpha/beta) versions that meet or exceed the minimum. + * + * @param balVersion The current Ballerina version string + * @param minSupportedVersion Minimum version (use createVersionNumber helper to generate) + * @returns true if current version is GA and meets minimum requirement + * + * @example + * // Check if version is at least 2201.1.30 + * isSupportedSLVersion("2201.1.30", createVersionNumber(2201, 1, 30)) + */ +export function isSupportedSLVersion( + balVersion: string, + minSupportedVersion: { major: number; minor: number; patch: number } +) { const ballerinaVersion: string = balVersion.toLocaleLowerCase(); const isGA: boolean = !ballerinaVersion.includes(VERSION.ALPHA) && !ballerinaVersion.includes(VERSION.BETA) && !ballerinaVersion.includes(VERSION.PREVIEW); + if (!isGA) { + return false; + } + + // Parse current version const regex = /(\d+)\.(\d+)\.(\d+)/; const match = ballerinaVersion.match(regex); - const currentVersionNumber = match ? Number(match.slice(1).join("")) : 0; - - if (minSupportedVersion <= currentVersionNumber && isGA) { - return true; + if (!match) { + return false; } - return false; + + const currentVersion = { + major: Number(match[1]), + minor: Number(match[2]), + patch: Number(match[3]) + }; + + // Compare versions component by component + return compareVersions(currentVersion, minSupportedVersion); } export function getTooltipIconComponent(title: string): React.ReactNode { diff --git a/workspaces/bi/bi-extension/src/biExtentionContext.ts b/workspaces/bi/bi-extension/src/biExtentionContext.ts index dfa13fe3303..91022f034e4 100644 --- a/workspaces/bi/bi-extension/src/biExtentionContext.ts +++ b/workspaces/bi/bi-extension/src/biExtentionContext.ts @@ -25,6 +25,7 @@ export class ExtensionVariables { public langClient!: ExtendedLangClientInterface; public biSupported?: boolean; public isNPSupported?: boolean; + public isWorkspaceSupported?: boolean; } export const extension = new ExtensionVariables(); From 148adbeab518060a9f725b7d5992743f53b59c94 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 11 Nov 2025 18:20:28 +0530 Subject: [PATCH 4/8] Add isSupportedSLVersion method and SemanticVersion interface to enhance version compatibility checks in the lang-client API --- .../ballerina-core/src/rpc-types/lang-client/index.ts | 3 ++- .../src/rpc-types/lang-client/interfaces.ts | 8 +++++++- .../ballerina-core/src/rpc-types/lang-client/rpc-type.ts | 3 ++- .../src/rpc-managers/lang-client/rpc-handler.ts | 5 ++++- .../src/rpc-managers/lang-client/rpc-manager.ts | 9 +++++++++ .../src/rpc-clients/lang-client/rpc-client.ts | 8 +++++++- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/index.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/index.ts index 9bc819ee589..da659413ea5 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/index.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/index.ts @@ -17,7 +17,7 @@ */ import { BallerinaPackagesParams, BallerinaProjectComponents, BallerinaSTParams, ComponentModels, ComponentModelsParams, ExecutorPositions, PartialST, PartialSTParams, ProjectDiagnosticsRequest, ProjectDiagnosticsResponse, STModifyParams, SymbolInfo, SymbolInfoParams, SyntaxTree, SyntaxTreeParams, TypeFromExpressionParams, TypeFromSymbolParams, TypesFromFnDefinitionParams } from "../../interfaces/extended-lang-client"; -import { BallerinaVersionResponse, CompletionRequest, CompletionResponse, DiagnosticsResponse, CodeActionRequest, CodeActionResponse, RenameRequest, RenameResponse, DefinitionPositionRequest, UpdateFileContentRequest, UpdateFileContentResponse, DefinitionResponse, ExecutorPositionsRequest, DidCloseRequest, TypesFromExpressionResponse, TypesFromSymbolResponse, DidOpenRequest, DidChangeRequest } from "./interfaces"; +import { BallerinaVersionResponse, CompletionRequest, CompletionResponse, DiagnosticsResponse, CodeActionRequest, CodeActionResponse, RenameRequest, RenameResponse, DefinitionPositionRequest, UpdateFileContentRequest, UpdateFileContentResponse, DefinitionResponse, ExecutorPositionsRequest, DidCloseRequest, TypesFromExpressionResponse, TypesFromSymbolResponse, DidOpenRequest, DidChangeRequest, SemanticVersion } from "./interfaces"; export interface LangClientAPI { getSyntaxTree: () => Promise; @@ -25,6 +25,7 @@ export interface LangClientAPI { getSTByRange: (params: BallerinaSTParams) => Promise; getBallerinaProjectComponents: (params: BallerinaPackagesParams) => Promise; getBallerinaVersion: () => Promise; + isSupportedSLVersion: (params: SemanticVersion) => Promise; getCompletion: (params: CompletionRequest) => Promise; getDiagnostics: (params: SyntaxTreeParams) => Promise; getProjectDiagnostics: (params: ProjectDiagnosticsRequest) => Promise; diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/interfaces.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/interfaces.ts index f9724336ad6..7ba401686ce 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/interfaces.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/interfaces.ts @@ -163,4 +163,10 @@ export interface TypesFromSymbolResponse { export interface ExecutorPositionsResponse { executorPositions?: ExecutorPosition[]; -} \ No newline at end of file +} + +export interface SemanticVersion { + major: number; + minor: number; + patch: number; +} diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/rpc-type.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/rpc-type.ts index bfb481eef1b..9aa69f5dea8 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/rpc-type.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/lang-client/rpc-type.ts @@ -18,7 +18,7 @@ * THIS FILE INCLUDES AUTO GENERATED CODE */ import { BallerinaPackagesParams, BallerinaProjectComponents, BallerinaSTParams, ComponentModels, ComponentModelsParams, ExecutorPositions, PartialST, PartialSTParams, ProjectDiagnosticsRequest, ProjectDiagnosticsResponse, STModifyParams, SymbolInfo, SymbolInfoParams, SyntaxTree, SyntaxTreeParams, TypeFromExpressionParams, TypeFromSymbolParams, TypesFromFnDefinitionParams } from "../../interfaces/extended-lang-client"; -import { BallerinaVersionResponse, CompletionRequest, CompletionResponse, DiagnosticsResponse, CodeActionRequest, CodeActionResponse, RenameRequest, RenameResponse, DefinitionPositionRequest, UpdateFileContentRequest, UpdateFileContentResponse, DefinitionResponse, ExecutorPositionsRequest, DidCloseRequest, TypesFromExpressionResponse, TypesFromSymbolResponse, DidOpenRequest, DidChangeRequest } from "./interfaces"; +import { BallerinaVersionResponse, CompletionRequest, CompletionResponse, DiagnosticsResponse, CodeActionRequest, CodeActionResponse, RenameRequest, RenameResponse, DefinitionPositionRequest, UpdateFileContentRequest, UpdateFileContentResponse, DefinitionResponse, ExecutorPositionsRequest, DidCloseRequest, TypesFromExpressionResponse, TypesFromSymbolResponse, DidOpenRequest, DidChangeRequest, SemanticVersion } from "./interfaces"; import { RequestType, NotificationType } from "vscode-messenger-common"; const _preFix = "lang-client"; @@ -27,6 +27,7 @@ export const getST: RequestType = { method: `${_pr export const getSTByRange: RequestType = { method: `${_preFix}/getSTByRange` }; export const getBallerinaProjectComponents: RequestType = { method: `${_preFix}/getBallerinaProjectComponents` }; export const getBallerinaVersion: RequestType = { method: `${_preFix}/getBallerinaVersion` }; +export const isSupportedSLVersion: RequestType = { method: `${_preFix}/isSupportedSLVersion` }; export const getCompletion: RequestType = { method: `${_preFix}/getCompletion` }; export const getDiagnostics: RequestType = { method: `${_preFix}/getDiagnostics` }; export const getProjectDiagnostics: RequestType = { method: `${_preFix}/getProjectDiagnostics` }; diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-handler.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-handler.ts index 120e15d7bac..26e1871d0dc 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-handler.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-handler.ts @@ -45,6 +45,7 @@ import { didOpen, getBallerinaProjectComponents, getBallerinaVersion, + isSupportedSLVersion, getCompletion, getDefinitionPosition, getDiagnostics, @@ -66,7 +67,8 @@ import { getTypesFromFnDefinition, rename, stModify, - updateFileContent + updateFileContent, + SemanticVersion } from "@wso2/ballerina-core"; import { Messenger } from "vscode-messenger"; import { LangClientRpcManager } from "./rpc-manager"; @@ -78,6 +80,7 @@ export function registerLangClientRpcHandlers(messenger: Messenger) { messenger.onRequest(getSTByRange, (args: BallerinaSTParams) => rpcManger.getSTByRange(args)); messenger.onRequest(getBallerinaProjectComponents, (args: BallerinaPackagesParams) => rpcManger.getBallerinaProjectComponents(args)); messenger.onRequest(getBallerinaVersion, () => rpcManger.getBallerinaVersion()); + messenger.onRequest(isSupportedSLVersion, (args: SemanticVersion) => rpcManger.isSupportedSLVersion(args)); messenger.onRequest(getCompletion, (args: CompletionRequest) => rpcManger.getCompletion(args)); messenger.onRequest(getDiagnostics, (args: SyntaxTreeParams) => rpcManger.getDiagnostics(args)); messenger.onRequest(getProjectDiagnostics, (args: ProjectDiagnosticsRequest) => rpcManger.getProjectDiagnostics(args)); diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-manager.ts index c6d7f63b837..8e22b7ae187 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-manager.ts @@ -22,6 +22,7 @@ import { BallerinaProjectComponents, BallerinaSTParams, BallerinaVersionResponse, + SemanticVersion, CodeActionRequest, CodeActionResponse, CompletionRequest, @@ -63,6 +64,7 @@ import { URI } from "vscode-uri"; import { extension } from "../../BalExtensionContext"; import { StateMachine } from "../../stateMachine"; import { modifyFileContent } from "../../utils/modification"; +import { isSupportedSLVersion as isSupportedSLVersionUtil } from "../../utils/config"; export class LangClientRpcManager implements LangClientAPI { @@ -283,6 +285,13 @@ export class LangClientRpcManager implements LangClientAPI { }); } + async isSupportedSLVersion(params: SemanticVersion): Promise { + return new Promise(async (resolve) => { + const isSupported = isSupportedSLVersionUtil(extension.ballerinaExtInstance, params); + resolve(isSupported); + }); + } + async getPackageComponentModels(params: ComponentModelsParams): Promise { return new Promise(async (resolve) => { const components = await StateMachine.langClient().getPackageComponentModels(params) as ComponentModels; diff --git a/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/lang-client/rpc-client.ts b/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/lang-client/rpc-client.ts index f8ca9980d51..d32b268b933 100644 --- a/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/lang-client/rpc-client.ts +++ b/workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/lang-client/rpc-client.ts @@ -62,6 +62,7 @@ import { didOpen, getBallerinaProjectComponents, getBallerinaVersion, + isSupportedSLVersion, getCompletion, getDefinitionPosition, getDiagnostics, @@ -83,7 +84,8 @@ import { getTypesFromFnDefinition, rename, stModify, - updateFileContent + updateFileContent, + SemanticVersion } from "@wso2/ballerina-core"; import { HOST_EXTENSION } from "vscode-messenger-common"; import { Messenger } from "vscode-messenger-webview"; @@ -115,6 +117,10 @@ export class LangClientRpcClient implements LangClientAPI { return this._messenger.sendRequest(getBallerinaVersion, HOST_EXTENSION); } + isSupportedSLVersion(params: SemanticVersion): Promise { + return this._messenger.sendRequest(isSupportedSLVersion, HOST_EXTENSION, params); + } + getCompletion(params: CompletionRequest): Promise { return this._messenger.sendRequest(getCompletion, HOST_EXTENSION, params); } From fa4eb1f618c1223fe1ac4ef1d4cfb7db08f1dc71 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 11 Nov 2025 18:22:36 +0530 Subject: [PATCH 5/8] Unify version compatibility checks --- .../ballerina-extension/src/utils/config.ts | 10 +-- .../record-creator/src/CreateRecord/index.tsx | 14 +-- .../record-creator/src/Hooks/index.tsx | 31 +++++++ .../src/RecordEditor/RecordEditorWrapper.tsx | 20 ++++- .../components/FormComponents/Utils/index.tsx | 88 ------------------- .../ballerina/record-creator/src/types.ts | 1 + .../type-editor/src/CreateRecord/index.tsx | 14 +-- .../ballerina/type-editor/src/Hooks/index.tsx | 31 +++++++ .../src/RecordEditor/RecordEditorWrapper.tsx | 20 ++++- .../components/FormComponents/Utils/index.tsx | 88 ------------------- workspaces/ballerina/type-editor/src/types.ts | 1 + 11 files changed, 107 insertions(+), 211 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/utils/config.ts b/workspaces/ballerina/ballerina-extension/src/utils/config.ts index 4d38326992a..16407d567ae 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/config.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/config.ts @@ -16,7 +16,7 @@ * under the License. */ -import { PackageTomlValues, SCOPE, WorkspaceTomlValues } from '@wso2/ballerina-core'; +import { SemanticVersion, PackageTomlValues, SCOPE, WorkspaceTomlValues } from '@wso2/ballerina-core'; import { BallerinaExtension } from '../core'; import { WorkspaceConfiguration, workspace, Uri, RelativePattern } from 'vscode'; import * as fs from 'fs'; @@ -116,7 +116,7 @@ export function createVersionNumber( major: number, minor: number, patch: number -): { major: number; minor: number; patch: number } { +): SemanticVersion { return { major, minor, patch }; } @@ -129,8 +129,8 @@ export function createVersionNumber( * @returns true if current >= minimum */ function compareVersions( - current: { major: number; minor: number; patch: number }, - minimum: { major: number; minor: number; patch: number } + current: SemanticVersion, + minimum: SemanticVersion ): boolean { // Compare major version first if (current.major !== minimum.major) { @@ -160,7 +160,7 @@ function compareVersions( */ export function isSupportedSLVersion( ballerinaExtInstance: BallerinaExtension, - minSupportedVersion: { major: number; minor: number; patch: number } + minSupportedVersion: SemanticVersion ) { const ballerinaVersion: string = ballerinaExtInstance.ballerinaVersion.toLocaleLowerCase(); const isGA: boolean = !ballerinaVersion.includes(VERSION.ALPHA) && !ballerinaVersion.includes(VERSION.BETA) && !ballerinaVersion.includes(VERSION.PREVIEW); diff --git a/workspaces/ballerina/record-creator/src/CreateRecord/index.tsx b/workspaces/ballerina/record-creator/src/CreateRecord/index.tsx index f51c66b75e5..ea7bb748fe8 100644 --- a/workspaces/ballerina/record-creator/src/CreateRecord/index.tsx +++ b/workspaces/ballerina/record-creator/src/CreateRecord/index.tsx @@ -23,7 +23,6 @@ import { RecordConfigTypeSelector } from "../RecordConfigTypeSelector"; import { RecordFromJson } from "../RecordFromJson"; import { RecordFromXml } from "../RecordFromXml"; import { Context } from "../Context"; -import { isSupportedSLVersion, createVersionNumber } from "../components/FormComponents/Utils"; import { FormContainer } from "../style"; enum ConfigState { @@ -43,9 +42,7 @@ export interface CreateRecordProps { export function CreateRecord(props: CreateRecordProps) { const { isDataMapper, showHeader, onSave, onCancel, onUpdate } = props; - const { - props: { targetPosition, ballerinaVersion }, - } = useContext(Context); + const { props: { isVersionCompatible } } = useContext(Context); const [editorState, setEditorState] = useState(ConfigState.STATE_SELECTOR); @@ -65,20 +62,13 @@ export function CreateRecord(props: CreateRecordProps) { onSave(value, pos); }; - const checkBallerinVersion = () => { - if (ballerinaVersion) { - return isSupportedSLVersion(ballerinaVersion, createVersionNumber(2201, 7, 2)); - } - return false; - }; - return ( <> {editorState === ConfigState.STATE_SELECTOR && ( diff --git a/workspaces/ballerina/record-creator/src/Hooks/index.tsx b/workspaces/ballerina/record-creator/src/Hooks/index.tsx index 5d1b9271b46..07e48ea7efa 100644 --- a/workspaces/ballerina/record-creator/src/Hooks/index.tsx +++ b/workspaces/ballerina/record-creator/src/Hooks/index.tsx @@ -51,6 +51,37 @@ export const useBallerinaVersion = ( return { ballerinaVersion, isFetching, isError, refetch }; }; +export const useVersionCompatibility = ( + langServerRpcClient: LangClientRpcClient +): { + isVersionCompatible: boolean; + isFetching: boolean; + isError: boolean; + refetch: any; +} => { + const checkVersionCompatibility = async () => { + try { + const isCompatible = await langServerRpcClient.isSupportedSLVersion({major: 2201, minor: 7, patch: 2}); + return isCompatible; + } catch (networkError: any) { + console.error("Error while checking version compatibility in record creator", networkError); + } + }; + + const { + data: isVersionCompatible, + isFetching, + isError, + refetch, + } = useQuery({ + queryKey: ["checkVersionCompatibility"], + queryFn: checkVersionCompatibility, + networkMode: 'always' + }); + + return { isVersionCompatible, isFetching, isError, refetch }; +}; + export const useFullST = ( filePath: string, langServerRpcClient: LangClientRpcClient diff --git a/workspaces/ballerina/record-creator/src/RecordEditor/RecordEditorWrapper.tsx b/workspaces/ballerina/record-creator/src/RecordEditor/RecordEditorWrapper.tsx index 4c365d880b6..f1379a5f85f 100644 --- a/workspaces/ballerina/record-creator/src/RecordEditor/RecordEditorWrapper.tsx +++ b/workspaces/ballerina/record-creator/src/RecordEditor/RecordEditorWrapper.tsx @@ -18,7 +18,7 @@ // tslint:disable: jsx-no-multiline-js import React, { useMemo } from "react"; import { Context } from "../Context"; -import { useBallerinaProjectComponent, useBallerinaVersion, useFullST } from "../Hooks"; +import { useBallerinaProjectComponent, useBallerinaVersion, useFullST, useVersionCompatibility } from "../Hooks"; import { RecordEditorC } from "./RecordEditorC"; import { RecordEditorProps } from "."; @@ -42,12 +42,18 @@ export function RecordEditorWrapper(props: RecordEditorProps) { onUpdate, } = props; const { ballerinaVersion, isFetching: isFetchingBallerinaVersion } = useBallerinaVersion(langServerRpcClient); + const { isVersionCompatible, isFetching: isFetchingVersionCompatibility } = useVersionCompatibility(langServerRpcClient); const { fullST, isFetching: isFetchingFullST } = useFullST(currentFile.path, langServerRpcClient); const { ballerinaProjectComponents, isFetching: isFetchingBallerinaProjectComponents } = useBallerinaProjectComponent(currentFile.path, langServerRpcClient); const contextValue = useMemo(() => { - if (isFetchingBallerinaVersion || isFetchingFullST || isFetchingBallerinaProjectComponents) { + if (isFetchingBallerinaVersion || + isFetchingFullST || + isFetchingBallerinaProjectComponents || + isFetchingVersionCompatibility || + !isVersionCompatible + ) { return undefined; } @@ -61,6 +67,7 @@ export function RecordEditorWrapper(props: RecordEditorProps) { importStatements, currentReferences, ballerinaVersion, + isVersionCompatible, fullST : fullSyntaxTree || fullST.syntaxTree, ballerinaProjectComponents, }, @@ -70,7 +77,14 @@ export function RecordEditorWrapper(props: RecordEditorProps) { onClose, }, }; - }, [isFetchingBallerinaVersion, isFetchingFullST, isFetchingBallerinaProjectComponents, fullSyntaxTree]); + }, [ + isFetchingBallerinaVersion, + isFetchingVersionCompatibility, + isFetchingFullST, + isFetchingBallerinaProjectComponents, + fullSyntaxTree, + isVersionCompatible, + ]); return ( diff --git a/workspaces/ballerina/record-creator/src/components/FormComponents/Utils/index.tsx b/workspaces/ballerina/record-creator/src/components/FormComponents/Utils/index.tsx index dec23f10840..5242193147a 100644 --- a/workspaces/ballerina/record-creator/src/components/FormComponents/Utils/index.tsx +++ b/workspaces/ballerina/record-creator/src/components/FormComponents/Utils/index.tsx @@ -22,7 +22,6 @@ import { LangClientRpcClient } from "@wso2/ballerina-rpc-client"; import { DiagnosticData, DiagnosticsResponse } from "@wso2/ballerina-core"; import { Codicon, Tooltip, Typography } from "@wso2/ui-toolkit"; import * as monaco from "monaco-editor"; -import { VERSION } from "../../../types"; export const FILE_SCHEME = "file://"; export const EXPR_SCHEME = "expr://"; @@ -109,93 +108,6 @@ async function sendDidChange(docUri: string, content: string, langServerRpcClien }); } -/** - * Creates a version object for comparison. - * - * @param major Major version number - * @param minor Minor version number - * @param patch Patch version number - * @returns A version object with major, minor, and patch components - * - * @example - * // Version 2201.1.30 - * createVersionNumber(2201, 1, 30) - * // Version 2201.12.10 - * createVersionNumber(2201, 12, 10) - */ -export function createVersionNumber(major: number, minor: number, patch: number): { major: number; minor: number; patch: number } { - return { major, minor, patch }; -} - -/** - * Compares two versions using semantic versioning rules. - * Returns true if current version >= minimum version. - * - * @param current Current version components - * @param minimum Minimum required version components - * @returns true if current >= minimum - */ -function compareVersions( - current: { major: number; minor: number; patch: number }, - minimum: { major: number; minor: number; patch: number } -): boolean { - // Compare major version first - if (current.major !== minimum.major) { - return current.major > minimum.major; - } - - // Major versions are equal, compare minor - if (current.minor !== minimum.minor) { - return current.minor > minimum.minor; - } - - // Major and minor are equal, compare patch - return current.patch >= minimum.patch; -} - -/** - * Compares the current Ballerina version against a minimum required version. - * Only returns true for GA (non-preview/alpha/beta) versions that meet or exceed the minimum. - * - * @param balVersion The current Ballerina version string - * @param minSupportedVersion Minimum version (use createVersionNumber helper to generate) - * @returns true if current version is GA and meets minimum requirement - * - * @example - * // Check if version is at least 2201.1.30 - * isSupportedSLVersion("2201.1.30", createVersionNumber(2201, 1, 30)) - */ -export function isSupportedSLVersion( - balVersion: string, - minSupportedVersion: { major: number; minor: number; patch: number } -) { - const ballerinaVersion: string = balVersion.toLocaleLowerCase(); - const isGA: boolean = - !ballerinaVersion.includes(VERSION.ALPHA) && - !ballerinaVersion.includes(VERSION.BETA) && - !ballerinaVersion.includes(VERSION.PREVIEW); - - if (!isGA) { - return false; - } - - // Parse current version - const regex = /(\d+)\.(\d+)\.(\d+)/; - const match = ballerinaVersion.match(regex); - if (!match) { - return false; - } - - const currentVersion = { - major: Number(match[1]), - minor: Number(match[2]), - patch: Number(match[3]) - }; - - // Compare versions component by component - return compareVersions(currentVersion, minSupportedVersion); -} - export function getTooltipIconComponent(title: string): React.ReactNode { return ( {title}} position="bottom-end"> diff --git a/workspaces/ballerina/record-creator/src/types.ts b/workspaces/ballerina/record-creator/src/types.ts index 8320cda9e97..7c50d693321 100644 --- a/workspaces/ballerina/record-creator/src/types.ts +++ b/workspaces/ballerina/record-creator/src/types.ts @@ -88,6 +88,7 @@ export interface RecordCreatorContext { props: IStatementEditorComponentProps & { recordCreatorRpcClient: RecordCreatorRpcClient; ballerinaVersion: string; + isVersionCompatible: boolean; fullST: STNode; ballerinaProjectComponents: BallerinaProjectComponents; }; diff --git a/workspaces/ballerina/type-editor/src/CreateRecord/index.tsx b/workspaces/ballerina/type-editor/src/CreateRecord/index.tsx index f51c66b75e5..ea7bb748fe8 100644 --- a/workspaces/ballerina/type-editor/src/CreateRecord/index.tsx +++ b/workspaces/ballerina/type-editor/src/CreateRecord/index.tsx @@ -23,7 +23,6 @@ import { RecordConfigTypeSelector } from "../RecordConfigTypeSelector"; import { RecordFromJson } from "../RecordFromJson"; import { RecordFromXml } from "../RecordFromXml"; import { Context } from "../Context"; -import { isSupportedSLVersion, createVersionNumber } from "../components/FormComponents/Utils"; import { FormContainer } from "../style"; enum ConfigState { @@ -43,9 +42,7 @@ export interface CreateRecordProps { export function CreateRecord(props: CreateRecordProps) { const { isDataMapper, showHeader, onSave, onCancel, onUpdate } = props; - const { - props: { targetPosition, ballerinaVersion }, - } = useContext(Context); + const { props: { isVersionCompatible } } = useContext(Context); const [editorState, setEditorState] = useState(ConfigState.STATE_SELECTOR); @@ -65,20 +62,13 @@ export function CreateRecord(props: CreateRecordProps) { onSave(value, pos); }; - const checkBallerinVersion = () => { - if (ballerinaVersion) { - return isSupportedSLVersion(ballerinaVersion, createVersionNumber(2201, 7, 2)); - } - return false; - }; - return ( <> {editorState === ConfigState.STATE_SELECTOR && ( diff --git a/workspaces/ballerina/type-editor/src/Hooks/index.tsx b/workspaces/ballerina/type-editor/src/Hooks/index.tsx index 5d1b9271b46..07e48ea7efa 100644 --- a/workspaces/ballerina/type-editor/src/Hooks/index.tsx +++ b/workspaces/ballerina/type-editor/src/Hooks/index.tsx @@ -51,6 +51,37 @@ export const useBallerinaVersion = ( return { ballerinaVersion, isFetching, isError, refetch }; }; +export const useVersionCompatibility = ( + langServerRpcClient: LangClientRpcClient +): { + isVersionCompatible: boolean; + isFetching: boolean; + isError: boolean; + refetch: any; +} => { + const checkVersionCompatibility = async () => { + try { + const isCompatible = await langServerRpcClient.isSupportedSLVersion({major: 2201, minor: 7, patch: 2}); + return isCompatible; + } catch (networkError: any) { + console.error("Error while checking version compatibility in record creator", networkError); + } + }; + + const { + data: isVersionCompatible, + isFetching, + isError, + refetch, + } = useQuery({ + queryKey: ["checkVersionCompatibility"], + queryFn: checkVersionCompatibility, + networkMode: 'always' + }); + + return { isVersionCompatible, isFetching, isError, refetch }; +}; + export const useFullST = ( filePath: string, langServerRpcClient: LangClientRpcClient diff --git a/workspaces/ballerina/type-editor/src/RecordEditor/RecordEditorWrapper.tsx b/workspaces/ballerina/type-editor/src/RecordEditor/RecordEditorWrapper.tsx index 4c365d880b6..f1379a5f85f 100644 --- a/workspaces/ballerina/type-editor/src/RecordEditor/RecordEditorWrapper.tsx +++ b/workspaces/ballerina/type-editor/src/RecordEditor/RecordEditorWrapper.tsx @@ -18,7 +18,7 @@ // tslint:disable: jsx-no-multiline-js import React, { useMemo } from "react"; import { Context } from "../Context"; -import { useBallerinaProjectComponent, useBallerinaVersion, useFullST } from "../Hooks"; +import { useBallerinaProjectComponent, useBallerinaVersion, useFullST, useVersionCompatibility } from "../Hooks"; import { RecordEditorC } from "./RecordEditorC"; import { RecordEditorProps } from "."; @@ -42,12 +42,18 @@ export function RecordEditorWrapper(props: RecordEditorProps) { onUpdate, } = props; const { ballerinaVersion, isFetching: isFetchingBallerinaVersion } = useBallerinaVersion(langServerRpcClient); + const { isVersionCompatible, isFetching: isFetchingVersionCompatibility } = useVersionCompatibility(langServerRpcClient); const { fullST, isFetching: isFetchingFullST } = useFullST(currentFile.path, langServerRpcClient); const { ballerinaProjectComponents, isFetching: isFetchingBallerinaProjectComponents } = useBallerinaProjectComponent(currentFile.path, langServerRpcClient); const contextValue = useMemo(() => { - if (isFetchingBallerinaVersion || isFetchingFullST || isFetchingBallerinaProjectComponents) { + if (isFetchingBallerinaVersion || + isFetchingFullST || + isFetchingBallerinaProjectComponents || + isFetchingVersionCompatibility || + !isVersionCompatible + ) { return undefined; } @@ -61,6 +67,7 @@ export function RecordEditorWrapper(props: RecordEditorProps) { importStatements, currentReferences, ballerinaVersion, + isVersionCompatible, fullST : fullSyntaxTree || fullST.syntaxTree, ballerinaProjectComponents, }, @@ -70,7 +77,14 @@ export function RecordEditorWrapper(props: RecordEditorProps) { onClose, }, }; - }, [isFetchingBallerinaVersion, isFetchingFullST, isFetchingBallerinaProjectComponents, fullSyntaxTree]); + }, [ + isFetchingBallerinaVersion, + isFetchingVersionCompatibility, + isFetchingFullST, + isFetchingBallerinaProjectComponents, + fullSyntaxTree, + isVersionCompatible, + ]); return ( diff --git a/workspaces/ballerina/type-editor/src/components/FormComponents/Utils/index.tsx b/workspaces/ballerina/type-editor/src/components/FormComponents/Utils/index.tsx index dec23f10840..5242193147a 100644 --- a/workspaces/ballerina/type-editor/src/components/FormComponents/Utils/index.tsx +++ b/workspaces/ballerina/type-editor/src/components/FormComponents/Utils/index.tsx @@ -22,7 +22,6 @@ import { LangClientRpcClient } from "@wso2/ballerina-rpc-client"; import { DiagnosticData, DiagnosticsResponse } from "@wso2/ballerina-core"; import { Codicon, Tooltip, Typography } from "@wso2/ui-toolkit"; import * as monaco from "monaco-editor"; -import { VERSION } from "../../../types"; export const FILE_SCHEME = "file://"; export const EXPR_SCHEME = "expr://"; @@ -109,93 +108,6 @@ async function sendDidChange(docUri: string, content: string, langServerRpcClien }); } -/** - * Creates a version object for comparison. - * - * @param major Major version number - * @param minor Minor version number - * @param patch Patch version number - * @returns A version object with major, minor, and patch components - * - * @example - * // Version 2201.1.30 - * createVersionNumber(2201, 1, 30) - * // Version 2201.12.10 - * createVersionNumber(2201, 12, 10) - */ -export function createVersionNumber(major: number, minor: number, patch: number): { major: number; minor: number; patch: number } { - return { major, minor, patch }; -} - -/** - * Compares two versions using semantic versioning rules. - * Returns true if current version >= minimum version. - * - * @param current Current version components - * @param minimum Minimum required version components - * @returns true if current >= minimum - */ -function compareVersions( - current: { major: number; minor: number; patch: number }, - minimum: { major: number; minor: number; patch: number } -): boolean { - // Compare major version first - if (current.major !== minimum.major) { - return current.major > minimum.major; - } - - // Major versions are equal, compare minor - if (current.minor !== minimum.minor) { - return current.minor > minimum.minor; - } - - // Major and minor are equal, compare patch - return current.patch >= minimum.patch; -} - -/** - * Compares the current Ballerina version against a minimum required version. - * Only returns true for GA (non-preview/alpha/beta) versions that meet or exceed the minimum. - * - * @param balVersion The current Ballerina version string - * @param minSupportedVersion Minimum version (use createVersionNumber helper to generate) - * @returns true if current version is GA and meets minimum requirement - * - * @example - * // Check if version is at least 2201.1.30 - * isSupportedSLVersion("2201.1.30", createVersionNumber(2201, 1, 30)) - */ -export function isSupportedSLVersion( - balVersion: string, - minSupportedVersion: { major: number; minor: number; patch: number } -) { - const ballerinaVersion: string = balVersion.toLocaleLowerCase(); - const isGA: boolean = - !ballerinaVersion.includes(VERSION.ALPHA) && - !ballerinaVersion.includes(VERSION.BETA) && - !ballerinaVersion.includes(VERSION.PREVIEW); - - if (!isGA) { - return false; - } - - // Parse current version - const regex = /(\d+)\.(\d+)\.(\d+)/; - const match = ballerinaVersion.match(regex); - if (!match) { - return false; - } - - const currentVersion = { - major: Number(match[1]), - minor: Number(match[2]), - patch: Number(match[3]) - }; - - // Compare versions component by component - return compareVersions(currentVersion, minSupportedVersion); -} - export function getTooltipIconComponent(title: string): React.ReactNode { return ( {title}} position="bottom-end"> diff --git a/workspaces/ballerina/type-editor/src/types.ts b/workspaces/ballerina/type-editor/src/types.ts index 8320cda9e97..7c50d693321 100644 --- a/workspaces/ballerina/type-editor/src/types.ts +++ b/workspaces/ballerina/type-editor/src/types.ts @@ -88,6 +88,7 @@ export interface RecordCreatorContext { props: IStatementEditorComponentProps & { recordCreatorRpcClient: RecordCreatorRpcClient; ballerinaVersion: string; + isVersionCompatible: boolean; fullST: STNode; ballerinaProjectComponents: BallerinaProjectComponents; }; From 01026e2c5cbb0baa4791888f81f0b2f55e85f5f1 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 11 Nov 2025 18:37:41 +0530 Subject: [PATCH 6/8] Address review suggestions --- .../src/rpc-managers/lang-client/rpc-manager.ts | 5 +---- workspaces/ballerina/type-editor/src/Hooks/index.tsx | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-manager.ts b/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-manager.ts index 8e22b7ae187..ce3f5d9ab22 100644 --- a/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-manager.ts +++ b/workspaces/ballerina/ballerina-extension/src/rpc-managers/lang-client/rpc-manager.ts @@ -286,10 +286,7 @@ export class LangClientRpcManager implements LangClientAPI { } async isSupportedSLVersion(params: SemanticVersion): Promise { - return new Promise(async (resolve) => { - const isSupported = isSupportedSLVersionUtil(extension.ballerinaExtInstance, params); - resolve(isSupported); - }); + return isSupportedSLVersionUtil(extension.ballerinaExtInstance, params); } async getPackageComponentModels(params: ComponentModelsParams): Promise { diff --git a/workspaces/ballerina/type-editor/src/Hooks/index.tsx b/workspaces/ballerina/type-editor/src/Hooks/index.tsx index 07e48ea7efa..bfc35f4607b 100644 --- a/workspaces/ballerina/type-editor/src/Hooks/index.tsx +++ b/workspaces/ballerina/type-editor/src/Hooks/index.tsx @@ -64,7 +64,7 @@ export const useVersionCompatibility = ( const isCompatible = await langServerRpcClient.isSupportedSLVersion({major: 2201, minor: 7, patch: 2}); return isCompatible; } catch (networkError: any) { - console.error("Error while checking version compatibility in record creator", networkError); + console.error("Error while checking version compatibility in type editor", networkError); } }; From 6b0f290844b7459b09d0330cfcc78c895fca3167 Mon Sep 17 00:00:00 2001 From: madushajg Date: Tue, 11 Nov 2025 18:49:49 +0530 Subject: [PATCH 7/8] Address review suggestions --- workspaces/ballerina/record-creator/src/Hooks/index.tsx | 8 +++++--- .../src/RecordEditor/RecordEditorWrapper.tsx | 3 +-- workspaces/ballerina/type-editor/src/Hooks/index.tsx | 8 +++++--- .../type-editor/src/RecordEditor/RecordEditorWrapper.tsx | 3 +-- .../bi/bi-extension/src/project-explorer/activate.ts | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/workspaces/ballerina/record-creator/src/Hooks/index.tsx b/workspaces/ballerina/record-creator/src/Hooks/index.tsx index 07e48ea7efa..3fdc22e71df 100644 --- a/workspaces/ballerina/record-creator/src/Hooks/index.tsx +++ b/workspaces/ballerina/record-creator/src/Hooks/index.tsx @@ -20,13 +20,15 @@ import { BallerinaProjectComponents, SyntaxTreeResponse } from "@wso2/ballerina- import { LangClientRpcClient } from "@wso2/ballerina-rpc-client"; import { URI } from "vscode-uri"; +const RECORD_CREATOR_SUPPORTED_VERSION = {major: 2201, minor: 7, patch: 2}; + export const useBallerinaVersion = ( langServerRpcClient: LangClientRpcClient ): { ballerinaVersion: string; isFetching: boolean; isError: boolean; - refetch: any; + refetch: () => void; } => { const fetchBallerinaVersion = async () => { try { @@ -57,11 +59,11 @@ export const useVersionCompatibility = ( isVersionCompatible: boolean; isFetching: boolean; isError: boolean; - refetch: any; + refetch: () => void; } => { const checkVersionCompatibility = async () => { try { - const isCompatible = await langServerRpcClient.isSupportedSLVersion({major: 2201, minor: 7, patch: 2}); + const isCompatible = await langServerRpcClient.isSupportedSLVersion(RECORD_CREATOR_SUPPORTED_VERSION); return isCompatible; } catch (networkError: any) { console.error("Error while checking version compatibility in record creator", networkError); diff --git a/workspaces/ballerina/record-creator/src/RecordEditor/RecordEditorWrapper.tsx b/workspaces/ballerina/record-creator/src/RecordEditor/RecordEditorWrapper.tsx index f1379a5f85f..715e001caa4 100644 --- a/workspaces/ballerina/record-creator/src/RecordEditor/RecordEditorWrapper.tsx +++ b/workspaces/ballerina/record-creator/src/RecordEditor/RecordEditorWrapper.tsx @@ -51,8 +51,7 @@ export function RecordEditorWrapper(props: RecordEditorProps) { if (isFetchingBallerinaVersion || isFetchingFullST || isFetchingBallerinaProjectComponents || - isFetchingVersionCompatibility || - !isVersionCompatible + isFetchingVersionCompatibility ) { return undefined; } diff --git a/workspaces/ballerina/type-editor/src/Hooks/index.tsx b/workspaces/ballerina/type-editor/src/Hooks/index.tsx index bfc35f4607b..703499e7b7a 100644 --- a/workspaces/ballerina/type-editor/src/Hooks/index.tsx +++ b/workspaces/ballerina/type-editor/src/Hooks/index.tsx @@ -20,13 +20,15 @@ import { BallerinaProjectComponents, SyntaxTreeResponse } from "@wso2/ballerina- import { LangClientRpcClient } from "@wso2/ballerina-rpc-client"; import { URI } from "vscode-uri"; +const TYPE_EDITOR_SUPPORTED_VERSION = {major: 2201, minor: 7, patch: 2}; + export const useBallerinaVersion = ( langServerRpcClient: LangClientRpcClient ): { ballerinaVersion: string; isFetching: boolean; isError: boolean; - refetch: any; + refetch: () => void; } => { const fetchBallerinaVersion = async () => { try { @@ -57,11 +59,11 @@ export const useVersionCompatibility = ( isVersionCompatible: boolean; isFetching: boolean; isError: boolean; - refetch: any; + refetch: () => void; } => { const checkVersionCompatibility = async () => { try { - const isCompatible = await langServerRpcClient.isSupportedSLVersion({major: 2201, minor: 7, patch: 2}); + const isCompatible = await langServerRpcClient.isSupportedSLVersion(TYPE_EDITOR_SUPPORTED_VERSION); return isCompatible; } catch (networkError: any) { console.error("Error while checking version compatibility in type editor", networkError); diff --git a/workspaces/ballerina/type-editor/src/RecordEditor/RecordEditorWrapper.tsx b/workspaces/ballerina/type-editor/src/RecordEditor/RecordEditorWrapper.tsx index f1379a5f85f..715e001caa4 100644 --- a/workspaces/ballerina/type-editor/src/RecordEditor/RecordEditorWrapper.tsx +++ b/workspaces/ballerina/type-editor/src/RecordEditor/RecordEditorWrapper.tsx @@ -51,8 +51,7 @@ export function RecordEditorWrapper(props: RecordEditorProps) { if (isFetchingBallerinaVersion || isFetchingFullST || isFetchingBallerinaProjectComponents || - isFetchingVersionCompatibility || - !isVersionCompatible + isFetchingVersionCompatibility ) { return undefined; } diff --git a/workspaces/bi/bi-extension/src/project-explorer/activate.ts b/workspaces/bi/bi-extension/src/project-explorer/activate.ts index f7b811b90eb..5b5aec60337 100644 --- a/workspaces/bi/bi-extension/src/project-explorer/activate.ts +++ b/workspaces/bi/bi-extension/src/project-explorer/activate.ts @@ -56,7 +56,7 @@ function createProjectTree(dataProvider: ProjectExplorerEntryProvider) { function registerBallerinaCommands(dataProvider: ProjectExplorerEntryProvider, isBI: boolean, isBalWorkspace?: boolean) { commands.registerCommand(BI_COMMANDS.REFRESH_COMMAND, () => dataProvider.refresh()); - commands.executeCommand('setContext', 'BI.isWorkspaceSupported', extension.isWorkspaceSupported); + commands.executeCommand('setContext', 'BI.isWorkspaceSupported', extension.isWorkspaceSupported ?? false); if (extension.isWorkspaceSupported && isBalWorkspace) { commands.executeCommand('setContext', 'BI.isBalWorkspace', true); From 28e958f61baece9e2fa3d4a5abfb65daccb454da Mon Sep 17 00:00:00 2001 From: choreo-cicd Date: Tue, 11 Nov 2025 16:27:34 +0000 Subject: [PATCH 8/8] Update version to ballerina-integrator-1.5.1 --- 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 36347194d47..526952b3fea 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.6.0", + "version": "5.6.1", "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 492ddd04d17..0d460f33f68 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.5.0", + "version": "1.5.1", "publisher": "wso2", "icon": "resources/images/wso2-ballerina-integrator-logo.png", "repository": {