fig-tree-evaluator 2.2.3 → 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)
@@ -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
@@ -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
  {
@@ -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,7 @@ 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)
1414
1431
  - **v2.2.3**: Change option `objects` name to `data` (but keep backward compatibility) (#66)
1415
1432
  - **v2.2.2**: Option to evaluate whole object if operator nodes are deep within it (#64)
1416
1433
  - **v2.2.1**: More efficient branch evaluation for condition/match operators (#63)
@@ -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/index.d.ts CHANGED
@@ -3,6 +3,7 @@ declare class FigTreeEvaluator {
3
3
  private options;
4
4
  private operators;
5
5
  private operatorAliases;
6
+ private cache;
6
7
  constructor(options?: FigTreeOptions);
7
8
  private typeChecker;
8
9
  evaluate(expression: EvaluatorNode, options?: FigTreeOptions): Promise<import("./types").EvaluatorOutput>;
package/build/index.js CHANGED
@@ -79,6 +79,7 @@ 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
84
  var FigTreeEvaluator = (function () {
84
85
  function FigTreeEvaluator(options) {
@@ -97,6 +98,7 @@ var FigTreeEvaluator = (function () {
97
98
  this.options = standardiseOptionNames(options);
98
99
  this.operators = operators;
99
100
  this.operatorAliases = _operatorAliases_json_1.default;
101
+ this.cache = new cache_1.default(options.maxCacheSize);
100
102
  }
101
103
  FigTreeEvaluator.prototype.evaluate = function (expression, options) {
102
104
  if (options === void 0) { options = {}; }
@@ -106,12 +108,15 @@ var FigTreeEvaluator = (function () {
106
108
  switch (_a.label) {
107
109
  case 0:
108
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);
109
113
  return [4, (0, evaluate_1.evaluatorFunction)(expression, {
110
114
  options: currentOptions,
111
115
  operators: this.operators,
112
116
  operatorAliases: this.operatorAliases,
113
117
  typeChecker: currentOptions.skipRuntimeTypeCheck ? function () { } : this.typeChecker,
114
118
  resolvedAliasNodes: {},
119
+ cache: this.cache,
115
120
  })];
116
121
  case 1: return [2, _a.sent()];
117
122
  }
@@ -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, data, 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
92
  _b = config.options, data = _b.data, 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)(data, 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()];
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
  }); };
@@ -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"];
@@ -22,6 +23,8 @@ export interface FigTreeOptions {
22
23
  allowJSONStringInput?: boolean;
23
24
  skipRuntimeTypeCheck?: boolean;
24
25
  evaluateFullObject?: boolean;
26
+ useCache?: boolean;
27
+ maxCacheSize?: number;
25
28
  }
26
29
  export interface FigTreeConfig {
27
30
  options: FigTreeOptions;
@@ -33,6 +36,7 @@ export interface FigTreeConfig {
33
36
  resolvedAliasNodes: {
34
37
  [key: string]: EvaluatorOutput;
35
38
  };
39
+ cache: FigTreeCache;
36
40
  }
37
41
  export type OutputType = 'string' | 'number' | 'boolean' | 'bool' | 'array';
38
42
  export interface BaseOperatorNode {
@@ -40,6 +44,7 @@ export interface BaseOperatorNode {
40
44
  outputType?: OutputType;
41
45
  children?: Array<EvaluatorNode>;
42
46
  fallback?: any;
47
+ useCache?: boolean;
43
48
  [key: string]: EvaluatorNode;
44
49
  }
45
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.3",
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",