@@ -844,36 +844,49 @@ export const spinner = ({ indicator = 'dots' }: SpinnerOptions = {}) => {
844
844
} ;
845
845
} ;
846
846
847
- export type PromptGroupAwaitedReturn < T > = {
848
- [ P in keyof T ] : Exclude < Awaited < T [ P ] > , symbol > ;
847
+ type Prettify < T > = {
848
+ [ P in keyof T ] : T [ P ] ;
849
+ } & { } ;
850
+
851
+ export type PromptAwaitedReturn < T > = Exclude < Awaited < T > , symbol > ;
852
+
853
+ export type PromptGroupAwaitedReturn < T > = Prettify < {
854
+ [ P in keyof T ] : PromptAwaitedReturn < T [ P ] > ;
855
+ } > ;
856
+
857
+ export type PromptWithOptions <
858
+ TResults ,
859
+ TResult ,
860
+ // biome-ignore lint/complexity/noBannedTypes: {} is initializing a empty object
861
+ TOptions extends Record < string , unknown > = { } ,
862
+ > = (
863
+ opts : Prettify <
864
+ {
865
+ results : PromptGroupAwaitedReturn < TResults > ;
866
+ } & TOptions
867
+ >
868
+ ) => TResult ;
869
+
870
+ export type PromptGroup < T > = {
871
+ [ P in keyof T ] : PromptWithOptions < Partial < Omit < T , P > > , undefined | Promise < T [ P ] | undefined > > ;
849
872
} ;
850
873
851
874
export interface PromptGroupOptions < T > {
852
875
/**
853
876
* Control how the group can be canceled
854
877
* if one of the prompts is canceled.
855
878
*/
856
- onCancel ?: ( opts : { results : Prettify < Partial < PromptGroupAwaitedReturn < T > > > } ) => void ;
879
+ onCancel ?: PromptWithOptions < Partial < T > , void > ;
857
880
}
858
881
859
- type Prettify < T > = {
860
- [ P in keyof T ] : T [ P ] ;
861
- } & { } ;
862
-
863
- export type PromptGroup < T > = {
864
- [ P in keyof T ] : ( opts : {
865
- results : Prettify < Partial < PromptGroupAwaitedReturn < Omit < T , P > > > > ;
866
- } ) => undefined | Promise < T [ P ] | undefined > ;
867
- } ;
868
-
869
882
/**
870
883
* Define a group of prompts to be displayed
871
884
* and return a results of objects within the group
872
885
*/
873
886
export const group = async < T > (
874
887
prompts : PromptGroup < T > ,
875
888
opts ?: PromptGroupOptions < T >
876
- ) : Promise < Prettify < PromptGroupAwaitedReturn < T > > > => {
889
+ ) : Promise < PromptGroupAwaitedReturn < T > > => {
877
890
const results = { } as any ;
878
891
const promptNames = Object . keys ( prompts ) ;
879
892
@@ -898,6 +911,121 @@ export const group = async <T>(
898
911
return results ;
899
912
} ;
900
913
914
+ type NextWorkflowBuilder <
915
+ TResults extends Record < string , unknown > ,
916
+ TKey extends string ,
917
+ TResult ,
918
+ > = WorkflowBuilder <
919
+ Prettify <
920
+ {
921
+ [ Key in keyof TResults ] : Key extends TKey ? TResult : TResults [ Key ] ;
922
+ } & {
923
+ [ Key in TKey as undefined extends TResult ? never : TKey ] : TResult ;
924
+ } & {
925
+ [ Key in TKey as undefined extends TResult ? TKey : never ] ?: TResult ;
926
+ }
927
+ >
928
+ > ;
929
+
930
+ type WorkflowStep < TName extends string , TResults , TResult = unknown > = {
931
+ name : TName ;
932
+ prompt : PromptWithOptions < TResults , TResult > ;
933
+ setResult : boolean ;
934
+ condition ?: PromptWithOptions < TResults , boolean > ;
935
+ } ;
936
+
937
+ // biome-ignore lint/complexity/noBannedTypes: <explanation>
938
+ class WorkflowBuilder < TResults extends Record < string , unknown > = { } > {
939
+ private results : TResults = { } as TResults ;
940
+ private steps : WorkflowStep < string , TResults > [ ] = [ ] ;
941
+ private cancelCallback : PromptWithOptions < Partial < TResults > , void > | undefined ;
942
+
943
+ public step < TName extends string , TResult > (
944
+ name : TName extends keyof TResults ? never : TName ,
945
+ prompt : PromptWithOptions < TResults , TResult >
946
+ ) : NextWorkflowBuilder < TResults , TName , PromptAwaitedReturn < TResult > > {
947
+ this . steps . push ( { name, prompt, setResult : true } ) ;
948
+ return this as any ;
949
+ }
950
+
951
+ public conditionalStep < TName extends string , TResult > (
952
+ name : TName ,
953
+ condition : PromptWithOptions < TResults , boolean > ,
954
+ prompt : PromptWithOptions < TResults , TResult >
955
+ ) : NextWorkflowBuilder <
956
+ TResults ,
957
+ TName ,
958
+ | ( TName extends keyof TResults ? TResults [ TName ] : never )
959
+ | PromptAwaitedReturn < TResult >
960
+ | undefined
961
+ > {
962
+ this . steps . push ( { name, prompt, condition, setResult : true } ) ;
963
+ return this as any ;
964
+ }
965
+
966
+ public forkStep < TName extends string , TResult extends Record < string , unknown > > (
967
+ name : TName ,
968
+ condition : PromptWithOptions < TResults , boolean > ,
969
+ subWorkflow : PromptWithOptions < TResults , WorkflowBuilder < TResult > >
970
+ ) : NextWorkflowBuilder <
971
+ TResults ,
972
+ TName ,
973
+ ( TName extends keyof TResults ? TResults [ TName ] : never ) | TResult | undefined
974
+ > {
975
+ this . steps . push ( {
976
+ name,
977
+ prompt : ( { results } ) => {
978
+ return subWorkflow ( { results } ) . run ( ) ;
979
+ } ,
980
+ condition,
981
+ setResult : true ,
982
+ } ) ;
983
+ return this as any ;
984
+ }
985
+
986
+ public logStep (
987
+ name : string ,
988
+ prompt : PromptWithOptions < TResults , void >
989
+ ) : WorkflowBuilder < TResults > {
990
+ this . steps . push ( { name, prompt, setResult : false } ) ;
991
+ return this ;
992
+ }
993
+
994
+ public customStep < TName extends string , TResult > (
995
+ step : WorkflowStep < TName , TResults , TResult >
996
+ ) : NextWorkflowBuilder < TResults , TName , PromptAwaitedReturn < TResult > > {
997
+ this . steps . push ( step ) ;
998
+ return this as any ;
999
+ }
1000
+
1001
+ public onCancel ( cb : PromptWithOptions < Partial < TResults > , void > ) : WorkflowBuilder < TResults > {
1002
+ this . cancelCallback = cb ;
1003
+ return this ;
1004
+ }
1005
+
1006
+ public async run ( ) : Promise < TResults > {
1007
+ for ( const step of this . steps ) {
1008
+ if ( step . condition && ! step . condition ( { results : this . results as any } ) ) {
1009
+ continue ;
1010
+ }
1011
+ const result = await step . prompt ( { results : this . results as any } ) ;
1012
+ if ( isCancel ( result ) ) {
1013
+ this . cancelCallback ?.( { results : this . results as any } ) ;
1014
+ continue ;
1015
+ }
1016
+ if ( step . setResult ) {
1017
+ //@ts -ignore
1018
+ this . results [ step . name ] = result ;
1019
+ }
1020
+ }
1021
+ return this . results ;
1022
+ }
1023
+ }
1024
+
1025
+ export const workflow = ( ) => {
1026
+ return new WorkflowBuilder ( ) ;
1027
+ } ;
1028
+
901
1029
export type Task = {
902
1030
/**
903
1031
* Task title
0 commit comments