fig-tree-evaluator 2.3.2 → 2.4.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 CHANGED
@@ -4,9 +4,11 @@
4
4
 
5
5
  **FigTree Evaluator** is a module to evaluate JSON-structured expression trees.
6
6
 
7
- A typical use case would be for evaluating **configuration** files, where you need to store dynamic values or arbitrary logic without allowing users to inject executable code (perhaps in a .json file, say). For example, a [form-builder app](https://github.com/openmsupply/conforma-web-app) might need to allow complex conditional logic for form element visibility based on previous responses, or for validation beyond what is available in standard validation libraries.
7
+ A typical use case would be for evaluating **configuration** files, where you need to store dynamic values or arbitrary logic without allowing users to inject executable code (perhaps in a .json file, say). Use cases could include:
8
8
 
9
- Another example would be to configure a [decision tree](https://en.wikipedia.org/wiki/Decision_tree) to implement branching logic.
9
+ - a [form-builder app](https://github.com/openmsupply/conforma-web-app) might need to allow complex conditional logic for form element visibility based on previous responses, or for validation beyond what is available in standard validation libraries.
10
+ - configure a [decision tree](https://en.wikipedia.org/wiki/Decision_tree) to implement branching logic. (See examples in `20_match.test.ts`)
11
+ - extend [JSON Forms](https://jsonforms.io) with more complex logic and dynamic lookups: https://github.com/CarlosNZ/jsonforms-with-figtree-demo
10
12
 
11
13
  A range of built-in operators are available, from simple logic, arithmetic and string manipulation, to data fetching from local sources or remote APIs.
12
14
 
@@ -47,6 +49,7 @@ A range of built-in operators are available, from simple logic, arithmetic and s
47
49
  - [PASSTHRU](#passthru)
48
50
  - [CUSTOM\_FUNCTIONS](#custom_functions)
49
51
  - [Alias Nodes](#alias-nodes)
52
+ - [Fragments](#fragments)
50
53
  - [Caching (Memoization)](#caching-memoization)
51
54
  - [More examples](#more-examples)
52
55
  - [Development environment](#development-environment)
@@ -138,6 +141,7 @@ The `options` parameter is an object with the following available properties (al
138
141
 
139
142
  - `data` -- a single object containing any *objects* in your application that may wish to be inspected using the [objectProperties](#object_properties) operator. (See [playground](LINK) for examples). If these objects are regularly changing, you'll probably want to pass them into each separate evaluation rather than with the initial constructor.
140
143
  - `functions` -- a single object containing any *custom functions* available for use by the [customFunctions](#custom_functions) operator.
144
+ - `fragments` -- commonly-used expressions (with optional parameters) that can be re-used in any other expression. See [Fragments](#fragments)
141
145
  - `pgConnection` -- if you wish to make calls to a Postgres database using the [`pgSQL` operator](#pg_sql), pass a [node-postres](https://node-postgres.com/) connection object here.
142
146
  - `graphQLConnection` -- a GraphQL connection object, if using the [`graphQL` operator](#graphql). See operator details below.
143
147
  - `baseEndpoint` -- A general http headers object that will be passed to *all* http-based operators (`GET`, `POST`, `GraphQL`). Useful if all http queries are to a common server -- then each individual node will only require a relative url. See specific operator for more details.
@@ -165,7 +169,7 @@ import { evaluateExpression } from 'fig-tree-evaluator'
165
169
 
166
170
  evaluateExpression(expression, [options]).then((result) => {
167
171
  // Do something with result
168
- }
172
+ })
169
173
  ```
170
174
 
171
175
  ## Operator nodes
@@ -202,13 +206,13 @@ For example, the following two representations are equivalent `conditional` oper
202
206
  }
203
207
  ```
204
208
 
205
- Most of the time named properties is preferable; however there are situations where the "children" array might be easier to deal with, or to generate from child nodes.
209
+ Most of the time named properties would be preferable; however there are occasional cases where the "children" array might be easier to deal with, or to build up from child nodes.
206
210
 
207
211
  ### Other common properties:
208
212
 
209
- In each operator node, as well as the operator-specific properties, the following two optional properties can be provided:
213
+ In each operator node, as well as the operator-specific properties, the following three optional properties can be provided:
210
214
 
211
- - `fallback`: if the operation throws an error, the `fallback` value will be returned instead. The `fallback` property can be provided at any level of the expression tree and bubbled up from where errors are caught to parent nodes.
215
+ - `fallback`: if the operation throws an error, the `fallback` value will be returned instead. The `fallback` property can be provided at any level of the expression tree and bubbled up from where errors are caught to parent nodes. *Fallbacks are strongly recommended if there is any chance of an error (e.g. a network "GET" request that doesn't yet have its parameters defined).*
212
216
  - `outputType` (or `type`): will convert the result of the current node to the specified `outputType`. Valid values are `string`, `number`, `boolean` (or `bool`), and `array`. You can experiment in the [demo app](https://carlosnz.github.io/fig-tree-evaluator/) to see the outcome of applying different `outputType` values to various results.
213
217
  - `useCache`: Overrides the global `useCache` value (from [options](#available-options)) for this node only. See [Caching/Memoization](#caching-memoization) below for more info.
214
218
 
@@ -431,7 +435,7 @@ e.g.
431
435
  ----
432
436
  ### MULTIPLY
433
437
 
434
- *Multiplcation*
438
+ *Multiplication*
435
439
 
436
440
  Aliases: `*`, `x`, `multiply`, `times`
437
441
 
@@ -634,6 +638,8 @@ e.g.
634
638
 
635
639
  `children` array: `[condition, valueIfTrue, valueIfFalse]`
636
640
 
641
+ **Note**: *For more complex branching logic, the ["match" operator](#match) can be used (it matches more than just a boolean condition)*
642
+
637
643
  ----
638
644
  ### REGEX
639
645
 
@@ -719,8 +725,10 @@ Here is the result of various values of `expression:`
719
725
  Notice the last example pulls multiple values out of an array of objects, in this case the "name". This is essentially a shorthand for:
720
726
 
721
727
  ```js
722
- const result = { operator: 'getProperty', path: 'user.enemies' }
723
- result.map((e) => e.name)
728
+ exp.evaluate(
729
+ { operator: 'getProperty', path: 'user.enemies' },
730
+ { data: { user } }
731
+ ).map((e) => e.name)
724
732
  ```
725
733
 
726
734
  The "objectProperties" operator uses [`object-property-extractor`](https://www.npmjs.com/package/object-property-extractor) internally, so please see the documentation of that package for more information.
@@ -840,7 +848,7 @@ Aliases: `split`, `arraySplit`
840
848
  - `excludeTrailing` (or `removeTrailing`, `excludeTrailingDelimiter`): (boolean, default `true`) -- if `false`, if the input string ends with the delimiter, the last member of the output array will be an empty string.
841
849
  i.e. `this, that, another,` (delimiter `","`) => `["this", "that", "another", ""]`
842
850
 
843
- The last two parameters (`timeWhiteSpace` and `excludeTrailing`) should rarely be needed.
851
+ The last two parameters (`timeWhiteSpace` and `excludeTrailing`) should *rarely* be needed to be changed from their default values.
844
852
 
845
853
  e.g.
846
854
  ```js
@@ -978,7 +986,7 @@ The required connection object is:
978
986
  }
979
987
  ```
980
988
 
981
- The following example expression uses the GraphQL connection: `{endpoint: 'https://countries.trevorblades.com/'}`
989
+ The following example expression uses the GraphQL connection (specified in constructor options): `{endpoint: 'https://countries.trevorblades.com/'}`
982
990
  ```js
983
991
  {
984
992
  operator: 'graphQL',
@@ -1121,7 +1129,7 @@ e.g.
1121
1129
 
1122
1130
  `children` array: `[key1, value1, key2, value2, ...]`
1123
1131
 
1124
- This is one of the few operators where the `children` array might actually be simpler to define than the `properties` property, depending on how deep the array elements are themselves operator nodes.
1132
+ This is one of the few cases where the `children` array might actually be simpler to define than the `properties` property, depending on how deep the array elements are themselves operator nodes.
1125
1133
 
1126
1134
  e.g.
1127
1135
  ```js
@@ -1221,7 +1229,6 @@ This expression could also be written as (with branch/case keys at the root leve
1221
1229
 
1222
1230
  The pairs of `key`/`value`s are constructed into the `branches` object, the same way the objects are built using `children` in the ["buildObject"](#build_object) operator.
1223
1231
 
1224
-
1225
1232
  For an example of a complex decision tree implementation, which includes [aliases](#alias-nodes), [fallbacks](#other-common-properties) and a range of operators, see the "match" test case file (`20_match.test.ts`).
1226
1233
 
1227
1234
  ----
@@ -1298,7 +1305,7 @@ Here is the result of various expressions:
1298
1305
  },
1299
1306
  ],
1300
1307
  }
1301
- // => "THE CURRENT YEAR IS: 2022"
1308
+ // => "THE CURRENT YEAR IS: 2023"
1302
1309
  ```
1303
1310
 
1304
1311
  `children` array: `[functionPath, ...args]`
@@ -1383,9 +1390,71 @@ Like all expression nodes, alias nodes can themselves contain complex expression
1383
1390
 
1384
1391
  (If an alias reference does not having a matching definition, the reference value will just be returned as a literal string, e.g. `"$getCountry"`)
1385
1392
 
1393
+ ## Fragments
1394
+
1395
+ You may find that the expressions you are building for your configuration files often use very similar sub-expressions (but perhaps with only some input values that differ). For example, your expressions might be regularly looking up a "countries" database and fetching the capital city, such as:
1396
+ ```js
1397
+ {
1398
+ operator: 'GET',
1399
+ url: {
1400
+ operator: 'stringSubstitution',
1401
+ string: 'https://restcountries.com/v3.1/name/%1',
1402
+ replacements: ['New Zealand'],
1403
+ },
1404
+ returnProperty: '[0].capital',
1405
+ outputType: 'string',
1406
+ }
1407
+ // => "Wellington"
1408
+ ```
1409
+
1410
+ In this case, you can pre-define the main part of the expression as part of an expression "**fragment**" in the evaluator [options](#available-options).
1411
+
1412
+ The idea is that you can "hard-code" some common expressions into your app, so that external configuration expressions can be simpler when using these common elements.
1413
+
1414
+ The syntax is similar to [Alias Nodes](#alias-nodes), in that any string values prefixed with `$` will be treated as "parameters" that will be replaced during evaluation. In the above example, you would define the fragment when instantiating the evaluator like so:
1415
+
1416
+ ```js
1417
+ const exp = new FigTreeEvaluator({
1418
+ fragments: {
1419
+ getCapital: {
1420
+ operator: 'GET',
1421
+ url: {
1422
+ operator: 'stringSubstitution',
1423
+ string: 'https://restcountries.com/v3.1/name/%1',
1424
+ replacements: [ "$country" ],
1425
+ },
1426
+ returnProperty: '[0].capital',
1427
+ outputType: 'string',
1428
+ },
1429
+ },
1430
+ })
1431
+ ```
1432
+
1433
+ Then any subsequent expressions can use this fragment by specifying a special "Fragment Node", which contains the `fragment` and (optionally) `parameters` fields:
1434
+ ```js
1435
+ {
1436
+ fragment: "getCapital",
1437
+ parameters: {
1438
+ $country: { operator: "getData", property: "path.to.country.name" }
1439
+ }
1440
+ }
1441
+ ```
1442
+
1443
+ Like the `branches` field in the ["Match" operator](#match), the properties of the `parameters` field can be specified at the root level as well -- it just depends on whichever is most appropriate for your use case. So the following is equivalent to the previous fragment node:
1444
+ ```js
1445
+ {
1446
+ fragment: "getCapital",
1447
+ $country: { operator: "getData", property: "path.to.country.name" }
1448
+ }
1449
+ ```
1450
+
1451
+ See `22_fragments.test.ts` for more complex examples.
1452
+
1453
+ Unlike Alias Nodes, which are evaluated *once* and then the result re-used whenever the alias is referenced, Fragments are evaluated every time, as the input parameters may differ.
1454
+
1386
1455
  ## Caching (Memoization)
1387
1456
 
1388
- FigTree Evaluator has basic [memoization](https://en.wikipedia.org/wiki/Memoization) functionality for certain nodes to speed up re-evaluation when the input parameters haven't changed. There is a single cache store per FigTree instance which persists for the lifetime of the instance. By default, it remembers the last 50 results, but this can be modified using the `maxCacheSize` option.
1457
+ FigTree Evaluator has basic [memoization](https://en.wikipedia.org/wiki/Memoization) functionality for certain nodes to speed up re-evaluation when the input parameters haven't changed. There is a single cache store per FigTree instance which persists for the lifetime of the instance. By default, it remembers the last 50 results, but this can be modified using the `maxCacheSize` option.
1389
1458
 
1390
1459
  Currently, caching is only implemented for the following operators, since they perform requests to external resources, which are inherently slow:
1391
1460
 
@@ -1431,6 +1500,9 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
1431
1500
 
1432
1501
  ## Changelog
1433
1502
 
1503
+ *Trivial upgrades (e.g. documentation only) not included*
1504
+
1505
+ - **v2.4.0**: Implement [Fragments](#fragments) (#74)
1434
1506
  - **v2.3.2**: Bug fix: alias nodes not working with `evaluateFullObject` (#72)
1435
1507
  - **v2.3.0**: Implement [caching/memoization](#caching-memoization) (#68)
1436
1508
  - **v2.2.3**: Change option `objects` name to `data` (but keep backward compatibility) (#66)
@@ -1455,6 +1527,5 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
1455
1527
  - more well-organised codebase
1456
1528
  - **v1.x.x**: created specifically for [Conforma](https://github.com/openmsupply/conforma-server/wiki/Query-Syntax) application manager by [mSupplyFoundation](https://github.com/openmsupply). v2 is a complete re-write with numerous improvements, but should be 99% backwards compatible.
1457
1529
 
1458
-
1459
1530
  ## Credit
1460
1531
  Icon: Tree by ka reemov from <a href="https://thenounproject.com/icon/tree-2665898/" target="_blank" title="Tree Icon">Noun Project</a>
package/build/evaluate.js CHANGED
@@ -51,102 +51,117 @@ exports.evaluatorFunction = void 0;
51
51
  var _operatorUtils_1 = require("./operators/_operatorUtils");
52
52
  var helpers_1 = require("./helpers");
53
53
  var evaluatorFunction = function (input, config) { return __awaiter(void 0, void 0, void 0, function () {
54
- var options, operators, operatorAliases, expression, _a, fallback, returnErrorAsString, operator, _b, _c, requiredProperties, propertyAliases, evaluate, parseChildren, _d, _e, validationError, _f, _g, _h, result, err_1, _j, outputType, evaluatedOutputType, _k, err_2, _l;
55
- var _m, _o;
56
- return __generator(this, function (_p) {
57
- switch (_p.label) {
54
+ var options, operators, operatorAliases, expression, _a, fallback, returnErrorAsString, _b, fragment, parameters, fragmentReplacement, _c, operator, _d, _e, requiredProperties, propertyAliases, evaluate, parseChildren, _f, _g, validationError, _h, _j, _k, result, err_1, _l, outputType, evaluatedOutputType, _m, err_2, _o;
55
+ var _p, _q, _r;
56
+ return __generator(this, function (_s) {
57
+ switch (_s.label) {
58
58
  case 0:
59
59
  options = config.options, operators = config.operators, operatorAliases = config.operatorAliases;
60
60
  expression = (options === null || options === void 0 ? void 0 : options.allowJSONStringInput) ? (0, helpers_1.parseIfJson)(input) : input;
61
61
  if (!Array.isArray(expression)) return [3, 2];
62
62
  return [4, (0, _operatorUtils_1.evaluateArray)(expression, config)];
63
63
  case 1:
64
- expression = _p.sent();
65
- _p.label = 2;
64
+ expression = _s.sent();
65
+ _s.label = 2;
66
66
  case 2:
67
- if (!(options.evaluateFullObject && !(0, helpers_1.isOperatorNode)(expression))) return [3, 4];
67
+ if (!(options.evaluateFullObject && !(0, helpers_1.isOperatorNode)(expression) && !(0, helpers_1.isFragmentNode)(expression))) return [3, 4];
68
68
  _a = helpers_1.replaceAliasNodeValues;
69
69
  return [4, (0, helpers_1.evaluateObject)(expression, config)];
70
- case 3: return [2, _a.apply(void 0, [_p.sent(), config])];
70
+ case 3: return [2, _a.apply(void 0, [_s.sent(), config])];
71
71
  case 4:
72
- if (!(0, helpers_1.isOperatorNode)(expression))
72
+ if (!(0, helpers_1.isOperatorNode)(expression) && !(0, helpers_1.isFragmentNode)(expression))
73
73
  return [2, (0, helpers_1.replaceAliasNodeValues)(expression, config)];
74
74
  fallback = expression.fallback;
75
- returnErrorAsString = (_m = options === null || options === void 0 ? void 0 : options.returnErrorAsString) !== null && _m !== void 0 ? _m : false;
76
- _p.label = 5;
75
+ returnErrorAsString = (_p = options === null || options === void 0 ? void 0 : options.returnErrorAsString) !== null && _p !== void 0 ? _p : false;
76
+ if (!(0, helpers_1.isFragmentNode)(expression)) return [3, 8];
77
+ return [4, (0, _operatorUtils_1.evaluateArray)([expression.fragment, expression.parameters], config)];
77
78
  case 5:
78
- _p.trys.push([5, 26, , 28]);
79
- operator = (0, helpers_1.getOperatorName)(expression.operator, operatorAliases);
80
- if (!!operator) return [3, 7];
81
- _b = helpers_1.fallbackOrError;
79
+ _b = (_s.sent()), fragment = _b[0], parameters = _b[1];
80
+ fragmentReplacement = (_q = options === null || options === void 0 ? void 0 : options.fragments) === null || _q === void 0 ? void 0 : _q[fragment];
81
+ if (!!fragmentReplacement) return [3, 7];
82
+ _c = helpers_1.fallbackOrError;
82
83
  return [4, (0, exports.evaluatorFunction)(fallback, config)];
83
- case 6: return [2, _b.apply(void 0, [_p.sent(), "Invalid operator: ".concat(expression.operator), returnErrorAsString])];
84
+ case 6: return [2, _c.apply(void 0, [_s.sent(), "Fragment not defined: ".concat(fragment), returnErrorAsString])];
84
85
  case 7:
85
- _c = operators[operator], requiredProperties = _c.requiredProperties, propertyAliases = _c.propertyAliases, evaluate = _c.evaluate, parseChildren = _c.parseChildren;
86
- expression = (0, helpers_1.mapPropertyAliases)(propertyAliases, expression);
87
- _d = config;
88
- _e = [__assign({}, config.resolvedAliasNodes)];
89
- return [4, (0, helpers_1.evaluateNodeAliases)(expression, config)];
86
+ if (!(0, helpers_1.isOperatorNode)(fragmentReplacement))
87
+ return [2, fragmentReplacement];
88
+ expression = __assign(__assign(__assign({}, expression), fragmentReplacement), parameters);
89
+ delete expression.fragment;
90
+ delete expression.parameters;
91
+ _s.label = 8;
90
92
  case 8:
91
- _d.resolvedAliasNodes = __assign.apply(void 0, _e.concat([(_p.sent())]));
92
- validationError = (0, helpers_1.checkRequiredNodes)(requiredProperties, expression);
93
- if (!validationError) return [3, 10];
94
- _f = helpers_1.fallbackOrError;
93
+ _s.trys.push([8, 29, , 31]);
94
+ operator = (0, helpers_1.getOperatorName)(expression.operator, operatorAliases);
95
+ if (!!operator) return [3, 10];
96
+ _d = helpers_1.fallbackOrError;
95
97
  return [4, (0, exports.evaluatorFunction)(fallback, config)];
96
- case 9: return [2, _f.apply(void 0, [_p.sent(), "Operator: ".concat(operator, "\n- ").concat(validationError), returnErrorAsString])];
98
+ case 9: return [2, _d.apply(void 0, [_s.sent(), "Invalid operator: ".concat(expression.operator), returnErrorAsString])];
97
99
  case 10:
98
- if (!('children' in expression)) return [3, 16];
99
- if (!!Array.isArray(expression.children)) return [3, 12];
100
- _g = expression;
101
- return [4, (0, exports.evaluatorFunction)(expression.children, config)];
100
+ _e = operators[operator], requiredProperties = _e.requiredProperties, propertyAliases = _e.propertyAliases, evaluate = _e.evaluate, parseChildren = _e.parseChildren;
101
+ expression = (0, helpers_1.mapPropertyAliases)(propertyAliases, expression);
102
+ _f = config;
103
+ _g = [__assign({}, config.resolvedAliasNodes)];
104
+ return [4, (0, helpers_1.evaluateNodeAliases)(expression, config)];
102
105
  case 11:
103
- _g.children = _p.sent();
104
- _p.label = 12;
105
- case 12:
106
- if (!!Array.isArray(expression.children)) return [3, 14];
106
+ _f.resolvedAliasNodes = __assign.apply(void 0, _g.concat([(_s.sent())]));
107
+ validationError = (0, helpers_1.checkRequiredNodes)(requiredProperties, expression);
108
+ if (!validationError) return [3, 13];
107
109
  _h = helpers_1.fallbackOrError;
108
110
  return [4, (0, exports.evaluatorFunction)(fallback, config)];
109
- case 13: return [2, _h.apply(void 0, [_p.sent(), "Operator: ".concat(operator, "\n- Property \"children\" is not of type: array"), returnErrorAsString])];
110
- case 14: return [4, parseChildren(expression, config)];
111
+ case 12: return [2, _h.apply(void 0, [_s.sent(), "Operator: ".concat(operator, "\n- ").concat(validationError), returnErrorAsString])];
112
+ case 13:
113
+ if (!('children' in expression)) return [3, 19];
114
+ if (!!Array.isArray(expression.children)) return [3, 15];
115
+ _j = expression;
116
+ return [4, (0, exports.evaluatorFunction)(expression.children, config)];
117
+ case 14:
118
+ _j.children = _s.sent();
119
+ _s.label = 15;
111
120
  case 15:
112
- expression = _p.sent();
113
- _p.label = 16;
114
- case 16:
115
- result = void 0;
116
- _p.label = 17;
117
- case 17:
118
- _p.trys.push([17, 19, , 21]);
119
- return [4, evaluate(expression, config)];
121
+ if (!!Array.isArray(expression.children)) return [3, 17];
122
+ _k = helpers_1.fallbackOrError;
123
+ return [4, (0, exports.evaluatorFunction)(fallback, config)];
124
+ case 16: return [2, _k.apply(void 0, [_s.sent(), "Operator: ".concat(operator, "\n- Property \"children\" is not of type: array"), returnErrorAsString])];
125
+ case 17: return [4, parseChildren(expression, config)];
120
126
  case 18:
121
- result = _p.sent();
122
- return [3, 21];
127
+ expression = _s.sent();
128
+ _s.label = 19;
123
129
  case 19:
124
- err_1 = _p.sent();
125
- _j = helpers_1.fallbackOrError;
126
- return [4, (0, exports.evaluatorFunction)(fallback, config)];
130
+ result = void 0;
131
+ _s.label = 20;
127
132
  case 20:
128
- result = _j.apply(void 0, [_p.sent(), "Operator: ".concat(operator, "\n").concat((0, helpers_1.errorMessage)(err_1)), returnErrorAsString]);
129
- return [3, 21];
133
+ _s.trys.push([20, 22, , 24]);
134
+ return [4, evaluate(expression, config)];
130
135
  case 21:
131
- outputType = (_o = expression === null || expression === void 0 ? void 0 : expression.outputType) !== null && _o !== void 0 ? _o : expression === null || expression === void 0 ? void 0 : expression.type;
136
+ result = _s.sent();
137
+ return [3, 24];
138
+ case 22:
139
+ err_1 = _s.sent();
140
+ _l = helpers_1.fallbackOrError;
141
+ return [4, (0, exports.evaluatorFunction)(fallback, config)];
142
+ case 23:
143
+ result = _l.apply(void 0, [_s.sent(), "Operator: ".concat(operator, "\n").concat((0, helpers_1.errorMessage)(err_1)), returnErrorAsString]);
144
+ return [3, 24];
145
+ case 24:
146
+ outputType = (_r = expression === null || expression === void 0 ? void 0 : expression.outputType) !== null && _r !== void 0 ? _r : expression === null || expression === void 0 ? void 0 : expression.type;
132
147
  if (!outputType)
133
148
  return [2, result];
134
149
  return [4, (0, exports.evaluatorFunction)(outputType, config)];
135
- case 22:
136
- evaluatedOutputType = (_p.sent());
137
- if (!!(evaluatedOutputType in helpers_1.convertOutputMethods)) return [3, 24];
138
- _k = helpers_1.fallbackOrError;
150
+ case 25:
151
+ evaluatedOutputType = (_s.sent());
152
+ if (!!(evaluatedOutputType in helpers_1.convertOutputMethods)) return [3, 27];
153
+ _m = helpers_1.fallbackOrError;
139
154
  return [4, (0, exports.evaluatorFunction)(fallback, config)];
140
- case 23: return [2, _k.apply(void 0, [_p.sent(), "Operator: ".concat(operator, "\n- Invalid output type: ").concat(evaluatedOutputType), returnErrorAsString])];
141
- case 24: return [2, helpers_1.convertOutputMethods[evaluatedOutputType](result)];
142
- case 25: return [3, 28];
143
- case 26:
144
- err_2 = _p.sent();
145
- _l = helpers_1.fallbackOrError;
155
+ case 26: return [2, _m.apply(void 0, [_s.sent(), "Operator: ".concat(operator, "\n- Invalid output type: ").concat(evaluatedOutputType), returnErrorAsString])];
156
+ case 27: return [2, helpers_1.convertOutputMethods[evaluatedOutputType](result)];
157
+ case 28: return [3, 31];
158
+ case 29:
159
+ err_2 = _s.sent();
160
+ _o = helpers_1.fallbackOrError;
146
161
  return [4, (0, exports.evaluatorFunction)(fallback, config)];
147
- case 27: return [2, _l.apply(void 0, [_p.sent(), (0, helpers_1.errorMessage)(err_2),
162
+ case 30: return [2, _o.apply(void 0, [_s.sent(), (0, helpers_1.errorMessage)(err_2),
148
163
  returnErrorAsString])];
149
- case 28: return [2];
164
+ case 31: return [2];
150
165
  }
151
166
  });
152
167
  }); };
@@ -1,6 +1,7 @@
1
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
+ export declare const isFragmentNode: (input: EvaluatorNode) => boolean;
4
5
  export declare const getOperatorName: (operator: string, operatorAliases: {
5
6
  [key: string]: "AND" | "OR" | "EQUAL" | "NOT_EQUAL" | "PLUS" | "SUBTRACT" | "MULTIPLY" | "DIVIDE" | "GREATER_THAN" | "LESS_THAN" | "CONDITIONAL" | "REGEX" | "OBJECT_PROPERTIES" | "STRING_SUBSTITUTION" | "SPLIT" | "COUNT" | "GET" | "POST" | "PG_SQL" | "GRAPHQL" | "BUILD_OBJECT" | "MATCH" | "CUSTOM_FUNCTIONS" | "PASSTHRU";
6
7
  }) => "AND" | "OR" | "EQUAL" | "NOT_EQUAL" | "PLUS" | "SUBTRACT" | "MULTIPLY" | "DIVIDE" | "GREATER_THAN" | "LESS_THAN" | "CONDITIONAL" | "REGEX" | "OBJECT_PROPERTIES" | "STRING_SUBSTITUTION" | "SPLIT" | "COUNT" | "GET" | "POST" | "PG_SQL" | "GRAPHQL" | "BUILD_OBJECT" | "MATCH" | "CUSTOM_FUNCTIONS" | "PASSTHRU";
package/build/helpers.js CHANGED
@@ -47,7 +47,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
47
47
  }
48
48
  };
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
- exports.evaluateObject = 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;
50
+ exports.evaluateObject = exports.errorMessage = exports.convertOutputMethods = exports.mergeOptions = exports.checkRequiredNodes = exports.replaceAliasNodeValues = exports.evaluateNodeAliases = exports.mapPropertyAliases = exports.fallbackOrError = exports.truncateString = exports.getOperatorName = exports.isFragmentNode = exports.isOperatorNode = exports.parseIfJson = void 0;
51
51
  var change_case_1 = require("change-case");
52
52
  var evaluate_1 = require("./evaluate");
53
53
  var _operatorUtils_1 = require("./operators/_operatorUtils");
@@ -67,6 +67,10 @@ var isOperatorNode = function (input) {
67
67
  return input instanceof Object && 'operator' in input;
68
68
  };
69
69
  exports.isOperatorNode = isOperatorNode;
70
+ var isFragmentNode = function (input) {
71
+ return input instanceof Object && 'fragment' in input;
72
+ };
73
+ exports.isFragmentNode = isFragmentNode;
70
74
  var standardiseOperatorName = function (name) {
71
75
  var camelCaseName = (0, change_case_1.camelCase)(name);
72
76
  return camelCaseName ? camelCaseName : name;
@@ -146,6 +150,8 @@ var mergeOptions = function (origOptions, newOptions) {
146
150
  combinedOptions.data = __assign(__assign({}, origOptions.data), newOptions.data);
147
151
  if (origOptions.functions || newOptions.functions)
148
152
  combinedOptions.functions = __assign(__assign({}, origOptions.functions), newOptions.functions);
153
+ if (origOptions.fragments || newOptions.fragments)
154
+ combinedOptions.fragments = __assign(__assign({}, origOptions.fragments), newOptions.fragments);
149
155
  if (origOptions.headers || newOptions.headers)
150
156
  combinedOptions.headers = __assign(__assign({}, origOptions.headers), newOptions.headers);
151
157
  return combinedOptions;
package/build/index.js CHANGED
@@ -1,15 +1,4 @@
1
1
  "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
3
  if (k2 === undefined) k2 = k;
15
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -127,7 +116,7 @@ var FigTreeEvaluator = (function () {
127
116
  return this.options;
128
117
  };
129
118
  FigTreeEvaluator.prototype.updateOptions = function (options) {
130
- this.options = __assign(__assign({}, this.options), standardiseOptionNames(options));
119
+ this.options = (0, helpers_1.mergeOptions)(this.options, standardiseOptionNames(options));
131
120
  };
132
121
  return FigTreeEvaluator;
133
122
  }());
package/build/types.d.ts CHANGED
@@ -12,6 +12,9 @@ export interface FigTreeOptions {
12
12
  functions?: {
13
13
  [key: string]: Function;
14
14
  };
15
+ fragments?: {
16
+ [key: string]: EvaluatorNode;
17
+ };
15
18
  pgConnection?: PGConnection;
16
19
  graphQLConnection?: GraphQLConnection;
17
20
  baseEndpoint?: string;
@@ -47,10 +50,17 @@ export interface BaseOperatorNode {
47
50
  useCache?: boolean;
48
51
  [key: string]: EvaluatorNode;
49
52
  }
53
+ export interface FragmentNode {
54
+ fragment: string;
55
+ parameters?: {
56
+ [key: string]: EvaluatorNode;
57
+ };
58
+ [key: string]: EvaluatorNode;
59
+ }
50
60
  export type CombinedOperatorNode = BaseOperatorNode & BasicExtendedNode & SubtractionNode & DivisionNode & ComparatorNode & ConditionalNode & RegexNode & StringSubNode & SplitNode & ObjPropNode & APINode & PGNode & GraphQLNode & BuildObjectNode & MatchNode & FunctionNode & PassThruNode;
51
61
  export type OperatorNodeUnion = BasicExtendedNode | SubtractionNode | DivisionNode | ComparatorNode | ConditionalNode | RegexNode | StringSubNode | SplitNode | ObjPropNode | PGNode | GraphQLNode | APINode | BuildObjectNode | MatchNode | FunctionNode | PassThruNode;
52
62
  export type EvaluatorOutput = string | boolean | number | GenericObject | null | undefined | any[];
53
- export type EvaluatorNode = CombinedOperatorNode | EvaluatorOutput;
63
+ export type EvaluatorNode = CombinedOperatorNode | FragmentNode | EvaluatorOutput;
54
64
  export type OperatorObject = {
55
65
  requiredProperties: readonly string[];
56
66
  operatorAliases: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fig-tree-evaluator",
3
- "version": "2.3.2",
3
+ "version": "2.4.0",
4
4
  "description": "Module to evaluate JSON-structured expression trees",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -37,7 +37,7 @@
37
37
  "axios": "^0.27.2",
38
38
  "change-case": "^4.1.2",
39
39
  "dequal": "^2.0.3",
40
- "object-property-extractor": "^1.0.3"
40
+ "object-property-extractor": "^1.0.6"
41
41
  },
42
42
  "bugs": {
43
43
  "url": "https://github.com/CarlosNZ/fig-tree-evaluator/issues"