@clipboard-health/rules-engine 0.1.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/CHANGELOG.md +10 -0
- package/README.md +99 -0
- package/package.json +12 -0
- package/src/index.d.ts +5 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +8 -0
- package/src/index.js.map +1 -0
- package/src/lib/append-output.d.ts +3 -0
- package/src/lib/append-output.d.ts.map +1 -0
- package/src/lib/append-output.js +11 -0
- package/src/lib/append-output.js.map +1 -0
- package/src/lib/rule.d.ts +9 -0
- package/src/lib/rule.d.ts.map +1 -0
- package/src/lib/rule.js +3 -0
- package/src/lib/rule.js.map +1 -0
- package/src/lib/runners/all.d.ts +9 -0
- package/src/lib/runners/all.d.ts.map +1 -0
- package/src/lib/runners/all.js +26 -0
- package/src/lib/runners/all.js.map +1 -0
- package/src/lib/runners/first-match.d.ts +9 -0
- package/src/lib/runners/first-match.d.ts.map +1 -0
- package/src/lib/runners/first-match.js +22 -0
- package/src/lib/runners/first-match.js.map +1 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
|
+
|
|
5
|
+
## 0.1.0 (2023-12-20)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **PRI-290:** Add rules-engine lib ([#477](https://github.com/ClipboardHealth/cbh-core/issues/477)) ([d7aa2ba](https://github.com/ClipboardHealth/cbh-core/commit/d7aa2ba3c698511c053a2b31dfe4b5e0e4a6081d))
|
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# @clipboard-health/rules-engine
|
|
2
|
+
|
|
3
|
+
Exposes a functional rules-engine with 2 engines:
|
|
4
|
+
|
|
5
|
+
- firstMatch: Runs the first rule that matches the criteria
|
|
6
|
+
- all: Runs all the rules that matches the criteria
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
- [Install](#install)
|
|
11
|
+
- [Usage](#usage)
|
|
12
|
+
- [Local development commands](#local-development-commands)
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @clipboard-health/rules-engine
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// ./examples/rules.ts
|
|
24
|
+
|
|
25
|
+
import { all, firstMatch, type Rule, type RuleContext } from "@clipboard-health/rules-engine";
|
|
26
|
+
|
|
27
|
+
interface Input {
|
|
28
|
+
number1: number;
|
|
29
|
+
number2: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface Output {
|
|
33
|
+
result: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ExampleRuleContext extends RuleContext<Input, Output> {}
|
|
37
|
+
|
|
38
|
+
const exampleContext: ExampleRuleContext = {
|
|
39
|
+
input: {
|
|
40
|
+
number1: 2,
|
|
41
|
+
number2: 5,
|
|
42
|
+
},
|
|
43
|
+
output: [],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const addNumbersIfPositiveRule: Rule<ExampleRuleContext> = {
|
|
47
|
+
runIf: (context) => context.input.number1 > 0 && context.input.number2 > 0,
|
|
48
|
+
run: (context) => {
|
|
49
|
+
const { number1, number2 } = context.input;
|
|
50
|
+
const sum = number1 + number2;
|
|
51
|
+
context.output.push({ result: sum });
|
|
52
|
+
return context;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const multiplyNumbersIfPositiveRule: Rule<ExampleRuleContext> = {
|
|
57
|
+
runIf: (context) => context.input.number1 > 0 && context.input.number2 > 0,
|
|
58
|
+
run: (context) => {
|
|
59
|
+
const { number1, number2 } = context.input;
|
|
60
|
+
const sum = number1 * number2;
|
|
61
|
+
context.output.push({ result: sum });
|
|
62
|
+
return context;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const divideNumbersIfNegative: Rule<ExampleRuleContext> = {
|
|
67
|
+
runIf: (context) => context.input.number1 < 0 && context.input.number2 < 0,
|
|
68
|
+
run: (context) => {
|
|
69
|
+
const { number1, number2 } = context.input;
|
|
70
|
+
const sum = number1 * number2;
|
|
71
|
+
context.output.push({ result: sum });
|
|
72
|
+
return context;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Using all() applies all the rules to the context
|
|
77
|
+
const allResult = all(
|
|
78
|
+
addNumbersIfPositiveRule,
|
|
79
|
+
divideNumbersIfNegative,
|
|
80
|
+
multiplyNumbersIfPositiveRule,
|
|
81
|
+
).run(exampleContext);
|
|
82
|
+
|
|
83
|
+
// eslint-disable-next-line no-console
|
|
84
|
+
console.log(allResult.output); // AllResult.output = [{ result: 7 }, { result: 10 }]
|
|
85
|
+
|
|
86
|
+
// Using firstMatch() applies the first the rules to the context
|
|
87
|
+
const firstMatchResult = firstMatch(
|
|
88
|
+
divideNumbersIfNegative,
|
|
89
|
+
addNumbersIfPositiveRule,
|
|
90
|
+
multiplyNumbersIfPositiveRule,
|
|
91
|
+
).run(exampleContext);
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line no-console
|
|
94
|
+
console.log(firstMatchResult.output); // [{ result: 7 }]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Local development commands
|
|
98
|
+
|
|
99
|
+
See [`package.json`](./package.json) `scripts` for a list of commands.
|
package/package.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@clipboard-health/rules-engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "./src/index.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "nx build rules-engine",
|
|
7
|
+
"embed": "embedme README.md",
|
|
8
|
+
"lint": "nx lint rules-engine",
|
|
9
|
+
"test": "nx test rules-engine"
|
|
10
|
+
},
|
|
11
|
+
"type": "commonjs"
|
|
12
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/rules-engine/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC"}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./lib/append-output"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./lib/rule"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./lib/runners/all"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./lib/runners/first-match"), exports);
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/rules-engine/src/index.ts"],"names":[],"mappings":";;;AAAA,8DAAoC;AACpC,qDAA2B;AAC3B,4DAAkC;AAClC,oEAA0C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append-output.d.ts","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/append-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAC1C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,MAAM,EAAE,OAAO,GACd,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAK9B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.appendOutput = void 0;
|
|
4
|
+
function appendOutput(context, output) {
|
|
5
|
+
return {
|
|
6
|
+
input: context.input,
|
|
7
|
+
output: [...context.output, output],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
exports.appendOutput = appendOutput;
|
|
11
|
+
//# sourceMappingURL=append-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append-output.js","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/append-output.ts"],"names":[],"mappings":";;;AAEA,SAAgB,YAAY,CAC1B,OAAqC,EACrC,MAAe;IAEf,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC;KACpC,CAAC;AACJ,CAAC;AARD,oCAQC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface RuleContext<TInput = unknown, TOutput = unknown> {
|
|
2
|
+
input: TInput;
|
|
3
|
+
output: TOutput[];
|
|
4
|
+
}
|
|
5
|
+
export interface Rule<TContext extends RuleContext> {
|
|
6
|
+
runIf: (context: TContext) => boolean;
|
|
7
|
+
run: (context: TContext) => TContext;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/rule.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,IAAI,CAAC,QAAQ,SAAS,WAAW;IAChD,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC;IACtC,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,QAAQ,CAAC;CACtC"}
|
package/src/lib/rule.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/rule.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Rule, type RuleContext } from "../..";
|
|
2
|
+
/**
|
|
3
|
+
* Runs all the rules that return true for `runIf`.
|
|
4
|
+
*
|
|
5
|
+
* @param rules The rules to run.
|
|
6
|
+
* @returns Rule<TContext>
|
|
7
|
+
*/
|
|
8
|
+
export declare function all<TContext extends RuleContext>(...rules: Array<Rule<TContext>>): Rule<TContext>;
|
|
9
|
+
//# sourceMappingURL=all.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../../../../../../packages/rules-engine/src/lib/runners/all.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,QAAQ,SAAS,WAAW,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAejG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.all = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Runs all the rules that return true for `runIf`.
|
|
6
|
+
*
|
|
7
|
+
* @param rules The rules to run.
|
|
8
|
+
* @returns Rule<TContext>
|
|
9
|
+
*/
|
|
10
|
+
function all(...rules) {
|
|
11
|
+
return {
|
|
12
|
+
runIf: function (context) {
|
|
13
|
+
return rules.some((rule) => rule.runIf(context));
|
|
14
|
+
},
|
|
15
|
+
run: function (context) {
|
|
16
|
+
return rules.reduce((previousContext, rule) => {
|
|
17
|
+
if (rule.runIf(context)) {
|
|
18
|
+
return rule.run(previousContext);
|
|
19
|
+
}
|
|
20
|
+
return previousContext;
|
|
21
|
+
}, context);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
exports.all = all;
|
|
26
|
+
//# sourceMappingURL=all.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all.js","sourceRoot":"","sources":["../../../../../../packages/rules-engine/src/lib/runners/all.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACH,SAAgB,GAAG,CAA+B,GAAG,KAA4B;IAC/E,OAAO;QACL,KAAK,EAAE,UAAU,OAAO;YACtB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,CAAC;QACD,GAAG,EAAE,UAAU,OAAO;YACpB,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,IAAI,EAAE,EAAE;gBAC5C,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;oBACvB,OAAO,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;iBAClC;gBAED,OAAO,eAAe,CAAC;YACzB,CAAC,EAAE,OAAO,CAAC,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC;AAfD,kBAeC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Rule, type RuleContext } from "../..";
|
|
2
|
+
/**
|
|
3
|
+
* Runs the first rule that returns true for `runIf`.
|
|
4
|
+
*
|
|
5
|
+
* @param rules The rules to run.
|
|
6
|
+
* @returns A rule that runs the first rule that matches the context.
|
|
7
|
+
*/
|
|
8
|
+
export declare function firstMatch<TContext extends RuleContext>(...rules: Array<Rule<TContext>>): Rule<TContext>;
|
|
9
|
+
//# sourceMappingURL=first-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"first-match.d.ts","sourceRoot":"","sources":["../../../../../../packages/rules-engine/src/lib/runners/first-match.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,SAAS,WAAW,EACrD,GAAG,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAC9B,IAAI,CAAC,QAAQ,CAAC,CAWhB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.firstMatch = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Runs the first rule that returns true for `runIf`.
|
|
6
|
+
*
|
|
7
|
+
* @param rules The rules to run.
|
|
8
|
+
* @returns A rule that runs the first rule that matches the context.
|
|
9
|
+
*/
|
|
10
|
+
function firstMatch(...rules) {
|
|
11
|
+
return {
|
|
12
|
+
runIf: function (context) {
|
|
13
|
+
return rules.some((rule) => rule.runIf(context));
|
|
14
|
+
},
|
|
15
|
+
run: function (context) {
|
|
16
|
+
const rule = rules.find((rule) => rule.runIf(context));
|
|
17
|
+
return rule ? rule.run(context) : context;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
exports.firstMatch = firstMatch;
|
|
22
|
+
//# sourceMappingURL=first-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"first-match.js","sourceRoot":"","sources":["../../../../../../packages/rules-engine/src/lib/runners/first-match.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACH,SAAgB,UAAU,CACxB,GAAG,KAA4B;IAE/B,OAAO;QACL,KAAK,EAAE,UAAU,OAAO;YACtB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,CAAC;QACD,GAAG,EAAE,UAAU,OAAO;YACpB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;YAEvD,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5C,CAAC;KACF,CAAC;AACJ,CAAC;AAbD,gCAaC"}
|