@@ -66,10 +66,14 @@ export type AnyInput = Json;
6666export type AnyOutput = Json ;
6767
6868// Step Types
69+ // Skippable mode: 'skip' makes deps optional, 'skip-cascade' keeps deps required
70+ // (because cascade skips dependents at runtime, so if handler runs, dep succeeded)
71+ export type SkippableMode = 'skip' | 'skip-cascade' | false ;
72+
6973// Step metadata structure - enriched type that tracks output and skippability
7074export interface StepMeta <
7175 TOutput = AnyOutput ,
72- TSkippable extends boolean = boolean
76+ TSkippable extends SkippableMode = SkippableMode
7377> {
7478 output : TOutput ;
7579 skippable : TSkippable ;
@@ -285,23 +289,34 @@ export type StepOutput<
285289 : never ;
286290
287291/**
288- * Checks if a step is skippable (has whenUnmet: 'skip' | 'skip-cascade' or retriesExhausted: 'skip' | 'skip-cascade' )
292+ * Gets the skippable mode for a step ( 'skip' | 'skip-cascade' | false )
289293 * @template TFlow - The Flow type
290294 * @template TStepSlug - The step slug to check
291295 */
292- export type IsStepSkippable <
296+ export type GetSkippableMode <
293297 TFlow extends AnyFlow ,
294298 TStepSlug extends string
295299> = TStepSlug extends keyof ExtractFlowStepsRaw < TFlow >
296300 ? ExtractFlowStepsRaw < TFlow > [ TStepSlug ] [ 'skippable' ]
297301 : false ;
298302
303+ /**
304+ * Checks if a step makes its dependents' deps optional (only 'skip' mode, not 'skip-cascade')
305+ * With 'skip-cascade', dependents are also skipped at runtime, so if handler runs, dep succeeded.
306+ */
307+ export type IsStepSkippable <
308+ TFlow extends AnyFlow ,
309+ TStepSlug extends string
310+ > = GetSkippableMode < TFlow , TStepSlug > extends 'skip' ? true : false ;
311+
299312// Helper types for StepInput with optional skippable deps
313+ // Only 'skip' mode makes deps optional (dependents run with undefined value)
314+ // 'skip-cascade' keeps deps required (dependents also skipped, so value guaranteed if running)
300315type RequiredDeps < TFlow extends AnyFlow , TStepSlug extends string > = {
301316 [ K in Extract <
302317 keyof ExtractFlowSteps < TFlow > ,
303318 StepDepsOf < TFlow , TStepSlug >
304- > as IsStepSkippable < TFlow , K & string > extends true
319+ > as GetSkippableMode < TFlow , K & string > extends 'skip'
305320 ? never
306321 : K ] : ExtractFlowSteps < TFlow > [ K ] ;
307322} ;
@@ -310,7 +325,7 @@ type OptionalDeps<TFlow extends AnyFlow, TStepSlug extends string> = {
310325 [ K in Extract <
311326 keyof ExtractFlowSteps < TFlow > ,
312327 StepDepsOf < TFlow , TStepSlug >
313- > as IsStepSkippable < TFlow , K & string > extends true
328+ > as GetSkippableMode < TFlow , K & string > extends 'skip'
314329 ? K
315330 : never ] ?: ExtractFlowSteps < TFlow > [ K ] ;
316331} ;
@@ -319,8 +334,10 @@ type OptionalDeps<TFlow extends AnyFlow, TStepSlug extends string> = {
319334 * Asymmetric step input type:
320335 * - Root steps (no dependencies): receive flow input directly
321336 * - Dependent steps: receive only their dependencies (flow input available via context)
322- * - Skippable deps (whenUnmet/retriesExhausted: 'skip' | 'skip-cascade') are optional
323- * - Required deps are required
337+ * - Skippable deps (whenUnmet/retriesExhausted: 'skip') are optional
338+ * - Cascade deps (whenUnmet/retriesExhausted: 'skip-cascade') are required
339+ * (because if handler runs, the dependency must have succeeded)
340+ * - All other deps are required
324341 *
325342 * This enables functional composition where subflows can receive typed inputs
326343 * without the 'run' wrapper that previously blocked type matching.
@@ -446,21 +463,23 @@ export type RetriesExhaustedMode = 'fail' | 'skip' | 'skip-cascade';
446463
447464/**
448465 * Helper type for dependent step handlers - creates deps object with correct optionality.
449- * Skippable deps ( steps with whenUnmet/retriesExhausted: 'skip' | 'skip-cascade') are optional.
450- * Required deps are required .
466+ * Only steps with 'skip' mode (not 'skip-cascade') make deps optional.
467+ * With 'skip-cascade', dependents are also skipped at runtime, so if handler runs, dep succeeded .
451468 */
452469type DepsWithOptionalSkippable <
453470 TSteps extends AnySteps ,
454471 TDeps extends string
455472> = {
473+ // Required deps: either not skippable or skip-cascade (cascade skips dependents, so value guaranteed)
456474 [ K in TDeps as K extends keyof TSteps
457- ? TSteps [ K ] [ 'skippable' ] extends true
475+ ? TSteps [ K ] [ 'skippable' ] extends 'skip'
458476 ? never
459477 : K
460478 : K ] : K extends keyof TSteps ? TSteps [ K ] [ 'output' ] : never ;
461479} & {
480+ // Optional deps: only 'skip' mode (dependents run with undefined value)
462481 [ K in TDeps as K extends keyof TSteps
463- ? TSteps [ K ] [ 'skippable' ] extends true
482+ ? TSteps [ K ] [ 'skippable' ] extends 'skip'
464483 ? K
465484 : never
466485 : never ] ?: K extends keyof TSteps ? TSteps [ K ] [ 'output' ] : never ;
@@ -726,9 +745,9 @@ export class Flow<
726745 [ K in Slug ] : StepMeta <
727746 Awaited < TOutput > ,
728747 TWhenUnmet extends 'skip' | 'skip-cascade'
729- ? true
748+ ? TWhenUnmet
730749 : TRetries extends 'skip' | 'skip-cascade'
731- ? true
750+ ? TRetries
732751 : false
733752 > ;
734753 } ,
@@ -775,9 +794,9 @@ export class Flow<
775794 [ K in Slug ] : StepMeta <
776795 Awaited < TOutput > ,
777796 TWhenUnmet extends 'skip' | 'skip-cascade'
778- ? true
797+ ? TWhenUnmet
779798 : TRetries extends 'skip' | 'skip-cascade'
780- ? true
799+ ? TRetries
781800 : false
782801 > ;
783802 } ,
@@ -891,9 +910,9 @@ export class Flow<
891910 [ K in Slug ] : StepMeta <
892911 Awaited < TOutput > ,
893912 TWhenUnmet extends 'skip' | 'skip-cascade'
894- ? true
913+ ? TWhenUnmet
895914 : TRetries extends 'skip' | 'skip-cascade'
896- ? true
915+ ? TRetries
897916 : false
898917 > ;
899918 } ,
@@ -939,9 +958,9 @@ export class Flow<
939958 [ K in Slug ] : StepMeta <
940959 Awaited < TOutput > ,
941960 TWhenUnmet extends 'skip' | 'skip-cascade'
942- ? true
961+ ? TWhenUnmet
943962 : TRetries extends 'skip' | 'skip-cascade'
944- ? true
963+ ? TRetries
945964 : false
946965 > ;
947966 } ,
@@ -999,9 +1018,9 @@ export class Flow<
9991018 [ K in Slug ] : StepMeta <
10001019 AwaitedReturn < THandler > [ ] ,
10011020 TWhenUnmet extends 'skip' | 'skip-cascade'
1002- ? true
1021+ ? TWhenUnmet
10031022 : TRetries extends 'skip' | 'skip-cascade'
1004- ? true
1023+ ? TRetries
10051024 : false
10061025 > ;
10071026 } ,
@@ -1048,9 +1067,9 @@ export class Flow<
10481067 [ K in Slug ] : StepMeta <
10491068 AwaitedReturn < THandler > [ ] ,
10501069 TWhenUnmet extends 'skip' | 'skip-cascade'
1051- ? true
1070+ ? TWhenUnmet
10521071 : TRetries extends 'skip' | 'skip-cascade'
1053- ? true
1072+ ? TRetries
10541073 : false
10551074 > ;
10561075 } ,
0 commit comments