drools-builder 0.1.1 → 0.1.3
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 +1 -0
- package/dist/index.cjs +129 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -6
- package/dist/index.d.ts +20 -6
- package/dist/index.js +129 -106
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ Plain metamodel objects are accepted everywhere a builder is expected.
|
|
|
77
77
|
const file = createFile('fraud-rules')
|
|
78
78
|
.import('com.example.Account')
|
|
79
79
|
.import('com.example.FraudAlert')
|
|
80
|
+
.global('com.example.AlertService', 'alertService')
|
|
80
81
|
.addRule(rule)
|
|
81
82
|
.addRule(createRule('Another Rule').when(...).then(...))
|
|
82
83
|
.build()
|
package/dist/index.cjs
CHANGED
|
@@ -430,6 +430,7 @@ function resolveRule(input) {
|
|
|
430
430
|
var DroolsFileBuilder = class {
|
|
431
431
|
constructor(name) {
|
|
432
432
|
this._imports = [];
|
|
433
|
+
this._globals = [];
|
|
433
434
|
this._rules = [];
|
|
434
435
|
this._name = name;
|
|
435
436
|
}
|
|
@@ -444,6 +445,17 @@ var DroolsFileBuilder = class {
|
|
|
444
445
|
this._imports.push(className);
|
|
445
446
|
return this;
|
|
446
447
|
}
|
|
448
|
+
/**
|
|
449
|
+
* Declare a global variable available to all rules in this file.
|
|
450
|
+
* Emits `global type name;` in the DRL header.
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* .global('com.example.AlertService', 'alertService')
|
|
454
|
+
*/
|
|
455
|
+
global(type, name) {
|
|
456
|
+
this._globals.push({ type, name });
|
|
457
|
+
return this;
|
|
458
|
+
}
|
|
447
459
|
/**
|
|
448
460
|
* Add a rule to the file.
|
|
449
461
|
* Accepts a plain Rule object or a RuleBuilder (auto-resolved via .build()).
|
|
@@ -460,6 +472,7 @@ var DroolsFileBuilder = class {
|
|
|
460
472
|
return {
|
|
461
473
|
name: this._name,
|
|
462
474
|
imports: [...this._imports],
|
|
475
|
+
globals: [...this._globals],
|
|
463
476
|
rules: [...this._rules]
|
|
464
477
|
};
|
|
465
478
|
}
|
|
@@ -524,6 +537,114 @@ function rawConsequence(code) {
|
|
|
524
537
|
return { kind: "RawConsequence", code };
|
|
525
538
|
}
|
|
526
539
|
|
|
540
|
+
// src/rule-builder/parser/MetaToDRLTransformer.ts
|
|
541
|
+
function generateConstraint(c) {
|
|
542
|
+
switch (c.kind) {
|
|
543
|
+
case "FieldConstraint":
|
|
544
|
+
return `${c.binding ? `${c.binding} : ` : ""}${c.field} ${c.operator} ${c.value}`;
|
|
545
|
+
case "BindingConstraint":
|
|
546
|
+
return `${c.binding} : ${c.field}`;
|
|
547
|
+
case "RawConstraint":
|
|
548
|
+
return c.expression;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function generateCondition(cond, indent = " ") {
|
|
552
|
+
switch (cond.kind) {
|
|
553
|
+
case "FactPattern": {
|
|
554
|
+
const binding = cond.binding ? `${cond.binding} : ` : "";
|
|
555
|
+
const constraints = cond.constraints.map(generateConstraint).join(", ");
|
|
556
|
+
return `${binding}${cond.factType}( ${constraints} )`;
|
|
557
|
+
}
|
|
558
|
+
case "UnboundPattern": {
|
|
559
|
+
const constraints = cond.constraints.map(generateConstraint).join(", ");
|
|
560
|
+
return `${cond.factType}( ${constraints} )`;
|
|
561
|
+
}
|
|
562
|
+
case "And": {
|
|
563
|
+
const parts = cond.conditions.map((c) => generateCondition(c, indent + " "));
|
|
564
|
+
return `( ${parts.join(`
|
|
565
|
+
${indent} and `)} )`;
|
|
566
|
+
}
|
|
567
|
+
case "Or": {
|
|
568
|
+
const parts = cond.conditions.map((c) => generateCondition(c, indent + " "));
|
|
569
|
+
return `( ${parts.join(`
|
|
570
|
+
${indent} or `)} )`;
|
|
571
|
+
}
|
|
572
|
+
case "Not":
|
|
573
|
+
return `not( ${generateCondition(cond.condition, indent)} )`;
|
|
574
|
+
case "Exists":
|
|
575
|
+
return `exists( ${generateCondition(cond.condition, indent)} )`;
|
|
576
|
+
case "Forall":
|
|
577
|
+
return `forall( ${generateCondition(cond.condition, indent)} )`;
|
|
578
|
+
case "Accumulate": {
|
|
579
|
+
const source = generateCondition(cond.source, indent + " ");
|
|
580
|
+
const fns = cond.functions.map((f) => `${f.binding} : ${f.function}( ${f.argument} )`).join(", ");
|
|
581
|
+
const result = cond.resultConstraint ? `;
|
|
582
|
+
${indent} ${cond.resultConstraint}` : "";
|
|
583
|
+
return `accumulate(
|
|
584
|
+
${indent} ${source};
|
|
585
|
+
${indent} ${fns}${result}
|
|
586
|
+
${indent})`;
|
|
587
|
+
}
|
|
588
|
+
case "From":
|
|
589
|
+
return `${generateCondition(cond.pattern, indent)} from ${cond.expression}`;
|
|
590
|
+
case "Eval":
|
|
591
|
+
return `eval( ${cond.expression} )`;
|
|
592
|
+
case "RawCondition":
|
|
593
|
+
return cond.drl;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function generateWhenBlock(conditions, indent = " ") {
|
|
597
|
+
const flat = conditions.length === 1 && conditions[0].kind === "And" ? conditions[0].conditions : conditions;
|
|
598
|
+
return flat.map((c) => `${indent}${generateCondition(c, indent)}`).join("\n");
|
|
599
|
+
}
|
|
600
|
+
function generateConsequence(cons, indent = " ") {
|
|
601
|
+
switch (cons.kind) {
|
|
602
|
+
case "ModifyConsequence": {
|
|
603
|
+
const mods = cons.modifications.map((m) => `${m.method}( ${m.args.join(", ")} )`).join(`,
|
|
604
|
+
${indent} `);
|
|
605
|
+
return `modify( ${cons.binding} ) {
|
|
606
|
+
${indent} ${mods}
|
|
607
|
+
${indent}}`;
|
|
608
|
+
}
|
|
609
|
+
case "InsertConsequence":
|
|
610
|
+
return `insert( ${cons.objectExpression} );`;
|
|
611
|
+
case "RetractConsequence":
|
|
612
|
+
return `retract( ${cons.binding} );`;
|
|
613
|
+
case "SetGlobalConsequence":
|
|
614
|
+
return `${cons.expression};`;
|
|
615
|
+
case "RawConsequence":
|
|
616
|
+
return `${cons.code};`;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
function generateRule(rule) {
|
|
620
|
+
const lines = [`rule "${rule.name}"`];
|
|
621
|
+
if (rule.salience !== void 0) lines.push(` salience ${rule.salience}`);
|
|
622
|
+
if (rule.agendaGroup !== void 0) lines.push(` agenda-group "${rule.agendaGroup}"`);
|
|
623
|
+
if (rule.ruleFlowGroup !== void 0) lines.push(` ruleflow-group "${rule.ruleFlowGroup}"`);
|
|
624
|
+
if (rule.noLoop) lines.push(" no-loop true");
|
|
625
|
+
if (rule.lockOnActive) lines.push(" lock-on-active true");
|
|
626
|
+
lines.push(" when");
|
|
627
|
+
lines.push(generateWhenBlock(rule.conditions));
|
|
628
|
+
lines.push(" then");
|
|
629
|
+
lines.push(rule.consequences.map((c) => ` ${generateConsequence(c)}`).join("\n"));
|
|
630
|
+
lines.push("end");
|
|
631
|
+
return lines.join("\n");
|
|
632
|
+
}
|
|
633
|
+
var MetaToDRLTransformer = {
|
|
634
|
+
generate(file) {
|
|
635
|
+
const sections = [];
|
|
636
|
+
if (file.imports.length > 0)
|
|
637
|
+
sections.push(file.imports.map((i) => `import ${i};`).join("\n"));
|
|
638
|
+
if (file.globals.length > 0)
|
|
639
|
+
sections.push(file.globals.map((g) => `global ${g.type} ${g.name};`).join("\n"));
|
|
640
|
+
sections.push(file.rules.map(generateRule).join("\n\n"));
|
|
641
|
+
return sections.join("\n\n");
|
|
642
|
+
},
|
|
643
|
+
generateRule(rule) {
|
|
644
|
+
return generateRule(rule);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
527
648
|
// src/rule-builder/parser/DRLToMetaTransformer.ts
|
|
528
649
|
function indexAtDepth0(text, needle) {
|
|
529
650
|
let depth = 0;
|
|
@@ -624,6 +745,13 @@ function parseImports(drl) {
|
|
|
624
745
|
while ((m = re.exec(drl)) !== null) imports.push(m[1].trim());
|
|
625
746
|
return imports;
|
|
626
747
|
}
|
|
748
|
+
function parseGlobals(drl) {
|
|
749
|
+
const globals = [];
|
|
750
|
+
const re = /^\s*global\s+(\S+)\s+(\S+?)\s*;?$/gm;
|
|
751
|
+
let m;
|
|
752
|
+
while ((m = re.exec(drl)) !== null) globals.push({ type: m[1], name: m[2] });
|
|
753
|
+
return globals;
|
|
754
|
+
}
|
|
627
755
|
function extractRuleBlocks(drl) {
|
|
628
756
|
const blocks = [];
|
|
629
757
|
const re = /\brule\s+"[^"]*"[\s\S]*?\bend\b/g;
|
|
@@ -864,6 +992,7 @@ var DRLToMetaTransformer = {
|
|
|
864
992
|
return {
|
|
865
993
|
name: "parsed",
|
|
866
994
|
imports: parseImports(clean),
|
|
995
|
+
globals: parseGlobals(clean),
|
|
867
996
|
rules: extractRuleBlocks(clean).map((block) => DRLToMetaTransformer.parseRule(block))
|
|
868
997
|
};
|
|
869
998
|
},
|
|
@@ -876,112 +1005,6 @@ var DRLToMetaTransformer = {
|
|
|
876
1005
|
};
|
|
877
1006
|
}
|
|
878
1007
|
};
|
|
879
|
-
|
|
880
|
-
// src/rule-builder/parser/MetaToDRLTransformer.ts
|
|
881
|
-
function generateConstraint(c) {
|
|
882
|
-
switch (c.kind) {
|
|
883
|
-
case "FieldConstraint":
|
|
884
|
-
return `${c.binding ? `${c.binding} : ` : ""}${c.field} ${c.operator} ${c.value}`;
|
|
885
|
-
case "BindingConstraint":
|
|
886
|
-
return `${c.binding} : ${c.field}`;
|
|
887
|
-
case "RawConstraint":
|
|
888
|
-
return c.expression;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
function generateCondition(cond, indent = " ") {
|
|
892
|
-
switch (cond.kind) {
|
|
893
|
-
case "FactPattern": {
|
|
894
|
-
const binding = cond.binding ? `${cond.binding} : ` : "";
|
|
895
|
-
const constraints = cond.constraints.map(generateConstraint).join(", ");
|
|
896
|
-
return `${binding}${cond.factType}( ${constraints} )`;
|
|
897
|
-
}
|
|
898
|
-
case "UnboundPattern": {
|
|
899
|
-
const constraints = cond.constraints.map(generateConstraint).join(", ");
|
|
900
|
-
return `${cond.factType}( ${constraints} )`;
|
|
901
|
-
}
|
|
902
|
-
case "And": {
|
|
903
|
-
const parts = cond.conditions.map((c) => generateCondition(c, indent + " "));
|
|
904
|
-
return `( ${parts.join(`
|
|
905
|
-
${indent} and `)} )`;
|
|
906
|
-
}
|
|
907
|
-
case "Or": {
|
|
908
|
-
const parts = cond.conditions.map((c) => generateCondition(c, indent + " "));
|
|
909
|
-
return `( ${parts.join(`
|
|
910
|
-
${indent} or `)} )`;
|
|
911
|
-
}
|
|
912
|
-
case "Not":
|
|
913
|
-
return `not( ${generateCondition(cond.condition, indent)} )`;
|
|
914
|
-
case "Exists":
|
|
915
|
-
return `exists( ${generateCondition(cond.condition, indent)} )`;
|
|
916
|
-
case "Forall":
|
|
917
|
-
return `forall( ${generateCondition(cond.condition, indent)} )`;
|
|
918
|
-
case "Accumulate": {
|
|
919
|
-
const source = generateCondition(cond.source, indent + " ");
|
|
920
|
-
const fns = cond.functions.map((f) => `${f.binding} : ${f.function}( ${f.argument} )`).join(", ");
|
|
921
|
-
const result = cond.resultConstraint ? `;
|
|
922
|
-
${indent} ${cond.resultConstraint}` : "";
|
|
923
|
-
return `accumulate(
|
|
924
|
-
${indent} ${source};
|
|
925
|
-
${indent} ${fns}${result}
|
|
926
|
-
${indent})`;
|
|
927
|
-
}
|
|
928
|
-
case "From":
|
|
929
|
-
return `${generateCondition(cond.pattern, indent)} from ${cond.expression}`;
|
|
930
|
-
case "Eval":
|
|
931
|
-
return `eval( ${cond.expression} )`;
|
|
932
|
-
case "RawCondition":
|
|
933
|
-
return cond.drl;
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
function generateWhenBlock(conditions, indent = " ") {
|
|
937
|
-
const flat = conditions.length === 1 && conditions[0].kind === "And" ? conditions[0].conditions : conditions;
|
|
938
|
-
return flat.map((c) => `${indent}${generateCondition(c, indent)}`).join("\n");
|
|
939
|
-
}
|
|
940
|
-
function generateConsequence(cons, indent = " ") {
|
|
941
|
-
switch (cons.kind) {
|
|
942
|
-
case "ModifyConsequence": {
|
|
943
|
-
const mods = cons.modifications.map((m) => `${m.method}( ${m.args.join(", ")} )`).join(`,
|
|
944
|
-
${indent} `);
|
|
945
|
-
return `modify( ${cons.binding} ) {
|
|
946
|
-
${indent} ${mods}
|
|
947
|
-
${indent}}`;
|
|
948
|
-
}
|
|
949
|
-
case "InsertConsequence":
|
|
950
|
-
return `insert( ${cons.objectExpression} );`;
|
|
951
|
-
case "RetractConsequence":
|
|
952
|
-
return `retract( ${cons.binding} );`;
|
|
953
|
-
case "SetGlobalConsequence":
|
|
954
|
-
return `${cons.expression};`;
|
|
955
|
-
case "RawConsequence":
|
|
956
|
-
return `${cons.code};`;
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
function generateRule(rule) {
|
|
960
|
-
const lines = [`rule "${rule.name}"`];
|
|
961
|
-
if (rule.salience !== void 0) lines.push(` salience ${rule.salience}`);
|
|
962
|
-
if (rule.agendaGroup !== void 0) lines.push(` agenda-group "${rule.agendaGroup}"`);
|
|
963
|
-
if (rule.ruleFlowGroup !== void 0) lines.push(` ruleflow-group "${rule.ruleFlowGroup}"`);
|
|
964
|
-
if (rule.noLoop) lines.push(" no-loop true");
|
|
965
|
-
if (rule.lockOnActive) lines.push(" lock-on-active true");
|
|
966
|
-
lines.push(" when");
|
|
967
|
-
lines.push(generateWhenBlock(rule.conditions));
|
|
968
|
-
lines.push(" then");
|
|
969
|
-
lines.push(rule.consequences.map((c) => ` ${generateConsequence(c)}`).join("\n"));
|
|
970
|
-
lines.push("end");
|
|
971
|
-
return lines.join("\n");
|
|
972
|
-
}
|
|
973
|
-
var MetaToDRLTransformer = {
|
|
974
|
-
generate(file) {
|
|
975
|
-
const sections = [];
|
|
976
|
-
if (file.imports.length > 0)
|
|
977
|
-
sections.push(file.imports.map((i) => `import ${i};`).join("\n"));
|
|
978
|
-
sections.push(file.rules.map(generateRule).join("\n\n"));
|
|
979
|
-
return sections.join("\n\n");
|
|
980
|
-
},
|
|
981
|
-
generateRule(rule) {
|
|
982
|
-
return generateRule(rule);
|
|
983
|
-
}
|
|
984
|
-
};
|
|
985
1008
|
// Annotate the CommonJS export names for ESM import in node:
|
|
986
1009
|
0 && (module.exports = {
|
|
987
1010
|
AccumulateBuilder,
|