@statedelta-actions/rules 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,15 +22,16 @@
22
22
  11. [JIT Compilation](#jit-compilation)
23
23
  12. [Batch Operations](#batch-operations)
24
24
  13. [Async Support](#async-support)
25
- 14. [Error Handling](#error-handling)
26
- 15. [HALT_HANDLER](#halt_handler)
27
- 16. [createInvokerMiddleware](#createinvokermiddleware)
28
- 17. [Full API Reference](#full-api-reference)
29
- 18. [Use Case: RPG Combat Tick](#use-case-rpg-combat-tick)
30
- 19. [Use Case: E-commerce Order Pipeline](#use-case-e-commerce-order-pipeline)
31
- 20. [Use Case: Deployment Approval Workflow](#use-case-deployment-approval-workflow)
32
- 21. [Performance Spectrum](#performance-spectrum)
33
- 22. [Internal Design Notes](#internal-design-notes)
25
+ 14. [Modo Interactive](#modo-interactive)
26
+ 15. [Error Handling](#error-handling)
27
+ 16. [HALT_HANDLER](#halt_handler)
28
+ 17. [createInvokerMiddleware](#createinvokermiddleware)
29
+ 18. [Full API Reference](#full-api-reference)
30
+ 19. [Use Case: RPG Combat Tick](#use-case-rpg-combat-tick)
31
+ 20. [Use Case: E-commerce Order Pipeline](#use-case-e-commerce-order-pipeline)
32
+ 21. [Use Case: Deployment Approval Workflow](#use-case-deployment-approval-workflow)
33
+ 22. [Performance Spectrum](#performance-spectrum)
34
+ 23. [Internal Design Notes](#internal-design-notes)
34
35
 
35
36
 
36
37
  ---
@@ -75,6 +76,8 @@ Middleware enriches the `params` envelope (execution scope), not the domain `ctx
75
76
 
76
77
  The engine never throws to the consumer during `evaluate()`. All runtime errors are collected in `RuleEvaluationResult.errors`. The only exception is a programmer error: calling `evaluate()` when `isAsync === true`.
77
78
 
79
+ `register()` is different — structural errors at boot time (missing handler for a directive type) throw an `Error` and abort the registration atomically. See [Registration](#registration).
80
+
78
81
  ### Feature not used = zero overhead
79
82
 
80
83
  When hooks are not registered, hook code is never executed — not as dead branches, but as absent code paths. The JIT compiler conditionally emits code only for features that are actually configured. Zero middleware means no middleware variables, no pipeline calls, no params allocation.
@@ -289,12 +292,21 @@ const result = ruleEngine.register([rule1, rule2, ...]);
289
292
 
290
293
  ### Registration pipeline
291
294
 
292
- 1. **Validate** each rule (id, priority, when, then/rules)
293
- 2. **Normalize** to hidden action: `rule:{id}`
294
- 3. **Collect sub-rules** recursively with dot-separated IDs
295
- 4. **Index** in internal registries (`_rules` sorted by priority desc)
296
- 5. **Register** hidden actions in ActionEngine
297
- 6. **Return** merged result
295
+ Three phases. Local registries are only mutated after the ActionEngine accepts every hidden action — `register()` is atomic.
296
+
297
+ 1. **Build** — validate each rule (id, priority, when, then/rules), collect sub-rules recursively, build hidden action definitions (`rule:{id}` for top-level, dot-separated for sub-rules). Soft validation errors (duplicate id, missing required field) accumulate in `errors[]`. No local indexing yet.
298
+ 2. **Delegate to ActionEngine** `actionEngine.register(hiddenActions)`. Structural errors propagate as throw (handler missing for a directive type). Soft errors from the ActionEngine are mapped back to rule ids and added to `errors[]`.
299
+ 3. **Index** only reached when phase 2 returns. Insert each rule into `_ruleRegistry` and `_rules` (sorted by priority desc), refresh per-rule `isAsync`/`isInteractive` flags from the mini-graph.
300
+
301
+ ### Throw vs collected errors
302
+
303
+ | Error | Behavior |
304
+ |-------|----------|
305
+ | Missing handler for a directive type in `then` (any depth, including sub-rules and `catch`) | **Throws `Error`** in `register()`. No local state mutated. Consumer wraps in try/catch if needed. |
306
+ | Duplicate rule id, missing `when`, missing `then`/`rules`, invalid priority | Collected in `errors[]`. Other valid rules in the same call still register. |
307
+ | `validate()` from a handler returns invalid or throws | Collected in `errors[]`. |
308
+
309
+ The throw path is reserved for boot-time structural errors that cannot become valid at runtime — typos, forgotten handler registration, refactor leftovers. Soft errors are domain-level and worth surveying.
298
310
 
299
311
  ### RuleRegisterResult
300
312
 
@@ -686,13 +698,31 @@ const result = engine.endBatch();
686
698
 
687
699
  ## Async Support
688
700
 
689
- ### Detection
701
+ ### Detection (per-rule transitivo)
690
702
 
691
703
  ```typescript
692
- const isAsync = ruleHookAnalysis.hasAnyAsync || actionEngine.isAsync;
704
+ const isAsync = ruleHookAnalysis.hasAnyAsync || hasAnyAsyncRuleTransitive;
693
705
  ```
694
706
 
695
- Computed once at construction. Immutable. If any rule hook is async, or if the ActionEngine has async directive hooks, `isAsync === true`.
707
+ Refrescado em `register()` / `unregister()` / `endBatch()` consultando o **mini-graph interno do ActionEngine** (ADR-026 do actions). Cada `StoredRule` cacheia `isAsync`/`isInteractive` per-rule (refletindo `actionEngine.isActionAsync(rule.actionId)`).
708
+
709
+ **Diferença importante:** antes era `actionEngine.isAsync` (global). Agora é per-rule transitivo. Engine híbrido (handler async + handler sync) com rules **100% sync na sub-árvore** permite `evaluate()` regular — só rules transitivamente async forçam `evaluateAsync()`.
710
+
711
+ ```typescript
712
+ // Engine híbrido — handler async existe, mas action `log` (usada em syncRule) é sync
713
+ const engine = createActionEngine({
714
+ handlers: { log, fetchAsync: { async: true, async execute() {...} } },
715
+ });
716
+ const rules = createRuleEngine({ actionEngine: engine });
717
+
718
+ rules.register([
719
+ { id: "syncRule", priority: 1, when: () => true, then: [{ type: "log", message: "x" }] },
720
+ ]);
721
+
722
+ engine.isAsync; // true (engine global async — fetchAsync existe)
723
+ rules.isAsync; // false (rule específica é sync transitivamente)
724
+ rules.evaluate(ctx); // ✅ funciona — não força evaluateAsync
725
+ ```
696
726
 
697
727
  ### Usage
698
728
 
@@ -722,15 +752,141 @@ const ruleEngine = createRuleEngine({
722
752
  },
723
753
  });
724
754
 
755
+
725
756
  // Must use evaluateAsync
726
757
  const result = await ruleEngine.evaluateAsync(ctx);
727
758
  ```
728
759
 
729
760
  ---
730
761
 
762
+ ## Modo Interactive
763
+
764
+ Permite **pausar avaliação de rules** e aguardar input externo via generators — espelhamento do modo interactive do ActionEngine. Ortogonal a sync/async.
765
+
766
+ **Habilitação:** ActionEngine precisa estar em modo interactive (`createActionEngine({ ..., interactive: {} })`). Rules detecta automaticamente quando alguma rule registrada tem ação transitivamente interactive.
767
+
768
+ ### Detection per-rule
769
+
770
+ `StoredRule.isInteractive` cacheia o resultado de `engine.isActionInteractive(rule.actionId)`. Refrescado em register/unregister/endBatch.
771
+
772
+ ```typescript
773
+ rules.isInteractive; // true se qualquer rule é interactive transitivamente
774
+ rules.evaluate(ctx); // throws "use evaluateInteractive"
775
+ rules.evaluateAsync(ctx); // idem
776
+ rules.evaluateInteractive(ctx); // ✅ retorna iterator
777
+ ```
778
+
779
+ ### evaluateInteractive
780
+
781
+ Retorna `InteractiveRuleSession` (sync) ou `AsyncInteractiveRuleSession` (async, quando `rules.isAsync`). Drenagem via `next(value)`:
782
+
783
+ > Handlers são primitivas Lego (ADR-028 do actions). Use `input` (genérico) com payload domain-specific. **Nunca crie `askUser`, `confirmEmail`, etc** — viola o princípio canonical.
784
+
785
+ ```typescript
786
+ const engine = createActionEngine({
787
+ handlers: {
788
+ // Primitiva canonical: yield + schema + retry interno
789
+ input: { interactive: true, *execute(d) {
790
+ const ans = yield { kind: "input", payload: d.payload, schema: d.schema };
791
+ return { ok: true, data: ans };
792
+ } },
793
+ log: { execute: (d, f) => { f.ctx.out.push(d.message); return { ok: true }; } },
794
+ },
795
+ interactive: {},
796
+ });
797
+
798
+ const rules = createRuleEngine({ actionEngine: engine });
799
+ rules.register([
800
+ { id: "wizard", priority: 1, when: () => true, then: [
801
+ { type: "log", message: "before" },
802
+ { type: "pause", message: "Confirmar?", as: "ack" },
803
+ { type: "input", payload: { kind: "text", label: "Nome" }, as: "name" },
804
+ { type: "log", resolve: (_c, s) => ({ message: `${s.name} (${s.ack})` }) },
805
+ ] },
806
+ ]);
807
+
808
+ const ctx = { out: [] };
809
+ const session = rules.evaluateInteractive(ctx);
810
+
811
+ session.next(); // → { source: "pause", payload: { message: "Confirmar?" } }
812
+ // ctx.out: ["before"]
813
+ session.next("yes"); // → { kind: "input", payload: { kind: "text", label: "Nome" } }
814
+ session.next("Anderson"); // → { done: true, value: RuleEvaluationResult }
815
+ // matched: ["wizard"]
816
+ // ctx.out: ["before", "Anderson (yes)"]
817
+ ```
818
+
819
+ ### Mistura sync + interactive (priority order)
820
+
821
+ Rules sync e interactive coexistem na mesma evaluation, respeitando priority. Rules sync rodam direto (sem yield); rules interactive pausam.
822
+
823
+ ```typescript
824
+ rules.register([
825
+ { id: "first", priority: 10, when: () => true, then: [{ type: "log", message: "first" }] },
826
+ { id: "wizard", priority: 5, when: () => true, then: [
827
+ { type: "pause", message: "?" },
828
+ ] },
829
+ { id: "last", priority: 1, when: () => true, then: [{ type: "log", message: "last" }] },
830
+ ]);
831
+
832
+ const session = rules.evaluateInteractive(ctx);
833
+ session.next(); // first roda direto, wizard pausa
834
+ // ctx.out: ["first"]
835
+ session.next("ok"); // wizard completa, last roda
836
+ // ctx.out: ["first", "last"]
837
+ ```
838
+
839
+ ### Async generator (engine async + rule interactive)
840
+
841
+ Quando alguma rule é async transitivamente E alguma é interactive, `evaluateInteractive` retorna `AsyncInteractiveRuleSession`. Drenagem via `await session.next()`:
842
+
843
+ ```typescript
844
+ rules.register([
845
+ { id: "loadData", priority: 10, when: () => true, then: [
846
+ { type: "fetchAsync", as: "v" },
847
+ { type: "log", resolve: (_c, s) => ({ message: `loaded ${s.v}` }) },
848
+ ] },
849
+ { id: "getName", priority: 5, when: () => true, then: [
850
+ { type: "input", payload: { kind: "text", label: "Nome" }, as: "name" },
851
+ ] },
852
+ ]);
853
+
854
+ const session = rules.evaluateInteractive(ctx); // AsyncIterator
855
+
856
+ await session.next(); // loadData rodou (fetch async), getName yieldou
857
+ await session.next("Anderson"); // matched: ["loadData", "getName"]
858
+ ```
859
+
860
+ ### Compilação JIT generator
861
+
862
+ Em `mode: "jit"`, `evaluateInteractive` usa **JIT generator compilado** (Fase R6 — `buildInteractiveRuleExecutor`). 4 wrappers possíveis:
863
+
864
+ | `isAsync` | `isInteractive` | Wrapper |
865
+ |-----------|-----------------|---------|
866
+ | false | false | `function ()` (path original — `evaluate`) |
867
+ | true | false | `async function ()` (`evaluateAsync`) |
868
+ | false | true | `function* ()` (`evaluateInteractive` sync) |
869
+ | true | true | `async function* ()` (`evaluateInteractive` async) |
870
+
871
+ JIT emit branch per-rule no loop:
872
+
873
+ ```javascript
874
+ if (stored.isInteractive) {
875
+ ir = yield* ae.invokeInteractive(stored.actionId, params);
876
+ } else if (stored.isAsync) {
877
+ ir = await ae.invokeAsync(stored.actionId, params);
878
+ } else {
879
+ ir = ae.invoke(stored.actionId, params);
880
+ }
881
+ ```
882
+
883
+ Decisão **per-rule em runtime**, baseada nas flags cached em `StoredRule.isAsync`/`isInteractive`. Em `mode: "interpret"` ou `"auto"` (antes do threshold), usa interpreter generator (mesmo behavior, sem unrolled performance).
884
+
885
+ ---
886
+
731
887
  ## Error Handling
732
888
 
733
- The engine **never throws** during evaluation. All errors are collected:
889
+ The engine **never throws** during evaluation. All runtime errors are collected:
734
890
 
735
891
  | Error source | Behavior |
736
892
  |-------------|----------|
@@ -740,9 +896,12 @@ The engine **never throws** during evaluation. All errors are collected:
740
896
  | `onRulesComplete` throws | Silenced |
741
897
  | Middleware throws | Error collected, invoke skipped, next rule |
742
898
  | ActionEngine invoke errors | Directive errors accumulated in counters |
899
+ | Hidden action id missing at invoke time | Pipeline aborts with `abortedBy: "action-not-found"`, `success: false` |
743
900
  | Sub-rule `when()` throws | Error collected, sub-rule skipped |
744
901
  | `maxSubRuleDepth` exceeded | Error collected, sub-tree skipped (no abort) |
745
902
 
903
+ `register()` follows different rules — see [Registration](#registration). Structural errors at boot (missing handler) throw fail-fast.
904
+
746
905
  ### Inspecting errors
747
906
 
748
907
  ```typescript
package/dist/index.cjs CHANGED
@@ -1,5 +1,9 @@
1
- 'use strict';var core=require('@statedelta-actions/core');function M(r,t,e,i,o,h,b,s,u){if(s>=u)return o.push({ruleIndex:h,error:`maxSubRuleDepth (${u}) exceeded`}),i.errors++,false;for(let c=0;c<r.length;c++){let a=r[c];if(a.definition.when){let d;try{d=a.definition.when(t);}catch(g){o.push({ruleIndex:h,error:String(g)}),i.errors++;continue}if(!d)continue}if(a.actionId){let d=e.invoke(a.actionId,b);if(i.directivesApplied+=d.appliedCount,i.directivesSkipped+=d.skippedCount,i.errors+=d.errors.length,d.aborted)return true}if(a.subRules&&a.subRules.length>0&&M(a.subRules,t,e,i,o,h,b,s+1,u))return true}return false}async function $(r,t,e,i,o,h,b,s,u){if(s>=u)return o.push({ruleIndex:h,error:`maxSubRuleDepth (${u}) exceeded`}),i.errors++,false;for(let c=0;c<r.length;c++){let a=r[c];if(a.definition.when){let d;try{d=a.definition.when(t);}catch(g){o.push({ruleIndex:h,error:String(g)}),i.errors++;continue}if(!d)continue}if(a.actionId){let d=await e.invokeAsync(a.actionId,b);if(i.directivesApplied+=d.appliedCount,i.directivesSkipped+=d.skippedCount,i.errors+=d.errors.length,d.aborted)return true}if(a.subRules&&a.subRules.length>0&&await $(a.subRules,t,e,i,o,h,b,s+1,u))return true}return false}function V(){return r=>({$invoker:{ruleId:r.id,priority:r.priority,tags:r.tags??[]}})}function D(r,t,e,i){let o=i?Object.assign({},i):{};for(let h of r)Object.assign(o,h(t,e,o));return o}function L(){return {rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0}}function H(r,t,e,i,o,h,b,s,u,c){return {success:r,aborted:t,abortedBy:e,matched:i,skipped:o,notMatched:h,errors:b,processedCount:s,totalCount:u,counters:c}}function W(r,t){let e=r.filledNames.has("beforeRule"),i=r.filledNames.has("afterRule"),o=r.filledNames.has("onRulesComplete"),h=e||i;return function(s,u,c,a,d){let g=s.length,m=[],n=[],A=[],v=[],p=L(),T=a.length>0,C=true,x=false,_,y=g;c.setContext(u);let k=h?{ctx:u,counters:p}:void 0;for(let f=0;f<g;f++){let R=s[f],E=R.definition.id;if(p.rulesEvaluated++,e)try{let l=d.beforeRule(R.definition,k);if(l==="skip"){n.push(E),p.rulesSkipped++;continue}if(l==="abort"){C=!1,x=!0,_="beforeRule",y=f+1;break}}catch(l){v.push({ruleIndex:f,error:`beforeRule: ${l}`}),p.errors++;}let w;try{w=R.definition.when(u);}catch(l){v.push({ruleIndex:f,error:String(l)}),A.push(E),p.errors++;continue}if(!w){A.push(E);continue}m.push(E),p.rulesMatched++;let S;if(T)try{S=D(a,R.definition,u);}catch(l){v.push({ruleIndex:f,error:`Middleware: ${l}`}),p.errors++;continue}if(R.actionId){let l=c.invoke(R.actionId,S);if(p.directivesApplied+=l.appliedCount,p.directivesSkipped+=l.skippedCount,p.errors+=l.errors.length,i)try{if(d.afterRule(R.definition,l,k)==="abort"){C=!1,x=!0,_="afterRule",y=f+1;break}}catch{}if(l.aborted){C=l.abortedBy==="halt",x=true,_=l.abortedBy,y=f+1;break}}if(R.subRules&&R.subRules.length>0&&M(R.subRules,u,c,p,v,f,S,0,t)){C=false,x=true,_="sub-rule",y=f+1;break}}let I=H(C,x,_,m,n,A,v,y,g,p);if(o)try{d.onRulesComplete(I);}catch{}return I}}function J(r,t){let e=r.filledNames.has("beforeRule"),i=r.filledNames.has("afterRule"),o=r.filledNames.has("onRulesComplete"),h=e||i;return async function(s,u,c,a,d){let g=s.length,m=[],n=[],A=[],v=[],p=L(),T=a.length>0,C=true,x=false,_,y=g;c.setContext(u);let k=h?{ctx:u,counters:p}:void 0;for(let f=0;f<g;f++){let R=s[f],E=R.definition.id;if(p.rulesEvaluated++,e)try{let l=await d.beforeRule(R.definition,k);if(l==="skip"){n.push(E),p.rulesSkipped++;continue}if(l==="abort"){C=!1,x=!0,_="beforeRule",y=f+1;break}}catch(l){v.push({ruleIndex:f,error:`beforeRule: ${l}`}),p.errors++;}let w;try{w=R.definition.when(u);}catch(l){v.push({ruleIndex:f,error:String(l)}),A.push(E),p.errors++;continue}if(!w){A.push(E);continue}m.push(E),p.rulesMatched++;let S;if(T)try{S=D(a,R.definition,u);}catch(l){v.push({ruleIndex:f,error:`Middleware: ${l}`}),p.errors++;continue}if(R.actionId){let l=await c.invokeAsync(R.actionId,S);if(p.directivesApplied+=l.appliedCount,p.directivesSkipped+=l.skippedCount,p.errors+=l.errors.length,i)try{if(await d.afterRule(R.definition,l,k)==="abort"){C=!1,x=!0,_="afterRule",y=f+1;break}}catch{}if(l.aborted){C=l.abortedBy==="halt",x=true,_=l.abortedBy,y=f+1;break}}if(R.subRules&&R.subRules.length>0&&await $(R.subRules,u,c,p,v,f,S,0,t)){C=false,x=true,_="sub-rule",y=f+1;break}}let I=H(C,x,_,m,n,A,v,y,g,p);if(o)try{await d.onRulesComplete(I);}catch{}return I}}function O(r,t,e){return t?J(r,e):W(r,e)}function B(r,t,e,i,o,h,b){r.push(`{const _er={success:${t},aborted:true,abortedBy:${e},matched,skipped,notMatched,errors,processedCount:${i},totalCount:${o},counters};`),h&&r.push(`try{${b}_hookOnComplete(_er);}catch(_e){}`),r.push("return _er;}");}function j(r,t,e,i){let{filledNames:o,asyncNames:h}=r,b=y=>o.has(y),s=y=>h.has(y)?"await ":"",u=b("beforeRule"),c=b("afterRule"),a=b("onRulesComplete"),d=s("beforeRule"),g=s("afterRule"),m=s("onRulesComplete"),n=[];n.push("const len=rules.length;"),n.push("const matched=[];const skipped=[];const notMatched=[];"),n.push("const errors=[];"),n.push("const counters={rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0};"),n.push("ae.setContext(ctx);"),(u||c)&&n.push("const evalCtx={ctx,counters};"),u&&n.push("const _hookBefore=hooks.beforeRule;"),c&&n.push("const _hookAfter=hooks.afterRule;"),a&&n.push("const _hookOnComplete=hooks.onRulesComplete;"),n.push("for(let i=0;i<len;i++){"),n.push("const stored=rules[i];"),n.push("const _rid=stored.definition.id;"),n.push("counters.rulesEvaluated++;"),u&&(n.push("try{"),n.push(`const _bd=${d}_hookBefore(stored.definition,evalCtx);`),n.push('if(_bd==="skip"){skipped.push(_rid);counters.rulesSkipped++;continue;}'),n.push('if(_bd==="abort")'),B(n,"false",'"beforeRule"',"i+1","len",a,m),n.push('}catch(_e){errors.push({ruleIndex:i,error:"beforeRule: "+_e});counters.errors++;}')),n.push("let ev;"),n.push("try{ev=stored.definition.when(ctx);}catch(_e){errors.push({ruleIndex:i,error:String(_e)});notMatched.push(_rid);counters.errors++;continue;}"),n.push("if(!ev){notMatched.push(_rid);continue;}"),n.push("matched.push(_rid);counters.rulesMatched++;"),t&&(n.push("let params;"),n.push('try{params=$.runMw(mw,stored.definition,ctx);}catch(_e){errors.push({ruleIndex:i,error:"Middleware: "+_e});counters.errors++;continue;}'));let A=t?",params":"",v=e?`await ae.invokeAsync(stored.actionId${A})`:`ae.invoke(stored.actionId${A})`;n.push("if(stored.actionId){"),n.push(`const ir=${v};`),n.push("counters.directivesApplied+=ir.appliedCount;counters.directivesSkipped+=ir.skippedCount;counters.errors+=ir.errors.length;"),c&&(n.push("try{"),n.push(`const _ad=${g}_hookAfter(stored.definition,ir,evalCtx);`),n.push('if(_ad==="abort")'),B(n,"false",'"afterRule"',"i+1","len",a,m),n.push("}catch(_e){}")),n.push("if(ir.aborted)"),B(n,'ir.abortedBy==="halt"',"ir.abortedBy","i+1","len",a,m),n.push("}");let p=t?"params":"undefined";n.push("if(stored.subRules&&stored.subRules.length>0){"),n.push(`if(${e?"await ":""}$.evalSub(stored.subRules,ctx,ae,counters,errors,i,${p},0,$.maxDepth))`),B(n,"false",'"sub-rule"',"i+1","len",a,m),n.push("}"),n.push("}"),n.push("const _r={success:true,aborted:false,abortedBy:undefined,matched,skipped,notMatched,errors,processedCount:len,totalCount:len,counters};"),a&&n.push(`try{${m}_hookOnComplete(_r);}catch(_e){}`),n.push("return _r;");let T=n.join(`
2
- `),C=e?"async ":"",x=new Function("rules","ctx","ae","mw","hooks","$",`"use strict";
3
- return(${C}()=>{
4
- ${T}
5
- })();`),_={maxDepth:i};return _.evalSub=e?$:M,t&&(_.runMw=D),function(k,I,f,R,E){return x(k,I,f,R,E,_)}}function P(r){if(!r.id||typeof r.id!="string")return {ruleId:r.id??"(missing)",code:"INVALID_RULE",message:"rule must have a string id"};if(typeof r.priority!="number")return {ruleId:r.id,code:"INVALID_RULE",message:"rule must have a numeric priority"};if(typeof r.when!="function")return {ruleId:r.id,code:"INVALID_RULE",message:"rule must have a when function"};let t=Array.isArray(r.then)&&r.then.length>0,e=Array.isArray(r.rules)&&r.rules.length>0;return !t&&!e?{ruleId:r.id,code:"INVALID_RULE",message:"rule must have then or sub-rules (or both)"}:null}function U(r,t){let e=`${t}.${r.id??"(missing)"}`;if(!r.id||typeof r.id!="string")return {ruleId:e,code:"INVALID_RULE",message:"sub-rule must have a string id"};let i=Array.isArray(r.then)&&r.then.length>0,o=Array.isArray(r.rules)&&r.rules.length>0;return !i&&!o?{ruleId:e,code:"INVALID_RULE",message:"sub-rule must have then or sub-rules (or both)"}:null}var N=()=>{},F=class{_actionEngine;_middleware;_ruleHooks;_isAsync;_maxSubRuleDepth;_requestedMode;_ruleHookAnalysis;_ruleEvaluator;_mode;_maybeAutoPromote;_ruleRegistry=new Map;_rules=[];_batch=null;constructor(t){if(this._actionEngine=t.actionEngine,this._middleware=t.middleware??[],this._ruleHooks=t.ruleHooks??{},this._maxSubRuleDepth=t.maxSubRuleDepth??10,this._requestedMode=t.mode??"auto",this._ruleHookAnalysis=core.analyzeSlots(this._ruleHooks,core.RULE_SLOT_NAMES),this._isAsync=this._ruleHookAnalysis.hasAnyAsync||this._actionEngine.isAsync,this._requestedMode==="jit")this._ruleEvaluator=this._buildJitRule(),this._mode="jit",this._maybeAutoPromote=N;else if(this._ruleEvaluator=O(this._ruleHookAnalysis,this._isAsync,this._maxSubRuleDepth),this._mode="interpret",this._requestedMode==="auto"){let e=0,i=t.autoJitThreshold??8;this._maybeAutoPromote=()=>{++e>=i&&this._promote();};}else this._maybeAutoPromote=N;}get actionEngine(){return this._actionEngine}get isAsync(){return this._isAsync}get compilationMode(){return this._mode}get size(){return this._rules.length}has(t){return this._ruleRegistry.has(t)}register(t){let e=[],i=[],o=[];for(let s of t){let u=P(s);if(u){i.push(u);continue}if(this._ruleRegistry.has(s.id)){i.push({ruleId:s.id,code:"DUPLICATE_ID",message:`rule "${s.id}" is already registered`});continue}let c=`rule:${s.id}`,a=Array.isArray(s.then)&&s.then.length>0,d=a?c:"",g=Array.isArray(s.rules)&&s.rules.length>0?this._collectSubRules(c,s.rules,i,o):void 0,m={definition:s,actionId:d,priority:s.priority,subRules:g?.length?g:void 0};if(this._ruleRegistry.set(s.id,m),Q(this._rules,m),a){let n=["rule"];s.tags&&n.push(...s.tags),o.push({id:d,tags:n,directives:s.then,declarations:s.declarations});}e.push(s.id);}let h=[];if(o.length>0){let s=this._actionEngine.register(o);for(let u of s.errors){let c=this._actionIdToRuleId(u.actionId);i.push({ruleId:c??u.actionId,code:u.code??"ACTION_ERROR",message:u.error});}h=s.warnings;}let b={registered:e,errors:i,warnings:h};return this._batch&&(this._batch.registered.push(...e),this._batch.errors.push(...i),this._batch.warnings.push(...h)),b}unregister(t){let e=this._ruleRegistry.get(t);return e?(this._ruleRegistry.delete(t),X(this._rules,e),e.actionId&&this._actionEngine.unregister(e.actionId),e.subRules&&this._unregisterSubRules(e.subRules),true):false}beginBatch(){this._batch||(this._batch={depth:0,registered:[],errors:[],warnings:[]}),this._batch.depth++,this._actionEngine.beginBatch();}endBatch(){if(!this._batch||this._batch.depth<=0)throw new Error("endBatch() called without matching beginBatch()");this._batch.depth--;let t=this._actionEngine.endBatch();if(this._batch.depth>0)return {registered:[],errors:[],warnings:[]};let e=this._batch;for(let o of t.errors){let h=this._actionIdToRuleId(o.actionId);e.errors.push({ruleId:h??o.actionId,code:o.code??"ACTION_ERROR",message:o.error});}e.warnings.push(...t.warnings);let i={registered:e.registered,errors:e.errors,warnings:e.warnings};return this._batch=null,i}batch(t){this.beginBatch();try{return t(this),this.endBatch()}catch(e){try{this.endBatch();}catch{}throw e}}evaluate(t){if(this._isAsync)throw new Error("Cannot call evaluate() with async hooks. Use evaluateAsync() instead.");let e=this._ruleEvaluator(this._rules,t,this._actionEngine,this._middleware,this._ruleHooks);return this._maybeAutoPromote(),e}async evaluateAsync(t){let e=await this._ruleEvaluator(this._rules,t,this._actionEngine,this._middleware,this._ruleHooks);return this._maybeAutoPromote(),e}compile(){this._requestedMode!=="interpret"&&this._mode!=="jit"&&this._promote();}_collectSubRules(t,e,i,o){let h=[],b=this._actionIdToRuleId(t);for(let s of e){let u=U(s,b);if(u){i.push(u);continue}let c=`${t}.${s.id}`,a=`${b}.${s.id}`,d=Array.isArray(s.then)&&s.then.length>0,g=Array.isArray(s.rules)&&s.rules.length>0?this._collectSubRules(c,s.rules,i,o):void 0,m={definition:s,actionId:d?c:"",subRules:g?.length?g:void 0};if(d){let n=["rule"];s.tags&&n.push(...s.tags),o.push({id:c,tags:n,directives:s.then,declarations:s.declarations});}this._ruleRegistry.set(a,m),h.push(m);}return h}_unregisterSubRules(t){for(let e of t){if(e.actionId){let i=this._actionIdToRuleId(e.actionId);i&&this._ruleRegistry.delete(i),this._actionEngine.unregister(e.actionId);}e.subRules&&this._unregisterSubRules(e.subRules);}}_actionIdToRuleId(t){return t.startsWith("rule:")?t.slice(5):null}_promote(){this._ruleEvaluator=this._buildJitRule(),this._mode="jit",this._maybeAutoPromote=N;}_buildJitRule(){return j(this._ruleHookAnalysis,this._middleware.length>0,this._isAsync,this._maxSubRuleDepth)}};function K(r){return new F(r)}function Q(r,t){let e=0,i=r.length;for(;e<i;){let o=e+i>>>1;r[o].priority>=t.priority?e=o+1:i=o;}r.splice(e,0,t);}function X(r,t){let e=r.indexOf(t);return e===-1?false:(r.splice(e,1),true)}var Y={analyze:()=>({capabilities:["halt"],dependencies:[]}),execute:()=>({ok:true,halt:true})};exports.HALT_HANDLER=Y;exports.createInvokerMiddleware=V;exports.createRuleEngine=K;
1
+ 'use strict';var core=require('@statedelta-actions/core');function B(n,r,t,o,c,d,b,R,a){if(R>=a)return c.push({ruleIndex:d,error:`maxSubRuleDepth (${a}) exceeded`}),o.errors++,false;for(let u=0;u<n.length;u++){let s=n[u];if(s.definition.when){let l;try{l=s.definition.when(r);}catch(v){c.push({ruleIndex:d,error:String(v)}),o.errors++;continue}if(!l)continue}if(s.actionId){let l=t.invoke(s.actionId,b);if(o.directivesApplied+=l.appliedCount,o.directivesSkipped+=l.skippedCount,o.errors+=l.errors.length,l.aborted)return true}if(s.subRules&&s.subRules.length>0&&B(s.subRules,r,t,o,c,d,b,R+1,a))return true}return false}async function F(n,r,t,o,c,d,b,R,a){if(R>=a)return c.push({ruleIndex:d,error:`maxSubRuleDepth (${a}) exceeded`}),o.errors++,false;for(let u=0;u<n.length;u++){let s=n[u];if(s.definition.when){let l;try{l=s.definition.when(r);}catch(v){c.push({ruleIndex:d,error:String(v)}),o.errors++;continue}if(!l)continue}if(s.actionId){let l=await t.invokeAsync(s.actionId,b);if(o.directivesApplied+=l.appliedCount,o.directivesSkipped+=l.skippedCount,o.errors+=l.errors.length,l.aborted)return true}if(s.subRules&&s.subRules.length>0&&await F(s.subRules,r,t,o,c,d,b,R+1,a))return true}return false}function Q(){return n=>({$invoker:{ruleId:n.id,priority:n.priority,tags:n.tags??[]}})}function $(n,r,t,o){let c=o?Object.assign({},o):{};for(let d of n)Object.assign(c,d(r,t,c));return c}function q(){return {rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0}}function U(n,r,t,o,c,d,b,R,a,u){return {success:n,aborted:r,abortedBy:t,matched:o,skipped:c,notMatched:d,errors:b,processedCount:R,totalCount:a,counters:u}}function X(n,r){let t=n.filledNames.has("beforeRule"),o=n.filledNames.has("afterRule"),c=n.filledNames.has("onRulesComplete"),d=t||o;return function(R,a,u,s,l){let v=R.length,_=[],e=[],A=[],y=[],h=q(),S=s.length>0,I=true,C=false,g,m=v;u.setContext(a);let E=d?{ctx:a,counters:h}:void 0;for(let f=0;f<v;f++){let p=R[f],k=p.definition.id;if(h.rulesEvaluated++,t)try{let i=l.beforeRule(p.definition,E);if(i==="skip"){e.push(k),h.rulesSkipped++;continue}if(i==="abort"){I=!1,C=!0,g="beforeRule",m=f+1;break}}catch(i){y.push({ruleIndex:f,error:`beforeRule: ${i}`}),h.errors++;}let T;try{T=p.definition.when(a);}catch(i){y.push({ruleIndex:f,error:String(i)}),A.push(k),h.errors++;continue}if(!T){A.push(k);continue}_.push(k),h.rulesMatched++;let x;if(S)try{x=$(s,p.definition,a);}catch(i){y.push({ruleIndex:f,error:`Middleware: ${i}`}),h.errors++;continue}if(p.actionId){let i=u.invoke(p.actionId,x);if(h.directivesApplied+=i.appliedCount,h.directivesSkipped+=i.skippedCount,h.errors+=i.errors.length,o)try{if(l.afterRule(p.definition,i,E)==="abort"){I=!1,C=!0,g="afterRule",m=f+1;break}}catch{}if(i.aborted){I=i.abortedBy==="halt",C=true,g=i.abortedBy,m=f+1;break}}if(p.subRules&&p.subRules.length>0&&B(p.subRules,a,u,h,y,f,x,0,r)){I=false,C=true,g="sub-rule",m=f+1;break}}let w=U(I,C,g,_,e,A,y,m,v,h);if(c)try{l.onRulesComplete(w);}catch{}return w}}function Y(n,r){let t=n.filledNames.has("beforeRule"),o=n.filledNames.has("afterRule"),c=n.filledNames.has("onRulesComplete"),d=t||o;return async function(R,a,u,s,l){let v=R.length,_=[],e=[],A=[],y=[],h=q(),S=s.length>0,I=true,C=false,g,m=v;u.setContext(a);let E=d?{ctx:a,counters:h}:void 0;for(let f=0;f<v;f++){let p=R[f],k=p.definition.id;if(h.rulesEvaluated++,t)try{let i=await l.beforeRule(p.definition,E);if(i==="skip"){e.push(k),h.rulesSkipped++;continue}if(i==="abort"){I=!1,C=!0,g="beforeRule",m=f+1;break}}catch(i){y.push({ruleIndex:f,error:`beforeRule: ${i}`}),h.errors++;}let T;try{T=p.definition.when(a);}catch(i){y.push({ruleIndex:f,error:String(i)}),A.push(k),h.errors++;continue}if(!T){A.push(k);continue}_.push(k),h.rulesMatched++;let x;if(S)try{x=$(s,p.definition,a);}catch(i){y.push({ruleIndex:f,error:`Middleware: ${i}`}),h.errors++;continue}if(p.actionId){let i=await u.invokeAsync(p.actionId,x);if(h.directivesApplied+=i.appliedCount,h.directivesSkipped+=i.skippedCount,h.errors+=i.errors.length,o)try{if(await l.afterRule(p.definition,i,E)==="abort"){I=!1,C=!0,g="afterRule",m=f+1;break}}catch{}if(i.aborted){I=i.abortedBy==="halt",C=true,g=i.abortedBy,m=f+1;break}}if(p.subRules&&p.subRules.length>0&&await F(p.subRules,a,u,h,y,f,x,0,r)){I=false,C=true,g="sub-rule",m=f+1;break}}let w=U(I,C,g,_,e,A,y,m,v,h);if(c)try{await l.onRulesComplete(w);}catch{}return w}}function O(n,r,t){return r?Y(n,t):X(n,t)}function G(){return {rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0}}function V(n,r,t,o,c,d,b,R,a,u){return {success:n,aborted:r,abortedBy:t,matched:o,skipped:c,notMatched:d,errors:b,processedCount:R,totalCount:a,counters:u}}function Z(n){let r=n.filledNames.has("beforeRule"),t=n.filledNames.has("afterRule"),o=n.filledNames.has("onRulesComplete"),c=r||t;return function*(b,R,a,u,s,l){let v=b.length,_=[],e=[],A=[],y=[],h=G(),S=u.length>0,I=true,C=false,g,m=v;a.setContext(R);let E=c?{ctx:R,counters:h}:void 0;for(let f=0;f<v;f++){let p=b[f],k=p.definition.id;if(h.rulesEvaluated++,r)try{let i=s.beforeRule(p.definition,E);if(i==="skip"){e.push(k),h.rulesSkipped++;continue}if(i==="abort"){I=!1,C=!0,g="beforeRule",m=f+1;break}}catch(i){y.push({ruleIndex:f,error:`beforeRule: ${i}`}),h.errors++;}let T;try{T=p.definition.when(R);}catch(i){y.push({ruleIndex:f,error:String(i)}),A.push(k),h.errors++;continue}if(!T){A.push(k);continue}_.push(k),h.rulesMatched++;let x;if(S)try{x=$(u,p.definition,R);}catch(i){y.push({ruleIndex:f,error:`Middleware: ${i}`}),h.errors++;continue}if(p.actionId){let i;if(p.isInteractive?i=yield*a.invokeInteractive(p.actionId,x):i=a.invoke(p.actionId,x),h.directivesApplied+=i.appliedCount,h.directivesSkipped+=i.skippedCount,h.errors+=i.errors.length,t)try{if(s.afterRule(p.definition,i,E)==="abort"){I=!1,C=!0,g="afterRule",m=f+1;break}}catch{}if(i.aborted){I=i.abortedBy==="halt",C=true,g=i.abortedBy,m=f+1;break}}if(p.subRules&&p.subRules.length>0&&(yield*D(p.subRules,R,a,h,y,f,x,0,l))){I=false,C=true,g="sub-rule",m=f+1;break}}let w=V(I,C,g,_,e,A,y,m,v,h);if(o)try{s.onRulesComplete(w);}catch{}return w}}function ee(n){let r=n.filledNames.has("beforeRule"),t=n.filledNames.has("afterRule"),o=n.filledNames.has("onRulesComplete"),c=r||t;return async function*(b,R,a,u,s,l){let v=b.length,_=[],e=[],A=[],y=[],h=G(),S=u.length>0,I=true,C=false,g,m=v;a.setContext(R);let E=c?{ctx:R,counters:h}:void 0;for(let f=0;f<v;f++){let p=b[f],k=p.definition.id;if(h.rulesEvaluated++,r)try{let i=await s.beforeRule(p.definition,E);if(i==="skip"){e.push(k),h.rulesSkipped++;continue}if(i==="abort"){I=!1,C=!0,g="beforeRule",m=f+1;break}}catch(i){y.push({ruleIndex:f,error:`beforeRule: ${i}`}),h.errors++;}let T;try{T=p.definition.when(R);}catch(i){y.push({ruleIndex:f,error:String(i)}),A.push(k),h.errors++;continue}if(!T){A.push(k);continue}_.push(k),h.rulesMatched++;let x;if(S)try{x=$(u,p.definition,R);}catch(i){y.push({ruleIndex:f,error:`Middleware: ${i}`}),h.errors++;continue}if(p.actionId){let i;if(p.isInteractive?i=yield*a.invokeInteractive(p.actionId,x):p.isAsync?i=await a.invokeAsync(p.actionId,x):i=a.invoke(p.actionId,x),h.directivesApplied+=i.appliedCount,h.directivesSkipped+=i.skippedCount,h.errors+=i.errors.length,t)try{if(await s.afterRule(p.definition,i,E)==="abort"){I=!1,C=!0,g="afterRule",m=f+1;break}}catch{}if(i.aborted){I=i.abortedBy==="halt",C=true,g=i.abortedBy,m=f+1;break}}if(p.subRules&&p.subRules.length>0&&(yield*N(p.subRules,R,a,h,y,f,x,0,l))){I=false,C=true,g="sub-rule",m=f+1;break}}let w=V(I,C,g,_,e,A,y,m,v,h);if(o)try{await s.onRulesComplete(w);}catch{}return w}}function*D(n,r,t,o,c,d,b,R,a){if(R>=a)return c.push({ruleIndex:d,error:`maxSubRuleDepth (${a}) exceeded`}),o.errors++,false;for(let u=0;u<n.length;u++){let s=n[u];if(s.definition.when){let l;try{l=s.definition.when(r);}catch(v){c.push({ruleIndex:d,error:String(v)}),o.errors++;continue}if(!l)continue}if(s.actionId){let l;if(s.isInteractive?l=yield*t.invokeInteractive(s.actionId,b):l=t.invoke(s.actionId,b),o.directivesApplied+=l.appliedCount,o.directivesSkipped+=l.skippedCount,o.errors+=l.errors.length,l.aborted)return true}if(s.subRules&&s.subRules.length>0&&(yield*D(s.subRules,r,t,o,c,d,b,R+1,a)))return true}return false}async function*N(n,r,t,o,c,d,b,R,a){if(R>=a)return c.push({ruleIndex:d,error:`maxSubRuleDepth (${a}) exceeded`}),o.errors++,false;for(let u=0;u<n.length;u++){let s=n[u];if(s.definition.when){let l;try{l=s.definition.when(r);}catch(v){c.push({ruleIndex:d,error:String(v)}),o.errors++;continue}if(!l)continue}if(s.actionId){let l;if(s.isInteractive?l=yield*t.invokeInteractive(s.actionId,b):s.isAsync?l=await t.invokeAsync(s.actionId,b):l=t.invoke(s.actionId,b),o.directivesApplied+=l.appliedCount,o.directivesSkipped+=l.skippedCount,o.errors+=l.errors.length,l.aborted)return true}if(s.subRules&&s.subRules.length>0&&(yield*N(s.subRules,r,t,o,c,d,b,R+1,a)))return true}return false}function W(n,r){return r?ee(n):Z(n)}function M(n,r,t,o,c,d,b){n.push(`{const _er={success:${r},aborted:true,abortedBy:${t},matched,skipped,notMatched,errors,processedCount:${o},totalCount:${c},counters};`),d&&n.push(`try{${b}_hookOnComplete(_er);}catch(_e){}`),n.push("return _er;}");}function J(n,r,t,o){let{filledNames:c,asyncNames:d}=n,b=m=>c.has(m),R=m=>d.has(m)?"await ":"",a=b("beforeRule"),u=b("afterRule"),s=b("onRulesComplete"),l=R("beforeRule"),v=R("afterRule"),_=R("onRulesComplete"),e=[];e.push("const len=rules.length;"),e.push("const matched=[];const skipped=[];const notMatched=[];"),e.push("const errors=[];"),e.push("const counters={rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0};"),e.push("ae.setContext(ctx);"),(a||u)&&e.push("const evalCtx={ctx,counters};"),a&&e.push("const _hookBefore=hooks.beforeRule;"),u&&e.push("const _hookAfter=hooks.afterRule;"),s&&e.push("const _hookOnComplete=hooks.onRulesComplete;"),e.push("for(let i=0;i<len;i++){"),e.push("const stored=rules[i];"),e.push("const _rid=stored.definition.id;"),e.push("counters.rulesEvaluated++;"),a&&(e.push("try{"),e.push(`const _bd=${l}_hookBefore(stored.definition,evalCtx);`),e.push('if(_bd==="skip"){skipped.push(_rid);counters.rulesSkipped++;continue;}'),e.push('if(_bd==="abort")'),M(e,"false",'"beforeRule"',"i+1","len",s,_),e.push('}catch(_e){errors.push({ruleIndex:i,error:"beforeRule: "+_e});counters.errors++;}')),e.push("let ev;"),e.push("try{ev=stored.definition.when(ctx);}catch(_e){errors.push({ruleIndex:i,error:String(_e)});notMatched.push(_rid);counters.errors++;continue;}"),e.push("if(!ev){notMatched.push(_rid);continue;}"),e.push("matched.push(_rid);counters.rulesMatched++;"),r&&(e.push("let params;"),e.push('try{params=$.runMw(mw,stored.definition,ctx);}catch(_e){errors.push({ruleIndex:i,error:"Middleware: "+_e});counters.errors++;continue;}'));let A=r?",params":"",y=t?`await ae.invokeAsync(stored.actionId${A})`:`ae.invoke(stored.actionId${A})`;e.push("if(stored.actionId){"),e.push(`const ir=${y};`),e.push("counters.directivesApplied+=ir.appliedCount;counters.directivesSkipped+=ir.skippedCount;counters.errors+=ir.errors.length;"),u&&(e.push("try{"),e.push(`const _ad=${v}_hookAfter(stored.definition,ir,evalCtx);`),e.push('if(_ad==="abort")'),M(e,"false",'"afterRule"',"i+1","len",s,_),e.push("}catch(_e){}")),e.push("if(ir.aborted)"),M(e,'ir.abortedBy==="halt"',"ir.abortedBy","i+1","len",s,_),e.push("}");let h=r?"params":"undefined";e.push("if(stored.subRules&&stored.subRules.length>0){"),e.push(`if(${t?"await ":""}$.evalSub(stored.subRules,ctx,ae,counters,errors,i,${h},0,$.maxDepth))`),M(e,"false",'"sub-rule"',"i+1","len",s,_),e.push("}"),e.push("}"),e.push("const _r={success:true,aborted:false,abortedBy:undefined,matched,skipped,notMatched,errors,processedCount:len,totalCount:len,counters};"),s&&e.push(`try{${_}_hookOnComplete(_r);}catch(_e){}`),e.push("return _r;");let S=e.join(`
2
+ `),I=t?"async ":"",C=new Function("rules","ctx","ae","mw","hooks","$",`"use strict";
3
+ return(${I}()=>{
4
+ ${S}
5
+ })();`),g={maxDepth:o};return g.evalSub=t?F:B,r&&(g.runMw=$),function(E,w,f,p,k){return C(E,w,f,p,k,g)}}function P(n,r,t,o){let{filledNames:c,asyncNames:d}=n,b=g=>c.has(g),R=g=>d.has(g)?"await ":"",a=b("beforeRule"),u=b("afterRule"),s=b("onRulesComplete"),l=R("beforeRule"),v=R("afterRule"),_=R("onRulesComplete"),e=[];e.push("const len=rules.length;"),e.push("const matched=[];const skipped=[];const notMatched=[];"),e.push("const errors=[];"),e.push("const counters={rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0};"),e.push("ae.setContext(ctx);"),(a||u)&&e.push("const evalCtx={ctx,counters};"),a&&e.push("const _hookBefore=hooks.beforeRule;"),u&&e.push("const _hookAfter=hooks.afterRule;"),s&&e.push("const _hookOnComplete=hooks.onRulesComplete;"),e.push("for(let i=0;i<len;i++){"),e.push("const stored=rules[i];"),e.push("const _rid=stored.definition.id;"),e.push("counters.rulesEvaluated++;"),a&&(e.push("try{"),e.push(`const _bd=${l}_hookBefore(stored.definition,evalCtx);`),e.push('if(_bd==="skip"){skipped.push(_rid);counters.rulesSkipped++;continue;}'),e.push('if(_bd==="abort")'),M(e,"false",'"beforeRule"',"i+1","len",s,_),e.push('}catch(_e){errors.push({ruleIndex:i,error:"beforeRule: "+_e});counters.errors++;}')),e.push("let ev;"),e.push("try{ev=stored.definition.when(ctx);}catch(_e){errors.push({ruleIndex:i,error:String(_e)});notMatched.push(_rid);counters.errors++;continue;}"),e.push("if(!ev){notMatched.push(_rid);continue;}"),e.push("matched.push(_rid);counters.rulesMatched++;"),r&&(e.push("let params;"),e.push('try{params=$.runMw(mw,stored.definition,ctx);}catch(_e){errors.push({ruleIndex:i,error:"Middleware: "+_e});counters.errors++;continue;}'));let A=r?",params":"";e.push("if(stored.actionId){"),e.push("let ir;"),e.push("if(stored.isInteractive){"),e.push(`ir=yield* ae.invokeInteractive(stored.actionId${A});`),e.push("}else "),t?(e.push("if(stored.isAsync){"),e.push(`ir=await ae.invokeAsync(stored.actionId${A});`),e.push("}else{")):e.push("{"),e.push(`ir=ae.invoke(stored.actionId${A});`),e.push("}"),e.push("counters.directivesApplied+=ir.appliedCount;counters.directivesSkipped+=ir.skippedCount;counters.errors+=ir.errors.length;"),u&&(e.push("try{"),e.push(`const _ad=${v}_hookAfter(stored.definition,ir,evalCtx);`),e.push('if(_ad==="abort")'),M(e,"false",'"afterRule"',"i+1","len",s,_),e.push("}catch(_e){}")),e.push("if(ir.aborted)"),M(e,'ir.abortedBy==="halt"',"ir.abortedBy","i+1","len",s,_),e.push("}");let y=r?"params":"undefined";e.push("if(stored.subRules&&stored.subRules.length>0){"),e.push(`if(yield* $.evalSubInt(stored.subRules,ctx,ae,counters,errors,i,${y},0,$.maxDepth))`),M(e,"false",'"sub-rule"',"i+1","len",s,_),e.push("}"),e.push("}"),e.push("const _r={success:true,aborted:false,abortedBy:undefined,matched,skipped,notMatched,errors,processedCount:len,totalCount:len,counters};"),s&&e.push(`try{${_}_hookOnComplete(_r);}catch(_e){}`),e.push("return _r;");let h=e.join(`
6
+ `),S=t?"async function*()":"function*()",I=new Function("rules","ctx","ae","mw","hooks","$",`"use strict";
7
+ return(${S}{
8
+ ${h}
9
+ })();`),C={maxDepth:o};return C.evalSubInt=t?N:D,r&&(C.runMw=$),function(m,E,w,f,p){return I(m,E,w,f,p,C)}}function z(n){if(!n.id||typeof n.id!="string")return {ruleId:n.id??"(missing)",code:"INVALID_RULE",message:"rule must have a string id"};if(typeof n.priority!="number")return {ruleId:n.id,code:"INVALID_RULE",message:"rule must have a numeric priority"};if(typeof n.when!="function")return {ruleId:n.id,code:"INVALID_RULE",message:"rule must have a when function"};let r=Array.isArray(n.then)&&n.then.length>0,t=Array.isArray(n.rules)&&n.rules.length>0;return !r&&!t?{ruleId:n.id,code:"INVALID_RULE",message:"rule must have then or sub-rules (or both)"}:null}function K(n,r){let t=`${r}.${n.id??"(missing)"}`;if(!n.id||typeof n.id!="string")return {ruleId:t,code:"INVALID_RULE",message:"sub-rule must have a string id"};let o=Array.isArray(n.then)&&n.then.length>0,c=Array.isArray(n.rules)&&n.rules.length>0;return !o&&!c?{ruleId:t,code:"INVALID_RULE",message:"sub-rule must have then or sub-rules (or both)"}:null}var L=()=>{},j=class{_actionEngine;_middleware;_ruleHooks;_maxSubRuleDepth;_requestedMode;_ruleHookAnalysis;_hasAnyInteractiveRule=false;_isAsync;_ruleEvaluator;_mode;_maybeAutoPromote;_interactiveEvaluator=null;_interactiveEvaluatorIsAsync=false;_ruleRegistry=new Map;_rules=[];_batch=null;constructor(r){if(this._actionEngine=r.actionEngine,this._middleware=r.middleware??[],this._ruleHooks=r.ruleHooks??{},this._maxSubRuleDepth=r.maxSubRuleDepth??10,this._requestedMode=r.mode??"auto",this._ruleHookAnalysis=core.analyzeSlots(this._ruleHooks,core.RULE_SLOT_NAMES),this._isAsync=this._ruleHookAnalysis.hasAnyAsync,this._requestedMode==="jit")this._ruleEvaluator=this._buildJitRule(),this._mode="jit",this._maybeAutoPromote=L;else if(this._ruleEvaluator=O(this._ruleHookAnalysis,this._isAsync,this._maxSubRuleDepth),this._mode="interpret",this._requestedMode==="auto"){let t=0,o=r.autoJitThreshold??8;this._maybeAutoPromote=()=>{++t>=o&&this._promote();};}else this._maybeAutoPromote=L;}get actionEngine(){return this._actionEngine}get isAsync(){return this._isAsync}get isInteractive(){return this._hasAnyInteractiveRule}get compilationMode(){return this._mode}get size(){return this._rules.length}has(r){return this._ruleRegistry.has(r)}register(r){let t=[],o=[],c=[],d=[];for(let u of r){let s=z(u);if(s){t.push(s);continue}if(this._ruleRegistry.has(u.id)||c.some(y=>y.id===u.id)){t.push({ruleId:u.id,code:"DUPLICATE_ID",message:`rule "${u.id}" is already registered`});continue}let l=`rule:${u.id}`,v=Array.isArray(u.then)&&u.then.length>0,_=v?l:"",e=Array.isArray(u.rules)&&u.rules.length>0?this._collectSubRules(l,u.rules,t,o,d):void 0,A={definition:u,actionId:_,priority:u.priority,subRules:e?.length?e:void 0,isAsync:false,isInteractive:false};if(v){let y=["rule"];u.tags&&y.push(...u.tags),o.push({id:_,tags:y,directives:u.then,declarations:u.declarations});}c.push({id:u.id,stored:A});}let b=[];if(o.length>0){let u=this._actionEngine.register(o);for(let s of u.errors){let l=this._actionIdToRuleId(s.actionId);t.push({ruleId:l??s.actionId,code:s.code??"ACTION_ERROR",message:s.error});}b=u.warnings;}let R=[];for(let{id:u,stored:s}of c)this._ruleRegistry.set(u,s),se(this._rules,s),R.push(u);for(let{qualifiedId:u,stored:s}of d)this._ruleRegistry.set(u,s);let a={registered:R,errors:t,warnings:b};return this._batch?(this._batch.registered.push(...R),this._batch.errors.push(...t),this._batch.warnings.push(...b)):R.length>0&&this._refreshRuleFlags(),a}unregister(r){let t=this._ruleRegistry.get(r);return t?(this._ruleRegistry.delete(r),ie(this._rules,t),t.actionId&&this._actionEngine.unregister(t.actionId),t.subRules&&this._unregisterSubRules(t.subRules),this._refreshRuleFlags(),true):false}beginBatch(){this._batch||(this._batch={depth:0,registered:[],errors:[],warnings:[]}),this._batch.depth++,this._actionEngine.beginBatch();}endBatch(){if(!this._batch||this._batch.depth<=0)throw new Error("endBatch() called without matching beginBatch()");this._batch.depth--;let r=this._actionEngine.endBatch();if(this._batch.depth>0)return {registered:[],errors:[],warnings:[]};let t=this._batch;for(let c of r.errors){let d=this._actionIdToRuleId(c.actionId);t.errors.push({ruleId:d??c.actionId,code:c.code??"ACTION_ERROR",message:c.error});}t.warnings.push(...r.warnings);let o={registered:t.registered,errors:t.errors,warnings:t.warnings};return this._batch=null,this._refreshRuleFlags(),o}batch(r){this.beginBatch();try{return r(this),this.endBatch()}catch(t){try{this.endBatch();}catch{}throw t}}evaluate(r){if(this._hasAnyInteractiveRule)throw new Error("Cannot call evaluate() \u2014 at least one rule is interactive (transitively). Use evaluateInteractive() instead.");if(this._isAsync)throw new Error("Cannot call evaluate() with async hooks or async rules. Use evaluateAsync() instead.");let t=this._ruleEvaluator(this._rules,r,this._actionEngine,this._middleware,this._ruleHooks);return this._maybeAutoPromote(),t}async evaluateAsync(r){if(this._hasAnyInteractiveRule)throw new Error("Cannot call evaluateAsync() \u2014 at least one rule is interactive (transitively). Use evaluateInteractive() instead.");let t=await this._ruleEvaluator(this._rules,r,this._actionEngine,this._middleware,this._ruleHooks);return this._maybeAutoPromote(),t}evaluateInteractive(r){if(!this._hasAnyInteractiveRule)throw new Error("Cannot call evaluateInteractive() \u2014 no registered rule is interactive (transitively). Use evaluate() or evaluateAsync() instead.");return (this._interactiveEvaluator===null||this._interactiveEvaluatorIsAsync!==this._isAsync)&&(this._interactiveEvaluator=this._mode==="jit"?P(this._ruleHookAnalysis,this._middleware.length>0,this._isAsync,this._maxSubRuleDepth):W(this._ruleHookAnalysis,this._isAsync),this._interactiveEvaluatorIsAsync=this._isAsync),this._interactiveEvaluator(this._rules,r,this._actionEngine,this._middleware,this._ruleHooks,this._maxSubRuleDepth)}compile(){this._requestedMode!=="interpret"&&this._mode!=="jit"&&this._promote();}_collectSubRules(r,t,o,c,d){let b=[],R=this._actionIdToRuleId(r);for(let a of t){let u=K(a,R);if(u){o.push(u);continue}let s=`${r}.${a.id}`,l=`${R}.${a.id}`,v=Array.isArray(a.then)&&a.then.length>0,_=Array.isArray(a.rules)&&a.rules.length>0?this._collectSubRules(s,a.rules,o,c,d):void 0,e={definition:a,actionId:v?s:"",subRules:_?.length?_:void 0,isAsync:false,isInteractive:false};if(v){let A=["rule"];a.tags&&A.push(...a.tags),c.push({id:s,tags:A,directives:a.then,declarations:a.declarations});}d.push({qualifiedId:l,stored:e}),b.push(e);}return b}_unregisterSubRules(r){for(let t of r){if(t.actionId){let o=this._actionIdToRuleId(t.actionId);o&&this._ruleRegistry.delete(o),this._actionEngine.unregister(t.actionId);}t.subRules&&this._unregisterSubRules(t.subRules);}}_actionIdToRuleId(r){return r.startsWith("rule:")?r.slice(5):null}_refreshRuleFlags(){let r=false,t=false,o=d=>{if(d.actionId?(d.isAsync=this._actionEngine.isActionAsync(d.actionId)??false,d.isInteractive=this._actionEngine.isActionInteractive(d.actionId)??false,d.isAsync&&(r=true),d.isInteractive&&(t=true)):(d.isAsync=false,d.isInteractive=false),d.subRules)for(let b of d.subRules)o(b);};for(let d of this._rules)o(d);this._hasAnyInteractiveRule=t;let c=this._ruleHookAnalysis.hasAnyAsync||r;c!==this._isAsync?(this._isAsync=c,this._rebuildEvaluator()):this._isAsync=c;}_rebuildEvaluator(){this._mode==="jit"?this._ruleEvaluator=this._buildJitRule():this._ruleEvaluator=O(this._ruleHookAnalysis,this._isAsync,this._maxSubRuleDepth);}_promote(){this._ruleEvaluator=this._buildJitRule(),this._mode="jit",this._maybeAutoPromote=L;}_buildJitRule(){return J(this._ruleHookAnalysis,this._middleware.length>0,this._isAsync,this._maxSubRuleDepth)}};function ne(n){return new j(n)}function se(n,r){let t=0,o=n.length;for(;t<o;){let c=t+o>>>1;n[c].priority>=r.priority?t=c+1:o=c;}n.splice(t,0,r);}function ie(n,r){let t=n.indexOf(r);return t===-1?false:(n.splice(t,1),true)}var oe={analyze:()=>({capabilities:["halt"],dependencies:[]}),execute:()=>({ok:true,halt:true})};exports.HALT_HANDLER=oe;exports.createInvokerMiddleware=Q;exports.createRuleEngine=ne;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Directive, RegisterWarning, RuleError, FrameCounters, HookFn, DirectiveResult } from '@statedelta-actions/core';
1
+ import { Directive, RuleError, FrameCounters, RegisterWarning, HookFn, DirectiveResult } from '@statedelta-actions/core';
2
2
  import { IActionEngine } from '@statedelta-actions/actions';
3
3
 
4
4
  interface SubRuleDefinition<TCtx = unknown> {
@@ -69,11 +69,37 @@ interface RuleEngineConfig<TCtx = unknown> {
69
69
  /** Threshold de evaluate() calls para auto-promote em modo "auto". Default: 8. */
70
70
  autoJitThreshold?: number;
71
71
  }
72
+ /**
73
+ * Sessão de avaliação de rules em modo interactive — retornada por
74
+ * `evaluateInteractive(ctx)`. Drena via `next()` igual a `InteractiveSession`
75
+ * do actions, mas o `done.value` final é `RuleEvaluationResult`.
76
+ */
77
+ interface InteractiveRuleSession extends Iterator<unknown, RuleEvaluationResult, unknown> {
78
+ next(value?: unknown): IteratorResult<unknown, RuleEvaluationResult>;
79
+ return(): IteratorResult<unknown, RuleEvaluationResult>;
80
+ throw(err?: unknown): IteratorResult<unknown, RuleEvaluationResult>;
81
+ [Symbol.iterator](): InteractiveRuleSession;
82
+ }
83
+ interface AsyncInteractiveRuleSession extends AsyncIterator<unknown, RuleEvaluationResult, unknown> {
84
+ next(value?: unknown): Promise<IteratorResult<unknown, RuleEvaluationResult>>;
85
+ return(): Promise<IteratorResult<unknown, RuleEvaluationResult>>;
86
+ throw(err?: unknown): Promise<IteratorResult<unknown, RuleEvaluationResult>>;
87
+ [Symbol.asyncIterator](): AsyncInteractiveRuleSession;
88
+ }
72
89
  interface IRuleEngine<TCtx = unknown> {
73
90
  register(rules: readonly RuleDefinition<TCtx>[]): RuleRegisterResult;
74
91
  unregister(id: string): boolean;
75
92
  evaluate(ctx: TCtx): RuleEvaluationResult;
76
93
  evaluateAsync(ctx: TCtx): Promise<RuleEvaluationResult>;
94
+ /**
95
+ * Avalia rules em modo interactive — retorna iterator drenável.
96
+ * Lança se nenhuma rule registrada é interactive transitivamente, ou se
97
+ * actionEngine não tem `interactive` configurado.
98
+ *
99
+ * Variante async (AsyncInteractiveRuleSession) é retornada quando alguma
100
+ * rule é async transitivamente OU algum hook é async.
101
+ */
102
+ evaluateInteractive(ctx: TCtx): InteractiveRuleSession | AsyncInteractiveRuleSession;
77
103
  beginBatch(): void;
78
104
  endBatch(): RuleRegisterResult;
79
105
  /** Executa fn dentro de um batch, com endBatch() garantido (inclusive em throw). */
@@ -84,6 +110,8 @@ interface IRuleEngine<TCtx = unknown> {
84
110
  has(id: string): boolean;
85
111
  readonly actionEngine: IActionEngine<TCtx>;
86
112
  readonly isAsync: boolean;
113
+ /** Alguma rule registrada é interactive transitivamente? Cached. */
114
+ readonly isInteractive: boolean;
87
115
  readonly compilationMode: "interpret" | "jit";
88
116
  /** Número de rules top-level registradas. */
89
117
  readonly size: number;
@@ -115,4 +143,4 @@ declare const HALT_HANDLER: {
115
143
 
116
144
  declare function createInvokerMiddleware<TCtx>(): RuleMiddleware<TCtx>;
117
145
 
118
- export { HALT_HANDLER, type IRuleEngine, type RuleDefinition, type RuleEngineConfig, type RuleEvaluationContext, type RuleEvaluationResult, type RuleHooks, type RuleMiddleware, type RuleRegisterError, type RuleRegisterResult, type SubRuleDefinition, createInvokerMiddleware, createRuleEngine };
146
+ export { type AsyncInteractiveRuleSession, HALT_HANDLER, type IRuleEngine, type InteractiveRuleSession, type RuleDefinition, type RuleEngineConfig, type RuleEvaluationContext, type RuleEvaluationResult, type RuleHooks, type RuleMiddleware, type RuleRegisterError, type RuleRegisterResult, type SubRuleDefinition, createInvokerMiddleware, createRuleEngine };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Directive, RegisterWarning, RuleError, FrameCounters, HookFn, DirectiveResult } from '@statedelta-actions/core';
1
+ import { Directive, RuleError, FrameCounters, RegisterWarning, HookFn, DirectiveResult } from '@statedelta-actions/core';
2
2
  import { IActionEngine } from '@statedelta-actions/actions';
3
3
 
4
4
  interface SubRuleDefinition<TCtx = unknown> {
@@ -69,11 +69,37 @@ interface RuleEngineConfig<TCtx = unknown> {
69
69
  /** Threshold de evaluate() calls para auto-promote em modo "auto". Default: 8. */
70
70
  autoJitThreshold?: number;
71
71
  }
72
+ /**
73
+ * Sessão de avaliação de rules em modo interactive — retornada por
74
+ * `evaluateInteractive(ctx)`. Drena via `next()` igual a `InteractiveSession`
75
+ * do actions, mas o `done.value` final é `RuleEvaluationResult`.
76
+ */
77
+ interface InteractiveRuleSession extends Iterator<unknown, RuleEvaluationResult, unknown> {
78
+ next(value?: unknown): IteratorResult<unknown, RuleEvaluationResult>;
79
+ return(): IteratorResult<unknown, RuleEvaluationResult>;
80
+ throw(err?: unknown): IteratorResult<unknown, RuleEvaluationResult>;
81
+ [Symbol.iterator](): InteractiveRuleSession;
82
+ }
83
+ interface AsyncInteractiveRuleSession extends AsyncIterator<unknown, RuleEvaluationResult, unknown> {
84
+ next(value?: unknown): Promise<IteratorResult<unknown, RuleEvaluationResult>>;
85
+ return(): Promise<IteratorResult<unknown, RuleEvaluationResult>>;
86
+ throw(err?: unknown): Promise<IteratorResult<unknown, RuleEvaluationResult>>;
87
+ [Symbol.asyncIterator](): AsyncInteractiveRuleSession;
88
+ }
72
89
  interface IRuleEngine<TCtx = unknown> {
73
90
  register(rules: readonly RuleDefinition<TCtx>[]): RuleRegisterResult;
74
91
  unregister(id: string): boolean;
75
92
  evaluate(ctx: TCtx): RuleEvaluationResult;
76
93
  evaluateAsync(ctx: TCtx): Promise<RuleEvaluationResult>;
94
+ /**
95
+ * Avalia rules em modo interactive — retorna iterator drenável.
96
+ * Lança se nenhuma rule registrada é interactive transitivamente, ou se
97
+ * actionEngine não tem `interactive` configurado.
98
+ *
99
+ * Variante async (AsyncInteractiveRuleSession) é retornada quando alguma
100
+ * rule é async transitivamente OU algum hook é async.
101
+ */
102
+ evaluateInteractive(ctx: TCtx): InteractiveRuleSession | AsyncInteractiveRuleSession;
77
103
  beginBatch(): void;
78
104
  endBatch(): RuleRegisterResult;
79
105
  /** Executa fn dentro de um batch, com endBatch() garantido (inclusive em throw). */
@@ -84,6 +110,8 @@ interface IRuleEngine<TCtx = unknown> {
84
110
  has(id: string): boolean;
85
111
  readonly actionEngine: IActionEngine<TCtx>;
86
112
  readonly isAsync: boolean;
113
+ /** Alguma rule registrada é interactive transitivamente? Cached. */
114
+ readonly isInteractive: boolean;
87
115
  readonly compilationMode: "interpret" | "jit";
88
116
  /** Número de rules top-level registradas. */
89
117
  readonly size: number;
@@ -115,4 +143,4 @@ declare const HALT_HANDLER: {
115
143
 
116
144
  declare function createInvokerMiddleware<TCtx>(): RuleMiddleware<TCtx>;
117
145
 
118
- export { HALT_HANDLER, type IRuleEngine, type RuleDefinition, type RuleEngineConfig, type RuleEvaluationContext, type RuleEvaluationResult, type RuleHooks, type RuleMiddleware, type RuleRegisterError, type RuleRegisterResult, type SubRuleDefinition, createInvokerMiddleware, createRuleEngine };
146
+ export { type AsyncInteractiveRuleSession, HALT_HANDLER, type IRuleEngine, type InteractiveRuleSession, type RuleDefinition, type RuleEngineConfig, type RuleEvaluationContext, type RuleEvaluationResult, type RuleHooks, type RuleMiddleware, type RuleRegisterError, type RuleRegisterResult, type SubRuleDefinition, createInvokerMiddleware, createRuleEngine };
package/dist/index.js CHANGED
@@ -1,5 +1,9 @@
1
- import {analyzeSlots,RULE_SLOT_NAMES}from'@statedelta-actions/core';function M(r,t,e,i,o,h,b,s,u){if(s>=u)return o.push({ruleIndex:h,error:`maxSubRuleDepth (${u}) exceeded`}),i.errors++,false;for(let c=0;c<r.length;c++){let a=r[c];if(a.definition.when){let d;try{d=a.definition.when(t);}catch(g){o.push({ruleIndex:h,error:String(g)}),i.errors++;continue}if(!d)continue}if(a.actionId){let d=e.invoke(a.actionId,b);if(i.directivesApplied+=d.appliedCount,i.directivesSkipped+=d.skippedCount,i.errors+=d.errors.length,d.aborted)return true}if(a.subRules&&a.subRules.length>0&&M(a.subRules,t,e,i,o,h,b,s+1,u))return true}return false}async function $(r,t,e,i,o,h,b,s,u){if(s>=u)return o.push({ruleIndex:h,error:`maxSubRuleDepth (${u}) exceeded`}),i.errors++,false;for(let c=0;c<r.length;c++){let a=r[c];if(a.definition.when){let d;try{d=a.definition.when(t);}catch(g){o.push({ruleIndex:h,error:String(g)}),i.errors++;continue}if(!d)continue}if(a.actionId){let d=await e.invokeAsync(a.actionId,b);if(i.directivesApplied+=d.appliedCount,i.directivesSkipped+=d.skippedCount,i.errors+=d.errors.length,d.aborted)return true}if(a.subRules&&a.subRules.length>0&&await $(a.subRules,t,e,i,o,h,b,s+1,u))return true}return false}function V(){return r=>({$invoker:{ruleId:r.id,priority:r.priority,tags:r.tags??[]}})}function D(r,t,e,i){let o=i?Object.assign({},i):{};for(let h of r)Object.assign(o,h(t,e,o));return o}function L(){return {rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0}}function H(r,t,e,i,o,h,b,s,u,c){return {success:r,aborted:t,abortedBy:e,matched:i,skipped:o,notMatched:h,errors:b,processedCount:s,totalCount:u,counters:c}}function W(r,t){let e=r.filledNames.has("beforeRule"),i=r.filledNames.has("afterRule"),o=r.filledNames.has("onRulesComplete"),h=e||i;return function(s,u,c,a,d){let g=s.length,m=[],n=[],A=[],v=[],p=L(),T=a.length>0,C=true,x=false,_,y=g;c.setContext(u);let k=h?{ctx:u,counters:p}:void 0;for(let f=0;f<g;f++){let R=s[f],E=R.definition.id;if(p.rulesEvaluated++,e)try{let l=d.beforeRule(R.definition,k);if(l==="skip"){n.push(E),p.rulesSkipped++;continue}if(l==="abort"){C=!1,x=!0,_="beforeRule",y=f+1;break}}catch(l){v.push({ruleIndex:f,error:`beforeRule: ${l}`}),p.errors++;}let w;try{w=R.definition.when(u);}catch(l){v.push({ruleIndex:f,error:String(l)}),A.push(E),p.errors++;continue}if(!w){A.push(E);continue}m.push(E),p.rulesMatched++;let S;if(T)try{S=D(a,R.definition,u);}catch(l){v.push({ruleIndex:f,error:`Middleware: ${l}`}),p.errors++;continue}if(R.actionId){let l=c.invoke(R.actionId,S);if(p.directivesApplied+=l.appliedCount,p.directivesSkipped+=l.skippedCount,p.errors+=l.errors.length,i)try{if(d.afterRule(R.definition,l,k)==="abort"){C=!1,x=!0,_="afterRule",y=f+1;break}}catch{}if(l.aborted){C=l.abortedBy==="halt",x=true,_=l.abortedBy,y=f+1;break}}if(R.subRules&&R.subRules.length>0&&M(R.subRules,u,c,p,v,f,S,0,t)){C=false,x=true,_="sub-rule",y=f+1;break}}let I=H(C,x,_,m,n,A,v,y,g,p);if(o)try{d.onRulesComplete(I);}catch{}return I}}function J(r,t){let e=r.filledNames.has("beforeRule"),i=r.filledNames.has("afterRule"),o=r.filledNames.has("onRulesComplete"),h=e||i;return async function(s,u,c,a,d){let g=s.length,m=[],n=[],A=[],v=[],p=L(),T=a.length>0,C=true,x=false,_,y=g;c.setContext(u);let k=h?{ctx:u,counters:p}:void 0;for(let f=0;f<g;f++){let R=s[f],E=R.definition.id;if(p.rulesEvaluated++,e)try{let l=await d.beforeRule(R.definition,k);if(l==="skip"){n.push(E),p.rulesSkipped++;continue}if(l==="abort"){C=!1,x=!0,_="beforeRule",y=f+1;break}}catch(l){v.push({ruleIndex:f,error:`beforeRule: ${l}`}),p.errors++;}let w;try{w=R.definition.when(u);}catch(l){v.push({ruleIndex:f,error:String(l)}),A.push(E),p.errors++;continue}if(!w){A.push(E);continue}m.push(E),p.rulesMatched++;let S;if(T)try{S=D(a,R.definition,u);}catch(l){v.push({ruleIndex:f,error:`Middleware: ${l}`}),p.errors++;continue}if(R.actionId){let l=await c.invokeAsync(R.actionId,S);if(p.directivesApplied+=l.appliedCount,p.directivesSkipped+=l.skippedCount,p.errors+=l.errors.length,i)try{if(await d.afterRule(R.definition,l,k)==="abort"){C=!1,x=!0,_="afterRule",y=f+1;break}}catch{}if(l.aborted){C=l.abortedBy==="halt",x=true,_=l.abortedBy,y=f+1;break}}if(R.subRules&&R.subRules.length>0&&await $(R.subRules,u,c,p,v,f,S,0,t)){C=false,x=true,_="sub-rule",y=f+1;break}}let I=H(C,x,_,m,n,A,v,y,g,p);if(o)try{await d.onRulesComplete(I);}catch{}return I}}function O(r,t,e){return t?J(r,e):W(r,e)}function B(r,t,e,i,o,h,b){r.push(`{const _er={success:${t},aborted:true,abortedBy:${e},matched,skipped,notMatched,errors,processedCount:${i},totalCount:${o},counters};`),h&&r.push(`try{${b}_hookOnComplete(_er);}catch(_e){}`),r.push("return _er;}");}function j(r,t,e,i){let{filledNames:o,asyncNames:h}=r,b=y=>o.has(y),s=y=>h.has(y)?"await ":"",u=b("beforeRule"),c=b("afterRule"),a=b("onRulesComplete"),d=s("beforeRule"),g=s("afterRule"),m=s("onRulesComplete"),n=[];n.push("const len=rules.length;"),n.push("const matched=[];const skipped=[];const notMatched=[];"),n.push("const errors=[];"),n.push("const counters={rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0};"),n.push("ae.setContext(ctx);"),(u||c)&&n.push("const evalCtx={ctx,counters};"),u&&n.push("const _hookBefore=hooks.beforeRule;"),c&&n.push("const _hookAfter=hooks.afterRule;"),a&&n.push("const _hookOnComplete=hooks.onRulesComplete;"),n.push("for(let i=0;i<len;i++){"),n.push("const stored=rules[i];"),n.push("const _rid=stored.definition.id;"),n.push("counters.rulesEvaluated++;"),u&&(n.push("try{"),n.push(`const _bd=${d}_hookBefore(stored.definition,evalCtx);`),n.push('if(_bd==="skip"){skipped.push(_rid);counters.rulesSkipped++;continue;}'),n.push('if(_bd==="abort")'),B(n,"false",'"beforeRule"',"i+1","len",a,m),n.push('}catch(_e){errors.push({ruleIndex:i,error:"beforeRule: "+_e});counters.errors++;}')),n.push("let ev;"),n.push("try{ev=stored.definition.when(ctx);}catch(_e){errors.push({ruleIndex:i,error:String(_e)});notMatched.push(_rid);counters.errors++;continue;}"),n.push("if(!ev){notMatched.push(_rid);continue;}"),n.push("matched.push(_rid);counters.rulesMatched++;"),t&&(n.push("let params;"),n.push('try{params=$.runMw(mw,stored.definition,ctx);}catch(_e){errors.push({ruleIndex:i,error:"Middleware: "+_e});counters.errors++;continue;}'));let A=t?",params":"",v=e?`await ae.invokeAsync(stored.actionId${A})`:`ae.invoke(stored.actionId${A})`;n.push("if(stored.actionId){"),n.push(`const ir=${v};`),n.push("counters.directivesApplied+=ir.appliedCount;counters.directivesSkipped+=ir.skippedCount;counters.errors+=ir.errors.length;"),c&&(n.push("try{"),n.push(`const _ad=${g}_hookAfter(stored.definition,ir,evalCtx);`),n.push('if(_ad==="abort")'),B(n,"false",'"afterRule"',"i+1","len",a,m),n.push("}catch(_e){}")),n.push("if(ir.aborted)"),B(n,'ir.abortedBy==="halt"',"ir.abortedBy","i+1","len",a,m),n.push("}");let p=t?"params":"undefined";n.push("if(stored.subRules&&stored.subRules.length>0){"),n.push(`if(${e?"await ":""}$.evalSub(stored.subRules,ctx,ae,counters,errors,i,${p},0,$.maxDepth))`),B(n,"false",'"sub-rule"',"i+1","len",a,m),n.push("}"),n.push("}"),n.push("const _r={success:true,aborted:false,abortedBy:undefined,matched,skipped,notMatched,errors,processedCount:len,totalCount:len,counters};"),a&&n.push(`try{${m}_hookOnComplete(_r);}catch(_e){}`),n.push("return _r;");let T=n.join(`
2
- `),C=e?"async ":"",x=new Function("rules","ctx","ae","mw","hooks","$",`"use strict";
3
- return(${C}()=>{
4
- ${T}
5
- })();`),_={maxDepth:i};return _.evalSub=e?$:M,t&&(_.runMw=D),function(k,I,f,R,E){return x(k,I,f,R,E,_)}}function P(r){if(!r.id||typeof r.id!="string")return {ruleId:r.id??"(missing)",code:"INVALID_RULE",message:"rule must have a string id"};if(typeof r.priority!="number")return {ruleId:r.id,code:"INVALID_RULE",message:"rule must have a numeric priority"};if(typeof r.when!="function")return {ruleId:r.id,code:"INVALID_RULE",message:"rule must have a when function"};let t=Array.isArray(r.then)&&r.then.length>0,e=Array.isArray(r.rules)&&r.rules.length>0;return !t&&!e?{ruleId:r.id,code:"INVALID_RULE",message:"rule must have then or sub-rules (or both)"}:null}function U(r,t){let e=`${t}.${r.id??"(missing)"}`;if(!r.id||typeof r.id!="string")return {ruleId:e,code:"INVALID_RULE",message:"sub-rule must have a string id"};let i=Array.isArray(r.then)&&r.then.length>0,o=Array.isArray(r.rules)&&r.rules.length>0;return !i&&!o?{ruleId:e,code:"INVALID_RULE",message:"sub-rule must have then or sub-rules (or both)"}:null}var N=()=>{},F=class{_actionEngine;_middleware;_ruleHooks;_isAsync;_maxSubRuleDepth;_requestedMode;_ruleHookAnalysis;_ruleEvaluator;_mode;_maybeAutoPromote;_ruleRegistry=new Map;_rules=[];_batch=null;constructor(t){if(this._actionEngine=t.actionEngine,this._middleware=t.middleware??[],this._ruleHooks=t.ruleHooks??{},this._maxSubRuleDepth=t.maxSubRuleDepth??10,this._requestedMode=t.mode??"auto",this._ruleHookAnalysis=analyzeSlots(this._ruleHooks,RULE_SLOT_NAMES),this._isAsync=this._ruleHookAnalysis.hasAnyAsync||this._actionEngine.isAsync,this._requestedMode==="jit")this._ruleEvaluator=this._buildJitRule(),this._mode="jit",this._maybeAutoPromote=N;else if(this._ruleEvaluator=O(this._ruleHookAnalysis,this._isAsync,this._maxSubRuleDepth),this._mode="interpret",this._requestedMode==="auto"){let e=0,i=t.autoJitThreshold??8;this._maybeAutoPromote=()=>{++e>=i&&this._promote();};}else this._maybeAutoPromote=N;}get actionEngine(){return this._actionEngine}get isAsync(){return this._isAsync}get compilationMode(){return this._mode}get size(){return this._rules.length}has(t){return this._ruleRegistry.has(t)}register(t){let e=[],i=[],o=[];for(let s of t){let u=P(s);if(u){i.push(u);continue}if(this._ruleRegistry.has(s.id)){i.push({ruleId:s.id,code:"DUPLICATE_ID",message:`rule "${s.id}" is already registered`});continue}let c=`rule:${s.id}`,a=Array.isArray(s.then)&&s.then.length>0,d=a?c:"",g=Array.isArray(s.rules)&&s.rules.length>0?this._collectSubRules(c,s.rules,i,o):void 0,m={definition:s,actionId:d,priority:s.priority,subRules:g?.length?g:void 0};if(this._ruleRegistry.set(s.id,m),Q(this._rules,m),a){let n=["rule"];s.tags&&n.push(...s.tags),o.push({id:d,tags:n,directives:s.then,declarations:s.declarations});}e.push(s.id);}let h=[];if(o.length>0){let s=this._actionEngine.register(o);for(let u of s.errors){let c=this._actionIdToRuleId(u.actionId);i.push({ruleId:c??u.actionId,code:u.code??"ACTION_ERROR",message:u.error});}h=s.warnings;}let b={registered:e,errors:i,warnings:h};return this._batch&&(this._batch.registered.push(...e),this._batch.errors.push(...i),this._batch.warnings.push(...h)),b}unregister(t){let e=this._ruleRegistry.get(t);return e?(this._ruleRegistry.delete(t),X(this._rules,e),e.actionId&&this._actionEngine.unregister(e.actionId),e.subRules&&this._unregisterSubRules(e.subRules),true):false}beginBatch(){this._batch||(this._batch={depth:0,registered:[],errors:[],warnings:[]}),this._batch.depth++,this._actionEngine.beginBatch();}endBatch(){if(!this._batch||this._batch.depth<=0)throw new Error("endBatch() called without matching beginBatch()");this._batch.depth--;let t=this._actionEngine.endBatch();if(this._batch.depth>0)return {registered:[],errors:[],warnings:[]};let e=this._batch;for(let o of t.errors){let h=this._actionIdToRuleId(o.actionId);e.errors.push({ruleId:h??o.actionId,code:o.code??"ACTION_ERROR",message:o.error});}e.warnings.push(...t.warnings);let i={registered:e.registered,errors:e.errors,warnings:e.warnings};return this._batch=null,i}batch(t){this.beginBatch();try{return t(this),this.endBatch()}catch(e){try{this.endBatch();}catch{}throw e}}evaluate(t){if(this._isAsync)throw new Error("Cannot call evaluate() with async hooks. Use evaluateAsync() instead.");let e=this._ruleEvaluator(this._rules,t,this._actionEngine,this._middleware,this._ruleHooks);return this._maybeAutoPromote(),e}async evaluateAsync(t){let e=await this._ruleEvaluator(this._rules,t,this._actionEngine,this._middleware,this._ruleHooks);return this._maybeAutoPromote(),e}compile(){this._requestedMode!=="interpret"&&this._mode!=="jit"&&this._promote();}_collectSubRules(t,e,i,o){let h=[],b=this._actionIdToRuleId(t);for(let s of e){let u=U(s,b);if(u){i.push(u);continue}let c=`${t}.${s.id}`,a=`${b}.${s.id}`,d=Array.isArray(s.then)&&s.then.length>0,g=Array.isArray(s.rules)&&s.rules.length>0?this._collectSubRules(c,s.rules,i,o):void 0,m={definition:s,actionId:d?c:"",subRules:g?.length?g:void 0};if(d){let n=["rule"];s.tags&&n.push(...s.tags),o.push({id:c,tags:n,directives:s.then,declarations:s.declarations});}this._ruleRegistry.set(a,m),h.push(m);}return h}_unregisterSubRules(t){for(let e of t){if(e.actionId){let i=this._actionIdToRuleId(e.actionId);i&&this._ruleRegistry.delete(i),this._actionEngine.unregister(e.actionId);}e.subRules&&this._unregisterSubRules(e.subRules);}}_actionIdToRuleId(t){return t.startsWith("rule:")?t.slice(5):null}_promote(){this._ruleEvaluator=this._buildJitRule(),this._mode="jit",this._maybeAutoPromote=N;}_buildJitRule(){return j(this._ruleHookAnalysis,this._middleware.length>0,this._isAsync,this._maxSubRuleDepth)}};function K(r){return new F(r)}function Q(r,t){let e=0,i=r.length;for(;e<i;){let o=e+i>>>1;r[o].priority>=t.priority?e=o+1:i=o;}r.splice(e,0,t);}function X(r,t){let e=r.indexOf(t);return e===-1?false:(r.splice(e,1),true)}var Y={analyze:()=>({capabilities:["halt"],dependencies:[]}),execute:()=>({ok:true,halt:true})};export{Y as HALT_HANDLER,V as createInvokerMiddleware,K as createRuleEngine};
1
+ import {analyzeSlots,RULE_SLOT_NAMES}from'@statedelta-actions/core';function B(n,r,t,o,c,d,b,R,a){if(R>=a)return c.push({ruleIndex:d,error:`maxSubRuleDepth (${a}) exceeded`}),o.errors++,false;for(let u=0;u<n.length;u++){let s=n[u];if(s.definition.when){let l;try{l=s.definition.when(r);}catch(v){c.push({ruleIndex:d,error:String(v)}),o.errors++;continue}if(!l)continue}if(s.actionId){let l=t.invoke(s.actionId,b);if(o.directivesApplied+=l.appliedCount,o.directivesSkipped+=l.skippedCount,o.errors+=l.errors.length,l.aborted)return true}if(s.subRules&&s.subRules.length>0&&B(s.subRules,r,t,o,c,d,b,R+1,a))return true}return false}async function F(n,r,t,o,c,d,b,R,a){if(R>=a)return c.push({ruleIndex:d,error:`maxSubRuleDepth (${a}) exceeded`}),o.errors++,false;for(let u=0;u<n.length;u++){let s=n[u];if(s.definition.when){let l;try{l=s.definition.when(r);}catch(v){c.push({ruleIndex:d,error:String(v)}),o.errors++;continue}if(!l)continue}if(s.actionId){let l=await t.invokeAsync(s.actionId,b);if(o.directivesApplied+=l.appliedCount,o.directivesSkipped+=l.skippedCount,o.errors+=l.errors.length,l.aborted)return true}if(s.subRules&&s.subRules.length>0&&await F(s.subRules,r,t,o,c,d,b,R+1,a))return true}return false}function Q(){return n=>({$invoker:{ruleId:n.id,priority:n.priority,tags:n.tags??[]}})}function $(n,r,t,o){let c=o?Object.assign({},o):{};for(let d of n)Object.assign(c,d(r,t,c));return c}function q(){return {rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0}}function U(n,r,t,o,c,d,b,R,a,u){return {success:n,aborted:r,abortedBy:t,matched:o,skipped:c,notMatched:d,errors:b,processedCount:R,totalCount:a,counters:u}}function X(n,r){let t=n.filledNames.has("beforeRule"),o=n.filledNames.has("afterRule"),c=n.filledNames.has("onRulesComplete"),d=t||o;return function(R,a,u,s,l){let v=R.length,_=[],e=[],A=[],y=[],h=q(),S=s.length>0,I=true,C=false,g,m=v;u.setContext(a);let E=d?{ctx:a,counters:h}:void 0;for(let f=0;f<v;f++){let p=R[f],k=p.definition.id;if(h.rulesEvaluated++,t)try{let i=l.beforeRule(p.definition,E);if(i==="skip"){e.push(k),h.rulesSkipped++;continue}if(i==="abort"){I=!1,C=!0,g="beforeRule",m=f+1;break}}catch(i){y.push({ruleIndex:f,error:`beforeRule: ${i}`}),h.errors++;}let T;try{T=p.definition.when(a);}catch(i){y.push({ruleIndex:f,error:String(i)}),A.push(k),h.errors++;continue}if(!T){A.push(k);continue}_.push(k),h.rulesMatched++;let x;if(S)try{x=$(s,p.definition,a);}catch(i){y.push({ruleIndex:f,error:`Middleware: ${i}`}),h.errors++;continue}if(p.actionId){let i=u.invoke(p.actionId,x);if(h.directivesApplied+=i.appliedCount,h.directivesSkipped+=i.skippedCount,h.errors+=i.errors.length,o)try{if(l.afterRule(p.definition,i,E)==="abort"){I=!1,C=!0,g="afterRule",m=f+1;break}}catch{}if(i.aborted){I=i.abortedBy==="halt",C=true,g=i.abortedBy,m=f+1;break}}if(p.subRules&&p.subRules.length>0&&B(p.subRules,a,u,h,y,f,x,0,r)){I=false,C=true,g="sub-rule",m=f+1;break}}let w=U(I,C,g,_,e,A,y,m,v,h);if(c)try{l.onRulesComplete(w);}catch{}return w}}function Y(n,r){let t=n.filledNames.has("beforeRule"),o=n.filledNames.has("afterRule"),c=n.filledNames.has("onRulesComplete"),d=t||o;return async function(R,a,u,s,l){let v=R.length,_=[],e=[],A=[],y=[],h=q(),S=s.length>0,I=true,C=false,g,m=v;u.setContext(a);let E=d?{ctx:a,counters:h}:void 0;for(let f=0;f<v;f++){let p=R[f],k=p.definition.id;if(h.rulesEvaluated++,t)try{let i=await l.beforeRule(p.definition,E);if(i==="skip"){e.push(k),h.rulesSkipped++;continue}if(i==="abort"){I=!1,C=!0,g="beforeRule",m=f+1;break}}catch(i){y.push({ruleIndex:f,error:`beforeRule: ${i}`}),h.errors++;}let T;try{T=p.definition.when(a);}catch(i){y.push({ruleIndex:f,error:String(i)}),A.push(k),h.errors++;continue}if(!T){A.push(k);continue}_.push(k),h.rulesMatched++;let x;if(S)try{x=$(s,p.definition,a);}catch(i){y.push({ruleIndex:f,error:`Middleware: ${i}`}),h.errors++;continue}if(p.actionId){let i=await u.invokeAsync(p.actionId,x);if(h.directivesApplied+=i.appliedCount,h.directivesSkipped+=i.skippedCount,h.errors+=i.errors.length,o)try{if(await l.afterRule(p.definition,i,E)==="abort"){I=!1,C=!0,g="afterRule",m=f+1;break}}catch{}if(i.aborted){I=i.abortedBy==="halt",C=true,g=i.abortedBy,m=f+1;break}}if(p.subRules&&p.subRules.length>0&&await F(p.subRules,a,u,h,y,f,x,0,r)){I=false,C=true,g="sub-rule",m=f+1;break}}let w=U(I,C,g,_,e,A,y,m,v,h);if(c)try{await l.onRulesComplete(w);}catch{}return w}}function O(n,r,t){return r?Y(n,t):X(n,t)}function G(){return {rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0}}function V(n,r,t,o,c,d,b,R,a,u){return {success:n,aborted:r,abortedBy:t,matched:o,skipped:c,notMatched:d,errors:b,processedCount:R,totalCount:a,counters:u}}function Z(n){let r=n.filledNames.has("beforeRule"),t=n.filledNames.has("afterRule"),o=n.filledNames.has("onRulesComplete"),c=r||t;return function*(b,R,a,u,s,l){let v=b.length,_=[],e=[],A=[],y=[],h=G(),S=u.length>0,I=true,C=false,g,m=v;a.setContext(R);let E=c?{ctx:R,counters:h}:void 0;for(let f=0;f<v;f++){let p=b[f],k=p.definition.id;if(h.rulesEvaluated++,r)try{let i=s.beforeRule(p.definition,E);if(i==="skip"){e.push(k),h.rulesSkipped++;continue}if(i==="abort"){I=!1,C=!0,g="beforeRule",m=f+1;break}}catch(i){y.push({ruleIndex:f,error:`beforeRule: ${i}`}),h.errors++;}let T;try{T=p.definition.when(R);}catch(i){y.push({ruleIndex:f,error:String(i)}),A.push(k),h.errors++;continue}if(!T){A.push(k);continue}_.push(k),h.rulesMatched++;let x;if(S)try{x=$(u,p.definition,R);}catch(i){y.push({ruleIndex:f,error:`Middleware: ${i}`}),h.errors++;continue}if(p.actionId){let i;if(p.isInteractive?i=yield*a.invokeInteractive(p.actionId,x):i=a.invoke(p.actionId,x),h.directivesApplied+=i.appliedCount,h.directivesSkipped+=i.skippedCount,h.errors+=i.errors.length,t)try{if(s.afterRule(p.definition,i,E)==="abort"){I=!1,C=!0,g="afterRule",m=f+1;break}}catch{}if(i.aborted){I=i.abortedBy==="halt",C=true,g=i.abortedBy,m=f+1;break}}if(p.subRules&&p.subRules.length>0&&(yield*D(p.subRules,R,a,h,y,f,x,0,l))){I=false,C=true,g="sub-rule",m=f+1;break}}let w=V(I,C,g,_,e,A,y,m,v,h);if(o)try{s.onRulesComplete(w);}catch{}return w}}function ee(n){let r=n.filledNames.has("beforeRule"),t=n.filledNames.has("afterRule"),o=n.filledNames.has("onRulesComplete"),c=r||t;return async function*(b,R,a,u,s,l){let v=b.length,_=[],e=[],A=[],y=[],h=G(),S=u.length>0,I=true,C=false,g,m=v;a.setContext(R);let E=c?{ctx:R,counters:h}:void 0;for(let f=0;f<v;f++){let p=b[f],k=p.definition.id;if(h.rulesEvaluated++,r)try{let i=await s.beforeRule(p.definition,E);if(i==="skip"){e.push(k),h.rulesSkipped++;continue}if(i==="abort"){I=!1,C=!0,g="beforeRule",m=f+1;break}}catch(i){y.push({ruleIndex:f,error:`beforeRule: ${i}`}),h.errors++;}let T;try{T=p.definition.when(R);}catch(i){y.push({ruleIndex:f,error:String(i)}),A.push(k),h.errors++;continue}if(!T){A.push(k);continue}_.push(k),h.rulesMatched++;let x;if(S)try{x=$(u,p.definition,R);}catch(i){y.push({ruleIndex:f,error:`Middleware: ${i}`}),h.errors++;continue}if(p.actionId){let i;if(p.isInteractive?i=yield*a.invokeInteractive(p.actionId,x):p.isAsync?i=await a.invokeAsync(p.actionId,x):i=a.invoke(p.actionId,x),h.directivesApplied+=i.appliedCount,h.directivesSkipped+=i.skippedCount,h.errors+=i.errors.length,t)try{if(await s.afterRule(p.definition,i,E)==="abort"){I=!1,C=!0,g="afterRule",m=f+1;break}}catch{}if(i.aborted){I=i.abortedBy==="halt",C=true,g=i.abortedBy,m=f+1;break}}if(p.subRules&&p.subRules.length>0&&(yield*N(p.subRules,R,a,h,y,f,x,0,l))){I=false,C=true,g="sub-rule",m=f+1;break}}let w=V(I,C,g,_,e,A,y,m,v,h);if(o)try{await s.onRulesComplete(w);}catch{}return w}}function*D(n,r,t,o,c,d,b,R,a){if(R>=a)return c.push({ruleIndex:d,error:`maxSubRuleDepth (${a}) exceeded`}),o.errors++,false;for(let u=0;u<n.length;u++){let s=n[u];if(s.definition.when){let l;try{l=s.definition.when(r);}catch(v){c.push({ruleIndex:d,error:String(v)}),o.errors++;continue}if(!l)continue}if(s.actionId){let l;if(s.isInteractive?l=yield*t.invokeInteractive(s.actionId,b):l=t.invoke(s.actionId,b),o.directivesApplied+=l.appliedCount,o.directivesSkipped+=l.skippedCount,o.errors+=l.errors.length,l.aborted)return true}if(s.subRules&&s.subRules.length>0&&(yield*D(s.subRules,r,t,o,c,d,b,R+1,a)))return true}return false}async function*N(n,r,t,o,c,d,b,R,a){if(R>=a)return c.push({ruleIndex:d,error:`maxSubRuleDepth (${a}) exceeded`}),o.errors++,false;for(let u=0;u<n.length;u++){let s=n[u];if(s.definition.when){let l;try{l=s.definition.when(r);}catch(v){c.push({ruleIndex:d,error:String(v)}),o.errors++;continue}if(!l)continue}if(s.actionId){let l;if(s.isInteractive?l=yield*t.invokeInteractive(s.actionId,b):s.isAsync?l=await t.invokeAsync(s.actionId,b):l=t.invoke(s.actionId,b),o.directivesApplied+=l.appliedCount,o.directivesSkipped+=l.skippedCount,o.errors+=l.errors.length,l.aborted)return true}if(s.subRules&&s.subRules.length>0&&(yield*N(s.subRules,r,t,o,c,d,b,R+1,a)))return true}return false}function W(n,r){return r?ee(n):Z(n)}function M(n,r,t,o,c,d,b){n.push(`{const _er={success:${r},aborted:true,abortedBy:${t},matched,skipped,notMatched,errors,processedCount:${o},totalCount:${c},counters};`),d&&n.push(`try{${b}_hookOnComplete(_er);}catch(_e){}`),n.push("return _er;}");}function J(n,r,t,o){let{filledNames:c,asyncNames:d}=n,b=m=>c.has(m),R=m=>d.has(m)?"await ":"",a=b("beforeRule"),u=b("afterRule"),s=b("onRulesComplete"),l=R("beforeRule"),v=R("afterRule"),_=R("onRulesComplete"),e=[];e.push("const len=rules.length;"),e.push("const matched=[];const skipped=[];const notMatched=[];"),e.push("const errors=[];"),e.push("const counters={rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0};"),e.push("ae.setContext(ctx);"),(a||u)&&e.push("const evalCtx={ctx,counters};"),a&&e.push("const _hookBefore=hooks.beforeRule;"),u&&e.push("const _hookAfter=hooks.afterRule;"),s&&e.push("const _hookOnComplete=hooks.onRulesComplete;"),e.push("for(let i=0;i<len;i++){"),e.push("const stored=rules[i];"),e.push("const _rid=stored.definition.id;"),e.push("counters.rulesEvaluated++;"),a&&(e.push("try{"),e.push(`const _bd=${l}_hookBefore(stored.definition,evalCtx);`),e.push('if(_bd==="skip"){skipped.push(_rid);counters.rulesSkipped++;continue;}'),e.push('if(_bd==="abort")'),M(e,"false",'"beforeRule"',"i+1","len",s,_),e.push('}catch(_e){errors.push({ruleIndex:i,error:"beforeRule: "+_e});counters.errors++;}')),e.push("let ev;"),e.push("try{ev=stored.definition.when(ctx);}catch(_e){errors.push({ruleIndex:i,error:String(_e)});notMatched.push(_rid);counters.errors++;continue;}"),e.push("if(!ev){notMatched.push(_rid);continue;}"),e.push("matched.push(_rid);counters.rulesMatched++;"),r&&(e.push("let params;"),e.push('try{params=$.runMw(mw,stored.definition,ctx);}catch(_e){errors.push({ruleIndex:i,error:"Middleware: "+_e});counters.errors++;continue;}'));let A=r?",params":"",y=t?`await ae.invokeAsync(stored.actionId${A})`:`ae.invoke(stored.actionId${A})`;e.push("if(stored.actionId){"),e.push(`const ir=${y};`),e.push("counters.directivesApplied+=ir.appliedCount;counters.directivesSkipped+=ir.skippedCount;counters.errors+=ir.errors.length;"),u&&(e.push("try{"),e.push(`const _ad=${v}_hookAfter(stored.definition,ir,evalCtx);`),e.push('if(_ad==="abort")'),M(e,"false",'"afterRule"',"i+1","len",s,_),e.push("}catch(_e){}")),e.push("if(ir.aborted)"),M(e,'ir.abortedBy==="halt"',"ir.abortedBy","i+1","len",s,_),e.push("}");let h=r?"params":"undefined";e.push("if(stored.subRules&&stored.subRules.length>0){"),e.push(`if(${t?"await ":""}$.evalSub(stored.subRules,ctx,ae,counters,errors,i,${h},0,$.maxDepth))`),M(e,"false",'"sub-rule"',"i+1","len",s,_),e.push("}"),e.push("}"),e.push("const _r={success:true,aborted:false,abortedBy:undefined,matched,skipped,notMatched,errors,processedCount:len,totalCount:len,counters};"),s&&e.push(`try{${_}_hookOnComplete(_r);}catch(_e){}`),e.push("return _r;");let S=e.join(`
2
+ `),I=t?"async ":"",C=new Function("rules","ctx","ae","mw","hooks","$",`"use strict";
3
+ return(${I}()=>{
4
+ ${S}
5
+ })();`),g={maxDepth:o};return g.evalSub=t?F:B,r&&(g.runMw=$),function(E,w,f,p,k){return C(E,w,f,p,k,g)}}function P(n,r,t,o){let{filledNames:c,asyncNames:d}=n,b=g=>c.has(g),R=g=>d.has(g)?"await ":"",a=b("beforeRule"),u=b("afterRule"),s=b("onRulesComplete"),l=R("beforeRule"),v=R("afterRule"),_=R("onRulesComplete"),e=[];e.push("const len=rules.length;"),e.push("const matched=[];const skipped=[];const notMatched=[];"),e.push("const errors=[];"),e.push("const counters={rulesEvaluated:0,rulesMatched:0,rulesSkipped:0,directivesApplied:0,directivesSkipped:0,subRunsCreated:0,errors:0};"),e.push("ae.setContext(ctx);"),(a||u)&&e.push("const evalCtx={ctx,counters};"),a&&e.push("const _hookBefore=hooks.beforeRule;"),u&&e.push("const _hookAfter=hooks.afterRule;"),s&&e.push("const _hookOnComplete=hooks.onRulesComplete;"),e.push("for(let i=0;i<len;i++){"),e.push("const stored=rules[i];"),e.push("const _rid=stored.definition.id;"),e.push("counters.rulesEvaluated++;"),a&&(e.push("try{"),e.push(`const _bd=${l}_hookBefore(stored.definition,evalCtx);`),e.push('if(_bd==="skip"){skipped.push(_rid);counters.rulesSkipped++;continue;}'),e.push('if(_bd==="abort")'),M(e,"false",'"beforeRule"',"i+1","len",s,_),e.push('}catch(_e){errors.push({ruleIndex:i,error:"beforeRule: "+_e});counters.errors++;}')),e.push("let ev;"),e.push("try{ev=stored.definition.when(ctx);}catch(_e){errors.push({ruleIndex:i,error:String(_e)});notMatched.push(_rid);counters.errors++;continue;}"),e.push("if(!ev){notMatched.push(_rid);continue;}"),e.push("matched.push(_rid);counters.rulesMatched++;"),r&&(e.push("let params;"),e.push('try{params=$.runMw(mw,stored.definition,ctx);}catch(_e){errors.push({ruleIndex:i,error:"Middleware: "+_e});counters.errors++;continue;}'));let A=r?",params":"";e.push("if(stored.actionId){"),e.push("let ir;"),e.push("if(stored.isInteractive){"),e.push(`ir=yield* ae.invokeInteractive(stored.actionId${A});`),e.push("}else "),t?(e.push("if(stored.isAsync){"),e.push(`ir=await ae.invokeAsync(stored.actionId${A});`),e.push("}else{")):e.push("{"),e.push(`ir=ae.invoke(stored.actionId${A});`),e.push("}"),e.push("counters.directivesApplied+=ir.appliedCount;counters.directivesSkipped+=ir.skippedCount;counters.errors+=ir.errors.length;"),u&&(e.push("try{"),e.push(`const _ad=${v}_hookAfter(stored.definition,ir,evalCtx);`),e.push('if(_ad==="abort")'),M(e,"false",'"afterRule"',"i+1","len",s,_),e.push("}catch(_e){}")),e.push("if(ir.aborted)"),M(e,'ir.abortedBy==="halt"',"ir.abortedBy","i+1","len",s,_),e.push("}");let y=r?"params":"undefined";e.push("if(stored.subRules&&stored.subRules.length>0){"),e.push(`if(yield* $.evalSubInt(stored.subRules,ctx,ae,counters,errors,i,${y},0,$.maxDepth))`),M(e,"false",'"sub-rule"',"i+1","len",s,_),e.push("}"),e.push("}"),e.push("const _r={success:true,aborted:false,abortedBy:undefined,matched,skipped,notMatched,errors,processedCount:len,totalCount:len,counters};"),s&&e.push(`try{${_}_hookOnComplete(_r);}catch(_e){}`),e.push("return _r;");let h=e.join(`
6
+ `),S=t?"async function*()":"function*()",I=new Function("rules","ctx","ae","mw","hooks","$",`"use strict";
7
+ return(${S}{
8
+ ${h}
9
+ })();`),C={maxDepth:o};return C.evalSubInt=t?N:D,r&&(C.runMw=$),function(m,E,w,f,p){return I(m,E,w,f,p,C)}}function z(n){if(!n.id||typeof n.id!="string")return {ruleId:n.id??"(missing)",code:"INVALID_RULE",message:"rule must have a string id"};if(typeof n.priority!="number")return {ruleId:n.id,code:"INVALID_RULE",message:"rule must have a numeric priority"};if(typeof n.when!="function")return {ruleId:n.id,code:"INVALID_RULE",message:"rule must have a when function"};let r=Array.isArray(n.then)&&n.then.length>0,t=Array.isArray(n.rules)&&n.rules.length>0;return !r&&!t?{ruleId:n.id,code:"INVALID_RULE",message:"rule must have then or sub-rules (or both)"}:null}function K(n,r){let t=`${r}.${n.id??"(missing)"}`;if(!n.id||typeof n.id!="string")return {ruleId:t,code:"INVALID_RULE",message:"sub-rule must have a string id"};let o=Array.isArray(n.then)&&n.then.length>0,c=Array.isArray(n.rules)&&n.rules.length>0;return !o&&!c?{ruleId:t,code:"INVALID_RULE",message:"sub-rule must have then or sub-rules (or both)"}:null}var L=()=>{},j=class{_actionEngine;_middleware;_ruleHooks;_maxSubRuleDepth;_requestedMode;_ruleHookAnalysis;_hasAnyInteractiveRule=false;_isAsync;_ruleEvaluator;_mode;_maybeAutoPromote;_interactiveEvaluator=null;_interactiveEvaluatorIsAsync=false;_ruleRegistry=new Map;_rules=[];_batch=null;constructor(r){if(this._actionEngine=r.actionEngine,this._middleware=r.middleware??[],this._ruleHooks=r.ruleHooks??{},this._maxSubRuleDepth=r.maxSubRuleDepth??10,this._requestedMode=r.mode??"auto",this._ruleHookAnalysis=analyzeSlots(this._ruleHooks,RULE_SLOT_NAMES),this._isAsync=this._ruleHookAnalysis.hasAnyAsync,this._requestedMode==="jit")this._ruleEvaluator=this._buildJitRule(),this._mode="jit",this._maybeAutoPromote=L;else if(this._ruleEvaluator=O(this._ruleHookAnalysis,this._isAsync,this._maxSubRuleDepth),this._mode="interpret",this._requestedMode==="auto"){let t=0,o=r.autoJitThreshold??8;this._maybeAutoPromote=()=>{++t>=o&&this._promote();};}else this._maybeAutoPromote=L;}get actionEngine(){return this._actionEngine}get isAsync(){return this._isAsync}get isInteractive(){return this._hasAnyInteractiveRule}get compilationMode(){return this._mode}get size(){return this._rules.length}has(r){return this._ruleRegistry.has(r)}register(r){let t=[],o=[],c=[],d=[];for(let u of r){let s=z(u);if(s){t.push(s);continue}if(this._ruleRegistry.has(u.id)||c.some(y=>y.id===u.id)){t.push({ruleId:u.id,code:"DUPLICATE_ID",message:`rule "${u.id}" is already registered`});continue}let l=`rule:${u.id}`,v=Array.isArray(u.then)&&u.then.length>0,_=v?l:"",e=Array.isArray(u.rules)&&u.rules.length>0?this._collectSubRules(l,u.rules,t,o,d):void 0,A={definition:u,actionId:_,priority:u.priority,subRules:e?.length?e:void 0,isAsync:false,isInteractive:false};if(v){let y=["rule"];u.tags&&y.push(...u.tags),o.push({id:_,tags:y,directives:u.then,declarations:u.declarations});}c.push({id:u.id,stored:A});}let b=[];if(o.length>0){let u=this._actionEngine.register(o);for(let s of u.errors){let l=this._actionIdToRuleId(s.actionId);t.push({ruleId:l??s.actionId,code:s.code??"ACTION_ERROR",message:s.error});}b=u.warnings;}let R=[];for(let{id:u,stored:s}of c)this._ruleRegistry.set(u,s),se(this._rules,s),R.push(u);for(let{qualifiedId:u,stored:s}of d)this._ruleRegistry.set(u,s);let a={registered:R,errors:t,warnings:b};return this._batch?(this._batch.registered.push(...R),this._batch.errors.push(...t),this._batch.warnings.push(...b)):R.length>0&&this._refreshRuleFlags(),a}unregister(r){let t=this._ruleRegistry.get(r);return t?(this._ruleRegistry.delete(r),ie(this._rules,t),t.actionId&&this._actionEngine.unregister(t.actionId),t.subRules&&this._unregisterSubRules(t.subRules),this._refreshRuleFlags(),true):false}beginBatch(){this._batch||(this._batch={depth:0,registered:[],errors:[],warnings:[]}),this._batch.depth++,this._actionEngine.beginBatch();}endBatch(){if(!this._batch||this._batch.depth<=0)throw new Error("endBatch() called without matching beginBatch()");this._batch.depth--;let r=this._actionEngine.endBatch();if(this._batch.depth>0)return {registered:[],errors:[],warnings:[]};let t=this._batch;for(let c of r.errors){let d=this._actionIdToRuleId(c.actionId);t.errors.push({ruleId:d??c.actionId,code:c.code??"ACTION_ERROR",message:c.error});}t.warnings.push(...r.warnings);let o={registered:t.registered,errors:t.errors,warnings:t.warnings};return this._batch=null,this._refreshRuleFlags(),o}batch(r){this.beginBatch();try{return r(this),this.endBatch()}catch(t){try{this.endBatch();}catch{}throw t}}evaluate(r){if(this._hasAnyInteractiveRule)throw new Error("Cannot call evaluate() \u2014 at least one rule is interactive (transitively). Use evaluateInteractive() instead.");if(this._isAsync)throw new Error("Cannot call evaluate() with async hooks or async rules. Use evaluateAsync() instead.");let t=this._ruleEvaluator(this._rules,r,this._actionEngine,this._middleware,this._ruleHooks);return this._maybeAutoPromote(),t}async evaluateAsync(r){if(this._hasAnyInteractiveRule)throw new Error("Cannot call evaluateAsync() \u2014 at least one rule is interactive (transitively). Use evaluateInteractive() instead.");let t=await this._ruleEvaluator(this._rules,r,this._actionEngine,this._middleware,this._ruleHooks);return this._maybeAutoPromote(),t}evaluateInteractive(r){if(!this._hasAnyInteractiveRule)throw new Error("Cannot call evaluateInteractive() \u2014 no registered rule is interactive (transitively). Use evaluate() or evaluateAsync() instead.");return (this._interactiveEvaluator===null||this._interactiveEvaluatorIsAsync!==this._isAsync)&&(this._interactiveEvaluator=this._mode==="jit"?P(this._ruleHookAnalysis,this._middleware.length>0,this._isAsync,this._maxSubRuleDepth):W(this._ruleHookAnalysis,this._isAsync),this._interactiveEvaluatorIsAsync=this._isAsync),this._interactiveEvaluator(this._rules,r,this._actionEngine,this._middleware,this._ruleHooks,this._maxSubRuleDepth)}compile(){this._requestedMode!=="interpret"&&this._mode!=="jit"&&this._promote();}_collectSubRules(r,t,o,c,d){let b=[],R=this._actionIdToRuleId(r);for(let a of t){let u=K(a,R);if(u){o.push(u);continue}let s=`${r}.${a.id}`,l=`${R}.${a.id}`,v=Array.isArray(a.then)&&a.then.length>0,_=Array.isArray(a.rules)&&a.rules.length>0?this._collectSubRules(s,a.rules,o,c,d):void 0,e={definition:a,actionId:v?s:"",subRules:_?.length?_:void 0,isAsync:false,isInteractive:false};if(v){let A=["rule"];a.tags&&A.push(...a.tags),c.push({id:s,tags:A,directives:a.then,declarations:a.declarations});}d.push({qualifiedId:l,stored:e}),b.push(e);}return b}_unregisterSubRules(r){for(let t of r){if(t.actionId){let o=this._actionIdToRuleId(t.actionId);o&&this._ruleRegistry.delete(o),this._actionEngine.unregister(t.actionId);}t.subRules&&this._unregisterSubRules(t.subRules);}}_actionIdToRuleId(r){return r.startsWith("rule:")?r.slice(5):null}_refreshRuleFlags(){let r=false,t=false,o=d=>{if(d.actionId?(d.isAsync=this._actionEngine.isActionAsync(d.actionId)??false,d.isInteractive=this._actionEngine.isActionInteractive(d.actionId)??false,d.isAsync&&(r=true),d.isInteractive&&(t=true)):(d.isAsync=false,d.isInteractive=false),d.subRules)for(let b of d.subRules)o(b);};for(let d of this._rules)o(d);this._hasAnyInteractiveRule=t;let c=this._ruleHookAnalysis.hasAnyAsync||r;c!==this._isAsync?(this._isAsync=c,this._rebuildEvaluator()):this._isAsync=c;}_rebuildEvaluator(){this._mode==="jit"?this._ruleEvaluator=this._buildJitRule():this._ruleEvaluator=O(this._ruleHookAnalysis,this._isAsync,this._maxSubRuleDepth);}_promote(){this._ruleEvaluator=this._buildJitRule(),this._mode="jit",this._maybeAutoPromote=L;}_buildJitRule(){return J(this._ruleHookAnalysis,this._middleware.length>0,this._isAsync,this._maxSubRuleDepth)}};function ne(n){return new j(n)}function se(n,r){let t=0,o=n.length;for(;t<o;){let c=t+o>>>1;n[c].priority>=r.priority?t=c+1:o=c;}n.splice(t,0,r);}function ie(n,r){let t=n.indexOf(r);return t===-1?false:(n.splice(t,1),true)}var oe={analyze:()=>({capabilities:["halt"],dependencies:[]}),execute:()=>({ok:true,halt:true})};export{oe as HALT_HANDLER,Q as createInvokerMiddleware,ne as createRuleEngine};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statedelta-actions/rules",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Rule evaluation engine with JIT-optimized sequential evaluation and sub-rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -18,8 +18,8 @@
18
18
  "README.md"
19
19
  ],
20
20
  "dependencies": {
21
- "@statedelta-actions/core": "0.3.0",
22
- "@statedelta-actions/actions": "0.3.0"
21
+ "@statedelta-actions/core": "0.5.0",
22
+ "@statedelta-actions/actions": "0.5.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "apex-store": "0.4.0"