@clipboard-health/rules-engine 0.1.0 → 0.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/CHANGELOG.md +9 -0
- package/README.md +19 -20
- package/package.json +4 -1
- package/src/lib/append-output.d.ts +5 -1
- package/src/lib/append-output.d.ts.map +1 -1
- package/src/lib/append-output.js +3 -0
- package/src/lib/append-output.js.map +1 -1
- package/src/lib/rule.d.ts +25 -5
- package/src/lib/rule.d.ts.map +1 -1
- package/src/lib/runners/all.d.ts +3 -4
- package/src/lib/runners/all.d.ts.map +1 -1
- package/src/lib/runners/all.js +5 -13
- package/src/lib/runners/all.js.map +1 -1
- package/src/lib/runners/first-match.d.ts +3 -3
- package/src/lib/runners/first-match.d.ts.map +1 -1
- package/src/lib/runners/first-match.js +4 -6
- package/src/lib/runners/first-match.js.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [0.1.2](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.1...rules-engine-0.1.2) (2024-02-27)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **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))
|
|
11
|
+
|
|
12
|
+
## [0.1.1](https://github.com/ClipboardHealth/cbh-core/compare/rules-engine-0.1.0...rules-engine-0.1.1) (2023-12-22)
|
|
13
|
+
|
|
5
14
|
## 0.1.0 (2023-12-20)
|
|
6
15
|
|
|
7
16
|
|
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Exposes a functional rules-engine with 2 engines:
|
|
4
4
|
|
|
5
|
-
- firstMatch
|
|
6
|
-
- all
|
|
5
|
+
- `firstMatch`: Runs the first rule that matches the criteria
|
|
6
|
+
- `all`: Runs all the rules that matches the criteria
|
|
7
7
|
|
|
8
8
|
## Table of Contents
|
|
9
9
|
|
|
@@ -22,7 +22,13 @@ npm install @clipboard-health/rules-engine
|
|
|
22
22
|
```ts
|
|
23
23
|
// ./examples/rules.ts
|
|
24
24
|
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
all,
|
|
27
|
+
appendOutput,
|
|
28
|
+
firstMatch,
|
|
29
|
+
type Rule,
|
|
30
|
+
type RuleContext,
|
|
31
|
+
} from "@clipboard-health/rules-engine";
|
|
26
32
|
|
|
27
33
|
interface Input {
|
|
28
34
|
number1: number;
|
|
@@ -33,9 +39,7 @@ interface Output {
|
|
|
33
39
|
result: number;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const exampleContext: ExampleRuleContext = {
|
|
42
|
+
const exampleContext: RuleContext<Input, Output> = {
|
|
39
43
|
input: {
|
|
40
44
|
number1: 2,
|
|
41
45
|
number2: 5,
|
|
@@ -43,33 +47,30 @@ const exampleContext: ExampleRuleContext = {
|
|
|
43
47
|
output: [],
|
|
44
48
|
};
|
|
45
49
|
|
|
46
|
-
const addNumbersIfPositiveRule: Rule<
|
|
47
|
-
runIf: (
|
|
50
|
+
const addNumbersIfPositiveRule: Rule<Input, Output> = {
|
|
51
|
+
runIf: (input) => input.number1 > 0 && input.number2 > 0,
|
|
48
52
|
run: (context) => {
|
|
49
53
|
const { number1, number2 } = context.input;
|
|
50
54
|
const sum = number1 + number2;
|
|
51
|
-
context
|
|
52
|
-
return context;
|
|
55
|
+
return appendOutput(context, { result: sum });
|
|
53
56
|
},
|
|
54
57
|
};
|
|
55
58
|
|
|
56
|
-
const multiplyNumbersIfPositiveRule: Rule<
|
|
57
|
-
runIf: (
|
|
59
|
+
const multiplyNumbersIfPositiveRule: Rule<Input, Output> = {
|
|
60
|
+
runIf: (input) => input.number1 > 0 && input.number2 > 0,
|
|
58
61
|
run: (context) => {
|
|
59
62
|
const { number1, number2 } = context.input;
|
|
60
63
|
const sum = number1 * number2;
|
|
61
|
-
context
|
|
62
|
-
return context;
|
|
64
|
+
return appendOutput(context, { result: sum });
|
|
63
65
|
},
|
|
64
66
|
};
|
|
65
67
|
|
|
66
|
-
const divideNumbersIfNegative: Rule<
|
|
67
|
-
runIf: (
|
|
68
|
+
const divideNumbersIfNegative: Rule<Input, Output> = {
|
|
69
|
+
runIf: (input) => input.number1 < 0 && input.number2 < 0,
|
|
68
70
|
run: (context) => {
|
|
69
71
|
const { number1, number2 } = context.input;
|
|
70
72
|
const sum = number1 * number2;
|
|
71
|
-
context
|
|
72
|
-
return context;
|
|
73
|
+
return appendOutput(context, { result: sum });
|
|
73
74
|
},
|
|
74
75
|
};
|
|
75
76
|
|
|
@@ -80,7 +81,6 @@ const allResult = all(
|
|
|
80
81
|
multiplyNumbersIfPositiveRule,
|
|
81
82
|
).run(exampleContext);
|
|
82
83
|
|
|
83
|
-
// eslint-disable-next-line no-console
|
|
84
84
|
console.log(allResult.output); // AllResult.output = [{ result: 7 }, { result: 10 }]
|
|
85
85
|
|
|
86
86
|
// Using firstMatch() applies the first the rules to the context
|
|
@@ -90,7 +90,6 @@ const firstMatchResult = firstMatch(
|
|
|
90
90
|
multiplyNumbersIfPositiveRule,
|
|
91
91
|
).run(exampleContext);
|
|
92
92
|
|
|
93
|
-
// eslint-disable-next-line no-console
|
|
94
93
|
console.log(firstMatchResult.output); // [{ result: 7 }]
|
|
95
94
|
```
|
|
96
95
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/rules-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "nx build rules-engine",
|
|
@@ -8,5 +8,8 @@
|
|
|
8
8
|
"lint": "nx lint rules-engine",
|
|
9
9
|
"test": "nx test rules-engine"
|
|
10
10
|
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"type-fest": "4.10.3"
|
|
13
|
+
},
|
|
11
14
|
"type": "commonjs"
|
|
12
15
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { type ReadonlyDeep } from "type-fest";
|
|
1
2
|
import { type RuleContext } from "./rule";
|
|
2
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Rule output is immutable, do not modify existing items, only append.
|
|
5
|
+
*/
|
|
6
|
+
export declare function appendOutput<TInput, TOutput>(context: RuleContext<TInput, TOutput>, output: ReadonlyDeep<TOutput>): RuleContext<TInput, TOutput>;
|
|
3
7
|
//# sourceMappingURL=append-output.d.ts.map
|
|
@@ -1 +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,
|
|
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
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.appendOutput = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Rule output is immutable, do not modify existing items, only append.
|
|
6
|
+
*/
|
|
4
7
|
function appendOutput(context, output) {
|
|
5
8
|
return {
|
|
6
9
|
input: context.input,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"append-output.js","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/append-output.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"append-output.js","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/append-output.ts"],"names":[],"mappings":";;;AAIA;;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;AARD,oCAQC"}
|
package/src/lib/rule.d.ts
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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>>;
|
|
4
13
|
}
|
|
5
|
-
export interface Rule<TContext extends RuleContext> {
|
|
6
|
-
|
|
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 modified by previous rules by the time `run` is called.
|
|
20
|
+
*/
|
|
21
|
+
runIf: (input: ReadonlyDeep<TInput>) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Runs the actual rule and returns a new context by appending to the output array.
|
|
24
|
+
*
|
|
25
|
+
* @see {@link appendOutput}
|
|
26
|
+
*/
|
|
7
27
|
run: (context: TContext) => TContext;
|
|
8
28
|
}
|
|
9
29
|
//# sourceMappingURL=rule.d.ts.map
|
package/src/lib/rule.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../../../../packages/rules-engine/src/lib/rule.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,
|
|
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;;;;OAIG;IACH,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,QAAQ,CAAC;CACtC"}
|
package/src/lib/runners/all.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { type Rule, type RuleContext } from "
|
|
1
|
+
import { type Rule, type RuleContext } from "../rule";
|
|
2
2
|
/**
|
|
3
|
-
* Runs all
|
|
3
|
+
* Runs all rules that return true for `runIf`.
|
|
4
4
|
*
|
|
5
5
|
* @param rules The rules to run.
|
|
6
|
-
* @returns Rule<TContext>
|
|
7
6
|
*/
|
|
8
|
-
export declare function all<TContext extends RuleContext
|
|
7
|
+
export declare function all<TInput, TOutput, TContext extends RuleContext<TInput, TOutput>>(...rules: Array<Rule<TInput, TOutput, TContext>>): Rule<TInput, TOutput, TContext>;
|
|
9
8
|
//# sourceMappingURL=all.d.ts.map
|
|
@@ -1 +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,
|
|
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
CHANGED
|
@@ -2,24 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.all = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* Runs all
|
|
5
|
+
* Runs all rules that return true for `runIf`.
|
|
6
6
|
*
|
|
7
7
|
* @param rules The rules to run.
|
|
8
|
-
* @returns Rule<TContext>
|
|
9
8
|
*/
|
|
10
9
|
function all(...rules) {
|
|
11
10
|
return {
|
|
12
|
-
runIf:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return rules.reduce((previousContext, rule) => {
|
|
17
|
-
if (rule.runIf(context)) {
|
|
18
|
-
return rule.run(previousContext);
|
|
19
|
-
}
|
|
20
|
-
return previousContext;
|
|
21
|
-
}, context);
|
|
22
|
-
},
|
|
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),
|
|
23
15
|
};
|
|
24
16
|
}
|
|
25
17
|
exports.all = all;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"all.js","sourceRoot":"","sources":["../../../../../../packages/rules-engine/src/lib/runners/all.ts"],"names":[],"mappings":";;;AAEA
|
|
1
|
+
{"version":3,"file":"all.js","sourceRoot":"","sources":["../../../../../../packages/rules-engine/src/lib/runners/all.ts"],"names":[],"mappings":";;;AAEA;;;;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;AAVD,kBAUC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type Rule, type RuleContext } from "
|
|
1
|
+
import { type Rule, type RuleContext } from "../rule";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Run the first rule that returns true for `runIf`.
|
|
4
4
|
*
|
|
5
5
|
* @param rules The rules to run.
|
|
6
6
|
* @returns A rule that runs the first rule that matches the context.
|
|
7
7
|
*/
|
|
8
|
-
export declare function firstMatch<TContext extends RuleContext
|
|
8
|
+
export declare function firstMatch<TInput, TOutput, TContext extends RuleContext<TInput, TOutput>>(...rules: Array<Rule<TInput, TOutput, TContext>>): Rule<TInput, TOutput, TContext>;
|
|
9
9
|
//# sourceMappingURL=first-match.d.ts.map
|
|
@@ -1 +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,
|
|
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;;;;;GAKG;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"}
|
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.firstMatch = void 0;
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Run the first rule that returns true for `runIf`.
|
|
6
6
|
*
|
|
7
7
|
* @param rules The rules to run.
|
|
8
8
|
* @returns A rule that runs the first rule that matches the context.
|
|
9
9
|
*/
|
|
10
10
|
function firstMatch(...rules) {
|
|
11
11
|
return {
|
|
12
|
-
runIf:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
run: function (context) {
|
|
16
|
-
const rule = rules.find((rule) => rule.runIf(context));
|
|
12
|
+
runIf: (input) => rules.some((rule) => rule.runIf(input)),
|
|
13
|
+
run: (context) => {
|
|
14
|
+
const rule = rules.find((rule) => rule.runIf(context.input));
|
|
17
15
|
return rule ? rule.run(context) : context;
|
|
18
16
|
},
|
|
19
17
|
};
|
|
@@ -1 +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,
|
|
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,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;AAVD,gCAUC"}
|