Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
45bb77a
feat: refactor DATA_TYPES structure and update type signatures for co…
nicosammito Mar 16, 2026
c97bb10
feat: update function identifiers and default values for consistency …
nicosammito Mar 16, 2026
b5e4622
feat: update getReferenceSuggestions tests to ensure type compatibili…
nicosammito Mar 16, 2026
07a2ff2
feat: enhance nodeValidation tests for improved validation accuracy a…
nicosammito Mar 16, 2026
077628a
feat: update return value handling in node function to ensure proper …
nicosammito Mar 16, 2026
482e3af
feat: refactor flow validation tests for improved readability and con…
nicosammito Mar 17, 2026
7ee92bc
feat: enhance flow validation tests to ensure diagnostics contain nod…
nicosammito Mar 17, 2026
ba62db8
feat: integrate flow source code generation for improved validation e…
nicosammito Mar 17, 2026
8d3718c
feat: enhance flow source code generation to support inference and im…
nicosammito Mar 17, 2026
1ef8d0d
feat: simplify call formatting by removing position comment for neste…
nicosammito Mar 17, 2026
1ffef30
feat: update flow validation test structure for improved clarity and …
nicosammito Mar 17, 2026
1ea6083
feat: remove unnecessary console log from flow validation test
nicosammito Mar 18, 2026
bd07ae2
feat: enhance reference suggestion tests and improve type handling in…
nicosammito Mar 18, 2026
5776783
feat: add test for HTTP method value suggestions in valueSuggestions
nicosammito Mar 18, 2026
b2f8255
feat: streamline imports in getFlowValidation by removing unused types
nicosammito Mar 18, 2026
87f0a7b
feat: refactor getReferenceSuggestions and related tests for improved…
nicosammito Mar 19, 2026
08e928d
feat: enhance type inference in getTypesFromNode by widening paramete…
nicosammito Mar 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 57 additions & 61 deletions src/extraction/getTypesFromNode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import ts from "typescript";
import {DataType, Flow, FunctionDefinition, NodeFunction, NodeParameter} from "@code0-tech/sagittarius-graphql-types";
import {createCompilerHost, getParameterCode, getSharedTypeDeclarations,} from "../utils";
import {getNodeValidation} from "../validation/getNodeValidation"; // Wieder hinzugefügt
import {DataType, Flow, FunctionDefinition, NodeFunction} from "@code0-tech/sagittarius-graphql-types";
import {getInferredTypesFromFlow, sanitizeId} from "../utils";

export interface NodeTypes {
parameters: string[];
Expand All @@ -16,73 +14,71 @@ export const getTypesFromNode = (
functions?: FunctionDefinition[],
dataTypes?: DataType[]
): NodeTypes => {
const funcMap = new Map(functions?.map(f => [f.identifier, f]));
const funcDef = funcMap.get(node?.functionDefinition?.identifier);
if (!node) return { parameters: [], returnType: "any" };

if (!funcDef) {
return {
parameters: [],
returnType: "any",
};
}
const nodeId = node.id || "temp_node_id";

const mockFlow: Flow = {
id: "gid://sagittarius/Flow/0" as any,
nodes: {__typename: "NodeFunctionConnection", nodes: [node]}
} as Flow;

const params = (node?.parameters?.nodes as NodeParameter[]) || [];
const paramCodes = params.map(param => getParameterCode(param, mockFlow, (f, n) => getNodeValidation(f, n, functions, dataTypes)));
// To ensure generics and triangulated types are resolved without hardcoding,
// we must ensure the compiler has enough context.
// If some parameters are missing values, the compiler might return 'any'.
// We create a version of the node where missing values are filled with a marker
// that the TypeScript engine can use to infer the intended type from the signature.

const funcCallArgs = paramCodes.map(code => code === 'undefined' ? '({} as any)' : code).join(", ");
const nodeWithDefaults = {
...node,
id: nodeId,
parameters: {
...node.parameters,
nodes: node.parameters?.nodes?.map(p => {
if (p?.value) return p;
// If value is missing, we still want the compiler to see the argument position
return { ...p, value: null };
}) || []
}
};

const signature = funcDef.signature;
const sourceCode = `
${getSharedTypeDeclarations(dataTypes)}
declare function testFunc${signature};
const result = testFunc(${funcCallArgs});
`;
// Create a version of the node with primitive literals removed
// This allows inferring the "expected" type of parameters (e.g. keyof T)
// rather than the specific type of the argument provided (e.g. "id").
const nodeIdParams = nodeId + "_params";
const nodeForParams = {
...nodeWithDefaults,
id: nodeIdParams,
parameters: {
...nodeWithDefaults.parameters,
nodes: nodeWithDefaults.parameters.nodes.map(p => {
// If it's a primitive literal, remove it to allow wider type inference for parameters
if (p.value?.__typename === "LiteralValue" && p.value.value !== null && typeof p.value.value !== 'object') {
return { ...p, value: null };
}
return p;
})
}
};

const fileName = "index.ts";
const host = createCompilerHost(fileName, sourceCode);
const sourceFile = host.getSourceFile(fileName)!;
const program = host.languageService.getProgram()!;
const checker = program.getTypeChecker();
const mockFlow: Flow = {
id: "gid://sagittarius/Flow/0" as any,
nodes: { __typename: "NodeFunctionConnection", nodes: [nodeWithDefaults, nodeForParams] }
} as Flow;

let inferredReturnType = "any";
let inferredParameterTypes: string[] = [];
const inferred = getInferredTypesFromFlow(mockFlow, functions, dataTypes);
const sId = sanitizeId(nodeId);
const sIdParams = sanitizeId(nodeIdParams);

const visitor = (n: ts.Node) => {
if (ts.isVariableDeclaration(n) && n.name.getText() === "result") {
const resultType = checker.getTypeAtLocation(n);
inferredReturnType = checker.typeToString(
resultType,
n,
ts.TypeFormatFlags.NoTruncation
);
const directParams = inferred.parameters.get(sId) || [];
const widenedParams = inferred.parameters.get(sIdParams) || [];

if (ts.isCallExpression(n.initializer!)) {
const callExpr = n.initializer;
const signature = checker.getResolvedSignature(callExpr);
if (signature) {
inferredParameterTypes = signature.getParameters().map(p => {
const type = checker.getTypeOfSymbolAtLocation(p, callExpr);
return checker.typeToString(
type,
callExpr,
ts.TypeFormatFlags.NoTruncation
);
});
}
}
// Merge parameters: prefer widened types unless they failed inference (any/unknown)
const parameters = directParams.map((p, i) => {
const wide = widenedParams[i];
if (wide && wide !== "any" && wide !== "unknown") {
return wide;
}
ts.forEachChild(n, visitor);
};

visitor(sourceFile);
return p;
});

return {
parameters: inferredParameterTypes,
returnType: inferredReturnType,
parameters,
returnType: inferred.nodes.get(sId) || "any",
};
};
4 changes: 2 additions & 2 deletions src/extraction/getValueFromType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ export const getValueFromType = (

// 2. Handle Primitives and Literals
if (flags & ts.TypeFlags.StringLiteral) return (t as ts.StringLiteralType).value;
if (flags & ts.TypeFlags.String) return "sample";
if (flags & ts.TypeFlags.String) return "";

if (flags & ts.TypeFlags.NumberLiteral) return (t as ts.NumberLiteralType).value;
if (flags & ts.TypeFlags.Number) return 1;
if (flags & ts.TypeFlags.Number) return 0;

if (flags & ts.TypeFlags.BooleanLiteral) return (t as any).intrinsicName === "true";
if (flags & ts.TypeFlags.Boolean) return false;
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ export * from './suggestion/getNodeSuggestions';
export * from './suggestion/getReferenceSuggestions';
export * from './suggestion/getValueSuggestions';
export * from './validation/getFlowValidation';
export * from './validation/getNodeValidation';
export * from './validation/getValueValidation';
Loading