fig-tree-evaluator 2.2.2 → 2.3.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
@@ -47,6 +47,7 @@ A range of built-in operators are available, from simple logic, arithmetic and s
47
47
  - [PASSTHRU](#passthru)
48
48
  - [CUSTOM\_FUNCTIONS](#custom_functions)
49
49
  - [Alias Nodes](#alias-nodes)
50
+ - [Caching (Memoization)](#caching-memoization)
50
51
  - [More examples](#more-examples)
51
52
  - [Development environment](#development-environment)
52
53
  - [Tests](#tests)
@@ -135,7 +136,7 @@ const result = await exp.evaluate(expression, [options])
135
136
 
136
137
  The `options` parameter is an object with the following available properties (all optional):
137
138
 
138
- - `objects` -- 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.
139
+ - `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.
139
140
  - `functions` -- a single object containing any *custom functions* available for use by the [customFunctions](#custom_functions) operator.
140
141
  - `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.
141
142
  - `graphQLConnection` -- a GraphQL connection object, if using the [`graphQL` operator](#graphql). See operator details below.
@@ -146,6 +147,8 @@ The `options` parameter is an object with the following available properties (al
146
147
  - `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`)
147
148
  - `nullEqualsUndefined` -- this only affects the [`equal`/`notEqual` operators](#equal) (see there for more detail).
148
149
  - `evaluateFullObject` -- by default, FigTree expects the root of an input expression to be an [Operator Node](#operator-nodes), and if not, will return the input unmodified. However, you may have cases where the evaluation expressions are deep within a larger structure (such as a JSON schema, for example). In this case, you can set `evaluateFullObject` to `true` and the evaluator will find *any* operator nodes within the structure and evaluate them in place.
150
+ - `useCache` -- caches the results from certain operators to avoid repeated network requests with the same input values. By default, this is set to `true`, and it can be overridden for specific nodes. See [Memoization/Caching section](#caching-memoization) for more detail
151
+ - `maxCacheSize` -- the maximum number of results that will be held in the aforementioned cache (default: `50`)
149
152
 
150
153
  As mentioned above, `options` can be provided as part of the constructor as part of each separate evaluation. You can also change the options permanently for a given evaluator instance with:
151
154
 
@@ -207,6 +210,7 @@ In each operator node, as well as the operator-specific properties, the followin
207
210
 
208
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.
209
212
  - `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
+ - `useCache`: Overrides the global `useCache` value (from [options](#available-options)) for this node only. See [Caching/Memoization](#caching-memoization) below for more info.
210
214
 
211
215
  Remember that *all* operator node properties can themselves be operator nodes, *including* the `fallback` and `outputType` properties.
212
216
 
@@ -304,7 +308,7 @@ Aliases: `=`, `eq`, `equal`, `equals`
304
308
  #### Properties
305
309
 
306
310
  - `values`<sup>*</sup>: (array) -- any number of elements; will be compared for strict equality. This includes simple types as well as deep equality of objects and arrays.
307
- - `nullEqualsUndefined`: (boolean, default `false`) -- there are times when it is convenient for `null` to be considered equal to `undefined`. If this is desiered, set this property to `true`, otherwise all equality checks will be "strict" equality. If you find that you want this setting enabled globally, then you can set it in the overall [evaluator options](#available-options) instead of having to add this additional property to every equality expression.
311
+ - `nullEqualsUndefined`: (boolean, default `false`) -- there are times when it is convenient for `null` to be considered equal to `undefined`. If this is desired, set this property to `true`, otherwise all equality checks will be "strict" equality. If you find that you want this setting enabled globally, then you can set it in the overall [evaluator options](#available-options) instead of having to add this additional property to every equality expression.
308
312
 
309
313
  e.g.
310
314
  ```js
@@ -659,16 +663,16 @@ e.g.
659
663
  ----
660
664
  ### OBJECT_PROPERTIES
661
665
 
662
- *Extracts values from objects in your application*
666
+ *Extracts values from data objects in your application*
663
667
 
664
- Aliases: `objectProperties`, `objProps`, `getProperty`, `getObjProp`
668
+ Aliases: `objectProperties`, `dataProperties`,`data`, `getData`, `objProps`, `getProperty`, `getObjProp`
665
669
 
666
670
  #### Properties
667
671
 
668
672
  - `property` (or `path`, `propertyName`)<sup>*</sup>: (string) -- the path to the required property in the object
669
- - `additionalObjects` (or `objects`, `additional`): (object) -- any other objects whose properties can be referenced in `property` (see below)
673
+ - `additionalData` (or `additionalObjects`, `additional`, `data`): (object) -- any other objects whose properties can be referenced in `property` (see below)
670
674
 
671
- Objects are normamlly expected to be passed in to the evaluator as part of the [options](#available-options), not as part of the expression itself. The reason for this is that the source objects are expected to be values internal to your application, whereas the evaluator provides an externally configurable mechanism to extract (and process) application data. (However, it is possible to pass objects directly as part of the expression using the `additionalObjects` property, so (in theory) objects could be dynamically generated from other expressions.)
675
+ Data objects are normally expected to be passed in to the evaluator as part of the [options](#available-options), not as part of the expression itself. This is because the source objects are expected to be values internal to your application, whereas the evaluator provides an externally configurable mechanism to extract (and process) application data. (However, it is possible to pass data objects directly as part of the expression using the `additionalObjects` property, so (in theory) data objects could be dynamically generated from other expressions.)
672
676
 
673
677
  For example, consider a `user` object and an fig-tree evaluator instance:
674
678
 
@@ -688,7 +692,7 @@ const exp = new FigTreeEvaluator()
688
692
 
689
693
  const expression = getExpressionFromConfig()
690
694
 
691
- exp.evaluate(expression, { objects: { user } })
695
+ exp.evaluate(expression, { data: { user } })
692
696
  ```
693
697
 
694
698
  Here is the result of various values of `expression:`
@@ -701,7 +705,7 @@ Here is the result of various values of `expression:`
701
705
  // => "Peter"
702
706
 
703
707
  {
704
- operator: 'getProperty',
708
+ operator: 'getData',
705
709
  path: 'user.friends[1]',
706
710
  }
707
711
  // => "MJ"
@@ -721,7 +725,7 @@ result.map((e) => e.name)
721
725
 
722
726
  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.
723
727
 
724
- The "objectProperties" operator will throw an error if an invalid path is provided, so it is recommended to provide a `fallback` value for the expression:
728
+ The "objectProperties" operator will throw an error if an invalid path is provided, so it is recommended to provide a [`fallback`](#other-common-properties) value for the expression:
725
729
  ```js
726
730
  {
727
731
  operator: 'objectProperties',
@@ -734,13 +738,13 @@ The "objectProperties" operator will throw an error if an invalid path is provid
734
738
  `children` array: `[property]`
735
739
 
736
740
 
737
- Example using "objects" passed in dynamically as part of expression:
741
+ Example using "data" passed in dynamically as part of expression:
738
742
 
739
743
  ```js
740
744
  {
741
745
  operator: 'objectProperties',
742
746
  property: 'user.name',
743
- additionalObjects: {
747
+ additionalData: {
744
748
  operator: '?',
745
749
  condition: { operator: '=', values: [{ operator: '+', values: [7, 8, 9] }, 25] },
746
750
  valueIfTrue: { user: { name: 'Bilbo' } },
@@ -760,7 +764,7 @@ Aliases: `stringSubstitution`, `substitute`, `stringSub`, `replace`
760
764
  #### Properties
761
765
 
762
766
  - `string`<sup>*</sup>: (string) -- a parameterized (`%1`, `%2`) string, where the parameters are to be replaced by dynamic values. E.g. `"My name is %1 (age %2)"`
763
- - `substitutions` (or `replacments`)<sup>*</sup>: (array) -- the values to be substituted into `string`
767
+ - `substitutions` (or `replacements`)<sup>*</sup>: (array) -- the values to be substituted into `string`
764
768
 
765
769
  The values in the `substitutions` array are replaced in the original `string` by matching their order to the numerical order of the parameters.
766
770
 
@@ -783,7 +787,7 @@ e.g.
783
787
  operator: 'replace',
784
788
  string: '%1 is actually %2 %3',
785
789
  substitutions: [
786
- // Using the 'user' object from above
790
+ // Using the 'user' object from above (OBJECT_PROPERTIES operator)
787
791
  {
788
792
  operator: 'objectProperties',
789
793
  property: 'user.alias',
@@ -798,7 +802,7 @@ e.g.
798
802
  },
799
803
  ],
800
804
  }
801
- // => "Spiderman is actually Peter Parker"
805
+ // => "Spider-man is actually Peter Parker"
802
806
 
803
807
  // Parameters can be repeated:
804
808
  {
@@ -1178,8 +1182,8 @@ e.g.
1178
1182
  },
1179
1183
  }
1180
1184
  // With:
1181
- // objects = { weather: "sunny", humidity: "high" } => "NO"
1182
- // objects = { weather: "rainy", wind: "weak" } => "YES"
1185
+ // data = { weather: "sunny", humidity: "high" } => "NO"
1186
+ // data = { weather: "rainy", wind: "weak" } => "YES"
1183
1187
 
1184
1188
  ```
1185
1189
 
@@ -1379,6 +1383,18 @@ Like all expression nodes, alias nodes can themselves contain complex expression
1379
1383
 
1380
1384
  (If an alias reference does not having a matching definition, the reference value will just be returned as a literal string, e.g. `"$getCountry"`)
1381
1385
 
1386
+ ## Caching (Memoization)
1387
+
1388
+ FigTree Evaluator has basic memoization functionality for certain nodes to speed up re-evaluation of previously evaluated nodes. 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
+
1390
+ Currently, caching is only implemented for the following operators, since they perform requests to external resources, which are inherently slow:
1391
+
1392
+ - GET (`useCache` default: `true`)
1393
+ - POST (`useCache` default: `true`)
1394
+ - PG_SQL (`useCache` default: `true`)
1395
+ - GRAPH_QL (`useCache` default: `true`)
1396
+ - CUSTOM_FUNCTIONS (`useCache` default: `false`)
1397
+
1382
1398
  ## More examples
1383
1399
 
1384
1400
  More examples, included large, complex expressions can be found within the test suites in the [repository](https://github.com/CarlosNZ/fig-tree-evaluator).
@@ -1411,6 +1427,8 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
1411
1427
 
1412
1428
  ## Changelog
1413
1429
 
1430
+ - **v2.3.0**: Implement [caching/memoization](#caching-memoization) (#68)
1431
+ - **v2.2.3**: Change option `objects` name to `data` (but keep backward compatibility) (#66)
1414
1432
  - **v2.2.2**: Option to evaluate whole object if operator nodes are deep within it (#64)
1415
1433
  - **v2.2.1**: More efficient branch evaluation for condition/match operators (#63)
1416
1434
  - **v2.2.0**:
@@ -0,0 +1,10 @@
1
+ declare class FigTreeCache {
2
+ private store;
3
+ private queue;
4
+ private maxSize;
5
+ constructor(maxSize?: number);
6
+ useCache: (shouldUseCache: boolean, action: Function, ...args: unknown[]) => Promise<any>;
7
+ getMax: () => number;
8
+ setMax: (size: number) => void;
9
+ }
10
+ export default FigTreeCache;
package/build/cache.js ADDED
@@ -0,0 +1,96 @@
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
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ var MAX_CACHED_ITEMS = 50;
40
+ var FigTreeCache = (function () {
41
+ function FigTreeCache(maxSize) {
42
+ var _this = this;
43
+ this.useCache = function (shouldUseCache, action) {
44
+ var args = [];
45
+ for (var _i = 2; _i < arguments.length; _i++) {
46
+ args[_i - 2] = arguments[_i];
47
+ }
48
+ return __awaiter(_this, void 0, void 0, function () {
49
+ var key, result, keysToRemove;
50
+ var _this = this;
51
+ return __generator(this, function (_a) {
52
+ switch (_a.label) {
53
+ case 0:
54
+ if (!!shouldUseCache) return [3, 2];
55
+ return [4, action.apply(void 0, args)];
56
+ case 1: return [2, _a.sent()];
57
+ case 2:
58
+ key = stringifyInput(args);
59
+ if (key in this.store) {
60
+ this.queue = this.queue.filter(function (val) { return val !== key; });
61
+ this.queue.unshift(key);
62
+ console.log('Using cached result:', this.store[key]);
63
+ return [2, this.store[key]];
64
+ }
65
+ return [4, action.apply(void 0, args)];
66
+ case 3:
67
+ result = _a.sent();
68
+ this.store[key] = result;
69
+ if (this.queue.length >= this.maxSize) {
70
+ keysToRemove = this.queue.slice(this.maxSize - 1);
71
+ keysToRemove.forEach(function (key) { return delete _this.store[key]; });
72
+ this.queue = this.queue.slice(0, this.maxSize - 1);
73
+ }
74
+ this.queue.unshift(key);
75
+ return [2, result];
76
+ }
77
+ });
78
+ });
79
+ };
80
+ this.getMax = function () { return _this.maxSize; };
81
+ this.setMax = function (size) {
82
+ _this.maxSize = size;
83
+ };
84
+ this.store = {};
85
+ this.queue = [];
86
+ this.maxSize = maxSize !== null && maxSize !== void 0 ? maxSize : MAX_CACHED_ITEMS;
87
+ }
88
+ return FigTreeCache;
89
+ }());
90
+ exports.default = FigTreeCache;
91
+ var stringifyInput = function (args) {
92
+ return args.reduce(function (outputString, arg, index) {
93
+ var part = typeof arg === 'object' ? JSON.stringify(arg) : arg;
94
+ return index === 0 ? "".concat(part) : "".concat(outputString, "_").concat(part);
95
+ }, '');
96
+ };
package/build/helpers.js CHANGED
@@ -142,8 +142,8 @@ var checkRequiredNodes = function (requiredProps, expression) {
142
142
  exports.checkRequiredNodes = checkRequiredNodes;
143
143
  var mergeOptions = function (origOptions, newOptions) {
144
144
  var combinedOptions = __assign(__assign({}, origOptions), newOptions);
145
- if (origOptions.objects || newOptions.objects)
146
- combinedOptions.objects = __assign(__assign({}, origOptions.objects), newOptions.objects);
145
+ if (origOptions.data || newOptions.data)
146
+ combinedOptions.data = __assign(__assign({}, origOptions.data), newOptions.data);
147
147
  if (origOptions.functions || newOptions.functions)
148
148
  combinedOptions.functions = __assign(__assign({}, origOptions.functions), newOptions.functions);
149
149
  if (origOptions.headers || newOptions.headers)
package/build/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { EvaluatorNode, FigTreeOptions } from './types';
2
- export declare const getVersion: () => any;
3
2
  declare class FigTreeEvaluator {
4
3
  private options;
5
4
  private operators;
6
5
  private operatorAliases;
6
+ private cache;
7
7
  constructor(options?: FigTreeOptions);
8
8
  private typeChecker;
9
9
  evaluate(expression: EvaluatorNode, options?: FigTreeOptions): Promise<import("./types").EvaluatorOutput>;
package/build/index.js CHANGED
@@ -73,15 +73,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
73
73
  return (mod && mod.__esModule) ? mod : { "default": mod };
74
74
  };
75
75
  Object.defineProperty(exports, "__esModule", { value: true });
76
- exports.evaluateExpression = exports.getVersion = void 0;
76
+ exports.evaluateExpression = void 0;
77
77
  var evaluate_1 = require("./evaluate");
78
78
  var typeCheck_1 = require("./typeCheck");
79
79
  var _operatorAliases_json_1 = __importDefault(require("./operators/_operatorAliases.json"));
80
80
  var operators = __importStar(require("./operators"));
81
81
  var helpers_1 = require("./helpers");
82
+ var cache_1 = __importDefault(require("./cache"));
82
83
  var pkg = require('../package.json');
83
- var getVersion = function () { return pkg.version; };
84
- exports.getVersion = getVersion;
85
84
  var FigTreeEvaluator = (function () {
86
85
  function FigTreeEvaluator(options) {
87
86
  if (options === void 0) { options = {}; }
@@ -95,10 +94,11 @@ var FigTreeEvaluator = (function () {
95
94
  return;
96
95
  throw new Error(result);
97
96
  };
98
- this.getVersion = exports.getVersion;
99
- this.options = options;
97
+ this.getVersion = function () { return pkg.version; };
98
+ this.options = standardiseOptionNames(options);
100
99
  this.operators = operators;
101
100
  this.operatorAliases = _operatorAliases_json_1.default;
101
+ this.cache = new cache_1.default(options.maxCacheSize);
102
102
  }
103
103
  FigTreeEvaluator.prototype.evaluate = function (expression, options) {
104
104
  if (options === void 0) { options = {}; }
@@ -107,13 +107,16 @@ var FigTreeEvaluator = (function () {
107
107
  return __generator(this, function (_a) {
108
108
  switch (_a.label) {
109
109
  case 0:
110
- currentOptions = (0, helpers_1.mergeOptions)(this.options, options);
110
+ currentOptions = (0, helpers_1.mergeOptions)(this.options, standardiseOptionNames(options));
111
+ if (currentOptions.maxCacheSize && currentOptions.maxCacheSize !== this.cache.getMax())
112
+ this.cache.setMax(currentOptions.maxCacheSize);
111
113
  return [4, (0, evaluate_1.evaluatorFunction)(expression, {
112
114
  options: currentOptions,
113
115
  operators: this.operators,
114
116
  operatorAliases: this.operatorAliases,
115
117
  typeChecker: currentOptions.skipRuntimeTypeCheck ? function () { } : this.typeChecker,
116
118
  resolvedAliasNodes: {},
119
+ cache: this.cache,
117
120
  })];
118
121
  case 1: return [2, _a.sent()];
119
122
  }
@@ -124,7 +127,7 @@ var FigTreeEvaluator = (function () {
124
127
  return this.options;
125
128
  };
126
129
  FigTreeEvaluator.prototype.updateOptions = function (options) {
127
- this.options = __assign(__assign({}, this.options), options);
130
+ this.options = __assign(__assign({}, this.options), standardiseOptionNames(options));
128
131
  };
129
132
  return FigTreeEvaluator;
130
133
  }());
@@ -133,3 +136,10 @@ var evaluateExpression = function (expression, options) {
133
136
  return new FigTreeEvaluator(options).evaluate(expression);
134
137
  };
135
138
  exports.evaluateExpression = evaluateExpression;
139
+ var standardiseOptionNames = function (options) {
140
+ if ('objects' in options) {
141
+ options.data = options.objects;
142
+ delete options.objects;
143
+ }
144
+ return options;
145
+ };
@@ -45,6 +45,9 @@
45
45
  "patternMatch": "REGEX",
46
46
  "regexp": "REGEX",
47
47
  "matchPattern": "REGEX",
48
+ "dataProperties": "OBJECT_PROPERTIES",
49
+ "data": "OBJECT_PROPERTIES",
50
+ "getData": "OBJECT_PROPERTIES",
48
51
  "objectProperties": "OBJECT_PROPERTIES",
49
52
  "objProps": "OBJECT_PROPERTIES",
50
53
  "getProperty": "OBJECT_PROPERTIES",
@@ -80,18 +80,37 @@ var propertyAliases = {
80
80
  variables: 'args',
81
81
  };
82
82
  var evaluate = function (expression, config) { return __awaiter(void 0, void 0, void 0, function () {
83
- var _a, functionPath, args, _b, objects, functions, func;
84
- var _c, _d;
85
- return __generator(this, function (_e) {
86
- switch (_e.label) {
83
+ var _a, functionPath, args, _b, data, functions, func, shouldUseCache, result;
84
+ var _c;
85
+ var _d, _e, _f, _g;
86
+ return __generator(this, function (_h) {
87
+ switch (_h.label) {
87
88
  case 0: return [4, (0, _operatorUtils_1.evaluateArray)(__spreadArray([expression.functionPath], (expression.args || []), true), config)];
88
89
  case 1:
89
- _a = (_e.sent()), functionPath = _a[0], args = _a.slice(1);
90
+ _a = (_h.sent()), functionPath = _a[0], args = _a.slice(1);
90
91
  config.typeChecker({ name: 'functionPath', value: functionPath, expectedType: 'string' });
91
- _b = config.options, objects = _b.objects, functions = _b.functions;
92
- func = ((_d = (_c = (0, object_property_extractor_1.default)(functions, functionPath, null)) !== null && _c !== void 0 ? _c : (0, object_property_extractor_1.default)(objects, functionPath, null)) !== null && _d !== void 0 ? _d : (0, object_property_extractor_1.default)(config.options, functionPath));
93
- return [4, func.apply(void 0, args)];
94
- case 2: return [2, _e.sent()];
92
+ _b = config.options, data = _b.data, functions = _b.functions;
93
+ func = ((_e = (_d = (0, object_property_extractor_1.default)(functions, functionPath, null)) !== null && _d !== void 0 ? _d : (0, object_property_extractor_1.default)(data, functionPath, null)) !== null && _e !== void 0 ? _e : (0, object_property_extractor_1.default)(config.options, functionPath));
94
+ shouldUseCache = (_g = (_f = expression.useCache) !== null && _f !== void 0 ? _f : config.options.useCache) !== null && _g !== void 0 ? _g : false;
95
+ return [4, (_c = config.cache).useCache.apply(_c, __spreadArray([shouldUseCache,
96
+ function (_) {
97
+ var args = [];
98
+ for (var _i = 1; _i < arguments.length; _i++) {
99
+ args[_i - 1] = arguments[_i];
100
+ }
101
+ return __awaiter(void 0, void 0, void 0, function () {
102
+ return __generator(this, function (_a) {
103
+ switch (_a.label) {
104
+ case 0: return [4, func.apply(void 0, args)];
105
+ case 1: return [2, _a.sent()];
106
+ }
107
+ });
108
+ });
109
+ },
110
+ functionPath], args, false))];
111
+ case 2:
112
+ result = _h.sent();
113
+ return [2, result];
95
114
  }
96
115
  });
97
116
  }); };
@@ -53,25 +53,36 @@ var requiredProperties = ['url'];
53
53
  var operatorAliases = ['get', 'api'];
54
54
  var propertyAliases = { endpoint: 'url', outputProperty: 'returnProperty' };
55
55
  var evaluate = function (expression, config) { return __awaiter(void 0, void 0, void 0, function () {
56
- var _a, urlObj, params, returnProperty, headers, _b, url, headersObj, baseUrl, httpHeaders, response;
57
- var _c, _d;
58
- return __generator(this, function (_e) {
59
- switch (_e.label) {
56
+ var _a, urlObj, params, returnProperty, headers, _b, url, headersObj, baseUrl, httpHeaders, shouldUseCache, result;
57
+ var _c, _d, _e, _f;
58
+ return __generator(this, function (_g) {
59
+ switch (_g.label) {
60
60
  case 0: return [4, (0, _operatorUtils_1.evaluateArray)([expression.url, expression.parameters, expression.returnProperty, expression.headers], config)];
61
61
  case 1:
62
- _a = (_e.sent()), urlObj = _a[0], params = _a[1], returnProperty = _a[2], headers = _a[3];
62
+ _a = (_g.sent()), urlObj = _a[0], params = _a[1], returnProperty = _a[2], headers = _a[3];
63
63
  _b = urlObj instanceof Object ? urlObj : { url: urlObj, headers: null }, url = _b.url, headersObj = _b.headers;
64
64
  config.typeChecker({ name: 'url', value: url, expectedType: 'string' }, { name: 'headers', value: headersObj, expectedType: ['object', 'null'] }, { name: 'headers', value: headers, expectedType: ['object', 'undefined'] }, { name: 'parameters', value: params, expectedType: ['object', 'undefined'] }, { name: 'returnProperty', value: returnProperty, expectedType: ['string', 'undefined'] });
65
65
  baseUrl = (_c = config.options.baseEndpoint) !== null && _c !== void 0 ? _c : '';
66
66
  httpHeaders = __assign(__assign(__assign({}, (_d = config.options) === null || _d === void 0 ? void 0 : _d.headers), headersObj), headers);
67
- return [4, (0, _operatorUtils_1.axiosRequest)({
68
- url: (0, _operatorUtils_1.isFullUrl)(url) ? url : (0, _operatorUtils_1.joinUrlParts)(baseUrl, url),
69
- params: params,
70
- headers: httpHeaders,
71
- })];
67
+ shouldUseCache = (_f = (_e = expression.useCache) !== null && _e !== void 0 ? _e : config.options.useCache) !== null && _f !== void 0 ? _f : true;
68
+ return [4, config.cache.useCache(shouldUseCache, function (url, params, headers, returnProperty) { return __awaiter(void 0, void 0, void 0, function () {
69
+ var response;
70
+ return __generator(this, function (_a) {
71
+ switch (_a.label) {
72
+ case 0: return [4, (0, _operatorUtils_1.axiosRequest)({
73
+ url: url,
74
+ params: params,
75
+ headers: headers,
76
+ })];
77
+ case 1:
78
+ response = _a.sent();
79
+ return [2, (0, _operatorUtils_1.extractAndSimplify)(response, returnProperty)];
80
+ }
81
+ });
82
+ }); }, (0, _operatorUtils_1.isFullUrl)(url) ? url : (0, _operatorUtils_1.joinUrlParts)(baseUrl, url), params, httpHeaders, returnProperty)];
72
83
  case 2:
73
- response = _e.sent();
74
- return [2, (0, _operatorUtils_1.extractAndSimplify)(response, returnProperty)];
84
+ result = _g.sent();
85
+ return [2, result];
75
86
  }
76
87
  });
77
88
  }); };
@@ -53,10 +53,10 @@ var requiredProperties = ['query'];
53
53
  var operatorAliases = ['graphQl', 'graphql', 'gql'];
54
54
  var propertyAliases = { endpoint: 'url', outputNode: 'returnNode', returnProperty: 'returnNode' };
55
55
  var evaluate = function (expression, config) { return __awaiter(void 0, void 0, void 0, function () {
56
- var _a, query, urlObj, variables, returnNode, headers, _b, url, headersObj, endpoint, baseEndpoint, fullUrl, data, response;
57
- var _c, _d, _e, _f, _g;
58
- return __generator(this, function (_h) {
59
- switch (_h.label) {
56
+ var _a, query, urlObj, variables, returnNode, headers, _b, url, headersObj, endpoint, baseEndpoint, fullUrl, data, shouldUseCache, result;
57
+ var _c, _d, _e, _f, _g, _h, _j;
58
+ return __generator(this, function (_k) {
59
+ switch (_k.label) {
60
60
  case 0: return [4, (0, _operatorUtils_1.evaluateArray)([
61
61
  expression.query,
62
62
  expression.url,
@@ -65,22 +65,28 @@ var evaluate = function (expression, config) { return __awaiter(void 0, void 0,
65
65
  expression.headers,
66
66
  ], config)];
67
67
  case 1:
68
- _a = (_h.sent()), query = _a[0], urlObj = _a[1], variables = _a[2], returnNode = _a[3], headers = _a[4];
68
+ _a = (_k.sent()), query = _a[0], urlObj = _a[1], variables = _a[2], returnNode = _a[3], headers = _a[4];
69
69
  _b = urlObj instanceof Object ? urlObj : { url: urlObj, headers: null }, url = _b.url, headersObj = _b.headers;
70
70
  config.typeChecker({ name: 'url', value: url, expectedType: ['string', 'undefined', 'null'] }, { name: 'query', value: query, expectedType: 'string' }, { name: 'headers', value: headersObj, expectedType: ['object', 'null'] }, { name: 'variables', value: variables, expectedType: ['object', 'undefined'] }, { name: 'returnNode', value: returnNode, expectedType: ['string', 'undefined'] });
71
71
  endpoint = !url || url.toLowerCase() === 'graphqlendpoint' ? '' : url;
72
72
  baseEndpoint = (_e = (_d = (_c = config.options.graphQLConnection) === null || _c === void 0 ? void 0 : _c.endpoint) !== null && _d !== void 0 ? _d : config.options.baseEndpoint) !== null && _e !== void 0 ? _e : '';
73
73
  fullUrl = (0, _operatorUtils_1.isFullUrl)(endpoint) ? endpoint : (0, _operatorUtils_1.joinUrlParts)(baseEndpoint, endpoint);
74
74
  data = { query: query, variables: variables };
75
- return [4, (0, _operatorUtils_1.axiosRequest)({
76
- url: fullUrl,
77
- method: 'post',
78
- data: data,
79
- headers: __assign(__assign(__assign(__assign({}, (_f = config.options.graphQLConnection) === null || _f === void 0 ? void 0 : _f.headers), (_g = config.options) === null || _g === void 0 ? void 0 : _g.headers), headersObj), headers),
80
- })];
75
+ shouldUseCache = (_g = (_f = expression.useCache) !== null && _f !== void 0 ? _f : config.options.useCache) !== null && _g !== void 0 ? _g : true;
76
+ return [4, config.cache.useCache(shouldUseCache, function (url, data, headers, returnNode) { return __awaiter(void 0, void 0, void 0, function () {
77
+ var response;
78
+ return __generator(this, function (_a) {
79
+ switch (_a.label) {
80
+ case 0: return [4, (0, _operatorUtils_1.axiosRequest)({ url: url, method: 'post', data: data, headers: headers })];
81
+ case 1:
82
+ response = _a.sent();
83
+ return [2, (0, _operatorUtils_1.extractAndSimplify)(response.data, returnNode)];
84
+ }
85
+ });
86
+ }); }, fullUrl, data, __assign(__assign(__assign(__assign({}, (_h = config.options.graphQLConnection) === null || _h === void 0 ? void 0 : _h.headers), (_j = config.options) === null || _j === void 0 ? void 0 : _j.headers), headersObj), headers), returnNode)];
81
87
  case 2:
82
- response = _h.sent();
83
- return [2, (0, _operatorUtils_1.extractAndSimplify)(response.data, returnNode)];
88
+ result = _k.sent();
89
+ return [2, result];
84
90
  }
85
91
  });
86
92
  }); };
@@ -3,7 +3,7 @@ declare const requiredProperties: string[];
3
3
  export type ObjPropNode = {
4
4
  [key in typeof requiredProperties[number]]: EvaluatorNode;
5
5
  } & BaseOperatorNode & {
6
- additionalObjects: object;
6
+ additionalData: object;
7
7
  };
8
8
  export declare const OBJECT_PROPERTIES: OperatorObject;
9
9
  export {};
@@ -54,24 +54,34 @@ exports.OBJECT_PROPERTIES = void 0;
54
54
  var object_property_extractor_1 = __importDefault(require("object-property-extractor"));
55
55
  var _operatorUtils_1 = require("./_operatorUtils");
56
56
  var requiredProperties = ['property'];
57
- var operatorAliases = ['objectProperties', 'objProps', 'getProperty', 'getObjProp'];
57
+ var operatorAliases = [
58
+ 'dataProperties',
59
+ 'data',
60
+ 'getData',
61
+ 'objectProperties',
62
+ 'objProps',
63
+ 'getProperty',
64
+ 'getObjProp',
65
+ ];
58
66
  var propertyAliases = {
59
67
  path: 'property',
60
68
  propertyName: 'property',
61
- additional: 'additionalObjects',
62
- objects: 'additionalObjects',
69
+ additional: 'additionalData',
70
+ objects: 'additionalData',
71
+ data: 'additionalData',
72
+ additionalObjects: 'additionalData',
63
73
  };
64
74
  var evaluate = function (expression, config) { return __awaiter(void 0, void 0, void 0, function () {
65
- var _a, property, additionalObjects, inputObject;
75
+ var _a, property, additionalData, inputObject;
66
76
  var _b, _c, _d, _e;
67
77
  return __generator(this, function (_f) {
68
78
  switch (_f.label) {
69
- case 0: return [4, (0, _operatorUtils_1.evaluateArray)([expression.property, expression.additionalObjects], config)];
79
+ case 0: return [4, (0, _operatorUtils_1.evaluateArray)([expression.property, expression.additionalData], config)];
70
80
  case 1:
71
- _a = (_f.sent()), property = _a[0], additionalObjects = _a[1];
72
- config.typeChecker({ name: 'property', value: property, expectedType: 'string' }, { name: 'additionalObjects', value: additionalObjects, expectedType: ['object', 'undefined'] });
73
- inputObject = additionalObjects
74
- ? __assign(__assign({}, ((_c = (_b = config.options) === null || _b === void 0 ? void 0 : _b.objects) !== null && _c !== void 0 ? _c : {})), additionalObjects) : (_e = (_d = config.options) === null || _d === void 0 ? void 0 : _d.objects) !== null && _e !== void 0 ? _e : {};
81
+ _a = (_f.sent()), property = _a[0], additionalData = _a[1];
82
+ config.typeChecker({ name: 'property', value: property, expectedType: 'string' }, { name: 'additionalData', value: additionalData, expectedType: ['object', 'undefined'] });
83
+ inputObject = additionalData
84
+ ? __assign(__assign({}, ((_c = (_b = config.options) === null || _b === void 0 ? void 0 : _b.data) !== null && _c !== void 0 ? _c : {})), additionalData) : (_e = (_d = config.options) === null || _d === void 0 ? void 0 : _d.data) !== null && _e !== void 0 ? _e : {};
75
85
  return [2, (0, object_property_extractor_1.default)(inputObject, property)];
76
86
  }
77
87
  });
@@ -62,25 +62,41 @@ var requiredProperties = ['query'];
62
62
  var operatorAliases = ['pgSql', 'sql', 'postgres', 'pg', 'pgDb'];
63
63
  var propertyAliases = { replacements: 'values' };
64
64
  var evaluate = function (expression, config) { return __awaiter(void 0, void 0, void 0, function () {
65
- var _a, query, values, err_1;
65
+ var _a, query, values, shouldUseCache, result;
66
66
  var _b;
67
- return __generator(this, function (_c) {
68
- switch (_c.label) {
67
+ var _c, _d, _e;
68
+ return __generator(this, function (_f) {
69
+ switch (_f.label) {
69
70
  case 0: return [4, (0, _operatorUtils_1.evaluateArray)(__spreadArray([expression.query], (expression.values || []), true), config)];
70
71
  case 1:
71
- _a = (_c.sent()), query = _a[0], values = _a.slice(1);
72
+ _a = (_f.sent()), query = _a[0], values = _a.slice(1);
72
73
  config.typeChecker({ name: 'query', value: query, expectedType: 'string' }, { name: 'values', value: values, expectedType: 'array' });
73
- if (!((_b = config.options) === null || _b === void 0 ? void 0 : _b.pgConnection))
74
+ if (!((_c = config.options) === null || _c === void 0 ? void 0 : _c.pgConnection))
74
75
  throw new Error('No Postgres database connection provided');
75
- _c.label = 2;
76
- case 2:
77
- _c.trys.push([2, 4, , 5]);
78
- return [4, processPgSQL(__spreadArray([query], values, true), config.options.pgConnection, expression === null || expression === void 0 ? void 0 : expression.type)];
79
- case 3: return [2, _c.sent()];
80
- case 4:
81
- err_1 = _c.sent();
82
- throw err_1;
83
- case 5: return [2];
76
+ shouldUseCache = (_e = (_d = expression.useCache) !== null && _d !== void 0 ? _d : config.options.useCache) !== null && _e !== void 0 ? _e : true;
77
+ try {
78
+ result = (_b = config.cache).useCache.apply(_b, __spreadArray([shouldUseCache,
79
+ function (query) {
80
+ var values = [];
81
+ for (var _i = 1; _i < arguments.length; _i++) {
82
+ values[_i - 1] = arguments[_i];
83
+ }
84
+ return __awaiter(void 0, void 0, void 0, function () {
85
+ return __generator(this, function (_a) {
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)];
88
+ case 1: return [2, _a.sent()];
89
+ }
90
+ });
91
+ });
92
+ },
93
+ query], values, false));
94
+ return [2, result];
95
+ }
96
+ catch (err) {
97
+ throw err;
98
+ }
99
+ return [2];
84
100
  }
85
101
  });
86
102
  }); };
@@ -89,7 +105,7 @@ var parseChildren = function (expression) {
89
105
  return __assign(__assign({}, expression), { query: query, values: values });
90
106
  };
91
107
  var processPgSQL = function (queryArray, connection, queryType) { return __awaiter(void 0, void 0, void 0, function () {
92
- var expression, res, result, err_2;
108
+ var expression, res, result, err_1;
93
109
  return __generator(this, function (_a) {
94
110
  switch (_a.label) {
95
111
  case 0:
@@ -119,8 +135,8 @@ var processPgSQL = function (queryArray, connection, queryType) { return __await
119
135
  }
120
136
  return [3, 4];
121
137
  case 3:
122
- err_2 = _a.sent();
123
- throw err_2;
138
+ err_1 = _a.sent();
139
+ throw err_1;
124
140
  case 4: return [2];
125
141
  }
126
142
  });
@@ -54,26 +54,37 @@ var requiredProperties = ['url'];
54
54
  var operatorAliases = ['post'];
55
55
  var propertyAliases = { endpoint: 'url', outputProperty: 'returnProperty' };
56
56
  var evaluate = function (expression, config) { return __awaiter(void 0, void 0, void 0, function () {
57
- var _a, urlObj, data, returnProperty, headers, _b, url, headersObj, baseUrl, httpHeaders, response;
58
- var _c, _d;
59
- return __generator(this, function (_e) {
60
- switch (_e.label) {
57
+ var _a, urlObj, data, returnProperty, headers, _b, url, headersObj, baseUrl, httpHeaders, shouldUseCache, result;
58
+ var _c, _d, _e, _f;
59
+ return __generator(this, function (_g) {
60
+ switch (_g.label) {
61
61
  case 0: return [4, (0, _operatorUtils_1.evaluateArray)([expression.url, expression.parameters, expression.returnProperty, expression.headers], config)];
62
62
  case 1:
63
- _a = (_e.sent()), urlObj = _a[0], data = _a[1], returnProperty = _a[2], headers = _a[3];
63
+ _a = (_g.sent()), urlObj = _a[0], data = _a[1], returnProperty = _a[2], headers = _a[3];
64
64
  _b = urlObj instanceof Object ? urlObj : { url: urlObj, headers: null }, url = _b.url, headersObj = _b.headers;
65
65
  config.typeChecker({ name: 'url', value: url, expectedType: 'string' }, { name: 'headers', value: headersObj, expectedType: ['object', 'null'] }, { name: 'data', value: data, expectedType: ['object', 'undefined'] }, { name: 'returnProperty', value: returnProperty, expectedType: ['string', 'undefined'] });
66
66
  baseUrl = (_c = config.options.baseEndpoint) !== null && _c !== void 0 ? _c : '';
67
67
  httpHeaders = __assign(__assign(__assign({}, (_d = config.options) === null || _d === void 0 ? void 0 : _d.headers), headersObj), headers);
68
- return [4, (0, _operatorUtils_1.axiosRequest)({
69
- url: (0, _operatorUtils_1.isFullUrl)(url) ? url : (0, _operatorUtils_1.joinUrlParts)(baseUrl, url),
70
- data: data,
71
- headers: httpHeaders,
72
- method: 'post',
73
- })];
68
+ shouldUseCache = (_f = (_e = expression.useCache) !== null && _e !== void 0 ? _e : config.options.useCache) !== null && _f !== void 0 ? _f : true;
69
+ return [4, config.cache.useCache(shouldUseCache, function (url, data, headers, returnProperty) { return __awaiter(void 0, void 0, void 0, function () {
70
+ var response;
71
+ return __generator(this, function (_a) {
72
+ switch (_a.label) {
73
+ case 0: return [4, (0, _operatorUtils_1.axiosRequest)({
74
+ url: url,
75
+ data: data,
76
+ headers: headers,
77
+ method: 'post',
78
+ })];
79
+ case 1:
80
+ response = _a.sent();
81
+ return [2, (0, _operatorUtils_1.extractAndSimplify)(response, returnProperty)];
82
+ }
83
+ });
84
+ }); }, (0, _operatorUtils_1.isFullUrl)(url) ? url : (0, _operatorUtils_1.joinUrlParts)(baseUrl, url), data, httpHeaders, returnProperty)];
74
85
  case 2:
75
- response = _e.sent();
76
- return [2, (0, _operatorUtils_1.extractAndSimplify)(response, returnProperty)];
86
+ result = _g.sent();
87
+ return [2, result];
77
88
  }
78
89
  });
79
90
  }); };
package/build/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import FigTreeCache from './cache';
1
2
  import { BasicExtendedNode, SubtractionNode, DivisionNode, ComparatorNode, ConditionalNode, RegexNode, StringSubNode, SplitNode, ObjPropNode, APINode, PGNode, GraphQLNode, BuildObjectNode, MatchNode, FunctionNode, PassThruNode, PGConnection, GraphQLConnection } from './operators';
2
3
  import { TypeCheckInput } from './typeCheck';
3
4
  export declare const Operators: readonly ["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,6 +7,7 @@ export type GenericObject = {
6
7
  [key: string]: any;
7
8
  };
8
9
  export interface FigTreeOptions {
10
+ data?: GenericObject;
9
11
  objects?: GenericObject;
10
12
  functions?: {
11
13
  [key: string]: Function;
@@ -21,6 +23,8 @@ export interface FigTreeOptions {
21
23
  allowJSONStringInput?: boolean;
22
24
  skipRuntimeTypeCheck?: boolean;
23
25
  evaluateFullObject?: boolean;
26
+ useCache?: boolean;
27
+ maxCacheSize?: number;
24
28
  }
25
29
  export interface FigTreeConfig {
26
30
  options: FigTreeOptions;
@@ -32,6 +36,7 @@ export interface FigTreeConfig {
32
36
  resolvedAliasNodes: {
33
37
  [key: string]: EvaluatorOutput;
34
38
  };
39
+ cache: FigTreeCache;
35
40
  }
36
41
  export type OutputType = 'string' | 'number' | 'boolean' | 'bool' | 'array';
37
42
  export interface BaseOperatorNode {
@@ -39,6 +44,7 @@ export interface BaseOperatorNode {
39
44
  outputType?: OutputType;
40
45
  children?: Array<EvaluatorNode>;
41
46
  fallback?: any;
47
+ useCache?: boolean;
42
48
  [key: string]: EvaluatorNode;
43
49
  }
44
50
  export type CombinedOperatorNode = BaseOperatorNode & BasicExtendedNode & SubtractionNode & DivisionNode & ComparatorNode & ConditionalNode & RegexNode & StringSubNode & SplitNode & ObjPropNode & APINode & PGNode & GraphQLNode & BuildObjectNode & MatchNode & FunctionNode & PassThruNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fig-tree-evaluator",
3
- "version": "2.2.2",
3
+ "version": "2.3.0",
4
4
  "description": "Module to evaluate JSON-structured expression trees",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",