Skip to content

Commit e16a63d

Browse files
author
Nico Sammito
authored
Merge pull request #23 from code0-tech/feat/#22
Test cases with real sagittarius data
2 parents 91ecde8 + 9dd3a56 commit e16a63d

20 files changed

Lines changed: 13920 additions & 1449 deletions

src/extraction/getTypesFromNode.ts

Lines changed: 57 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import ts from "typescript";
2-
import {DataType, Flow, FunctionDefinition, NodeFunction, NodeParameter} from "@code0-tech/sagittarius-graphql-types";
3-
import {createCompilerHost, getParameterCode, getSharedTypeDeclarations,} from "../utils";
4-
import {getNodeValidation} from "../validation/getNodeValidation"; // Wieder hinzugefügt
1+
import {DataType, Flow, FunctionDefinition, NodeFunction} from "@code0-tech/sagittarius-graphql-types";
2+
import {getInferredTypesFromFlow, sanitizeId} from "../utils";
53

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

22-
if (!funcDef) {
23-
return {
24-
parameters: [],
25-
returnType: "any",
26-
};
27-
}
19+
const nodeId = node.id || "temp_node_id";
2820

29-
const mockFlow: Flow = {
30-
id: "gid://sagittarius/Flow/0" as any,
31-
nodes: {__typename: "NodeFunctionConnection", nodes: [node]}
32-
} as Flow;
33-
34-
const params = (node?.parameters?.nodes as NodeParameter[]) || [];
35-
const paramCodes = params.map(param => getParameterCode(param, mockFlow, (f, n) => getNodeValidation(f, n, functions, dataTypes)));
21+
// To ensure generics and triangulated types are resolved without hardcoding,
22+
// we must ensure the compiler has enough context.
23+
// If some parameters are missing values, the compiler might return 'any'.
24+
// We create a version of the node where missing values are filled with a marker
25+
// that the TypeScript engine can use to infer the intended type from the signature.
3626

37-
const funcCallArgs = paramCodes.map(code => code === 'undefined' ? '({} as any)' : code).join(", ");
27+
const nodeWithDefaults = {
28+
...node,
29+
id: nodeId,
30+
parameters: {
31+
...node.parameters,
32+
nodes: node.parameters?.nodes?.map(p => {
33+
if (p?.value) return p;
34+
// If value is missing, we still want the compiler to see the argument position
35+
return { ...p, value: null };
36+
}) || []
37+
}
38+
};
3839

39-
const signature = funcDef.signature;
40-
const sourceCode = `
41-
${getSharedTypeDeclarations(dataTypes)}
42-
declare function testFunc${signature};
43-
const result = testFunc(${funcCallArgs});
44-
`;
40+
// Create a version of the node with primitive literals removed
41+
// This allows inferring the "expected" type of parameters (e.g. keyof T)
42+
// rather than the specific type of the argument provided (e.g. "id").
43+
const nodeIdParams = nodeId + "_params";
44+
const nodeForParams = {
45+
...nodeWithDefaults,
46+
id: nodeIdParams,
47+
parameters: {
48+
...nodeWithDefaults.parameters,
49+
nodes: nodeWithDefaults.parameters.nodes.map(p => {
50+
// If it's a primitive literal, remove it to allow wider type inference for parameters
51+
if (p.value?.__typename === "LiteralValue" && p.value.value !== null && typeof p.value.value !== 'object') {
52+
return { ...p, value: null };
53+
}
54+
return p;
55+
})
56+
}
57+
};
4558

46-
const fileName = "index.ts";
47-
const host = createCompilerHost(fileName, sourceCode);
48-
const sourceFile = host.getSourceFile(fileName)!;
49-
const program = host.languageService.getProgram()!;
50-
const checker = program.getTypeChecker();
59+
const mockFlow: Flow = {
60+
id: "gid://sagittarius/Flow/0" as any,
61+
nodes: { __typename: "NodeFunctionConnection", nodes: [nodeWithDefaults, nodeForParams] }
62+
} as Flow;
5163

52-
let inferredReturnType = "any";
53-
let inferredParameterTypes: string[] = [];
64+
const inferred = getInferredTypesFromFlow(mockFlow, functions, dataTypes);
65+
const sId = sanitizeId(nodeId);
66+
const sIdParams = sanitizeId(nodeIdParams);
5467

55-
const visitor = (n: ts.Node) => {
56-
if (ts.isVariableDeclaration(n) && n.name.getText() === "result") {
57-
const resultType = checker.getTypeAtLocation(n);
58-
inferredReturnType = checker.typeToString(
59-
resultType,
60-
n,
61-
ts.TypeFormatFlags.NoTruncation
62-
);
68+
const directParams = inferred.parameters.get(sId) || [];
69+
const widenedParams = inferred.parameters.get(sIdParams) || [];
6370

64-
if (ts.isCallExpression(n.initializer!)) {
65-
const callExpr = n.initializer;
66-
const signature = checker.getResolvedSignature(callExpr);
67-
if (signature) {
68-
inferredParameterTypes = signature.getParameters().map(p => {
69-
const type = checker.getTypeOfSymbolAtLocation(p, callExpr);
70-
return checker.typeToString(
71-
type,
72-
callExpr,
73-
ts.TypeFormatFlags.NoTruncation
74-
);
75-
});
76-
}
77-
}
71+
// Merge parameters: prefer widened types unless they failed inference (any/unknown)
72+
const parameters = directParams.map((p, i) => {
73+
const wide = widenedParams[i];
74+
if (wide && wide !== "any" && wide !== "unknown") {
75+
return wide;
7876
}
79-
ts.forEachChild(n, visitor);
80-
};
81-
82-
visitor(sourceFile);
77+
return p;
78+
});
8379

8480
return {
85-
parameters: inferredParameterTypes,
86-
returnType: inferredReturnType,
81+
parameters,
82+
returnType: inferred.nodes.get(sId) || "any",
8783
};
8884
};

src/extraction/getValueFromType.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ export const getValueFromType = (
6262

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

6767
if (flags & ts.TypeFlags.NumberLiteral) return (t as ts.NumberLiteralType).value;
68-
if (flags & ts.TypeFlags.Number) return 1;
68+
if (flags & ts.TypeFlags.Number) return 0;
6969

7070
if (flags & ts.TypeFlags.BooleanLiteral) return (t as any).intrinsicName === "true";
7171
if (flags & ts.TypeFlags.Boolean) return false;

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,4 @@ export * from './suggestion/getNodeSuggestions';
66
export * from './suggestion/getReferenceSuggestions';
77
export * from './suggestion/getValueSuggestions';
88
export * from './validation/getFlowValidation';
9-
export * from './validation/getNodeValidation';
109
export * from './validation/getValueValidation';

src/suggestion/getNodeSuggestions.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {DataType, FunctionDefinition, NodeFunction} from "@code0-tech/sagittarius-graphql-types";
22
import {createCompilerHost, getSharedTypeDeclarations} from "../utils";
3+
import {DataTypeVariant, getTypeVariant} from "../extraction/getTypeVariant";
34

45
/**
56
* Suggests NodeFunctions based on a given type and a list of available FunctionDefinitions.
@@ -13,9 +14,11 @@ export const getNodeSuggestions = (
1314

1415
let functionToSuggest = functions
1516

16-
if (type && functions) {
17+
const typeVariant = type ? getTypeVariant(type, dataTypes) : null;
18+
19+
if (type && functions && typeVariant !== DataTypeVariant.NODE) {
1720
function getGenericsCount(input: string): number {
18-
const match = input.match(/<([^>]+)>/);
21+
const match = input.trim().match(/^<([^>]+)>/);
1922
if (!match) return 0;
2023
return match[1].split(',').map(s => s.trim()).filter(Boolean).length;
2124
}
@@ -30,7 +33,8 @@ export const getNodeSuggestions = (
3033
`;
3134
}).join("\n")}
3235
${functions?.map((_, i) => `const check${i}: TargetType = {} as F${i};`).join("\n")}
33-
`;
36+
`;
37+
3438

3539
const fileName = "index.ts";
3640
const host = createCompilerHost(fileName, sourceCode);
@@ -54,7 +58,7 @@ export const getNodeSuggestions = (
5458
}
5559

5660

57-
return functionToSuggest?.map(f=> {
61+
return functionToSuggest?.map(f => {
5862
return {
5963
__typename: "NodeFunction",
6064
id: `gid://sagittarius/NodeFunction/1`,

0 commit comments

Comments
 (0)