Skip to content

Commit 66c2aaa

Browse files
committed
feat: enhance suggestion logic for nested nullable object properties in ReferenceValue paths
1 parent 5511325 commit 66c2aaa

2 files changed

Lines changed: 79 additions & 3 deletions

File tree

src/util/references.util.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,11 @@ const extractObjectProperties = (
271271
results.push({ path: currentPath, type });
272272
}
273273

274-
// Recursively traverse into object properties
275-
if (isRealObjectType(type)) {
276-
const properties = type.getProperties();
274+
// Recursively traverse into object properties. Traversal also runs on the
275+
// non-nullable type: a nullable object in the chain (e.g. `{bla?: TEXT} | null`)
276+
// is a union whose getProperties() is empty, which would cut off all nested paths.
277+
if (isRealObjectType(nonNullableType)) {
278+
const properties = nonNullableType.getProperties();
277279
if (properties && properties.length > 0) {
278280
properties.forEach((property) => {
279281
const propType = checker.getTypeOfSymbolAtLocation(property, property.valueDeclaration!);

test/schema/schema.test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,4 +825,78 @@ describe("Schema", () => {
825825
).toBe(true);
826826
});
827827

828+
it('offers a nested nullable object property as a ReferenceValue path suggestion for a plain TEXT parameter', () => {
829+
// Node 1: custom::text::nested_nullable_object(): {test?: {bla?: TEXT | null} | null}
830+
// — no parameters, returns an object whose `test` property is an optional,
831+
// nullable object which itself holds an optional, nullable `bla` property.
832+
// Node 2: std::text::split(value: TEXT, delimiter: TEXT): LIST<TEXT>
833+
//
834+
// The `value` parameter is declared as plain TEXT (string). Node 1's `test.bla`
835+
// property has type string | null | undefined behind a nullable `test` object.
836+
// Strict assignability would reject it, but the editor must still offer node 1's
837+
// `test.bla` property as a ReferenceValue suggestion with referencePath
838+
// [{path: "test"}, {path: "bla"}]: the nullish parts along the reference chain
839+
// should be ignored for suggestion scoping.
840+
const NESTED_NULLABLE_OBJECT_FN: FunctionDefinition = {
841+
id: "gid://sagittarius/FunctionDefinition/9003",
842+
identifier: "custom::text::nested_nullable_object",
843+
signature: "(): {test?: {bla?: TEXT | null} | null}",
844+
};
845+
846+
const flow: Flow = {
847+
id: "gid://sagittarius/Flow/1",
848+
startingNodeId: "gid://sagittarius/NodeFunction/1",
849+
signature: "(): void",
850+
nodes: {
851+
nodes: [
852+
{
853+
id: "gid://sagittarius/NodeFunction/1",
854+
functionDefinition: {identifier: "custom::text::nested_nullable_object"},
855+
nextNodeId: "gid://sagittarius/NodeFunction/2",
856+
parameters: {
857+
nodes: [],
858+
},
859+
},
860+
{
861+
id: "gid://sagittarius/NodeFunction/2",
862+
functionDefinition: {identifier: "std::text::split"},
863+
parameters: {
864+
nodes: [
865+
{value: null},
866+
{value: {__typename: "LiteralValue", value: ","}},
867+
],
868+
},
869+
},
870+
],
871+
},
872+
};
873+
874+
const [valueSchema] = getSignatureSchema(
875+
flow,
876+
DATA_TYPES,
877+
[...FUNCTION_SIGNATURES, NESTED_NULLABLE_OBJECT_FN],
878+
"gid://sagittarius/NodeFunction/2",
879+
);
880+
881+
// value: TEXT → free-form text input.
882+
expect(valueSchema.schema.input).toBe("text");
883+
884+
// Node 1's `test.bla` property is TEXT | null | undefined nested inside the
885+
// nullable `test` object; stripping the nullish parts leaves TEXT, which is
886+
// assignable to the TEXT parameter → node 1 must be offered with
887+
// referencePath [{path: "test"}, {path: "bla"}].
888+
const suggestions = (valueSchema.schema.suggestions ?? []) as any[];
889+
expect(
890+
suggestions.some(
891+
(s) =>
892+
s.__typename === "ReferenceValue" &&
893+
s.nodeFunctionId === "gid://sagittarius/NodeFunction/1" &&
894+
Array.isArray(s.referencePath) &&
895+
s.referencePath.length === 2 &&
896+
s.referencePath[0].path === "test" &&
897+
s.referencePath[1].path === "bla",
898+
),
899+
).toBe(true);
900+
});
901+
828902
})

0 commit comments

Comments
 (0)