@@ -112,13 +112,16 @@ describe('ExtractFlowOutput utility type', () => {
112112 . step ( { slug : 'step3' , dependsOn : [ 'step1' ] } , ( deps ) => ( {
113113 value : deps . step1 . value - 1 ,
114114 } ) )
115- . step ( { slug : 'step4' , dependsOn : [ 'step2' , 'step3' ] } , async ( deps , ctx ) => {
116- const flowInput = await ctx . flowInput ;
117- return {
118- sum : deps . step2 . value + deps . step3 . value ,
119- original : flowInput . input ,
120- } ;
121- } ) ;
115+ . step (
116+ { slug : 'step4' , dependsOn : [ 'step2' , 'step3' ] } ,
117+ async ( deps , ctx ) => {
118+ const flowInput = await ctx . flowInput ;
119+ return {
120+ sum : deps . step2 . value + deps . step3 . value ,
121+ original : flowInput . input ,
122+ } ;
123+ }
124+ ) ;
122125
123126 type FlowOutput = ExtractFlowOutput < typeof complexFlow > ;
124127
@@ -138,4 +141,79 @@ describe('ExtractFlowOutput utility type', () => {
138141 step3 : unknown ;
139142 } > ( ) ;
140143 } ) ;
144+
145+ it ( 'makes skippable leaf steps optional in flow output' , ( ) => {
146+ const skippableLeafFlow = new Flow < { input : string } > ( {
147+ slug : 'skippable_leaf_flow' ,
148+ } )
149+ . step ( { slug : 'prepare' } , ( flowInput ) => ( { text : flowInput . input } ) )
150+ . step (
151+ {
152+ slug : 'leaf_optional' ,
153+ dependsOn : [ 'prepare' ] ,
154+ if : { prepare : { text : 'run' } } ,
155+ whenUnmet : 'skip' ,
156+ } ,
157+ ( deps ) => ( { value : deps . prepare . text . toUpperCase ( ) } )
158+ ) ;
159+
160+ type FlowOutput = ExtractFlowOutput < typeof skippableLeafFlow > ;
161+
162+ expectTypeOf < FlowOutput > ( ) . toMatchTypeOf < {
163+ leaf_optional ?: StepOutput < typeof skippableLeafFlow , 'leaf_optional' > ;
164+ } > ( ) ;
165+
166+ expectTypeOf < {
167+ leaf_optional ?: StepOutput < typeof skippableLeafFlow , 'leaf_optional' > ;
168+ } > ( ) . toMatchTypeOf < FlowOutput > ( ) ;
169+ } ) ;
170+
171+ it ( 'keeps non-skippable leaf steps required in flow output' , ( ) => {
172+ const requiredLeafFlow = new Flow < { input : string } > ( {
173+ slug : 'required_leaf_flow' ,
174+ } ) . step ( { slug : 'leaf_required' } , ( flowInput ) => ( {
175+ value : flowInput . input . length ,
176+ } ) ) ;
177+
178+ type FlowOutput = ExtractFlowOutput < typeof requiredLeafFlow > ;
179+
180+ expectTypeOf < FlowOutput > ( ) . toMatchTypeOf < {
181+ leaf_required : StepOutput < typeof requiredLeafFlow , 'leaf_required' > ;
182+ } > ( ) ;
183+
184+ expectTypeOf < {
185+ leaf_required : StepOutput < typeof requiredLeafFlow , 'leaf_required' > ;
186+ } > ( ) . toMatchTypeOf < FlowOutput > ( ) ;
187+ } ) ;
188+
189+ it ( 'supports mixed required and skippable leaf outputs' , ( ) => {
190+ const mixedLeafFlow = new Flow < { input : string } > ( {
191+ slug : 'mixed_leaf_flow' ,
192+ } )
193+ . step ( { slug : 'prepare' } , ( flowInput ) => ( { text : flowInput . input } ) )
194+ . step ( { slug : 'required_leaf' , dependsOn : [ 'prepare' ] } , ( deps ) => ( {
195+ value : deps . prepare . text . length ,
196+ } ) )
197+ . step (
198+ {
199+ slug : 'optional_leaf' ,
200+ dependsOn : [ 'prepare' ] ,
201+ if : { prepare : { text : 'run' } } ,
202+ whenUnmet : 'skip-cascade' ,
203+ } ,
204+ ( deps ) => ( { value : deps . prepare . text . toUpperCase ( ) } )
205+ ) ;
206+
207+ type FlowOutput = ExtractFlowOutput < typeof mixedLeafFlow > ;
208+
209+ expectTypeOf < FlowOutput > ( ) . toMatchTypeOf < {
210+ required_leaf : StepOutput < typeof mixedLeafFlow , 'required_leaf' > ;
211+ optional_leaf ?: StepOutput < typeof mixedLeafFlow , 'optional_leaf' > ;
212+ } > ( ) ;
213+
214+ expectTypeOf < {
215+ required_leaf : StepOutput < typeof mixedLeafFlow , 'required_leaf' > ;
216+ optional_leaf ?: StepOutput < typeof mixedLeafFlow , 'optional_leaf' > ;
217+ } > ( ) . toMatchTypeOf < FlowOutput > ( ) ;
218+ } ) ;
141219} ) ;
0 commit comments