1+ import ts from "typescript" ;
2+ import { FunctionDefinition , SubFlowValue , SubFlowValueSetting } from "@code0-tech/sagittarius-graphql-types" ;
3+ import { isSubFlow } from "./schema.util" ;
4+
5+ /**
6+ * Extracts and creates a list of SubFlowValue objects from function declarations
7+ * that are compatible with the given parameter type.
8+ *
9+ * This function filters function declarations based on type compatibility and
10+ * converts matching functions into SubFlowValue objects that can be used as
11+ * sub-flows in a larger workflow.
12+ *
13+ * @param {ts.TypeChecker } checker - The TypeScript type checker for type analysis
14+ * @param {ts.FunctionDeclaration[] } functionDeclarations - Array of function declarations to analyze
15+ * @param {FunctionDefinition[] } functions - Array of function definitions for reference lookup
16+ * @param {ts.Type } paramType - The target parameter type that functions must be compatible with
17+ *
18+ * @returns {SubFlowValue[] } An array of SubFlowValue objects for compatible functions,
19+ * or an empty array if the paramType is not a sub-flow type
20+ */
21+ export const getSubFlows = (
22+ checker : ts . TypeChecker ,
23+ functionDeclarations : ts . FunctionDeclaration [ ] ,
24+ functions : FunctionDefinition [ ] ,
25+ paramType : ts . Type
26+ ) : SubFlowValue [ ] => {
27+
28+ if ( ! isSubFlow ( paramType ) ) {
29+ return [ ] ;
30+ }
31+
32+ return functionDeclarations . flatMap ( ( func ) => {
33+ const subFlow = createSubFlowIfCompatible ( checker , func , functions , paramType ) ;
34+ return subFlow ? [ subFlow ] : [ ] ;
35+ } ) ;
36+
37+ }
38+
39+ /**
40+ * Verifies that a function declaration is compatible with a given parameter type
41+ * and creates a SubFlowValue if the types match.
42+ *
43+ * Type compatibility is checked by comparing the function type against the expected
44+ * parameter type (e.g., a function signature like (number: number) => void).
45+ * If compatible, the function is converted into a SubFlowValue object with its
46+ * associated parameter settings.
47+ *
48+ * @param {ts.TypeChecker } checker - The TypeScript type checker for type analysis
49+ * @param {ts.FunctionDeclaration } func - The function declaration to check
50+ * @param {FunctionDefinition[] } functions - Array of function definitions for lookup
51+ * @param {ts.Type } paramType - The expected parameter type to check compatibility against
52+ *
53+ * @returns {SubFlowValue | null } A SubFlowValue object if the function is compatible
54+ * with the paramType and a matching function definition exists, or null otherwise
55+ */
56+ const createSubFlowIfCompatible = (
57+ checker : ts . TypeChecker ,
58+ func : ts . FunctionDeclaration ,
59+ functions : FunctionDefinition [ ] ,
60+ paramType : ts . Type
61+ ) : SubFlowValue | null => {
62+
63+ // Get the full function type from the declaration
64+ const functionType = checker . getTypeAtLocation ( func ) ;
65+
66+ // Check if the function type is assignable to the parameter type
67+ // This ensures the function signature matches the expected interface
68+ // e.g., paramType might be (number: number) => void, and functionType should be compatible
69+ if ( ! checker . isTypeAssignableTo ( functionType , paramType ) ) {
70+ return null ;
71+ }
72+
73+ // If type-compatible, find the function definition and create the SubFlowValue
74+ const functionName = normalizeFunctionName ( func . name ?. getText ( ) ) ;
75+ const functionDefinition = functions . find ( ( f ) => f . identifier === functionName ) ;
76+
77+ if ( ! functionDefinition ) {
78+ return null ;
79+ }
80+
81+ return buildSubFlowValue ( functionDefinition ) ;
82+
83+ }
84+
85+ /**
86+ * Normalizes a function name by removing prefixes and replacing underscores with
87+ * double colons (::).
88+ *
89+ * This function applies the following transformations:
90+ * 1. Removes the "fn_" prefix
91+ * 2. Replaces the first underscore with "::"
92+ * 3. Replaces the second underscore with "::"
93+ *
94+ * Example: "fn_module_submodule" becomes "module::submodule"
95+ *
96+ * @param {string | undefined } rawName - The raw function name from the declaration
97+ *
98+ * @returns {string } The normalized function name, or an empty string if the input
99+ * is undefined
100+ *
101+ * @private
102+ */
103+ const normalizeFunctionName = ( rawName : string | undefined ) : string => {
104+ if ( ! rawName ) {
105+ return "" ;
106+ }
107+ return rawName
108+ . replace ( "fn_" , "" )
109+ . replace ( "_" , "::" )
110+ . replace ( "_" , "::" ) ;
111+ }
112+
113+ /**
114+ * Constructs a SubFlowValue object from a function definition.
115+ *
116+ * This function transforms a FunctionDefinition into a SubFlowValue with all
117+ * its parameter settings properly configured. Each parameter from the function
118+ * definition is converted into a SubFlowValueSetting object that includes
119+ * metadata such as identifier, default values, hidden status, and optional status.
120+ *
121+ * @param {FunctionDefinition } functionDefinition - The function definition to convert
122+ *
123+ * @returns {SubFlowValue } A SubFlowValue object with the function definition
124+ * and all parameter settings configured
125+ */
126+ const buildSubFlowValue = ( functionDefinition : FunctionDefinition ) : SubFlowValue => {
127+ return {
128+ __typename : "SubFlowValue" ,
129+ functionDefinition : functionDefinition ,
130+ signature : functionDefinition . signature ,
131+ settings : functionDefinition . parameterDefinitions ?. nodes ?. map ( param => {
132+ const setting : SubFlowValueSetting = {
133+ __typename : "SubFlowValueSetting" ,
134+ identifier : param ?. identifier ,
135+ defaultValue : param ?. defaultValue ?? null ,
136+ hidden : param ?. hidden ?? false ,
137+ optional : param ?. optional ?? false ,
138+ }
139+ return setting
140+ } )
141+ }
142+ }
0 commit comments