88 NodeParameter ,
99 ReferenceValue
1010} from "@code0-tech/sagittarius-graphql-types" ;
11- import { createCompilerHost , getSharedTypeDeclarations , ValidationResult } from "../utils" ;
11+ import { createCompilerHost , generateFlowSourceCode , getSharedTypeDeclarations , ValidationResult } from "../utils" ;
1212
1313const sanitizeId = ( id : string ) => id . replace ( / [ ^ a - z A - Z 0 - 9 ] / g, '_' ) ;
1414
@@ -20,101 +20,9 @@ export const getFlowValidation = (
2020 functions ?: FunctionDefinition [ ] ,
2121 dataTypes ?: DataType [ ]
2222) : ValidationResult => {
23- const visited = new Set < string > ( ) ;
24- const nodes = flow ?. nodes ?. nodes || [ ] ;
2523
26- const funcMap = new Map ( functions ?. map ( f => [ f . identifier , f ] ) ) ;
2724
28- /**
29- * Recursive function to generate TypeScript code for a node and its execution path.
30- */
31- const generateNodeCode = (
32- nodeId : string ,
33- indent : string = ""
34- ) : string => {
35- if ( visited . has ( nodeId ) ) return "" ;
36-
37- const node = nodes . find ( n => n ?. id === nodeId ) ;
38- if ( ! node || ! node . functionDefinition ) return "" ;
39-
40- visited . add ( nodeId ) ;
41-
42- const funcDef = funcMap . get ( node . functionDefinition ?. identifier ) ;
43- if ( ! funcDef ) return `${ indent } // Error: Function ${ node . functionDefinition . identifier } not found\n` ;
44-
45- const params = node . parameters ?. nodes as NodeParameter [ ] || [ ] ;
46-
47- const args = params . map ( ( p , index ) => {
48- const val = p . value ;
49- if ( ! val ) return "undefined" ;
50-
51- if ( val . __typename === "ReferenceValue" ) {
52- const ref = val as ReferenceValue ;
53- if ( ! ref . nodeFunctionId ) return "undefined" ;
54-
55- let refCode = ref . parameterIndex !== undefined
56- ? `p_${ sanitizeId ( ref . nodeFunctionId ) } _${ ref . parameterIndex } `
57- : `node_${ sanitizeId ( ref . nodeFunctionId ) } ` ;
58-
59- ref . referencePath ?. forEach ( pathObj => {
60- refCode += `?.${ pathObj . path } ` ;
61- } ) ;
62-
63- return `/* @pos ${ nodeId } ${ index } */ ${ refCode } ` ;
64- }
65-
66- if ( val . __typename === "LiteralValue" ) {
67- return `/* @pos ${ nodeId } ${ index } */ ${ JSON . stringify ( val . value ) } ` ;
68- }
69-
70- if ( val . __typename === "NodeFunctionIdWrapper" ) {
71- const wrapper = val as NodeFunctionIdWrapper ;
72- const lambdaArgName = `p_${ sanitizeId ( nodeId ) } _${ index } ` ;
73- const subTreeCode = generateNodeCode ( wrapper . id ! , indent + " " ) ;
74- return `/* @pos ${ nodeId } ${ index } */ (${ lambdaArgName } ) => {\n${ subTreeCode } ${ indent } }` ;
75- }
76-
77- return "undefined" ;
78- } ) . join ( ", " ) ;
79-
80- const varName = `node_${ sanitizeId ( node . id ! ) } ` ;
81- const funcName = `fn_${ node ?. functionDefinition ?. identifier ?. replace ( / : : / g, '_' ) } ` ;
82-
83- // Add 'as any' cast only if undefined arguments are passed to a generic function to avoid false-positive errors.
84- const needsAnyCast = args . includes ( "undefined" ) ;
85- const isReturnNode = node . functionDefinition . identifier === "std::control::return" ;
86- let code = `${ indent } ${ isReturnNode ? "return " : `var ${ varName } = ` } ${ funcName } (${ args } )${ needsAnyCast ? " as any" : "" } ;\n` ;
87-
88- if ( node . nextNodeId ) {
89- code += generateNodeCode ( node . nextNodeId , indent ) ;
90- }
91-
92- return code ;
93- } ;
94-
95- // 1. Generate Declarations
96- const typeDefs = getSharedTypeDeclarations ( dataTypes ) ;
97-
98- const funcDeclarations = functions ?. map ( funcDef => {
99- return `declare function fn_${ funcDef . identifier ?. replace ( / : : / g, '_' ) } ${ funcDef . signature } ` ;
100- } ) . join ( '\n' ) ;
101-
102- const nextNodeIds = new Set ( nodes . map ( n => n ?. nextNodeId ) . filter ( id => ! ! id ) ) ;
103- const subTreeIds = new Set < string > ( ) ;
104- nodes . forEach ( n => {
105- n ?. parameters ?. nodes ?. forEach ( ( p : any ) => {
106- if ( p ?. value ?. __typename === "NodeFunctionIdWrapper" && p . value . id ) {
107- subTreeIds . add ( p . value . id ) ;
108- }
109- } ) ;
110- } ) ;
111-
112- const executionCode = nodes
113- . filter ( n => n ?. id && ! nextNodeIds . has ( n . id ) && ! subTreeIds . has ( n . id ) )
114- . map ( n => generateNodeCode ( n ! . id ! ) )
115- . join ( '\n' ) ;
116-
117- const sourceCode = `${ typeDefs } \n${ funcDeclarations } \n\n// --- Flow ---\n${ executionCode } ` ;
25+ const sourceCode = generateFlowSourceCode ( flow , functions , dataTypes ) ;
11826
11927 // 3. Virtual TypeScript Compilation
12028 const fileName = "index.ts" ;
@@ -134,11 +42,38 @@ export const getFlowValidation = (
13442
13543 if ( d . start !== undefined ) {
13644 const fullText = sourceFile . getFullText ( ) ;
137- const textBefore = fullText . substring ( 0 , d . start ) ;
138- const posMatch = textBefore . match ( / \/ \* @ p o s ( [ ^ ] + ) ( \d + ) \* \/ \s * $ / ) ;
139- if ( posMatch ) {
140- nodeId = posMatch [ 1 ] as NodeFunction [ 'id' ] ;
141- parameterIndex = parseInt ( posMatch [ 2 ] , 10 ) ;
45+
46+ // Search for position marker comment near the error location
47+ // The error position is typically the start of the problematic token (e.g., "undefined")
48+ const searchStart = Math . max ( 0 , d . start - 300 ) ;
49+ const searchEnd = Math . min ( fullText . length , d . start ) ;
50+ const searchText = fullText . substring ( searchStart , searchEnd ) ;
51+
52+ // Find all @pos comments in the search range
53+ const posRegex = / \/ \* @ p o s ( [ ^ ] + ) ( \d + ) \* \/ / g;
54+ let match ;
55+ let closestMatch : RegExpExecArray | null = null ;
56+ let closestCommentEnd = - 1 ;
57+
58+ // Collect all matches and find the one whose end is closest to d.start
59+ // We want the comment that is immediately before the error
60+ while ( ( match = posRegex . exec ( searchText ) ) !== null ) {
61+ const commentStart = searchStart + match . index ;
62+ const commentEnd = commentStart + match [ 0 ] . length ;
63+
64+ // Only consider comments that end before or very close to the error start
65+ // This ensures we get the @pos comment that directly precedes the problematic argument
66+ if ( commentEnd <= d . start ! ) {
67+ if ( commentEnd > closestCommentEnd ) {
68+ closestCommentEnd = commentEnd ;
69+ closestMatch = match ;
70+ }
71+ }
72+ }
73+
74+ if ( closestMatch ) {
75+ nodeId = closestMatch [ 1 ] as NodeFunction [ 'id' ] ;
76+ parameterIndex = parseInt ( closestMatch [ 2 ] , 10 ) ;
14277 }
14378 }
14479
0 commit comments