@clipboard-health/rules-engine 1.0.3 → 1.1.2
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/.eslintrc.json +8 -0
- package/README.md +5 -3
- package/examples/rules.ts +71 -0
- package/jest.config.ts +19 -0
- package/package.json +18 -7
- package/project.json +33 -0
- package/src/index.ts +3 -0
- package/src/lib/append-output.ts +16 -0
- package/src/lib/rule.ts +37 -0
- package/src/lib/runners/all.spec.ts +64 -0
- package/src/lib/runners/all.ts +18 -0
- package/src/lib/runners/first-match.spec.ts +59 -0
- package/src/lib/runners/first-match.ts +18 -0
- package/src/lib/runners/index.ts +2 -0
- package/tsconfig.json +19 -0
- package/tsconfig.lib.json +9 -0
- package/tsconfig.lint.json +7 -0
- package/tsconfig.spec.json +7 -0
- package/typedoc.json +4 -0
- package/CHANGELOG.md +0 -68
- package/src/index.d.ts +0 -5
- package/src/index.d.ts.map +0 -1
- package/src/index.js +0 -8
- package/src/index.js.map +0 -1
- package/src/lib/append-output.d.ts +0 -7
- package/src/lib/append-output.d.ts.map +0 -1
- package/src/lib/append-output.js +0 -13
- package/src/lib/append-output.js.map +0 -1
- package/src/lib/rule.d.ts +0 -30
- package/src/lib/rule.d.ts.map +0 -1
- package/src/lib/rule.js +0 -3
- package/src/lib/rule.js.map +0 -1
- package/src/lib/runners/all.d.ts +0 -8
- package/src/lib/runners/all.d.ts.map +0 -1
- package/src/lib/runners/all.js +0 -17
- package/src/lib/runners/all.js.map +0 -1
- package/src/lib/runners/first-match.d.ts +0 -8
- package/src/lib/runners/first-match.d.ts.map +0 -1
- package/src/lib/runners/first-match.js +0 -18
- package/src/lib/runners/first-match.js.map +0 -1
package/.eslintrc.json
ADDED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @clipboard-health/rules-engine
|
|
2
2
|
|
|
3
|
-
Exposes a functional rules
|
|
3
|
+
Exposes a functional rules engine with two rule runners:
|
|
4
4
|
|
|
5
5
|
- `firstMatch`: Runs the first rule that matches the criteria
|
|
6
6
|
- `all`: Runs all the rules that matches the criteria
|
|
@@ -81,7 +81,8 @@ const allResult = all(
|
|
|
81
81
|
multiplyNumbersIfPositiveRule,
|
|
82
82
|
).run(exampleContext);
|
|
83
83
|
|
|
84
|
-
console.log(allResult.output);
|
|
84
|
+
console.log(allResult.output);
|
|
85
|
+
// => [{ result: 7 }, { result: 10 }]
|
|
85
86
|
|
|
86
87
|
// Using firstMatch() applies the first the rules to the context
|
|
87
88
|
const firstMatchResult = firstMatch(
|
|
@@ -90,7 +91,8 @@ const firstMatchResult = firstMatch(
|
|
|
90
91
|
multiplyNumbersIfPositiveRule,
|
|
91
92
|
).run(exampleContext);
|
|
92
93
|
|
|
93
|
-
console.log(firstMatchResult.output);
|
|
94
|
+
console.log(firstMatchResult.output);
|
|
95
|
+
// => [{ result: 7 }]
|
|
94
96
|
```
|
|
95
97
|
|
|
96
98
|
## Local development commands
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
all,
|
|
3
|
+
appendOutput,
|
|
4
|
+
firstMatch,
|
|
5
|
+
type Rule,
|
|
6
|
+
type RuleContext,
|
|
7
|
+
} from "@clipboard-health/rules-engine";
|
|
8
|
+
|
|
9
|
+
interface Input {
|
|
10
|
+
number1: number;
|
|
11
|
+
number2: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Output {
|
|
15
|
+
result: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const exampleContext: RuleContext<Input, Output> = {
|
|
19
|
+
input: {
|
|
20
|
+
number1: 2,
|
|
21
|
+
number2: 5,
|
|
22
|
+
},
|
|
23
|
+
output: [],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const addNumbersIfPositiveRule: Rule<Input, Output> = {
|
|
27
|
+
runIf: (input) => input.number1 > 0 && input.number2 > 0,
|
|
28
|
+
run: (context) => {
|
|
29
|
+
const { number1, number2 } = context.input;
|
|
30
|
+
const sum = number1 + number2;
|
|
31
|
+
return appendOutput(context, { result: sum });
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const multiplyNumbersIfPositiveRule: Rule<Input, Output> = {
|
|
36
|
+
runIf: (input) => input.number1 > 0 && input.number2 > 0,
|
|
37
|
+
run: (context) => {
|
|
38
|
+
const { number1, number2 } = context.input;
|
|
39
|
+
const sum = number1 * number2;
|
|
40
|
+
return appendOutput(context, { result: sum });
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const divideNumbersIfNegative: Rule<Input, Output> = {
|
|
45
|
+
runIf: (input) => input.number1 < 0 && input.number2 < 0,
|
|
46
|
+
run: (context) => {
|
|
47
|
+
const { number1, number2 } = context.input;
|
|
48
|
+
const sum = number1 * number2;
|
|
49
|
+
return appendOutput(context, { result: sum });
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Using all() applies all the rules to the context
|
|
54
|
+
const allResult = all(
|
|
55
|
+
addNumbersIfPositiveRule,
|
|
56
|
+
divideNumbersIfNegative,
|
|
57
|
+
multiplyNumbersIfPositiveRule,
|
|
58
|
+
).run(exampleContext);
|
|
59
|
+
|
|
60
|
+
console.log(allResult.output);
|
|
61
|
+
// => [{ result: 7 }, { result: 10 }]
|
|
62
|
+
|
|
63
|
+
// Using firstMatch() applies the first the rules to the context
|
|
64
|
+
const firstMatchResult = firstMatch(
|
|
65
|
+
divideNumbersIfNegative,
|
|
66
|
+
addNumbersIfPositiveRule,
|
|
67
|
+
multiplyNumbersIfPositiveRule,
|
|
68
|
+
).run(exampleContext);
|
|
69
|
+
|
|
70
|
+
console.log(firstMatchResult.output);
|
|
71
|
+
// => [{ result: 7 }]
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
coverageDirectory: "../../coverage/packages/rules-engine",
|
|
3
|
+
coveragePathIgnorePatterns: [],
|
|
4
|
+
coverageThreshold: {
|
|
5
|
+
global: {
|
|
6
|
+
branches: 100,
|
|
7
|
+
functions: 100,
|
|
8
|
+
lines: 100,
|
|
9
|
+
statements: 100,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
displayName: "rules-engine",
|
|
13
|
+
moduleFileExtensions: ["ts", "js"],
|
|
14
|
+
preset: "../../jest.preset.js",
|
|
15
|
+
testEnvironment: "node",
|
|
16
|
+
transform: {
|
|
17
|
+
"^.+\\.[tj]s$": ["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }],
|
|
18
|
+
},
|
|
19
|
+
};
|
package/package.json
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/rules-engine",
|
|
3
|
-
"
|
|
3
|
+
"description": "",
|
|
4
|
+
"version": "1.1.2",
|
|
5
|
+
"bugs": "https://github.com/clipboardhealth/core-utils/issues",
|
|
4
6
|
"dependencies": {
|
|
7
|
+
"tslib": "2.6.3",
|
|
5
8
|
"type-fest": "4.23.0"
|
|
6
9
|
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"license": "MIT",
|
|
7
12
|
"main": "./src/index.js",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/clipboardhealth/core-utils.git",
|
|
19
|
+
"directory": "packages/rules-engine"
|
|
20
|
+
},
|
|
8
21
|
"scripts": {
|
|
9
|
-
"
|
|
10
|
-
"embed": "embedme README.md",
|
|
11
|
-
"lint": "nx lint rules-engine",
|
|
12
|
-
"test": "nx test rules-engine"
|
|
22
|
+
"embed": "embedme README.md"
|
|
13
23
|
},
|
|
14
|
-
"type": "commonjs"
|
|
15
|
-
|
|
24
|
+
"type": "commonjs",
|
|
25
|
+
"typings": "./src/index.d.ts"
|
|
26
|
+
}
|
package/project.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
3
|
+
"name": "rules-engine",
|
|
4
|
+
"projectType": "library",
|
|
5
|
+
"sourceRoot": "packages/rules-engine/src",
|
|
6
|
+
"tags": [],
|
|
7
|
+
"targets": {
|
|
8
|
+
"build": {
|
|
9
|
+
"executor": "@nx/js:tsc",
|
|
10
|
+
"options": {
|
|
11
|
+
"assets": ["packages/rules-engine/*.md"],
|
|
12
|
+
"main": "packages/rules-engine/src/index.js",
|
|
13
|
+
"outputPath": "dist/packages/rules-engine",
|
|
14
|
+
"tsConfig": "packages/rules-engine/tsconfig.lib.json"
|
|
15
|
+
},
|
|
16
|
+
"outputs": ["{options.outputPath}"]
|
|
17
|
+
},
|
|
18
|
+
"lint": {
|
|
19
|
+
"executor": "@nx/eslint:lint",
|
|
20
|
+
"options": {
|
|
21
|
+
"lintFilePatterns": ["packages/rules-engine/**/*.[jt]s"],
|
|
22
|
+
"maxWarnings": 0
|
|
23
|
+
},
|
|
24
|
+
"outputs": ["{options.outputFile}"]
|
|
25
|
+
},
|
|
26
|
+
"test": {
|
|
27
|
+
"executor": "@nx/jest:jest",
|
|
28
|
+
"options": {
|
|
29
|
+
"jestConfig": "packages/rules-engine/jest.config.ts"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReadonlyDeep } from "type-fest";
|
|
2
|
+
|
|
3
|
+
import { type RuleContext } from "./rule";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Rule output is immutable, do not modify existing items, only append using this function.
|
|
7
|
+
*/
|
|
8
|
+
export function appendOutput<TInput, TOutput>(
|
|
9
|
+
context: RuleContext<TInput, TOutput>,
|
|
10
|
+
output: ReadonlyDeep<TOutput>,
|
|
11
|
+
): RuleContext<TInput, TOutput> {
|
|
12
|
+
return {
|
|
13
|
+
input: context.input,
|
|
14
|
+
output: [...context.output, output],
|
|
15
|
+
};
|
|
16
|
+
}
|
package/src/lib/rule.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type ReadonlyDeep } from "type-fest";
|
|
2
|
+
|
|
3
|
+
export interface RuleContext<TInput, TOutput> {
|
|
4
|
+
/**
|
|
5
|
+
* Input is immutable, rules must not modify it.
|
|
6
|
+
*/
|
|
7
|
+
input: ReadonlyDeep<TInput>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Output is immutable, do not modify existing items, only append.
|
|
11
|
+
*
|
|
12
|
+
* @see {@link appendOutput}
|
|
13
|
+
*/
|
|
14
|
+
output: ReadonlyArray<ReadonlyDeep<TOutput>>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Rule<
|
|
18
|
+
TInput,
|
|
19
|
+
TOutput,
|
|
20
|
+
TContext extends RuleContext<TInput, TOutput> = RuleContext<TInput, TOutput>,
|
|
21
|
+
> {
|
|
22
|
+
/**
|
|
23
|
+
* Returns whether the rule should run or not.
|
|
24
|
+
*
|
|
25
|
+
* Only the `input` and not the full `context` is passed to `runIf`. This prevents rules from
|
|
26
|
+
* relying on `output`, which may be appended to by previous rules by the time `run` is called.
|
|
27
|
+
*/
|
|
28
|
+
runIf: (input: ReadonlyDeep<TInput>) => boolean;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A pure function that runs rule logic and returns a new context by appending to the output
|
|
32
|
+
* array.
|
|
33
|
+
*
|
|
34
|
+
* @see {@link appendOutput}
|
|
35
|
+
*/
|
|
36
|
+
run: (context: TContext) => TContext;
|
|
37
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type Rule, type RuleContext } from "../..";
|
|
2
|
+
import { appendOutput } from "../append-output";
|
|
3
|
+
import { all } from "./all";
|
|
4
|
+
|
|
5
|
+
interface Input {
|
|
6
|
+
a: number;
|
|
7
|
+
b: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type Output = number;
|
|
11
|
+
|
|
12
|
+
type TestContext = RuleContext<Input, Output>;
|
|
13
|
+
|
|
14
|
+
type TestRule = Rule<Input, Output>;
|
|
15
|
+
|
|
16
|
+
const context: TestContext = {
|
|
17
|
+
input: { a: 1, b: 2 },
|
|
18
|
+
output: [],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const testRule1: TestRule = {
|
|
22
|
+
runIf: () => false,
|
|
23
|
+
run: (context) => appendOutput(context, 1),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const testRule2: TestRule = {
|
|
27
|
+
runIf: () => true,
|
|
28
|
+
run: (context) => appendOutput(context, 2),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const testRule3: TestRule = {
|
|
32
|
+
runIf: () => true,
|
|
33
|
+
run: (context) => appendOutput(context, 3),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const testRule4: TestRule = {
|
|
37
|
+
runIf: () => false,
|
|
38
|
+
run: (context) => appendOutput(context, 4),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
describe("all", () => {
|
|
42
|
+
describe("if", () => {
|
|
43
|
+
it("returns true if any rules are true", () => {
|
|
44
|
+
expect(all(testRule1, testRule2, testRule3).runIf(context.input)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns false if all rules are false", () => {
|
|
48
|
+
expect(all(testRule1, testRule4).runIf(context.input)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("run", () => {
|
|
53
|
+
it("runs all the matching rules", () => {
|
|
54
|
+
expect(all(testRule1, testRule2, testRule3, testRule4).run(context)).toEqual({
|
|
55
|
+
...context,
|
|
56
|
+
output: [2, 3],
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("returns the received context if no rule can be run", () => {
|
|
61
|
+
expect(all(testRule1, testRule4).run(context)).toEqual(context);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Rule, type RuleContext } from "../rule";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Run all rules that return true for `runIf`.
|
|
5
|
+
*
|
|
6
|
+
* @param rules The rules to run.
|
|
7
|
+
*/
|
|
8
|
+
export function all<TInput, TOutput, TContext extends RuleContext<TInput, TOutput>>(
|
|
9
|
+
...rules: Array<Rule<TInput, TOutput, TContext>>
|
|
10
|
+
): Rule<TInput, TOutput, TContext> {
|
|
11
|
+
return {
|
|
12
|
+
runIf: (input) => rules.some((rule) => rule.runIf(input)),
|
|
13
|
+
run: (context) =>
|
|
14
|
+
rules
|
|
15
|
+
.filter((rule) => rule.runIf(context.input))
|
|
16
|
+
.reduce((previousContext, rule) => rule.run(previousContext), context),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Rule, type RuleContext } from "../..";
|
|
2
|
+
import { appendOutput } from "../append-output";
|
|
3
|
+
import { firstMatch } from "./first-match";
|
|
4
|
+
|
|
5
|
+
interface Input {
|
|
6
|
+
a: number;
|
|
7
|
+
b: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type Output = number;
|
|
11
|
+
|
|
12
|
+
type TestContext = RuleContext<Input, Output>;
|
|
13
|
+
|
|
14
|
+
type TestRule = Rule<Input, Output>;
|
|
15
|
+
|
|
16
|
+
const context: TestContext = {
|
|
17
|
+
input: { a: 1, b: 2 },
|
|
18
|
+
output: [],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const testRule1: TestRule = {
|
|
22
|
+
runIf: () => false,
|
|
23
|
+
run: (context) => appendOutput(context, 1),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const testRule2: TestRule = {
|
|
27
|
+
runIf: () => true,
|
|
28
|
+
run: (context) => appendOutput(context, 2),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const testRule3: TestRule = {
|
|
32
|
+
runIf: () => false,
|
|
33
|
+
run: (context) => appendOutput(context, 3),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
describe("firstMatch", () => {
|
|
37
|
+
describe("if", () => {
|
|
38
|
+
it("returns true if any rules are true", () => {
|
|
39
|
+
expect(firstMatch(testRule1, testRule2, testRule3).runIf(context.input)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("returns false if all rules are false", () => {
|
|
43
|
+
expect(firstMatch(testRule3).runIf(context.input)).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("run", () => {
|
|
48
|
+
it("runs the first matching rule", () => {
|
|
49
|
+
expect(firstMatch(testRule1, testRule2, testRule3).run(context)).toEqual({
|
|
50
|
+
...context,
|
|
51
|
+
output: [2],
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("returns the received context if no rule can be run", () => {
|
|
56
|
+
expect(firstMatch(testRule1, testRule3).run(context)).toEqual(context);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Rule, type RuleContext } from "../rule";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Run the first rule that returns true for `runIf`.
|
|
5
|
+
*
|
|
6
|
+
* @param rules The rules to run.
|
|
7
|
+
*/
|
|
8
|
+
export function firstMatch<TInput, TOutput, TContext extends RuleContext<TInput, TOutput>>(
|
|
9
|
+
...rules: Array<Rule<TInput, TOutput, TContext>>
|
|
10
|
+
): Rule<TInput, TOutput, TContext> {
|
|
11
|
+
return {
|
|
12
|
+
runIf: (input) => rules.some((rule) => rule.runIf(input)),
|
|
13
|
+
run: (context) => {
|
|
14
|
+
const rule = rules.find((rule) => rule.runIf(context.input));
|
|
15
|
+
return rule ? rule.run(context) : context;
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc"
|
|
5
|
+
},
|
|
6
|
+
"files": [],
|
|
7
|
+
"include": [],
|
|
8
|
+
"references": [
|
|
9
|
+
{
|
|
10
|
+
"path": "./tsconfig.lib.json"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"path": "./tsconfig.lint.json"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "./tsconfig.spec.json"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
package/typedoc.json
ADDED
package/CHANGELOG.md
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
|
-
|
|
5
|
-
## [1.0.3](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-1.0.2...rules-engine-1.0.3) (2024-07-25)
|
|
6
|
-
|
|
7
|
-
## [1.0.2](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-1.0.1...rules-engine-1.0.2) (2024-07-18)
|
|
8
|
-
|
|
9
|
-
## [1.0.1](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-1.0.0...rules-engine-1.0.1) (2024-07-18)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
### Bug Fixes
|
|
13
|
-
|
|
14
|
-
* add build postTarget ([#692](https://github.com/ClipboardHealth/cbh-core/issues/692)) ([3b42efb](https://github.com/ClipboardHealth/cbh-core/commit/3b42efb603572c7a273c8eedd554773c6a364acf))
|
|
15
|
-
|
|
16
|
-
## [1.0.0](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.6...rules-engine-1.0.0) (2024-07-18)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
### ⚠ BREAKING CHANGES
|
|
20
|
-
|
|
21
|
-
* upgrade Nx to v18 (#690)
|
|
22
|
-
|
|
23
|
-
### Code Refactoring
|
|
24
|
-
|
|
25
|
-
* upgrade Nx to v18 ([#690](https://github.com/ClipboardHealth/cbh-core/issues/690)) ([e6dd982](https://github.com/ClipboardHealth/cbh-core/commit/e6dd982db39022a72dfbf888f707768bae834ccb))
|
|
26
|
-
|
|
27
|
-
## [0.1.6](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.5...rules-engine-0.1.6) (2024-07-12)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
### Bug Fixes
|
|
31
|
-
|
|
32
|
-
* **deps:** update dependency type-fest to v4 ([#676](https://github.com/ClipboardHealth/cbh-core/issues/676)) ([3c5d3ec](https://github.com/ClipboardHealth/cbh-core/commit/3c5d3ec5c15f7ed01b842fc813e5290fc3ca6021))
|
|
33
|
-
|
|
34
|
-
## [0.1.5](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.4...rules-engine-0.1.5) (2024-07-12)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
### Bug Fixes
|
|
38
|
-
|
|
39
|
-
* testing Nx upgrade, attempt 2 ([4066dab](https://github.com/ClipboardHealth/cbh-core/commit/4066dab6f40e5b9b06d7d93d1973c09135c52817))
|
|
40
|
-
|
|
41
|
-
## [0.1.4](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.3...rules-engine-0.1.4) (2024-07-12)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
### Bug Fixes
|
|
45
|
-
|
|
46
|
-
* testing Nx upgrade ([f823f5d](https://github.com/ClipboardHealth/cbh-core/commit/f823f5d3e9624d33f55683df8346f99d51bef9e7))
|
|
47
|
-
|
|
48
|
-
## [0.1.3](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.2...rules-engine-0.1.3) (2024-07-12)
|
|
49
|
-
|
|
50
|
-
## [0.1.3](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.2...rules-engine-0.1.3) (2024-07-11)
|
|
51
|
-
|
|
52
|
-
## [0.1.3](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.2...rules-engine-0.1.3) (2024-07-11)
|
|
53
|
-
|
|
54
|
-
## [0.1.2](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.1...rules-engine-0.1.2) (2024-02-27)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
### Bug Fixes
|
|
58
|
-
|
|
59
|
-
* **deps:** update dependency type-fest to v4 ([#531](https://github.com/ClipboardHealth/cbh-core/issues/531)) ([f366907](https://github.com/ClipboardHealth/cbh-core/commit/f36690743f2373fc6c62a589a6b50fa7f660bbae))
|
|
60
|
-
|
|
61
|
-
## [0.1.1](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.0...rules-engine-0.1.1) (2023-12-22)
|
|
62
|
-
|
|
63
|
-
## 0.1.0 (2023-12-20)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
### Features
|
|
67
|
-
|
|
68
|
-
* **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/src/index.d.ts
DELETED
package/src/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
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
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/rules-engine/src/index.ts"],"names":[],"mappings":";;;AAAA,8DAAoC;AACpC,qDAA2B;AAC3B,4DAAkC;AAClC,oEAA0C"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type ReadonlyDeep } from "type-fest";
|
|
2
|
-
import { type RuleContext } from "./rule";
|
|
3
|
-
/**
|
|
4
|
-
* Rule output is immutable, do not modify existing items, only append using this function.
|
|
5
|
-
*/
|
|
6
|
-
export declare function appendOutput<TInput, TOutput>(context: RuleContext<TInput, TOutput>, output: ReadonlyDeep<TOutput>): RuleContext<TInput, TOutput>;
|
|
7
|
-
//# sourceMappingURL=append-output.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"append-output.d.ts","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/append-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAC1C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,GAC5B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAK9B"}
|
package/src/lib/append-output.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.appendOutput = appendOutput;
|
|
4
|
-
/**
|
|
5
|
-
* Rule output is immutable, do not modify existing items, only append using this function.
|
|
6
|
-
*/
|
|
7
|
-
function appendOutput(context, output) {
|
|
8
|
-
return {
|
|
9
|
-
input: context.input,
|
|
10
|
-
output: [...context.output, output],
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=append-output.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"append-output.js","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/append-output.ts"],"names":[],"mappings":";;AAOA,oCAQC;AAXD;;GAEG;AACH,SAAgB,YAAY,CAC1B,OAAqC,EACrC,MAA6B;IAE7B,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC;KACpC,CAAC;AACJ,CAAC"}
|
package/src/lib/rule.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { type ReadonlyDeep } from "type-fest";
|
|
2
|
-
export interface RuleContext<TInput, TOutput> {
|
|
3
|
-
/**
|
|
4
|
-
* Input is immutable, rules must not modify it.
|
|
5
|
-
*/
|
|
6
|
-
input: ReadonlyDeep<TInput>;
|
|
7
|
-
/**
|
|
8
|
-
* Output is immutable, do not modify existing items, only append.
|
|
9
|
-
*
|
|
10
|
-
* @see {@link appendOutput}
|
|
11
|
-
*/
|
|
12
|
-
output: ReadonlyArray<ReadonlyDeep<TOutput>>;
|
|
13
|
-
}
|
|
14
|
-
export interface Rule<TInput, TOutput, TContext extends RuleContext<TInput, TOutput> = RuleContext<TInput, TOutput>> {
|
|
15
|
-
/**
|
|
16
|
-
* Returns whether the rule should run or not.
|
|
17
|
-
*
|
|
18
|
-
* Only the `input` and not the full `context` is passed to `runIf`. This prevents rules from
|
|
19
|
-
* relying on `output`, which may be appended to by previous rules by the time `run` is called.
|
|
20
|
-
*/
|
|
21
|
-
runIf: (input: ReadonlyDeep<TInput>) => boolean;
|
|
22
|
-
/**
|
|
23
|
-
* A pure function that runs rule logic and returns a new context by appending to the output
|
|
24
|
-
* array.
|
|
25
|
-
*
|
|
26
|
-
* @see {@link appendOutput}
|
|
27
|
-
*/
|
|
28
|
-
run: (context: TContext) => TContext;
|
|
29
|
-
}
|
|
30
|
-
//# sourceMappingURL=rule.d.ts.map
|
package/src/lib/rule.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,WAAW,WAAW,CAAC,MAAM,EAAE,OAAO;IAC1C;;OAEG;IACH,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAE5B;;;;OAIG;IACH,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,IAAI,CACnB,MAAM,EACN,OAAO,EACP,QAAQ,SAAS,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;IAE5E;;;;;OAKG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;IAEhD;;;;;OAKG;IACH,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,QAAQ,CAAC;CACtC"}
|
package/src/lib/rule.js
DELETED
package/src/lib/rule.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/rule.ts"],"names":[],"mappings":""}
|
package/src/lib/runners/all.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { type Rule, type RuleContext } from "../rule";
|
|
2
|
-
/**
|
|
3
|
-
* Runs all rules that return true for `runIf`.
|
|
4
|
-
*
|
|
5
|
-
* @param rules The rules to run.
|
|
6
|
-
*/
|
|
7
|
-
export declare function all<TInput, TOutput, TContext extends RuleContext<TInput, TOutput>>(...rules: Array<Rule<TInput, TOutput, TContext>>): Rule<TInput, TOutput, TContext>;
|
|
8
|
-
//# sourceMappingURL=all.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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,SAAS,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,SAAS,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,EAChF,GAAG,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,GAC/C,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAQjC"}
|
package/src/lib/runners/all.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.all = all;
|
|
4
|
-
/**
|
|
5
|
-
* Runs all rules that return true for `runIf`.
|
|
6
|
-
*
|
|
7
|
-
* @param rules The rules to run.
|
|
8
|
-
*/
|
|
9
|
-
function all(...rules) {
|
|
10
|
-
return {
|
|
11
|
-
runIf: (input) => rules.some((rule) => rule.runIf(input)),
|
|
12
|
-
run: (context) => rules
|
|
13
|
-
.filter((rule) => rule.runIf(context.input))
|
|
14
|
-
.reduce((previousContext, rule) => rule.run(previousContext), context),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
//# sourceMappingURL=all.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"all.js","sourceRoot":"","sources":["../../../../../../packages/rules-engine/src/lib/runners/all.ts"],"names":[],"mappings":";;AAOA,kBAUC;AAfD;;;;GAIG;AACH,SAAgB,GAAG,CACjB,GAAG,KAA6C;IAEhD,OAAO;QACL,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzD,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,CACf,KAAK;aACF,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;aAC3C,MAAM,CAAC,CAAC,eAAe,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;KAC3E,CAAC;AACJ,CAAC"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { type Rule, type RuleContext } from "../rule";
|
|
2
|
-
/**
|
|
3
|
-
* Run the first rule that returns true for `runIf`.
|
|
4
|
-
*
|
|
5
|
-
* @param rules The rules to run.
|
|
6
|
-
*/
|
|
7
|
-
export declare function firstMatch<TInput, TOutput, TContext extends RuleContext<TInput, TOutput>>(...rules: Array<Rule<TInput, TOutput, TContext>>): Rule<TInput, TOutput, TContext>;
|
|
8
|
-
//# sourceMappingURL=first-match.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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,SAAS,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,SAAS,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,EACvF,GAAG,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,GAC/C,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAQjC"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.firstMatch = firstMatch;
|
|
4
|
-
/**
|
|
5
|
-
* Run the first rule that returns true for `runIf`.
|
|
6
|
-
*
|
|
7
|
-
* @param rules The rules to run.
|
|
8
|
-
*/
|
|
9
|
-
function firstMatch(...rules) {
|
|
10
|
-
return {
|
|
11
|
-
runIf: (input) => rules.some((rule) => rule.runIf(input)),
|
|
12
|
-
run: (context) => {
|
|
13
|
-
const rule = rules.find((rule) => rule.runIf(context.input));
|
|
14
|
-
return rule ? rule.run(context) : context;
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=first-match.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"first-match.js","sourceRoot":"","sources":["../../../../../../packages/rules-engine/src/lib/runners/first-match.ts"],"names":[],"mappings":";;AAOA,gCAUC;AAfD;;;;GAIG;AACH,SAAgB,UAAU,CACxB,GAAG,KAA6C;IAEhD,OAAO;QACL,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzD,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE;YACf,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YAC7D,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5C,CAAC;KACF,CAAC;AACJ,CAAC"}
|