@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 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,5 @@
1
+ export * from "./lib/append-output";
2
+ export * from "./lib/rule";
3
+ export * from "./lib/runners/all";
4
+ export * from "./lib/runners/first-match";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -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
@@ -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,3 @@
1
+ import { type RuleContext } from "./rule";
2
+ export declare function appendOutput<TInput, TOutput>(context: RuleContext<TInput, TOutput>, output: TOutput): RuleContext<TInput, TOutput>;
3
+ //# sourceMappingURL=append-output.d.ts.map
@@ -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"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=rule.js.map
@@ -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"}