type PromiseType < T > = T extends Promise < infer R > ? R : T ; type Step = ( ... args : any ) => any ; type Before = ( func : ( ... args : any ) => any , args : any [ ] ) => Promise < void > ; type After = ( func : ( ... args : any ) => any , args : any [ ] , result : any ) => Promise < void > ; let before ; let after ; export const onStep = ( options = { } ) => { before = options . before ; after = options . after ; } ; export const $ = < T extends Step > ( func ) => { const fn = async ( ... args ) : Promise < PromiseType < ReturnType < T > > > => { try { await before ?. ( func , args ) ; } catch ( e ) { console . warn ( e ) ; } const result = await func . apply ( null , args ) ; try { await after ?. ( func , args , result ) ; } catch ( e ) { console . warn ( e ) ; } return result ; } ; return fn ; } ; const Given = < T > ( desc , props ? ) => new Promise < T | undefined > ( ( resolve ) => { resolve ( props ) ; } ) ; const $Given = $ < Step > ( Given ) ; const When = < T > ( desc , props ? ) => new Promise < T | undefined > ( ( resolve ) => { resolve ( props ) ; } ) ; const $When = $ < Step > ( When ) ; const Then = < T > ( desc , props ? ) => new Promise < T | undefined > ( ( resolve ) => { resolve ( props ) ; } ) ; const $Then = $ < Step > ( Then ) ; const And = < T > ( desc , props ? ) => new Promise < T | undefined > ( ( resolve ) => { resolve ( props ) ; } ) ; const $And = $ < Step > ( And ) ; export { $Given as Given , $When as When , $Then as Then , $And as And } ;