@@ -6,79 +6,113 @@ export const createRuleRunner = (validator, opts, emit) => {
66 const processor = createFactMapProcessor ( validator , opts , emit ) ;
77 const executor = createActionExecutor ( opts , emit ) ;
88 return async ( [ rule , { when, ...rest } ] ) => {
9- // interpolated can be an array FactMap[] OR an object NamedFactMap
10- const interpolated = interpolateDeep (
11- when ,
12- opts . context ,
13- opts . pattern ,
14- opts . resolver ,
15- ) ;
9+ try {
10+ // interpolated can be an array FactMap[] OR an object NamedFactMap
11+ const interpolated = interpolateDeep (
12+ when ,
13+ opts . context ,
14+ opts . pattern ,
15+ opts . resolver ,
16+ ) ;
1617
17- const process = processor ( rule ) ;
18+ emit ( 'debug' , {
19+ type : 'STARTING_RULE' ,
20+ rule,
21+ interpolated,
22+ context : opts . context ,
23+ } ) ;
1824
19- const ruleResults = await Promise . all (
20- Array . isArray ( interpolated )
21- ? interpolated . map ( process )
22- : Object . entries ( interpolated ) . map ( ( [ factMap , id ] ) =>
23- process ( factMap , id ) ,
24- ) ,
25- ) ;
25+ const process = processor ( rule ) ;
2626
27- // create the context and evaluate whether the rules have passed or errored in a single loop
28- const { passed, error, context } = ruleResults . reduce (
29- ( { passed, error, context } , result ) => {
30- if ( error ) return { error } ;
31- passed =
32- passed && Object . values ( result ) . every ( ( { __passed } ) => __passed ) ;
33- error = Object . values ( result ) . some ( ( { __error } ) => __error ) ;
34- return { passed, error, context : { ...context , ...result } } ;
35- } ,
36- {
37- passed : true ,
38- error : false ,
39- context : { } ,
40- } ,
41- ) ;
27+ const ruleResults = await Promise . all (
28+ Array . isArray ( interpolated )
29+ ? interpolated . map ( process )
30+ : Object . entries ( interpolated ) . map ( ( [ factMap , id ] ) =>
31+ process ( factMap , id ) ,
32+ ) ,
33+ ) ;
4234
43- const nextContext = { ...opts . context , results : context } ;
44- const ret = ( rest = { } ) => ( { [ rule ] : { ...rest , results : ruleResults } } ) ;
35+ // create the context and evaluate whether the rules have passed or errored in a single loop
36+ const { passed, error, context } = ruleResults . reduce (
37+ ( { passed, error, context } , result ) => {
38+ if ( error ) return { error } ;
39+ passed =
40+ passed && Object . values ( result ) . every ( ( { __passed } ) => __passed ) ;
41+ error = Object . values ( result ) . some ( ( { __error } ) => __error ) ;
42+ return { passed, error, context : { ...context , ...result } } ;
43+ } ,
44+ {
45+ passed : true ,
46+ error : false ,
47+ context : { } ,
48+ } ,
49+ ) ;
4550
46- if ( error ) return ret ( { error : true } ) ;
47- const key = passed ? 'then' : 'otherwise' ;
48- const which = rest [ key ] ;
49- if ( ! which ) return ret ( ) ;
51+ const nextContext = { ...opts . context , results : context } ;
52+ const ret = ( rest = { } ) => ( {
53+ [ rule ] : {
54+ __error : error ,
55+ __passed : passed ,
56+ ...rest ,
57+ results : ruleResults ,
58+ } ,
59+ } ) ;
5060
51- const { actions, when : nextWhen } = which ;
61+ const key = passed ? 'then' : 'otherwise' ;
62+ const which = rest [ key ] ;
63+ if ( error || ! which ) return ret ( ) ;
5264
53- const [ actionResults , nestedReults ] = await Promise . all ( [
54- actions
55- ? Promise . all (
56- interpolateDeep (
57- actions ,
58- nextContext ,
59- opts . pattern ,
60- opts . resolver ,
61- ) . map ( async ( action ) => {
62- try {
63- return { ...action , result : await executor ( action ) } ;
64- } catch ( error ) {
65- emit ( 'error' , { type : 'ActionExecutionError' , action } ) ;
66- return { ...action , error } ;
67- }
68- } ) ,
69- )
70- : null ,
71- nextWhen
72- ? createRuleRunner (
73- validator ,
74- { ...opts , context : nextContext } ,
75- emit ,
76- ) ( [ `${ rule } .${ key } ` , which ] )
77- : null ,
78- ] ) ;
65+ const { actions, when : nextWhen } = which ;
7966
80- const toRet = ret ( { actions : actionResults } ) ;
67+ const [ actionResults , nestedReults ] = await Promise . all ( [
68+ actions
69+ ? Promise . all (
70+ interpolateDeep (
71+ actions ,
72+ nextContext ,
73+ opts . pattern ,
74+ opts . resolver ,
75+ ) . map ( async ( action ) => {
76+ try {
77+ return { ...action , result : await executor ( action ) } ;
78+ } catch ( error ) {
79+ emit ( 'error' , {
80+ type : 'ActionExecutionError' ,
81+ rule,
82+ action,
83+ error,
84+ params : action . params ,
85+ } ) ;
86+ return { ...action , error } ;
87+ }
88+ } ) ,
89+ ) . then ( ( actionResults ) => {
90+ // we've effectively finished this rule. The nested rules, if any, will print their own debug messages (I think this is acceptable behavior?)
91+ emit ( 'debug' , {
92+ type : 'FINISHED_RULE' ,
93+ rule,
94+ interpolated,
95+ context : opts . context ,
96+ result : { actions : actionResults , results : ruleResults } ,
97+ } ) ;
98+ return actionResults ;
99+ } )
100+ : null ,
101+ nextWhen
102+ ? createRuleRunner (
103+ validator ,
104+ { ...opts , context : nextContext } ,
105+ emit ,
106+ ) ( [ `${ rule } .${ key } ` , which ] )
107+ : null ,
108+ ] ) ;
81109
82- return nestedReults ? { ...toRet , ...nestedReults } : toRet ;
110+ const toRet = ret ( { actions : actionResults } ) ;
111+
112+ return nestedReults ? { ...toRet , ...nestedReults } : toRet ;
113+ } catch ( error ) {
114+ emit ( 'error' , { type : 'RuleExecutionError' , error } ) ;
115+ return { [ rule ] : { } } ;
116+ }
83117 } ;
84118} ;
0 commit comments