@@ -49,6 +49,10 @@ export const whileTaskConfigSchema = {
4949 condition : { } ,
5050 maxIterations : { type : "integer" , minimum : 1 } ,
5151 chainIterations : { type : "boolean" } ,
52+ conditionField : { type : "string" } ,
53+ conditionOperator : { type : "string" } ,
54+ conditionValue : { type : "string" } ,
55+ iterationInputConfig : { type : "object" , additionalProperties : true } ,
5256 } ,
5357 additionalProperties : false ,
5458} as const satisfies DataPortSchema ;
@@ -76,6 +80,18 @@ export type WhileTaskConfig<Output extends TaskOutput = TaskOutput> = GraphAsTas
7680 * @default true
7781 */
7882 readonly chainIterations ?: boolean ;
83+
84+ /** Output field to evaluate for the loop condition. */
85+ readonly conditionField ?: string ;
86+
87+ /** Comparison operator for the loop condition. */
88+ readonly conditionOperator ?: string ;
89+
90+ /** Value to compare against for the loop condition. */
91+ readonly conditionValue ?: string ;
92+
93+ /** Per-property iteration input configuration (scalar/array/flexible). */
94+ readonly iterationInputConfig ?: Record < string , { mode : string ; baseSchema ?: unknown } > ;
7995} ;
8096
8197/**
@@ -184,22 +200,16 @@ export class WhileTask<
184200
185201 /**
186202 * Gets the maximum iterations limit.
187- * Falls back to extras.whileConfig.maxIterations for JSON-deserialized tasks.
188203 */
189204 public get maxIterations ( ) : number {
190- if ( this . config . maxIterations !== undefined ) return this . config . maxIterations ;
191- const wc = this . config . extras ?. whileConfig as { maxIterations ?: number } | undefined ;
192- return wc ?. maxIterations ?? 100 ;
205+ return this . config . maxIterations ?? 100 ;
193206 }
194207
195208 /**
196209 * Whether to chain iteration outputs to inputs.
197- * Falls back to extras.whileConfig.chainIterations for JSON-deserialized tasks.
198210 */
199211 public get chainIterations ( ) : boolean {
200- if ( this . config . chainIterations !== undefined ) return this . config . chainIterations ;
201- const wc = this . config . extras ?. whileConfig as { chainIterations ?: boolean } | undefined ;
202- return wc ?. chainIterations ?? true ;
212+ return this . config . chainIterations ?? true ;
203213 }
204214
205215 /**
@@ -217,20 +227,15 @@ export class WhileTask<
217227 * Execute the while loop.
218228 */
219229 /**
220- * Builds a condition function from the serialized whileConfig in extras
221- * when no condition function is directly provided in config.
230+ * Builds a condition function from the serialized condition fields in config.
222231 */
223- private buildConditionFromExtras ( ) : WhileConditionFn < Output > | undefined {
224- const wc = this . config . extras ?. whileConfig as
225- | { conditionField ?: string ; conditionOperator ?: string ; conditionValue ?: string }
226- | undefined ;
232+ private buildConditionFromConfig ( ) : WhileConditionFn < Output > | undefined {
233+ const { conditionOperator, conditionField, conditionValue } = this . config ;
227234
228- if ( ! wc ?. conditionOperator ) {
235+ if ( ! conditionOperator ) {
229236 return undefined ;
230237 }
231238
232- const { conditionField, conditionOperator, conditionValue } = wc ;
233-
234239 return ( output : Output ) => {
235240 const fieldValue = conditionField
236241 ? getNestedValue ( output as Record < string , unknown > , conditionField )
@@ -251,16 +256,12 @@ export class WhileTask<
251256 iteratedValues : Record < string , unknown [ ] > ;
252257 iterationCount : number ;
253258 } | null {
254- const wc = this . config . extras ?. whileConfig as
255- | { iterationInputConfig ?: Record < string , { mode : string ; baseSchema ?: unknown } > }
256- | undefined ;
257-
258- if ( ! wc ?. iterationInputConfig ) {
259+ if ( ! this . config . iterationInputConfig ) {
259260 return null ;
260261 }
261262
262263 const inputData = input as Record < string , unknown > ;
263- const config = wc . iterationInputConfig ;
264+ const config = this . config . iterationInputConfig ! ;
264265
265266 const arrayPorts : string [ ] = [ ] ;
266267 const scalarPorts : string [ ] = [ ] ;
@@ -350,7 +351,7 @@ export class WhileTask<
350351 }
351352
352353 // Use provided condition or auto-build from serialized whileConfig
353- const condition = this . condition ?? this . buildConditionFromExtras ( ) ;
354+ const condition = this . condition ?? this . buildConditionFromConfig ( ) ;
354355
355356 if ( ! condition ) {
356357 throw new TaskConfigurationError ( `${ this . type } : No condition function provided` ) ;
@@ -426,15 +427,12 @@ export class WhileTask<
426427 * This provides streaming output for the final result while still
427428 * supporting iteration chaining.
428429 */
429- async * executeStream (
430- input : Input ,
431- context : IExecuteContext
432- ) : AsyncIterable < StreamEvent < Output > > {
430+ async * executeStream ( input : Input , context : IExecuteContext ) : AsyncIterable < StreamEvent < Output > > {
433431 if ( ! this . hasChildren ( ) ) {
434432 throw new TaskConfigurationError ( `${ this . type } : No subgraph set for while loop` ) ;
435433 }
436434
437- const condition = this . condition ?? this . buildConditionFromExtras ( ) ;
435+ const condition = this . condition ?? this . buildConditionFromConfig ( ) ;
438436 if ( ! condition ) {
439437 throw new TaskConfigurationError ( `${ this . type } : No condition function provided` ) ;
440438 }
@@ -550,19 +548,15 @@ export class WhileTask<
550548 const baseSchema = super . inputSchema ( ) ;
551549 if ( typeof baseSchema === "boolean" ) return baseSchema ;
552550
553- const wc = this . config . extras ?. whileConfig as
554- | { iterationInputConfig ?: Record < string , { mode : string ; baseSchema ?: DataPortSchema } > }
555- | undefined ;
556-
557- if ( ! wc ?. iterationInputConfig ) {
551+ if ( ! this . config . iterationInputConfig ) {
558552 return baseSchema ;
559553 }
560554
561555 // Wrap array-mode ports in anyOf (scalar | array) schemas.
562556 // Using anyOf instead of plain type:"array" to avoid addInput's array-merge behavior
563557 // which would prepend an undefined element when runInputData starts empty.
564558 const properties = { ...( baseSchema . properties || { } ) } as Record < string , DataPortSchema > ;
565- for ( const [ key , propConfig ] of Object . entries ( wc . iterationInputConfig ) ) {
559+ for ( const [ key , propConfig ] of Object . entries ( this . config . iterationInputConfig ) ) {
566560 if ( propConfig . mode === "array" && properties [ key ] ) {
567561 const scalarSchema = properties [ key ] as DataPortSchema ;
568562 properties [ key ] = {
0 commit comments