@@ -10,6 +10,8 @@ import {
1010} from "@code0-tech/sagittarius-graphql-types" ;
1111import ts from "typescript" ;
1212import { createSystem , createVirtualTypeScriptEnvironment , VirtualTypeScriptEnvironment } from "@typescript/vfs"
13+ import { getTypesFromNode } from "./extraction/getTypesFromNode" ;
14+ import { DataTypeVariant , getTypeVariant } from "./extraction/getTypeVariant" ;
1315
1416/**
1517 * Result of a node or flow validation.
@@ -130,12 +132,47 @@ export const sanitizeId = (id: string) => id.replace(/[^a-zA-Z0-9]/g, '_');
130132export function generateFlowSourceCode (
131133 flow ?: Flow ,
132134 functions ?: FunctionDefinition [ ] ,
133- dataTypes ?: DataType [ ]
135+ dataTypes ?: DataType [ ] ,
136+ isForInference : boolean = false
134137) : string {
135138 const nodes = flow ?. nodes ?. nodes || [ ] ;
136139 const funcMap = new Map ( functions ?. map ( f => [ f . identifier , f ] ) ) ;
137140 const visited = new Set < string > ( ) ;
138141
142+ const generateNodeCall = ( nodeId : string , parentNodeId ?: string , parentParamIndex ?: number ) : string => {
143+ const node = nodes . find ( n => n ?. id === nodeId ) ;
144+ if ( ! node || ! node . functionDefinition ?. identifier ) return "undefined" ;
145+
146+ const params = ( node . parameters ?. nodes as NodeParameter [ ] ) || [ ] ;
147+ const args = params . map ( ( p , paramIdx ) => {
148+ const val = p . value ;
149+ if ( ! val ) return `/* @pos ${ nodeId } ${ paramIdx } */ undefined` ;
150+ if ( val . __typename === "ReferenceValue" ) {
151+ const ref = val as ReferenceValue ;
152+ if ( ! ref . nodeFunctionId ) return `/* @pos ${ nodeId } ${ paramIdx } */ undefined` ;
153+ let refCode = ref . inputIndex !== undefined
154+ ? `p_${ sanitizeId ( ref . nodeFunctionId ) } [${ ref . inputIndex } ]`
155+ : `node_${ sanitizeId ( ref . nodeFunctionId ) } ` ;
156+ ref . referencePath ?. forEach ( pathObj => { refCode += `?.${ pathObj . path } ` ; } ) ;
157+ return `/* @pos ${ nodeId } ${ paramIdx } */ ${ refCode } ` ;
158+ }
159+ if ( val . __typename === "LiteralValue" ) return `/* @pos ${ nodeId } ${ paramIdx } */ ${ JSON . stringify ( val . value ) } ` ;
160+ if ( val . __typename === "NodeFunctionIdWrapper" ) {
161+ const wrapper = val as NodeFunctionIdWrapper ;
162+ return generateNodeCall ( wrapper . id ! , nodeId , paramIdx ) ;
163+ }
164+ return `/* @pos ${ nodeId } ${ paramIdx } */ undefined` ;
165+ } ) . join ( ", " ) ;
166+
167+ const funcName = `fn_${ node . functionDefinition . identifier . replace ( / : : / g, '_' ) } ` ;
168+ const call = `${ funcName } (${ args } )` ;
169+ // Add position comment only for nested calls (when called from within an argument)
170+ if ( parentNodeId !== undefined && parentParamIndex !== undefined ) {
171+ return `/* @pos ${ nodeId } 0 */ ${ call } ` ;
172+ }
173+ return call ;
174+ } ;
175+
139176 const generateNodeCode = ( nodeId : string , indent : string = "" ) : string => {
140177 if ( visited . has ( nodeId ) ) return "" ;
141178 const node = nodes . find ( n => n ?. id === nodeId ) ;
@@ -145,34 +182,57 @@ export function generateFlowSourceCode(
145182 const funcDef = funcMap . get ( node . functionDefinition . identifier ) ;
146183 if ( ! funcDef ) return `${ indent } // Error: Function ${ node . functionDefinition . identifier } not found\n` ;
147184
185+ // Only use getTypesFromNode if we are NOT already doing inference to avoid infinite recursion
186+ let nodeTypes : any = { parameters : [ ] } ;
187+ if ( ! isForInference ) {
188+ nodeTypes = getTypesFromNode ( node , functions , dataTypes ) ;
189+ }
190+
148191 const params = ( node . parameters ?. nodes as NodeParameter [ ] ) || [ ] ;
149192 const args = params . map ( ( p , index ) => {
150193 const val = p . value ;
151- if ( ! val ) return " undefined" ;
194+ if ( ! val ) return `/* @pos ${ nodeId } ${ index } */ undefined` ;
152195 if ( val . __typename === "ReferenceValue" ) {
153196 const ref = val as ReferenceValue ;
154- if ( ! ref . nodeFunctionId ) return " undefined" ;
155- let refCode = ref . parameterIndex !== undefined
156- ? `p_${ sanitizeId ( ref . nodeFunctionId ) } _ ${ ref . parameterIndex } `
197+ if ( ! ref . nodeFunctionId ) return `/* @pos ${ nodeId } ${ index } */ undefined` ;
198+ let refCode = ref . inputIndex !== undefined
199+ ? `p_${ sanitizeId ( ref . nodeFunctionId ) } [ ${ ref . inputIndex } ] `
157200 : `node_${ sanitizeId ( ref . nodeFunctionId ) } ` ;
158201 ref . referencePath ?. forEach ( pathObj => { refCode += `?.${ pathObj . path } ` ; } ) ;
159202 return `/* @pos ${ nodeId } ${ index } */ ${ refCode } ` ;
160203 }
161204 if ( val . __typename === "LiteralValue" ) return `/* @pos ${ nodeId } ${ index } */ ${ JSON . stringify ( val . value ) } ` ;
162205 if ( val . __typename === "NodeFunctionIdWrapper" ) {
163206 const wrapper = val as NodeFunctionIdWrapper ;
164- const lambdaArgName = `p_${ sanitizeId ( nodeId ) } _${ index } ` ;
165- const subTreeCode = generateNodeCode ( wrapper . id ! , indent + " " ) ;
166- return `/* @pos ${ nodeId } ${ index } */ (${ lambdaArgName } ) => {\n${ subTreeCode } ${ indent } }` ;
207+
208+ if ( ! isForInference ) {
209+ const expectedType = nodeTypes . parameters [ index ] ;
210+ const isFunctionType = expectedType ? getTypeVariant ( expectedType , dataTypes ) === DataTypeVariant . NODE : false ;
211+
212+ if ( isFunctionType ) {
213+ const lambdaArgName = `p_${ sanitizeId ( nodeId ) } ` ;
214+ const subTreeCode = generateNodeCode ( wrapper . id ! , indent + " " ) ;
215+ return `/* @pos ${ nodeId } ${ index } */ (...${ lambdaArgName } ) => {\n${ subTreeCode } ${ indent } }` ;
216+ } else {
217+ const nestedCall = generateNodeCall ( wrapper . id ! , nodeId , index ) ;
218+ return `/* @pos ${ nodeId } ${ index } */ ${ nestedCall } ` ;
219+ }
220+ } else {
221+ // During inference, we just need something valid.
222+ // Defaulting to a lambda is safer for type inference of the parent node's parameters.
223+ const lambdaArgName = `p_${ sanitizeId ( nodeId ) } ` ;
224+ const subTreeCode = generateNodeCode ( wrapper . id ! , indent + " " ) ;
225+ return `/* @pos ${ nodeId } ${ index } */ (...${ lambdaArgName } ) => {\n${ subTreeCode } ${ indent } }` ;
226+ }
167227 }
168- return " undefined" ;
228+ return `/* @pos ${ nodeId } ${ index } */ undefined` ;
169229 } ) . join ( ", " ) ;
170230
171231 const varName = `node_${ sanitizeId ( node . id ! ) } ` ;
172- const funcName = `fn_${ node . functionDefinition . identifier . replace ( / : : / g, '_' ) } ` ;
232+ const funcName = `fn_${ node ? .functionDefinition ? .identifier ? .replace ( / : : / g, '_' ) } ` ;
173233 const needsAnyCast = args . includes ( "undefined" ) ;
174234 const isReturnNode = node . functionDefinition . identifier === "std::control::return" ;
175- let code = `${ indent } ${ isReturnNode ? "return " : `var ${ varName } = ` } ${ funcName } (${ args } )${ needsAnyCast ? " as any " : "" } ;\n` ;
235+ let code = `${ indent } ${ isReturnNode ? "return " : `var ${ varName } = ` } ${ funcName } (${ args } )${ needsAnyCast ? "" : "" } ;\n` ;
176236 if ( node . nextNodeId ) code += generateNodeCode ( node . nextNodeId , indent ) ;
177237 return code ;
178238 } ;
@@ -207,7 +267,7 @@ export function getInferredTypesFromFlow(
207267 functions ?: FunctionDefinition [ ] ,
208268 dataTypes ?: DataType [ ]
209269) : InferredTypes {
210- const sourceCode = generateFlowSourceCode ( flow , functions , dataTypes ) ;
270+ const sourceCode = generateFlowSourceCode ( flow , functions , dataTypes , true ) ;
211271 const fileName = "index.ts" ;
212272 const host = createCompilerHost ( fileName , sourceCode ) ;
213273 const sourceFile = host . getSourceFile ( fileName ) ! ;
@@ -249,4 +309,3 @@ export function getInferredTypesFromFlow(
249309
250310 return { nodes : nodeTypes , parameters : parameterTypes } ;
251311}
252-
0 commit comments