@clipboard-health/rules-engine 1.1.2 → 1.1.5

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@clipboard-health/rules-engine",
3
3
  "description": "",
4
- "version": "1.1.2",
4
+ "version": "1.1.5",
5
5
  "bugs": "https://github.com/clipboardhealth/core-utils/issues",
6
6
  "dependencies": {
7
7
  "tslib": "2.6.3",
@@ -22,5 +22,6 @@
22
22
  "embed": "embedme README.md"
23
23
  },
24
24
  "type": "commonjs",
25
- "typings": "./src/index.d.ts"
25
+ "typings": "./src/index.d.ts",
26
+ "types": "./src/index.d.ts"
26
27
  }
package/src/index.js ADDED
@@ -0,0 +1,7 @@
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"), exports);
7
+ //# 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,wDAA8B"}
@@ -0,0 +1,6 @@
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>;
@@ -0,0 +1,13 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,29 @@
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
+ }
@@ -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,7 @@
1
+ import { type Rule, type RuleContext } from "../rule";
2
+ /**
3
+ * Run 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>;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.all = all;
4
+ /**
5
+ * Run 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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,7 @@
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>;
@@ -0,0 +1,18 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./all"), exports);
5
+ tslib_1.__exportStar(require("./first-match"), exports);
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/rules-engine/src/lib/runners/index.ts"],"names":[],"mappings":";;;AAAA,gDAAsB;AACtB,wDAA8B"}
package/.eslintrc.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "extends": ["../../.eslintrc.json"],
3
- "ignorePatterns": ["!**/*"],
4
- "parserOptions": {
5
- "project": "tsconfig.lint.json",
6
- "tsconfigRootDir": "packages/rules-engine"
7
- }
8
- }
package/examples/rules.ts DELETED
@@ -1,71 +0,0 @@
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 DELETED
@@ -1,19 +0,0 @@
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/project.json DELETED
@@ -1,33 +0,0 @@
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
- }
@@ -1,16 +0,0 @@
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 DELETED
@@ -1,37 +0,0 @@
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
- }
@@ -1,64 +0,0 @@
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
- });
@@ -1,18 +0,0 @@
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
- }
@@ -1,59 +0,0 @@
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
- });
@@ -1,18 +0,0 @@
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 DELETED
@@ -1,19 +0,0 @@
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/tsconfig.lib.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "declaration": true,
5
- "types": ["node"]
6
- },
7
- "include": ["src/**/*.ts"],
8
- "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
9
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "types": ["jest", "node"]
5
- },
6
- "include": ["."]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "types": ["jest", "node"]
5
- },
6
- "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
7
- }
package/typedoc.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "extends": ["../../typedoc.base.json"],
3
- "entryPoints": ["src/index.ts"]
4
- }
File without changes
File without changes