fig-tree-evaluator 2.4.3 → 2.5.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
@@ -105,7 +105,7 @@ Or, with a deeper structure that results in the same final output:
105
105
  // -> 6
106
106
  ```
107
107
 
108
- Which would be represented diagramatically with the following expression tree:
108
+ Which would be represented diagrammatically with the following expression tree:
109
109
 
110
110
  ![Example 2](/docs/img/Example_1.png)
111
111
 
@@ -135,6 +135,12 @@ exp.evaluate(expression, [options]) // Options over-ride initial options for thi
135
135
  const result = await exp.evaluate(expression, [options])
136
136
  ```
137
137
 
138
+ FigTreeEvaluator is written in **Typescript**, and the following types are available to import from the package:
139
+ - `FigTreeOptions`: `options` object, as per [options](#available-options) below
140
+ - `Operator`: string literal canonical [Operator](#operator-nodes) names (`AND`, `OR`, `EQUAL`, etc.)
141
+ - `EvaluatorNode`: Evaluator input
142
+ - `EvaluatorOutput`
143
+
138
144
  ## Available options
139
145
 
140
146
  The `options` parameter is an object with the following available properties (all optional):
@@ -142,10 +148,10 @@ The `options` parameter is an object with the following available properties (al
142
148
  - `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.
143
149
  - `functions` -- a single object containing any *custom functions* available for use by the [customFunctions](#custom_functions) operator.
144
150
  - `fragments` -- commonly-used expressions (with optional parameters) that can be re-used in any other expression. See [Fragments](#fragments)
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.
151
+ - `pgConnection` -- if you wish to make calls to a Postgres database using the [`pgSQL` operator](#pg_sql), pass a [node-postgres](https://node-postgres.com/) connection object here.
146
152
  - `graphQLConnection` -- a GraphQL connection object, if using the [`graphQL` operator](#graphql). See operator details below.
147
153
  - `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.
148
- - `headers` -- A general http headers object that will be passed to *all* http-based operators. Useful for authenticatian headers, for example. Each operator and instance can have its own headers, though, so see specific operator reference for details.
154
+ - `headers` -- A general http headers object that will be passed to *all* http-based operators. Useful for authentication headers, for example. Each operator and instance can have its own headers, though, so see specific operator reference for details.
149
155
  - `returnErrorAsString` -- by default the evaluator will throw errors with invalid evaluation expressions (with helpful error messages indicating the node which threw the error and what the problem was). But if you have `returnErrorAsString: true` set, the evaluator will never throw, but instead return error messages as a valid string output. (See also the [`fallback`](#other-common-properties) parameter below)
150
156
  - `allowJSONStringInput` -- the evaluator is expecting the input expression to be a javascript object. However, it will also accept JSON strings if this option is set to `true`. We have to perform additional logic on every evaluation input to determine if a string is a JSON expression or a standard string, so this is skipped by default for performance reasons. However, if you want to send (for example) user input directly to the evaluator without running it through your own `JSON.parse()`, then enable this option.
151
157
  - `skipRuntimeTypeCheck` -- we perform comprehensive type checking at runtime to ensure that each operator only performs its operation on valid inputs. If type checking fails, we throw an error detailing the explicit problem. However, if `skipRuntimeTypeCheck` is set to `true`, then all inputs are passed to the operator regardless, and any errors will come from whatever standard javascript errors might be encountered (e.g. trying to pass a primitive value when an array is expected => `.map is not a function`)
@@ -1043,7 +1049,7 @@ Aliases: `pgSql`, `sql`, `postgres`, `pg`, `pgDb`
1043
1049
 
1044
1050
  - `query`<sup>*</sup>: (string) -- SQL query string, with parameterised replacements (i.e. `$1`, `$2`, etc)
1045
1051
  - `values` (or `replacements`): (array) -- replacements for the `query` parameters
1046
- - `type`: (`"array" | "string" | "number"`) -- determines the shape of the resulting data. To quote `node-postgres`:
1052
+ - `type` (or `queryType`): (`"array" | "string" | "number"`) -- determines the shape of the resulting data. To quote `node-postgres`:
1047
1053
  > By default node-postgres reads rows and collects them into JavaScript objects with the keys matching the column names and the values matching the corresponding row value for each column. If you do not need or do not want this behavior you can pass rowMode: 'array' to a query object. This will inform the result parser to bypass collecting rows into a JavaScript object, and instead will return each row as an array of values.
1048
1054
 
1049
1055
  We extend this a step further by flattening the array, and (if `"string"` or `"number"`) converting the result to a concatenated string or (if possible) number.
@@ -1468,6 +1474,8 @@ This is different to the memoization provided by [Alias Nodes](#alias-nodes):
1468
1474
  - Alias nodes are still evaluated once for every evaluation -- they're more for re-use *within* a complex expression.
1469
1475
  - Cached nodes will persist *between* different evaluations as long as the input values are the same as a previously evaluated node.
1470
1476
 
1477
+ Caching is enabled by default for most of the above operators, but this can be overridden by setting `useCache: false` in [options](#available-options), either globally or per expression. If you're querying a database or API that is likely to have a different result for the same request (i.e. data has changed), then you probably want to turn the cache off.
1478
+
1471
1479
  ## More examples
1472
1480
 
1473
1481
  More examples, included large, complex expressions can be found within the test suites in the [repository](https://github.com/CarlosNZ/fig-tree-evaluator).
@@ -1502,6 +1510,9 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
1502
1510
 
1503
1511
  *Trivial upgrades (e.g. documentation, small re-factors, types, etc.) not included*
1504
1512
 
1513
+ - **v2.5.0**:
1514
+ - Bug fixes for edge cases (mainly related to backwards compatibility)
1515
+ - More backwards compatibility for very old (pre-v1) syntax (undocumented)
1505
1516
  - **v2.4.1**: Small bug fix for Fragments edge case
1506
1517
  - **v2.4.0**: Implement [Fragments](#fragments) (#74)
1507
1518
  - **v2.3.2**: Bug fix: alias nodes not working with `evaluateFullObject` (#72)
package/build/evaluate.js CHANGED
@@ -51,7 +51,7 @@ 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, _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;
54
+ var options, operators, operatorAliases, expression, isOperator, isFragment, _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
55
  var _p, _q, _r;
56
56
  return __generator(this, function (_s) {
57
57
  switch (_s.label) {
@@ -64,16 +64,21 @@ var evaluatorFunction = function (input, config) { return __awaiter(void 0, void
64
64
  expression = _s.sent();
65
65
  _s.label = 2;
66
66
  case 2:
67
- if (!(options.evaluateFullObject && !(0, helpers_1.isOperatorNode)(expression) && !(0, helpers_1.isFragmentNode)(expression))) return [3, 4];
67
+ isOperator = (0, helpers_1.isOperatorNode)(expression);
68
+ isFragment = (0, helpers_1.isFragmentNode)(expression);
69
+ if (!(options.evaluateFullObject && !isOperator && !isFragment)) return [3, 4];
68
70
  _a = helpers_1.replaceAliasNodeValues;
69
71
  return [4, (0, helpers_1.evaluateObject)(expression, config)];
70
72
  case 3: return [2, _a.apply(void 0, [_s.sent(), config])];
71
73
  case 4:
72
- if (!(0, helpers_1.isOperatorNode)(expression) && !(0, helpers_1.isFragmentNode)(expression))
74
+ if (!isOperator && !isFragment) {
75
+ if (options.supportDeprecatedValueNodes && (0, helpers_1.isObject)(expression) && 'value' in expression)
76
+ return [2, expression.value];
73
77
  return [2, (0, helpers_1.replaceAliasNodeValues)(expression, config)];
78
+ }
74
79
  fallback = expression.fallback;
75
80
  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];
81
+ if (!isFragment) return [3, 8];
77
82
  return [4, (0, _operatorUtils_1.evaluateArray)([expression.fragment, expression.parameters], config)];
78
83
  case 5:
79
84
  _b = (_s.sent()), fragment = _b[0], parameters = _b[1];
@@ -138,7 +143,7 @@ var evaluatorFunction = function (input, config) { return __awaiter(void 0, void
138
143
  case 22:
139
144
  err_1 = _s.sent();
140
145
  _l = helpers_1.fallbackOrError;
141
- return [4, (0, exports.evaluatorFunction)(fallback, config)];
146
+ return [4, (0, exports.evaluatorFunction)(expression.fallback, config)];
142
147
  case 23:
143
148
  result = _l.apply(void 0, [_s.sent(), "Operator: ".concat(operator, "\n").concat((0, helpers_1.errorMessage)(err_1)), returnErrorAsString]);
144
149
  return [3, 24];
@@ -20,4 +20,5 @@ export declare const convertOutputMethods: {
20
20
  [key in OutputType]: (value: EvaluatorOutput) => EvaluatorOutput | EvaluatorOutput[];
21
21
  };
22
22
  export declare const errorMessage: (err: unknown) => string;
23
+ export declare const isObject: (input: unknown) => boolean;
23
24
  export declare const evaluateObject: (input: EvaluatorNode, config: FigTreeConfig) => Promise<EvaluatorOutput>;
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.isFragmentNode = exports.isOperatorNode = exports.parseIfJson = void 0;
50
+ exports.evaluateObject = exports.isObject = 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");
@@ -178,12 +178,13 @@ var extractNumber = function (input) {
178
178
  var isObject = function (input) {
179
179
  return typeof input === 'object' && input !== null && !Array.isArray(input);
180
180
  };
181
+ exports.isObject = isObject;
181
182
  var evaluateObject = function (input, config) { return __awaiter(void 0, void 0, void 0, function () {
182
183
  var newObjectEntries, result;
183
184
  return __generator(this, function (_a) {
184
185
  switch (_a.label) {
185
186
  case 0:
186
- if (!isObject(input))
187
+ if (!(0, exports.isObject)(input))
187
188
  return [2, input];
188
189
  newObjectEntries = [];
189
190
  Object.entries(input).forEach(function (_a) {
@@ -124,6 +124,8 @@ var joinUrlParts = function () {
124
124
  var startSliceIndex = curr[0] === '/' ? 1 : 0;
125
125
  var endSliceIndex = curr.slice(-1) === '/' ? -1 : undefined;
126
126
  var newPart = curr.slice(startSliceIndex, endSliceIndex);
127
+ if (newPart === '')
128
+ return acc;
127
129
  return acc + (index === 0 ? '' : '/') + newPart;
128
130
  }, '');
129
131
  };
@@ -90,16 +90,20 @@ var evaluate = function (expression, config) { return __awaiter(void 0, void 0,
90
90
  }
91
91
  });
92
92
  }); };
93
- var parseChildren = function (expression) { return __awaiter(void 0, void 0, void 0, function () {
93
+ var parseChildren = function (expression, config) { return __awaiter(void 0, void 0, void 0, function () {
94
94
  var _a, query, _b, url, fieldNames, rest, values, variables, output;
95
95
  return __generator(this, function (_c) {
96
- _a = expression.children, query = _a[0], _b = _a[1], url = _b === void 0 ? '' : _b, fieldNames = _a[2], rest = _a.slice(3);
97
- values = rest.slice(0, fieldNames.length);
98
- variables = (0, _operatorUtils_1.zipArraysToObject)(fieldNames, values);
99
- output = __assign(__assign({}, expression), { query: query, url: url, variables: variables });
100
- if (rest.length > fieldNames.length)
101
- output.returnNode = rest.pop();
102
- return [2, output];
96
+ switch (_c.label) {
97
+ case 0: return [4, (0, _operatorUtils_1.evaluateArray)(expression.children, config)];
98
+ case 1:
99
+ _a = (_c.sent()), query = _a[0], _b = _a[1], url = _b === void 0 ? '' : _b, fieldNames = _a[2], rest = _a.slice(3);
100
+ values = rest.slice(0, fieldNames.length);
101
+ variables = (0, _operatorUtils_1.zipArraysToObject)(fieldNames, values);
102
+ output = __assign(__assign({}, expression), { query: query, url: url, variables: variables });
103
+ if (rest.length > fieldNames.length)
104
+ output.returnNode = rest.pop();
105
+ return [2, output];
106
+ }
103
107
  });
104
108
  }); };
105
109
  exports.GRAPHQL = {
@@ -60,37 +60,38 @@ exports.PG_SQL = void 0;
60
60
  var _operatorUtils_1 = require("./_operatorUtils");
61
61
  var requiredProperties = ['query'];
62
62
  var operatorAliases = ['pgSql', 'sql', 'postgres', 'pg', 'pgDb'];
63
- var propertyAliases = { replacements: 'values' };
63
+ var propertyAliases = { replacements: 'values', queryType: 'type' };
64
64
  var evaluate = function (expression, config) { return __awaiter(void 0, void 0, void 0, function () {
65
- var _a, query, values, shouldUseCache, result;
65
+ var _a, query, type, values, shouldUseCache, result;
66
66
  var _b;
67
67
  var _c, _d, _e;
68
68
  return __generator(this, function (_f) {
69
69
  switch (_f.label) {
70
- case 0: return [4, (0, _operatorUtils_1.evaluateArray)(__spreadArray([expression.query], (expression.values || []), true), config)];
70
+ case 0: return [4, (0, _operatorUtils_1.evaluateArray)(__spreadArray([expression.query, expression.type], (expression.values || []), true), config)];
71
71
  case 1:
72
- _a = (_f.sent()), query = _a[0], values = _a.slice(1);
73
- config.typeChecker({ name: 'query', value: query, expectedType: 'string' }, { name: 'values', value: values, expectedType: 'array' });
72
+ _a = (_f.sent()), query = _a[0], type = _a[1], values = _a.slice(2);
73
+ config.typeChecker({ name: 'query', value: query, expectedType: 'string' }, { name: 'values', value: values, expectedType: 'array' }, { name: 'type', value: type, expectedType: ['string', 'undefined'] });
74
74
  if (!((_c = config.options) === null || _c === void 0 ? void 0 : _c.pgConnection))
75
75
  throw new Error('No Postgres database connection provided');
76
76
  shouldUseCache = (_e = (_d = expression.useCache) !== null && _d !== void 0 ? _d : config.options.useCache) !== null && _e !== void 0 ? _e : true;
77
77
  try {
78
78
  result = (_b = config.cache).useCache.apply(_b, __spreadArray([shouldUseCache,
79
- function (query) {
79
+ function (query, type) {
80
80
  var values = [];
81
- for (var _i = 1; _i < arguments.length; _i++) {
82
- values[_i - 1] = arguments[_i];
81
+ for (var _i = 2; _i < arguments.length; _i++) {
82
+ values[_i - 2] = arguments[_i];
83
83
  }
84
84
  return __awaiter(void 0, void 0, void 0, function () {
85
85
  return __generator(this, function (_a) {
86
86
  switch (_a.label) {
87
- case 0: return [4, processPgSQL(__spreadArray([query], values, true), config.options.pgConnection, expression === null || expression === void 0 ? void 0 : expression.type)];
87
+ case 0: return [4, processPgSQL(__spreadArray([query], values, true), config.options.pgConnection, type)];
88
88
  case 1: return [2, _a.sent()];
89
89
  }
90
90
  });
91
91
  });
92
92
  },
93
- query], values, false));
93
+ query,
94
+ type], values, false));
94
95
  return [2, result];
95
96
  }
96
97
  catch (err) {
package/build/types.d.ts CHANGED
@@ -28,6 +28,7 @@ export interface FigTreeOptions {
28
28
  evaluateFullObject?: boolean;
29
29
  useCache?: boolean;
30
30
  maxCacheSize?: number;
31
+ supportDeprecatedValueNodes?: boolean;
31
32
  }
32
33
  export interface FigTreeConfig {
33
34
  options: FigTreeOptions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fig-tree-evaluator",
3
- "version": "2.4.3",
3
+ "version": "2.5.0",
4
4
  "description": "Module to evaluate JSON-structured expression trees",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",