@@ -305,7 +305,7 @@ describe("Schema", () => {
305305 } ,
306306 } ;
307307
308- const [ first ] = getSignatureSchema (
308+ const { parameters : [ first ] } = getSignatureSchema (
309309 flow ,
310310 DATA_TYPES ,
311311 FUNCTION_SIGNATURES ,
@@ -371,13 +371,13 @@ describe("Schema", () => {
371371 value : "hello" ,
372372 } ) ;
373373
374- const [ refResult ] = getSignatureSchema (
374+ const { parameters : [ refResult ] } = getSignatureSchema (
375375 refFlow ,
376376 DATA_TYPES ,
377377 FUNCTION_SIGNATURES ,
378378 "gid://sagittarius/NodeFunction/2" ,
379379 ) ;
380- const [ literalResult ] = getSignatureSchema (
380+ const { parameters : [ literalResult ] } = getSignatureSchema (
381381 literalFlow ,
382382 DATA_TYPES ,
383383 FUNCTION_SIGNATURES ,
@@ -486,7 +486,7 @@ describe("Schema", () => {
486486 } ) ;
487487
488488 const probe = ( returnValue : any ) => {
489- const [ r ] = getSignatureSchema (
489+ const { parameters : [ r ] } = getSignatureSchema (
490490 buildFlow ( returnValue ) ,
491491 DATA_TYPES ,
492492 FUNCTION_SIGNATURES ,
@@ -563,7 +563,7 @@ describe("Schema", () => {
563563 } ,
564564 } ;
565565
566- const [ listSchema ] = getSignatureSchema (
566+ const { parameters : [ listSchema ] } = getSignatureSchema (
567567 flow ,
568568 DATA_TYPES ,
569569 FUNCTION_SIGNATURES ,
@@ -609,7 +609,7 @@ describe("Schema", () => {
609609 } ,
610610 } ;
611611
612- const [ first ] = getSignatureSchema (
612+ const { parameters : [ first ] } = getSignatureSchema (
613613 flow ,
614614 DATA_TYPES ,
615615 FUNCTION_SIGNATURES ,
@@ -669,7 +669,7 @@ describe("Schema", () => {
669669 ) ;
670670
671671 // payload is the 4th parameter of rest::control::respond.
672- const payloadSchema = result [ 3 ] ;
672+ const payloadSchema = result . parameters [ 3 ] ;
673673
674674 // application/json → HTTP_PAYLOAD<S> = OBJECT<{}> → open object input.
675675 expect ( payloadSchema . schema . input ) . toBe ( "data" ) ;
@@ -731,7 +731,7 @@ describe("Schema", () => {
731731 } ,
732732 } ;
733733
734- const [ valueSchema ] = getSignatureSchema (
734+ const { parameters : [ valueSchema ] } = getSignatureSchema (
735735 flow ,
736736 DATA_TYPES ,
737737 [ ...FUNCTION_SIGNATURES , NULLABLE_TEXT_FN ] ,
@@ -799,7 +799,7 @@ describe("Schema", () => {
799799 } ,
800800 } ;
801801
802- const [ valueSchema ] = getSignatureSchema (
802+ const { parameters : [ valueSchema ] } = getSignatureSchema (
803803 flow ,
804804 DATA_TYPES ,
805805 [ ...FUNCTION_SIGNATURES , NULLABLE_OBJECT_FN ] ,
@@ -871,7 +871,7 @@ describe("Schema", () => {
871871 } ,
872872 } ;
873873
874- const [ valueSchema ] = getSignatureSchema (
874+ const { parameters : [ valueSchema ] } = getSignatureSchema (
875875 flow ,
876876 DATA_TYPES ,
877877 [ ...FUNCTION_SIGNATURES , NESTED_NULLABLE_OBJECT_FN ] ,
@@ -940,7 +940,7 @@ describe("Schema", () => {
940940 } ,
941941 } ;
942942
943- const [ valueSchema ] = getSignatureSchema (
943+ const { parameters : [ valueSchema ] } = getSignatureSchema (
944944 flow ,
945945 DATA_TYPES ,
946946 [ ...FUNCTION_SIGNATURES , DEEPLY_NESTED_FN ] ,
@@ -1019,7 +1019,7 @@ describe("Schema", () => {
10191019 } ,
10201020 } ;
10211021
1022- const [ valueSchema ] = getSignatureSchema (
1022+ const { parameters : [ valueSchema ] } = getSignatureSchema (
10231023 flow ,
10241024 [ ...DATA_TYPES , ...RECURSIVE_DATA_TYPES ] ,
10251025 [ ...FUNCTION_SIGNATURES , RECURSIVE_FN ] ,
@@ -1050,4 +1050,149 @@ describe("Schema", () => {
10501050 expect ( paths ) . not . toContain ( "chain.next.next.next.next.next.next.value" ) ;
10511051 } ) ;
10521052
1053+ describe ( "return schema" , ( ) => {
1054+ // Single-node flow calling `identifier` with the given parameters, probed at that node.
1055+ const singleNode = ( identifier : string , params : any [ ] ) : Flow => ( {
1056+ id : "gid://sagittarius/Flow/1" ,
1057+ startingNodeId : "gid://sagittarius/NodeFunction/1" ,
1058+ signature : "(): void" ,
1059+ nodes : {
1060+ nodes : [
1061+ {
1062+ id : "gid://sagittarius/NodeFunction/1" ,
1063+ functionDefinition : { identifier} ,
1064+ parameters : { nodes : params } ,
1065+ } ,
1066+ ] ,
1067+ } ,
1068+ } ) ;
1069+
1070+ const returnOf = ( identifier : string , params : any [ ] ) =>
1071+ getSignatureSchema (
1072+ singleNode ( identifier , params ) ,
1073+ DATA_TYPES ,
1074+ FUNCTION_SIGNATURES ,
1075+ "gid://sagittarius/NodeFunction/1" ,
1076+ ) . return ;
1077+
1078+ // Asserts every schema node in the tree (root, nested properties, list
1079+ // items) has no suggestions — a return schema describes an output, so it
1080+ // never carries input suggestions.
1081+ const expectNoSuggestionsAnywhere = ( schema : any , path = "return" ) : void => {
1082+ expect (
1083+ schema . suggestions === undefined ||
1084+ ( Array . isArray ( schema . suggestions ) && schema . suggestions . length === 0 ) ,
1085+ `${ path } unexpectedly has suggestions: ${ JSON . stringify ( schema . suggestions ) } ` ,
1086+ ) . toBe ( true ) ;
1087+ for ( const [ key , child ] of Object . entries ( schema . properties ?? { } ) ) {
1088+ const children = Array . isArray ( child ) ? child : [ child ] ;
1089+ children . forEach ( ( c , i ) =>
1090+ expectNoSuggestionsAnywhere ( c , `${ path } .properties.${ key } [${ i } ]` ) ,
1091+ ) ;
1092+ }
1093+ ( schema . items ?? [ ] ) . forEach ( ( item : any , i : number ) =>
1094+ expectNoSuggestionsAnywhere ( item , `${ path } .items[${ i } ]` ) ,
1095+ ) ;
1096+ } ;
1097+
1098+ it ( "resolves a NUMBER return to a number input" , ( ) => {
1099+ // std::boolean::as_number → (value: BOOLEAN): NUMBER
1100+ const ret = returnOf ( "std::boolean::as_number" , [
1101+ { value : { __typename : "LiteralValue" , value : true } } ,
1102+ ] ) ;
1103+ expect ( ret ) . toEqual ( { input : "number" } ) ;
1104+ } ) ;
1105+
1106+ it ( "resolves a TEXT return to a text input" , ( ) => {
1107+ // std::boolean::as_text → (value: BOOLEAN): TEXT
1108+ const ret = returnOf ( "std::boolean::as_text" , [
1109+ { value : { __typename : "LiteralValue" , value : true } } ,
1110+ ] ) ;
1111+ expect ( ret ) . toEqual ( { input : "text" } ) ;
1112+ } ) ;
1113+
1114+ it ( "resolves a BOOLEAN return to a boolean input" , ( ) => {
1115+ // std::boolean::from_number → (value: NUMBER): BOOLEAN
1116+ const ret = returnOf ( "std::boolean::from_number" , [
1117+ { value : { __typename : "LiteralValue" , value : 1 } } ,
1118+ ] ) ;
1119+ expect ( ret ) . toEqual ( { input : "boolean" } ) ;
1120+ } ) ;
1121+
1122+ it ( "resolves an object return (HTTP_RESPONSE) to a data input with its properties" , ( ) => {
1123+ // http::request::send → (...): HTTP_RESPONSE<any>
1124+ const ret = returnOf ( "http::request::send" , [
1125+ { value : { __typename : "LiteralValue" , value : "GET" } } ,
1126+ { value : { __typename : "LiteralValue" , value : "/x" } } ,
1127+ { value : null } ,
1128+ { value : null } ,
1129+ { value : null } ,
1130+ { value : null } ,
1131+ { value : null } ,
1132+ { value : null } ,
1133+ ] ) ;
1134+ expect ( ret . input ) . toBe ( "data" ) ;
1135+ const dataRet = ret as { properties : Record < string , unknown > ; required : string [ ] } ;
1136+ expect ( Object . keys ( dataRet . properties ) ) . toEqual (
1137+ expect . arrayContaining ( [ "payload" , "headers" , "http_status_code" ] ) ,
1138+ ) ;
1139+ expect ( dataRet . required ) . toEqual (
1140+ expect . arrayContaining ( [ "payload" , "headers" , "http_status_code" ] ) ,
1141+ ) ;
1142+ // The return type describes an output, so it carries no input
1143+ // suggestions — at any nesting level.
1144+ expectNoSuggestionsAnywhere ( ret ) ;
1145+ } ) ;
1146+
1147+ it ( "instantiates a generic return type from the supplied arguments" , ( ) => {
1148+ // std::list::at → <T>(list: LIST<T>, index: NUMBER): T
1149+ // A list of numbers binds T = NUMBER, so the return is a number input.
1150+ const ret = returnOf ( "std::list::at" , [
1151+ { value : { __typename : "LiteralValue" , value : [ 10 , 20 , 30 ] } } ,
1152+ { value : { __typename : "LiteralValue" , value : 0 } } ,
1153+ ] ) ;
1154+ expect ( ret ) . toEqual ( { input : "number" } ) ;
1155+ } ) ;
1156+
1157+ it ( "returns a generic input for a void signature and no nodeId at the flow level" , ( ) => {
1158+ const result = getSignatureSchema (
1159+ singleNode ( "std::boolean::as_number" , [
1160+ { value : { __typename : "LiteralValue" , value : true } } ,
1161+ ] ) ,
1162+ DATA_TYPES ,
1163+ FUNCTION_SIGNATURES ,
1164+ ) ;
1165+ // Flow-level probe: no target node, flow signature is `(): void`.
1166+ expect ( result . nodeId ) . toBeUndefined ( ) ;
1167+ expect ( result . return ) . toEqual ( { input : "generic" } ) ;
1168+ } ) ;
1169+
1170+ it ( "never carries suggestions, whatever the return type" , ( ) => {
1171+ // Primitive, object, list and generic returns all stay suggestion-free.
1172+ expectNoSuggestionsAnywhere (
1173+ returnOf ( "std::boolean::as_number" , [
1174+ { value : { __typename : "LiteralValue" , value : true } } ,
1175+ ] ) ,
1176+ ) ;
1177+ expectNoSuggestionsAnywhere (
1178+ returnOf ( "std::list::at" , [
1179+ { value : { __typename : "LiteralValue" , value : [ 1 , 2 , 3 ] } } ,
1180+ { value : { __typename : "LiteralValue" , value : 0 } } ,
1181+ ] ) ,
1182+ ) ;
1183+ expectNoSuggestionsAnywhere (
1184+ returnOf ( "http::request::send" , [
1185+ { value : { __typename : "LiteralValue" , value : "GET" } } ,
1186+ { value : { __typename : "LiteralValue" , value : "/x" } } ,
1187+ { value : null } ,
1188+ { value : null } ,
1189+ { value : null } ,
1190+ { value : null } ,
1191+ { value : null } ,
1192+ { value : null } ,
1193+ ] ) ,
1194+ ) ;
1195+ } ) ;
1196+ } ) ;
1197+
10531198} )
0 commit comments