Skip to content

Commit bc75d7e

Browse files
authored
Merge pull request #94 from code0-tech/feat/#76
Add sub flow suggestions
2 parents be60d91 + 718a119 commit bc75d7e

4 files changed

Lines changed: 165 additions & 6 deletions

File tree

src/util/nodes.util.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ const createNodeFunctionIfCompatible = (
6868
functions: FunctionDefinition[],
6969
paramType: ts.Type
7070
): NodeFunction | null => {
71+
72+
if (func.parameters.length > 0)
73+
return null;
74+
7175
// Extract the function signature and its return type
7276
const signature = checker.getSignatureFromDeclaration(func);
7377
const returnType = checker.getReturnTypeOfSignature(signature!);

src/util/schema.util.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@ import ts, {FunctionDeclaration} from "typescript";
22
import {getValues} from "./values.util";
33
import {getReferences} from "./references.util";
44
import {getNodes} from "./nodes.util";
5-
import {FunctionDefinition, LiteralValue, NodeFunction, ReferenceValue,} from "@code0-tech/sagittarius-graphql-types";
5+
import {
6+
FunctionDefinition,
7+
LiteralValue,
8+
NodeFunction,
9+
ReferenceValue,
10+
SubFlowValue,
11+
} from "@code0-tech/sagittarius-graphql-types";
12+
import {getSubFlows} from "./subflows.util";
613

714

815
/**
@@ -13,7 +20,7 @@ export interface Input {
1320
/** The type of input (string representation) */
1421
input?: string;
1522
/** Array of suggested values (functions, references, or literals) */
16-
suggestions?: (NodeFunction | ReferenceValue | LiteralValue)[];
23+
suggestions?: (NodeFunction | ReferenceValue | LiteralValue | SubFlowValue)[];
1724
}
1825

1926
/**
@@ -135,6 +142,12 @@ export const getSchema = (
135142
functions,
136143
parameterType
137144
),
145+
...getSubFlows(
146+
checker,
147+
functionDeclarations,
148+
functions,
149+
parameterType
150+
),
138151
],
139152
} : {};
140153

src/util/subflows.util.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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+
}

test/schema/schema.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {describe, expect, it} from "vitest";
1+
import {describe, it} from "vitest";
22
import {Flow} from "@code0-tech/sagittarius-graphql-types";
3-
import {getFlowValidation, getSignatureSchema, getTypeSchema} from "../../src";
3+
import {getSignatureSchema, getTypeSchema} from "../../src";
44
import {DATA_TYPES, FUNCTION_SIGNATURES} from "../data";
55

66
describe("Schema", () => {
@@ -36,8 +36,8 @@ describe("Schema", () => {
3636
},
3737
{
3838
value: {
39-
__typename: "NodeFunctionIdWrapper",
40-
id: "gid://sagittarius/NodeFunction/3"
39+
__typename: "SubFlowValue",
40+
startingNodeId: "gid://sagittarius/NodeFunction/3"
4141
}
4242
}
4343
]

0 commit comments

Comments
 (0)