fig-tree-evaluator 2.0.4 → 2.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/README.md +72 -0
- package/build/evaluate.js +2 -1
- package/build/helpers.d.ts +5 -1
- package/build/helpers.js +29 -1
- package/build/index.js +1 -0
- package/build/types.d.ts +4 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ A range of built-in operators are available, from simple logic, arithmetic and s
|
|
|
43
43
|
- [BUILD_OBJECT](#build_object)
|
|
44
44
|
- [PASSTHRU](#passthru)
|
|
45
45
|
- [CUSTOM_FUNCTIONS](#custom_functions)
|
|
46
|
+
- [Alias Nodes](#alias-nodes)
|
|
46
47
|
- [More examples](#more-examples)
|
|
47
48
|
- [Development environment](#development-environment)
|
|
48
49
|
- [Tests](#tests)
|
|
@@ -1213,6 +1214,77 @@ e.g.
|
|
|
1213
1214
|
// => 198
|
|
1214
1215
|
```
|
|
1215
1216
|
|
|
1217
|
+
## Alias Nodes
|
|
1218
|
+
|
|
1219
|
+
If you have a node that is used more than once in a complex expression, it's possible to just evaluate the repeated node once, and refer to it throughout using an "alias" reference.
|
|
1220
|
+
|
|
1221
|
+
For example, if you have the expression:
|
|
1222
|
+
```js
|
|
1223
|
+
{
|
|
1224
|
+
operator: "?",
|
|
1225
|
+
condition: {
|
|
1226
|
+
operator: "!=",
|
|
1227
|
+
values: [
|
|
1228
|
+
{
|
|
1229
|
+
operator: "GET",
|
|
1230
|
+
children: [
|
|
1231
|
+
"https://restcountries.com/v3.1/name/zealand",
|
|
1232
|
+
[],
|
|
1233
|
+
"name.common"
|
|
1234
|
+
],
|
|
1235
|
+
type: "string"
|
|
1236
|
+
},
|
|
1237
|
+
null
|
|
1238
|
+
]
|
|
1239
|
+
},
|
|
1240
|
+
valueIfTrue: {
|
|
1241
|
+
operator: "GET",
|
|
1242
|
+
children: [
|
|
1243
|
+
"https://restcountries.com/v3.1/name/zealand",
|
|
1244
|
+
[],
|
|
1245
|
+
"name.common"
|
|
1246
|
+
],
|
|
1247
|
+
type: "string"
|
|
1248
|
+
},
|
|
1249
|
+
valueIfFalse: "Not New Zealand"
|
|
1250
|
+
}
|
|
1251
|
+
```
|
|
1252
|
+
|
|
1253
|
+
The `GET` operation is used twice -- once to compare it for non-equality with `null`, and once again to return its value if `true`. This is particularly wasteful since it is a network request.
|
|
1254
|
+
|
|
1255
|
+
We can create an alias for this whole node, resulting in this equivalent expression:
|
|
1256
|
+
```js
|
|
1257
|
+
{
|
|
1258
|
+
$getCountry: {
|
|
1259
|
+
operator: "GET",
|
|
1260
|
+
children: [
|
|
1261
|
+
"https://restcountries.com/v3.1/name/zealand",
|
|
1262
|
+
[],
|
|
1263
|
+
"name.common"
|
|
1264
|
+
],
|
|
1265
|
+
type: "string"
|
|
1266
|
+
},
|
|
1267
|
+
operator: "?",
|
|
1268
|
+
condition: {
|
|
1269
|
+
operator: "!=",
|
|
1270
|
+
values: [
|
|
1271
|
+
"$getCountry",
|
|
1272
|
+
null
|
|
1273
|
+
]
|
|
1274
|
+
},
|
|
1275
|
+
valueIfTrue: "$getCountry",
|
|
1276
|
+
valueIfFalse: "Not New Zealand"
|
|
1277
|
+
}
|
|
1278
|
+
```
|
|
1279
|
+
|
|
1280
|
+
Alias nodes are defined as part of the expression object, using `$` prefix to identify them as such, in this case `$getCountry`, which returns the result of the network request that was called twice in the previous expression.
|
|
1281
|
+
|
|
1282
|
+
Alias nodes are evaluated first, then the results are substituted in whenever they are referenced elsewhere.
|
|
1283
|
+
|
|
1284
|
+
Like all expression nodes, alias nodes can themselves contain complex expressions with their own alias nodes defined within. As long as the alias references are descendent nodes of the alias definition, they will be resolved.
|
|
1285
|
+
|
|
1286
|
+
(If an alias reference does not having a matching definition, the reference value will just be returned as a literal string, e.g. `"$getCountry"`)
|
|
1287
|
+
|
|
1216
1288
|
## More examples
|
|
1217
1289
|
|
|
1218
1290
|
More examples, included large, complex expressions can be found within the test suites in the [repository](https://github.com/CarlosNZ/fig-tree-evaluator).
|
package/build/evaluate.js
CHANGED
|
@@ -20,7 +20,7 @@ const evaluatorFunction = (input, config) => __awaiter(void 0, void 0, void 0, f
|
|
|
20
20
|
expression = yield (0, _operatorUtils_1.evaluateArray)(expression, config);
|
|
21
21
|
}
|
|
22
22
|
if (!(0, helpers_1.isOperatorNode)(expression))
|
|
23
|
-
return expression;
|
|
23
|
+
return (0, helpers_1.replaceAliasNodeValues)(expression, config);
|
|
24
24
|
const { fallback } = expression;
|
|
25
25
|
const returnErrorAsString = (_a = options === null || options === void 0 ? void 0 : options.returnErrorAsString) !== null && _a !== void 0 ? _a : false;
|
|
26
26
|
try {
|
|
@@ -29,6 +29,7 @@ const evaluatorFunction = (input, config) => __awaiter(void 0, void 0, void 0, f
|
|
|
29
29
|
return (0, helpers_1.fallbackOrError)(yield (0, exports.evaluatorFunction)(fallback, config), `Invalid operator: ${expression.operator}`, returnErrorAsString);
|
|
30
30
|
const { requiredProperties, propertyAliases, evaluate, parseChildren } = operators[operator];
|
|
31
31
|
expression = (0, helpers_1.mapPropertyAliases)(propertyAliases, expression);
|
|
32
|
+
config.resolvedAliasNodes = Object.assign(Object.assign({}, config.resolvedAliasNodes), (yield (0, helpers_1.evaluateNodeAliases)(expression, config)));
|
|
32
33
|
const validationError = (0, helpers_1.checkRequiredNodes)(requiredProperties, expression);
|
|
33
34
|
if (validationError)
|
|
34
35
|
return (0, helpers_1.fallbackOrError)(yield (0, exports.evaluatorFunction)(fallback, config), `Operator: ${operator}\n- ${validationError}`, returnErrorAsString);
|
package/build/helpers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OutputType, EvaluatorNode, CombinedOperatorNode, Operator, EvaluatorOutput, FigTreeOptions } from './types';
|
|
1
|
+
import { OutputType, EvaluatorNode, CombinedOperatorNode, Operator, EvaluatorOutput, FigTreeOptions, OperatorNodeUnion, FigTreeConfig } from './types';
|
|
2
2
|
export declare const parseIfJson: (input: EvaluatorNode) => any;
|
|
3
3
|
export declare const isOperatorNode: (input: EvaluatorNode) => boolean;
|
|
4
4
|
export declare const getOperatorName: (operator: string, operatorAliases: {
|
|
@@ -9,6 +9,10 @@ export declare const fallbackOrError: (fallback: any, errorMessage: string, retu
|
|
|
9
9
|
export declare const mapPropertyAliases: (propertyAliases: {
|
|
10
10
|
[key: string]: string;
|
|
11
11
|
}, expression: CombinedOperatorNode) => CombinedOperatorNode;
|
|
12
|
+
export declare const evaluateNodeAliases: (expression: OperatorNodeUnion, config: FigTreeConfig) => Promise<{
|
|
13
|
+
[x: string]: EvaluatorOutput;
|
|
14
|
+
}>;
|
|
15
|
+
export declare const replaceAliasNodeValues: (value: EvaluatorOutput, { resolvedAliasNodes }: FigTreeConfig) => EvaluatorOutput;
|
|
12
16
|
export declare const checkRequiredNodes: (requiredProps: readonly string[], expression: CombinedOperatorNode) => string | false;
|
|
13
17
|
export declare const mergeOptions: (origOptions: FigTreeOptions, newOptions: FigTreeOptions) => FigTreeOptions;
|
|
14
18
|
export declare const convertOutputMethods: {
|
package/build/helpers.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.errorMessage = exports.convertOutputMethods = exports.mergeOptions = exports.checkRequiredNodes = exports.mapPropertyAliases = exports.fallbackOrError = exports.truncateString = exports.getOperatorName = exports.isOperatorNode = exports.parseIfJson = void 0;
|
|
12
|
+
exports.errorMessage = exports.convertOutputMethods = exports.mergeOptions = exports.checkRequiredNodes = exports.replaceAliasNodeValues = exports.evaluateNodeAliases = exports.mapPropertyAliases = exports.fallbackOrError = exports.truncateString = exports.getOperatorName = exports.isOperatorNode = exports.parseIfJson = void 0;
|
|
4
13
|
const change_case_1 = require("change-case");
|
|
14
|
+
const evaluate_1 = require("./evaluate");
|
|
15
|
+
const _operatorUtils_1 = require("./operators/_operatorUtils");
|
|
5
16
|
const parseIfJson = (input) => {
|
|
6
17
|
if (typeof input !== 'string')
|
|
7
18
|
return input;
|
|
@@ -40,6 +51,23 @@ const mapObjectKeys = (inputObj, mapFunction) => {
|
|
|
40
51
|
const mappedKeys = keyVals.map(([key, value]) => [mapFunction(key), value]);
|
|
41
52
|
return Object.fromEntries(mappedKeys);
|
|
42
53
|
};
|
|
54
|
+
const isAliasString = (value) => /^\$.+/.test(value);
|
|
55
|
+
const evaluateNodeAliases = (expression, config) => __awaiter(void 0, void 0, void 0, function* () {
|
|
56
|
+
const aliasKeys = Object.keys(expression).filter(isAliasString);
|
|
57
|
+
if (aliasKeys.length === 0)
|
|
58
|
+
return expression;
|
|
59
|
+
const evaluations = [];
|
|
60
|
+
aliasKeys.forEach((alias) => evaluations.push((0, evaluate_1.evaluatorFunction)(expression[alias], config)));
|
|
61
|
+
return (0, _operatorUtils_1.zipArraysToObject)(aliasKeys, yield Promise.all(evaluations));
|
|
62
|
+
});
|
|
63
|
+
exports.evaluateNodeAliases = evaluateNodeAliases;
|
|
64
|
+
const replaceAliasNodeValues = (value, { resolvedAliasNodes }) => {
|
|
65
|
+
var _a;
|
|
66
|
+
if (typeof value !== 'string' || !isAliasString(value))
|
|
67
|
+
return value;
|
|
68
|
+
return (_a = resolvedAliasNodes === null || resolvedAliasNodes === void 0 ? void 0 : resolvedAliasNodes[value]) !== null && _a !== void 0 ? _a : value;
|
|
69
|
+
};
|
|
70
|
+
exports.replaceAliasNodeValues = replaceAliasNodeValues;
|
|
43
71
|
const checkRequiredNodes = (requiredProps, expression) => {
|
|
44
72
|
const missingProps = requiredProps.filter((prop) => !(prop in expression));
|
|
45
73
|
if (missingProps.length === 0)
|
package/build/index.js
CHANGED
package/build/types.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export interface FigTreeConfig {
|
|
|
28
28
|
[key: string]: Operator;
|
|
29
29
|
};
|
|
30
30
|
typeChecker: (...input: TypeCheckInput[]) => void;
|
|
31
|
+
resolvedAliasNodes: {
|
|
32
|
+
[key: string]: EvaluatorOutput;
|
|
33
|
+
};
|
|
31
34
|
}
|
|
32
35
|
export declare type OutputType = 'string' | 'number' | 'boolean' | 'bool' | 'array';
|
|
33
36
|
export interface BaseOperatorNode {
|
|
@@ -35,6 +38,7 @@ export interface BaseOperatorNode {
|
|
|
35
38
|
outputType?: OutputType;
|
|
36
39
|
children?: Array<EvaluatorNode>;
|
|
37
40
|
fallback?: any;
|
|
41
|
+
[key: string]: EvaluatorNode;
|
|
38
42
|
}
|
|
39
43
|
export declare type CombinedOperatorNode = BaseOperatorNode & BasicExtendedNode & SubtractionNode & DivisionNode & ComparatorNode & ConditionalNode & RegexNode & StringSubNode & SplitNode & ObjPropNode & APINode & PGNode & GraphQLNode & BuildObjectNode & FunctionNode & PassThruNode;
|
|
40
44
|
export declare type OperatorNodeUnion = BasicExtendedNode | SubtractionNode | DivisionNode | ComparatorNode | ConditionalNode | RegexNode | StringSubNode | SplitNode | ObjPropNode | PGNode | GraphQLNode | APINode | BuildObjectNode | FunctionNode | PassThruNode;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fig-tree-evaluator",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Module to evaluate JSON-structured expression trees",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"build/**/*"
|
|
9
9
|
],
|
|
10
|
-
"repository": "git+https://github.com/CarlosNZ/fig-tree.git",
|
|
10
|
+
"repository": "git+https://github.com/CarlosNZ/fig-tree-evaluator.git",
|
|
11
11
|
"author": "Carl Smith <5456533+CarlosNZ@users.noreply.github.com>",
|
|
12
12
|
"license": "GPL-3.0",
|
|
13
13
|
"devDependencies": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"object-property-extractor": "^1.0.3"
|
|
40
40
|
},
|
|
41
41
|
"bugs": {
|
|
42
|
-
"url": "https://github.com/CarlosNZ/fig-tree/issues"
|
|
42
|
+
"url": "https://github.com/CarlosNZ/fig-tree-evaluator/issues"
|
|
43
43
|
},
|
|
44
|
-
"homepage": "https://github.com/CarlosNZ/fig-tree#readme"
|
|
44
|
+
"homepage": "https://github.com/CarlosNZ/fig-tree-evaluator#readme"
|
|
45
45
|
}
|