fig-tree-evaluator 2.7.0 → 2.8.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.
Files changed (174) hide show
  1. package/README.md +194 -12
  2. package/build/FigTreeEvaluator.d.ts +18 -0
  3. package/build/FigTreeEvaluator.js +17 -0
  4. package/build/evaluate.js +16 -10
  5. package/build/helpers.d.ts +1 -0
  6. package/build/helpers.js +5 -4
  7. package/build/operators/AND/data.d.ts +5 -0
  8. package/build/operators/AND/data.js +23 -0
  9. package/build/operators/AND/index.d.ts +1 -0
  10. package/build/operators/AND/index.js +5 -0
  11. package/build/operators/{logicalAnd.d.ts → AND/operator.d.ts} +2 -3
  12. package/build/operators/AND/operator.js +54 -0
  13. package/build/operators/BUILD_OBJECT/data.d.ts +5 -0
  14. package/build/operators/BUILD_OBJECT/data.js +23 -0
  15. package/build/operators/BUILD_OBJECT/index.d.ts +1 -0
  16. package/build/operators/BUILD_OBJECT/index.js +17 -0
  17. package/build/operators/{buildObject.d.ts → BUILD_OBJECT/operator.d.ts} +2 -3
  18. package/build/operators/{buildObject.js → BUILD_OBJECT/operator.js} +29 -12
  19. package/build/operators/CONDITIONAL/data.d.ts +5 -0
  20. package/build/operators/CONDITIONAL/data.js +37 -0
  21. package/build/operators/CONDITIONAL/index.d.ts +1 -0
  22. package/build/operators/CONDITIONAL/index.js +17 -0
  23. package/build/operators/{conditional.d.ts → CONDITIONAL/operator.d.ts} +2 -3
  24. package/build/operators/{conditional.js → CONDITIONAL/operator.js} +28 -7
  25. package/build/operators/COUNT/data.d.ts +5 -0
  26. package/build/operators/COUNT/data.js +23 -0
  27. package/build/operators/COUNT/index.d.ts +1 -0
  28. package/build/operators/COUNT/index.js +17 -0
  29. package/build/operators/COUNT/operator.d.ts +2 -0
  30. package/build/operators/COUNT/operator.js +50 -0
  31. package/build/operators/CUSTOM_FUNCTIONS/data.d.ts +5 -0
  32. package/build/operators/CUSTOM_FUNCTIONS/data.js +37 -0
  33. package/build/operators/CUSTOM_FUNCTIONS/index.d.ts +1 -0
  34. package/build/operators/CUSTOM_FUNCTIONS/index.js +17 -0
  35. package/build/operators/{customFunctions.d.ts → CUSTOM_FUNCTIONS/operator.d.ts} +2 -3
  36. package/build/operators/{customFunctions.js → CUSTOM_FUNCTIONS/operator.js} +29 -22
  37. package/build/operators/DIVIDE/data.d.ts +5 -0
  38. package/build/operators/DIVIDE/data.js +44 -0
  39. package/build/operators/DIVIDE/index.d.ts +1 -0
  40. package/build/operators/DIVIDE/index.js +17 -0
  41. package/build/operators/{divide.d.ts → DIVIDE/operator.d.ts} +2 -2
  42. package/build/operators/DIVIDE/operator.js +62 -0
  43. package/build/operators/EQUAL/data.d.ts +5 -0
  44. package/build/operators/EQUAL/data.js +30 -0
  45. package/build/operators/EQUAL/index.d.ts +1 -0
  46. package/build/operators/EQUAL/index.js +17 -0
  47. package/build/operators/EQUAL/operator.d.ts +2 -0
  48. package/build/operators/{equal.js → EQUAL/operator.js} +31 -10
  49. package/build/operators/GET/data.d.ts +5 -0
  50. package/build/operators/GET/data.js +51 -0
  51. package/build/operators/GET/index.d.ts +1 -0
  52. package/build/operators/GET/index.js +5 -0
  53. package/build/operators/{getRequest.d.ts → GET/operator.d.ts} +2 -3
  54. package/build/operators/{getRequest.js → GET/operator.js} +51 -13
  55. package/build/operators/GRAPHQL/data.d.ts +5 -0
  56. package/build/operators/GRAPHQL/data.js +58 -0
  57. package/build/operators/GRAPHQL/index.d.ts +1 -0
  58. package/build/operators/GRAPHQL/index.js +17 -0
  59. package/build/operators/{graphQL.d.ts → GRAPHQL/operator.d.ts} +2 -3
  60. package/build/operators/{graphQL.js → GRAPHQL/operator.js} +29 -8
  61. package/build/operators/GREATER_THAN/data.d.ts +5 -0
  62. package/build/operators/GREATER_THAN/data.js +30 -0
  63. package/build/operators/GREATER_THAN/index.d.ts +1 -0
  64. package/build/operators/GREATER_THAN/index.js +17 -0
  65. package/build/operators/{greaterThan.d.ts → GREATER_THAN/operator.d.ts} +2 -2
  66. package/build/operators/GREATER_THAN/operator.js +55 -0
  67. package/build/operators/LESS_THAN/data.d.ts +5 -0
  68. package/build/operators/LESS_THAN/data.js +30 -0
  69. package/build/operators/LESS_THAN/index.d.ts +1 -0
  70. package/build/operators/LESS_THAN/index.js +17 -0
  71. package/build/operators/{lessThan.d.ts → LESS_THAN/operator.d.ts} +1 -1
  72. package/build/operators/LESS_THAN/operator.js +55 -0
  73. package/build/operators/MATCH/data.d.ts +5 -0
  74. package/build/operators/MATCH/data.js +30 -0
  75. package/build/operators/MATCH/index.d.ts +1 -0
  76. package/build/operators/MATCH/index.js +17 -0
  77. package/build/operators/{match.d.ts → MATCH/operator.d.ts} +2 -3
  78. package/build/operators/{match.js → MATCH/operator.js} +31 -18
  79. package/build/operators/MULTIPLY/data.d.ts +5 -0
  80. package/build/operators/MULTIPLY/data.js +23 -0
  81. package/build/operators/MULTIPLY/index.d.ts +1 -0
  82. package/build/operators/MULTIPLY/index.js +17 -0
  83. package/build/operators/{multiply.d.ts → MULTIPLY/operator.d.ts} +1 -1
  84. package/build/operators/MULTIPLY/operator.js +50 -0
  85. package/build/operators/NOT_EQUAL/data.d.ts +5 -0
  86. package/build/operators/NOT_EQUAL/data.js +30 -0
  87. package/build/operators/NOT_EQUAL/index.d.ts +1 -0
  88. package/build/operators/NOT_EQUAL/index.js +17 -0
  89. package/build/operators/{notEqual.d.ts → NOT_EQUAL/operator.d.ts} +1 -1
  90. package/build/operators/{notEqual.js → NOT_EQUAL/operator.js} +31 -10
  91. package/build/operators/OBJECT_PROPERTIES/data.d.ts +5 -0
  92. package/build/operators/OBJECT_PROPERTIES/data.js +38 -0
  93. package/build/operators/OBJECT_PROPERTIES/index.d.ts +1 -0
  94. package/build/operators/OBJECT_PROPERTIES/index.js +17 -0
  95. package/build/operators/{objectProperties.d.ts → OBJECT_PROPERTIES/operator.d.ts} +2 -3
  96. package/build/operators/{objectProperties.js → OBJECT_PROPERTIES/operator.js} +33 -24
  97. package/build/operators/OR/data.d.ts +5 -0
  98. package/build/operators/OR/data.js +23 -0
  99. package/build/operators/OR/index.d.ts +1 -0
  100. package/build/operators/OR/index.js +17 -0
  101. package/build/operators/OR/operator.d.ts +2 -0
  102. package/build/operators/OR/operator.js +50 -0
  103. package/build/operators/PASSTHRU/data.d.ts +5 -0
  104. package/build/operators/PASSTHRU/data.js +23 -0
  105. package/build/operators/PASSTHRU/index.d.ts +1 -0
  106. package/build/operators/PASSTHRU/index.js +17 -0
  107. package/build/operators/{passThru.d.ts → PASSTHRU/operator.d.ts} +3 -4
  108. package/build/operators/PASSTHRU/operator.js +52 -0
  109. package/build/operators/PG_SQL/data.d.ts +5 -0
  110. package/build/operators/PG_SQL/data.js +37 -0
  111. package/build/operators/PG_SQL/index.d.ts +1 -0
  112. package/build/operators/PG_SQL/index.js +17 -0
  113. package/build/operators/{pgSQL.d.ts → PG_SQL/operator.d.ts} +2 -2
  114. package/build/operators/{pgSQL.js → PG_SQL/operator.js} +29 -8
  115. package/build/operators/PLUS/data.d.ts +5 -0
  116. package/build/operators/PLUS/data.js +30 -0
  117. package/build/operators/PLUS/index.d.ts +1 -0
  118. package/build/operators/PLUS/index.js +17 -0
  119. package/build/operators/{plus.d.ts → PLUS/operator.d.ts} +3 -4
  120. package/build/operators/{plus.js → PLUS/operator.js} +34 -10
  121. package/build/operators/POST/data.d.ts +5 -0
  122. package/build/operators/POST/data.js +51 -0
  123. package/build/operators/POST/index.d.ts +1 -0
  124. package/build/operators/POST/index.js +17 -0
  125. package/build/operators/POST/operator.d.ts +2 -0
  126. package/build/operators/{postRequest.js → POST/operator.js} +31 -10
  127. package/build/operators/REGEX/data.d.ts +5 -0
  128. package/build/operators/REGEX/data.js +30 -0
  129. package/build/operators/REGEX/index.d.ts +1 -0
  130. package/build/operators/REGEX/index.js +17 -0
  131. package/build/operators/{regex.d.ts → REGEX/operator.d.ts} +2 -3
  132. package/build/operators/{regex.js → REGEX/operator.js} +30 -16
  133. package/build/operators/SPLIT/data.d.ts +5 -0
  134. package/build/operators/SPLIT/data.js +44 -0
  135. package/build/operators/SPLIT/index.d.ts +1 -0
  136. package/build/operators/SPLIT/index.js +17 -0
  137. package/build/operators/{split.d.ts → SPLIT/operator.d.ts} +2 -3
  138. package/build/operators/{split.js → SPLIT/operator.js} +34 -15
  139. package/build/operators/STRING_SUBSTITUTION/data.d.ts +5 -0
  140. package/build/operators/STRING_SUBSTITUTION/data.js +30 -0
  141. package/build/operators/STRING_SUBSTITUTION/index.d.ts +1 -0
  142. package/build/operators/STRING_SUBSTITUTION/index.js +17 -0
  143. package/build/operators/{stringSubstitution.d.ts → STRING_SUBSTITUTION/operator.d.ts} +2 -3
  144. package/build/operators/{stringSubstitution.js → STRING_SUBSTITUTION/operator.js} +29 -9
  145. package/build/operators/SUBTRACT/data.d.ts +5 -0
  146. package/build/operators/SUBTRACT/data.js +37 -0
  147. package/build/operators/SUBTRACT/index.d.ts +1 -0
  148. package/build/operators/SUBTRACT/index.js +17 -0
  149. package/build/operators/{subtract.d.ts → SUBTRACT/operator.d.ts} +2 -2
  150. package/build/operators/SUBTRACT/operator.js +53 -0
  151. package/build/operators/_operatorAliases.json +1 -0
  152. package/build/operators/_operatorUtils.d.ts +9 -1
  153. package/build/operators/_operatorUtils.js +19 -1
  154. package/build/operators/index.d.ts +24 -24
  155. package/build/operators/index.js +24 -27
  156. package/build/shorthandSyntax.d.ts +2 -0
  157. package/build/shorthandSyntax.js +55 -0
  158. package/build/typeCheck.d.ts +1 -1
  159. package/build/typeCheck.js +1 -0
  160. package/build/types.d.ts +30 -12
  161. package/package.json +1 -1
  162. package/build/operators/count.d.ts +0 -2
  163. package/build/operators/count.js +0 -29
  164. package/build/operators/divide.js +0 -41
  165. package/build/operators/equal.d.ts +0 -2
  166. package/build/operators/greaterThan.js +0 -35
  167. package/build/operators/lessThan.js +0 -35
  168. package/build/operators/logicalAnd.js +0 -33
  169. package/build/operators/logicalOr.d.ts +0 -2
  170. package/build/operators/logicalOr.js +0 -29
  171. package/build/operators/multiply.js +0 -29
  172. package/build/operators/passThru.js +0 -30
  173. package/build/operators/postRequest.d.ts +0 -2
  174. package/build/operators/subtract.js +0 -32
package/README.md CHANGED
@@ -50,7 +50,9 @@ A range of built-in operators are available, from simple logic, arithmetic and s
50
50
  - [CUSTOM\_FUNCTIONS](#custom_functions)
51
51
  - [Alias Nodes](#alias-nodes)
52
52
  - [Fragments](#fragments)
53
+ - [Shorthand syntax](#shorthand-syntax)
53
54
  - [Caching (Memoization)](#caching-memoization)
55
+ - [Metadata](#metadata)
54
56
  - [More examples](#more-examples)
55
57
  - [Development environment](#development-environment)
56
58
  - [Tests](#tests)
@@ -123,16 +125,16 @@ or\
123
125
  import FigTreeEvaluator from 'fig-tree-evaluator'
124
126
 
125
127
  // New evaluator instance
126
- const exp = new FigTreeEvaluator([ options ]) // See available options below
128
+ const fig = new FigTreeEvaluator([ options ]) // See available options below
127
129
 
128
130
  // Evaluate expressions
129
- exp.evaluate(expression, [options]) // Options over-ride initial options for this evaluation
131
+ fig.evaluate(expression, [options]) // Options over-ride initial options for this evaluation
130
132
  .then((result) => { // "evaluate" is async method
131
133
  // Do something with result
132
134
  })
133
135
 
134
136
  // Or within async function:
135
- const result = await exp.evaluate(expression, [options])
137
+ const result = await fig.evaluate(expression, [options])
136
138
  ```
137
139
 
138
140
  FigTreeEvaluator is written in **Typescript**, and the following types are available to import from the package:
@@ -160,14 +162,15 @@ The `options` parameter is an object with the following available properties (al
160
162
  - `excludeOperators` -- an array of operator names (or [aliases](#operator--property-aliases)) to prohibit from being used in expressions. You may wish to restrict (for example) database access via FigTree configurations, in which case these exclusions can be defined when instantiating the FigTree instance (or updated on the fly).
161
163
  - `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
162
164
  - `maxCacheSize` -- the maximum number of results that will be held in the aforementioned cache (default: `50`)
165
+ - `noShorthand` -- there is a [shorthand syntax](#shorthand-syntax) available for writing expressions. Internally, this is pre-processed into the standard expression form before evaluation. If you have no use for this and you'd rather all expressions were written with full verbosity, set `noShorthand: true` to save a small amount in performance by skipping internal pre-processing.
163
166
 
164
167
  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:
165
168
 
166
- `exp.updateOptions(options)`
169
+ `fig.updateOptions(options)`
167
170
 
168
171
  You can also retrieve the current options state at any time with:
169
172
 
170
- `exp.getOptions()`
173
+ `fig.getOptions()`
171
174
 
172
175
  It's also possible to run one-off evaluations by importing the evaluation method directly rather than using the constructor:
173
176
 
@@ -701,11 +704,11 @@ const user = {
701
704
  ],
702
705
  }
703
706
 
704
- const exp = new FigTreeEvaluator()
707
+ const fig = new FigTreeEvaluator()
705
708
 
706
709
  const expression = getExpressionFromConfig()
707
710
 
708
- exp.evaluate(expression, { data: { user } })
711
+ fig.evaluate(expression, { data: { user } })
709
712
  ```
710
713
 
711
714
  Here is the result of various values of `expression:`
@@ -732,7 +735,7 @@ Here is the result of various values of `expression:`
732
735
  Notice the last example pulls multiple values out of an array of objects, in this case the "name". This is essentially a shorthand for:
733
736
 
734
737
  ```js
735
- exp.evaluate(
738
+ fig.evaluate(
736
739
  { operator: 'getProperty', path: 'user.enemies' },
737
740
  { data: { user } }
738
741
  ).map((e) => e.name)
@@ -912,7 +915,7 @@ e.g.
912
915
  `children` array: `[urlObject, parameterKeys, ...values, returnProperty]`
913
916
 
914
917
  - `urlObject`: either a url string, or an object structured as `{url: <string>, headers: <object>}` (if additional headers are required)
915
- - `parameterKeys`: an array of strings representing the keys of any query parameters
918
+ - `parameterKeys`: an array of strings representing the keys of any query parameters (or just a single string if only one)
916
919
  - `...values`: one value for each key specified in `parameterKeys`
917
920
  - `returnProperty` (optional): as above
918
921
 
@@ -1063,7 +1066,7 @@ const pgConnect = new Client(pgConfig) // pgConfig = database details, see node-
1063
1066
 
1064
1067
  pgConnect.connect()
1065
1068
 
1066
- const exp = new FigTreeEvaluator({ pgConnection: pgConnect })
1069
+ const fig = new FigTreeEvaluator({ pgConnection: pgConnect })
1067
1070
  ```
1068
1071
 
1069
1072
  The following examples query a default installation of the [Northwind](https://github.com/pthom/northwind_psql) demo database.
@@ -1280,7 +1283,7 @@ Custom functions are stored in the evaluator `options`, in the `functions` prope
1280
1283
 
1281
1284
  For examples, consider the following fig-tree instance:
1282
1285
  ```js
1283
- const exp = new FigTreeEvaluator({
1286
+ const fig = new FigTreeEvaluator({
1284
1287
  functions: {
1285
1288
  double: (x) => x * 2,
1286
1289
  getCurrentYear: () => new Date().toLocaleString('en', { year: 'numeric' }),
@@ -1421,7 +1424,7 @@ The idea is that you can "hard-code" some common expressions into your app, so t
1421
1424
  The syntax is similar to [Alias Nodes](#alias-nodes), in that any string values prefixed with `$` will be treated as "parameters" that will be replaced during evaluation. In the above example, you would define the fragment when instantiating the evaluator like so:
1422
1425
 
1423
1426
  ```js
1424
- const exp = new FigTreeEvaluator({
1427
+ const fig = new FigTreeEvaluator({
1425
1428
  fragments: {
1426
1429
  getCapital: {
1427
1430
  operator: 'GET',
@@ -1432,6 +1435,11 @@ const exp = new FigTreeEvaluator({
1432
1435
  },
1433
1436
  returnProperty: '[0].capital',
1434
1437
  outputType: 'string',
1438
+ metadata: {
1439
+ // Not required, but useful for external consumers -- see Metadata below
1440
+ description: "Fetches the capital city of a country",
1441
+ parameters: { $country: { type: 'string', required: true } },
1442
+ }
1435
1443
  },
1436
1444
  },
1437
1445
  })
@@ -1459,6 +1467,63 @@ See `22_fragments.test.ts` for more complex examples.
1459
1467
 
1460
1468
  Unlike Alias Nodes, which are evaluated *once* and then the result re-used whenever the alias is referenced, Fragments are evaluated every time, as the input parameters may differ.
1461
1469
 
1470
+ ## Shorthand syntax
1471
+
1472
+ It's possible to express FigTree expressions in a more compact syntax, as follows:
1473
+
1474
+ - Fragment and Operator nodes can be represented by putting the name of the fragment or operator as a property name (prefixed by `$`), then putting the parameters in an object as the property value. For example:
1475
+ ```js
1476
+ {
1477
+ operator: 'regex',
1478
+ string: "home@myplace.com",
1479
+ pattern: '^[A-Za-z0-9.]+@[A-Za-z0-9]+\\.[A-Za-z0-9.]+$'
1480
+ }
1481
+ ```
1482
+ can be written as:
1483
+ ```js
1484
+ {
1485
+ $regex:
1486
+ {
1487
+ string: 'home@myplace.com',
1488
+ pattern: '^[A-Za-z0-9.]+@[A-Za-z0-9]+\\.[A-Za-z0-9.]+$'
1489
+ },
1490
+ }
1491
+ ```
1492
+
1493
+ - For operator nodes (not fragments, as they always require named parameters), parameters can be represented positionally (equivalent to the `children` property in normal operator nodes) in an array. The above example could also be expressed as:
1494
+ ```js
1495
+ {
1496
+ $regex: [ 'home@myplace.com', '^[A-Za-z0-9.]+@[A-Za-z0-9]+\\.[A-Za-z0-9.]+$' ]
1497
+ }
1498
+ ```
1499
+
1500
+ - Operator nodes with a single parameter can just be placed directly as the single property value. For example:
1501
+ ```js
1502
+ {
1503
+ operator: "getData",
1504
+ property: "user.firstName"
1505
+ }
1506
+ ```
1507
+ can become, simply:
1508
+ ```js
1509
+ { $getData: "user.firstName" }
1510
+ ```
1511
+
1512
+ - Operator nodes can even be represented as strings, written to resemble functions. The above example can be reduced further to just:
1513
+ ```js
1514
+ "$getData(user.firstName)"
1515
+ ```
1516
+ Multiple parameters are interpreted positionally, as above. These string-functions *can* be nested, although it is generally recommended to limit them to "leaf" nodes for readability.
1517
+
1518
+ Fragments can also be represented in this "string-function" syntax, but only if they have no parameters, for example:
1519
+ ```js
1520
+ "$myFragment()"
1521
+ ```
1522
+ would be replaced with the fragment `myFragment`.
1523
+
1524
+ For more examples, see `23_shorthand.test.ts`, or have a play with the [demo app](https://carlosnz.github.io/fig-tree-evaluator/) app.
1525
+
1526
+
1462
1527
  ## Caching (Memoization)
1463
1528
 
1464
1529
  FigTree Evaluator has basic [memoization](https://en.wikipedia.org/wiki/Memoization) functionality for certain nodes to speed up re-evaluation when the input parameters haven't changed. There is a single cache store per FigTree instance which persists for the lifetime of the instance. By default, it remembers the last 50 results, but this can be modified using the `maxCacheSize` option.
@@ -1477,6 +1542,120 @@ This is different to the memoization provided by [Alias Nodes](#alias-nodes):
1477
1542
 
1478
1543
  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.
1479
1544
 
1545
+ ## Metadata
1546
+
1547
+ Evaluator expression can be configured by hand, with [aliases](#alias-nodes), [fragments](#fragments) and [shorthand](#shorthand-syntax) available to make the job easier.
1548
+
1549
+ However, you may wish to build an external UI for building FigTree expression. To this end, the FigTree instance provides three methods that could be useful for populating your configuration UI:
1550
+
1551
+ #### New FigTree instance
1552
+
1553
+ Containing fragments and [custom functions](#custom_functions).
1554
+
1555
+ ```js
1556
+ const fig = new FigTreeEvaluator({
1557
+ fragments: {
1558
+ getCapital: {
1559
+ operator: 'GET',
1560
+ url: {
1561
+ operator: 'stringSubstitution',
1562
+ string: 'https://restcountries.com/v3.1/name/%1',
1563
+ replacements: [ "$country" ],
1564
+ },
1565
+ returnProperty: '[0].capital',
1566
+ outputType: 'string',
1567
+ // Metadata used by getFragments() (below)
1568
+ metadata: {
1569
+ description: "Fetches the capital city of a country",
1570
+ parameters: { $country: { type: 'string', required: true } },
1571
+ }
1572
+ },
1573
+ ... // More fragments
1574
+ },
1575
+ functions: {
1576
+ doubleArray: (...args) => args.map((e) => e + e),
1577
+ getDate: (dateString) => new Date(dateString),
1578
+ ... // More functions
1579
+ }
1580
+ ... // More options
1581
+ })
1582
+ ```
1583
+
1584
+ #### Retrieve Operator info
1585
+
1586
+ ```js
1587
+ fig.getOperators()
1588
+ ```
1589
+
1590
+ This will return an array of operators with detailed info about their [aliases](#operator--property-aliases), and parameter requirements:
1591
+
1592
+ ```js
1593
+ [
1594
+ ...
1595
+ {
1596
+ operator: 'PLUS',
1597
+ description: 'Add, concatenate or merge multiple values',
1598
+ aliases: ['+', 'plus', 'add', 'concat', 'join', 'merge'],
1599
+ parameters: [
1600
+ {
1601
+ name: 'values',
1602
+ description: 'Array of values to check to add together',
1603
+ aliases: [],
1604
+ required: true,
1605
+ type: 'array',
1606
+ },
1607
+ {
1608
+ name: 'type',
1609
+ description: 'Data type to coerce input values to before addition',
1610
+ aliases: [],
1611
+ required: false,
1612
+ type: 'string',
1613
+ },
1614
+ ],
1615
+ },
1616
+ ... // More operators
1617
+ ]
1618
+ ```
1619
+
1620
+ #### Retrieve Fragment info
1621
+
1622
+ Because Fragments are defined within the FigTree instance, optional metadata can be provided to make working with these fragments easier in a configuration UI:
1623
+
1624
+ ```js
1625
+ fig.getOperators()
1626
+ ```
1627
+
1628
+ This will return something like:
1629
+
1630
+ ```js
1631
+ [
1632
+ {
1633
+ name: 'getCapital',
1634
+ description: 'Fetches the capital city of a country',
1635
+ parameters: { $country: { type: 'string', required: true } },
1636
+ },
1637
+ { name: 'simpleFragment' }, // No metadata provided
1638
+ ... // More fragments
1639
+ ]
1640
+ ```
1641
+
1642
+ #### Retrieve customFunction info
1643
+
1644
+ Similarly, we can fetch basic info about custom functions in the current FigTree instance, although with more limited detail:
1645
+ ```js
1646
+ fig.getCustomFunctions()
1647
+ ```
1648
+
1649
+ Returns:
1650
+
1651
+ ```js
1652
+ [
1653
+ { name: 'doubleArray', numRequiredArgs: 0 },
1654
+ { name: 'getDate', numRequiredArgs: 1 },
1655
+ ... // More functions
1656
+ ]
1657
+ ```
1658
+
1480
1659
  ## More examples
1481
1660
 
1482
1661
  More examples, included large, complex expressions can be found within the test suites in the [repository](https://github.com/CarlosNZ/fig-tree-evaluator).
@@ -1511,6 +1690,9 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
1511
1690
 
1512
1691
  *Trivial upgrades (e.g. documentation, small re-factors, types, etc.) not included*
1513
1692
 
1693
+ - **v2.8.0**:
1694
+ - [Shorthand syntax](#shorthand-syntax) (#80)
1695
+ - Methods to retrieve [metadata](#metadata) about operators, fragments and functions (#82)
1514
1696
  - **v2.7.0**: Add `excludeOperators` option to allow certain operators to be prohibited (e.g. database lookups) (#54)
1515
1697
  - **v2.6.0**: Resolve alias nodes that are not part of an Operator node when `evaluateFullObject` is enabled (#78)
1516
1698
  - **v2.5.0**:
@@ -9,6 +9,24 @@ declare class FigTreeEvaluator {
9
9
  evaluate(expression: EvaluatorNode, options?: FigTreeOptions): Promise<import("./types").EvaluatorOutput>;
10
10
  getOptions(): FigTreeOptions;
11
11
  updateOptions(options: FigTreeOptions): void;
12
+ getOperators(): {
13
+ description: string;
14
+ aliases: string[];
15
+ parameters: import("./types").Parameter[];
16
+ operator: string;
17
+ }[];
18
+ getFragments(): {
19
+ description?: string | undefined;
20
+ parameters?: Record<string, {
21
+ type: string | string[];
22
+ required: boolean;
23
+ }> | undefined;
24
+ name: string;
25
+ }[];
26
+ getCustomFunctions(): {
27
+ name: string;
28
+ numRequiredArgs: number;
29
+ }[];
12
30
  getVersion: () => any;
13
31
  }
14
32
  export default FigTreeEvaluator;
@@ -84,6 +84,23 @@ class FigTreeEvaluator {
84
84
  if (this.options.excludeOperators)
85
85
  this.operators = (0, helpers_1.filterOperators)(operators, this.options.excludeOperators, operatorAliases);
86
86
  }
87
+ getOperators() {
88
+ const validOperators = this.options.excludeOperators
89
+ ? (0, helpers_1.filterOperators)(operators, this.options.excludeOperators, operatorAliases)
90
+ : this.operators;
91
+ return Object.entries(validOperators).map(([key, value]) => (Object.assign({ operator: key }, value.operatorData)));
92
+ }
93
+ getFragments() {
94
+ var _a;
95
+ return Object.entries((_a = this.options.fragments) !== null && _a !== void 0 ? _a : {}).map(([key, value]) => (Object.assign({ name: key }, value === null || value === void 0 ? void 0 : value.metadata)));
96
+ }
97
+ getCustomFunctions() {
98
+ var _a;
99
+ return Object.entries((_a = this.options.functions) !== null && _a !== void 0 ? _a : {}).map(([name, value]) => ({
100
+ name,
101
+ numRequiredArgs: value.length,
102
+ }));
103
+ }
87
104
  }
88
105
  exports.default = FigTreeEvaluator;
89
106
  const evaluateExpression = (expression, options) => new FigTreeEvaluator(options).evaluate(expression);
package/build/evaluate.js CHANGED
@@ -11,11 +11,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.evaluatorFunction = void 0;
13
13
  const _operatorUtils_1 = require("./operators/_operatorUtils");
14
+ const shorthandSyntax_1 = require("./shorthandSyntax");
14
15
  const helpers_1 = require("./helpers");
15
16
  const evaluatorFunction = (input, config) => __awaiter(void 0, void 0, void 0, function* () {
16
- var _a, _b, _c;
17
+ var _a, _b, _c, _d;
17
18
  const { options, operators, operatorAliases } = config;
18
19
  let expression = (options === null || options === void 0 ? void 0 : options.allowJSONStringInput) ? (0, helpers_1.parseIfJson)(input) : input;
20
+ expression = (0, shorthandSyntax_1.preProcessShorthand)(expression, (_a = config.options) === null || _a === void 0 ? void 0 : _a.fragments, !options.noShorthand);
19
21
  if (Array.isArray(expression)) {
20
22
  expression = yield (0, _operatorUtils_1.evaluateArray)(expression, config);
21
23
  }
@@ -29,14 +31,14 @@ const evaluatorFunction = (input, config) => __awaiter(void 0, void 0, void 0, f
29
31
  return (0, helpers_1.replaceAliasNodeValues)(expression, config);
30
32
  }
31
33
  const { fallback } = expression;
32
- const returnErrorAsString = (_a = options === null || options === void 0 ? void 0 : options.returnErrorAsString) !== null && _a !== void 0 ? _a : false;
34
+ const returnErrorAsString = (_b = options === null || options === void 0 ? void 0 : options.returnErrorAsString) !== null && _b !== void 0 ? _b : false;
33
35
  if (isFragment) {
34
36
  const [fragment, parameters] = (yield (0, _operatorUtils_1.evaluateArray)([expression.fragment, expression.parameters], config));
35
- const fragmentReplacement = (_b = options === null || options === void 0 ? void 0 : options.fragments) === null || _b === void 0 ? void 0 : _b[fragment];
37
+ const fragmentReplacement = (0, shorthandSyntax_1.preProcessShorthand)((_c = options === null || options === void 0 ? void 0 : options.fragments) === null || _c === void 0 ? void 0 : _c[fragment], options.fragments, !options.noShorthand);
36
38
  if (fragmentReplacement === undefined)
37
39
  return (0, helpers_1.fallbackOrError)(yield (0, exports.evaluatorFunction)(fallback, config), `Fragment not defined: ${fragment}`, returnErrorAsString);
38
40
  if (!(0, helpers_1.isOperatorNode)(fragmentReplacement))
39
- return fragmentReplacement;
41
+ return (0, helpers_1.replaceAliasNodeValues)(fragmentReplacement, config);
40
42
  expression = Object.assign(Object.assign(Object.assign({}, expression), fragmentReplacement), parameters);
41
43
  delete expression.fragment;
42
44
  delete expression.parameters;
@@ -49,25 +51,29 @@ const evaluatorFunction = (input, config) => __awaiter(void 0, void 0, void 0, f
49
51
  return (0, helpers_1.fallbackOrError)(yield (0, exports.evaluatorFunction)(fallback, config), `Excluded operator: ${expression.operator}`, returnErrorAsString);
50
52
  const { requiredProperties, propertyAliases, evaluate, parseChildren } = operators[operator];
51
53
  expression = (0, helpers_1.mapPropertyAliases)(propertyAliases, expression);
52
- config.resolvedAliasNodes = Object.assign(Object.assign({}, config.resolvedAliasNodes), (yield (0, helpers_1.evaluateNodeAliases)(expression, config)));
54
+ const newAliasNodes = yield (0, helpers_1.evaluateNodeAliases)(expression, config);
55
+ if (!isFragment)
56
+ Object.entries(newAliasNodes).forEach(([alias, result]) => (config.resolvedAliasNodes[alias] = result));
57
+ const childConfig = isFragment
58
+ ? Object.assign(Object.assign({}, config), { resolvedAliasNodes: Object.assign(Object.assign({}, config.resolvedAliasNodes), newAliasNodes) }) : config;
53
59
  const validationError = (0, helpers_1.checkRequiredNodes)(requiredProperties, expression);
54
60
  if (validationError)
55
- return (0, helpers_1.fallbackOrError)(yield (0, exports.evaluatorFunction)(fallback, config), `Operator: ${operator}\n- ${validationError}`, returnErrorAsString);
61
+ return (0, helpers_1.fallbackOrError)(yield (0, exports.evaluatorFunction)(fallback, childConfig), `Operator: ${operator}\n- ${validationError}`, returnErrorAsString);
56
62
  if ('children' in expression) {
57
63
  if (!Array.isArray(expression.children))
58
- expression.children = yield (0, exports.evaluatorFunction)(expression.children, config);
64
+ expression.children = yield (0, exports.evaluatorFunction)(expression.children, childConfig);
59
65
  if (!Array.isArray(expression.children))
60
66
  return (0, helpers_1.fallbackOrError)(yield (0, exports.evaluatorFunction)(fallback, config), `Operator: ${operator}\n- Property "children" is not of type: array`, returnErrorAsString);
61
- expression = yield parseChildren(expression, config);
67
+ expression = yield parseChildren(expression, childConfig);
62
68
  }
63
69
  let result;
64
70
  try {
65
- result = yield evaluate(expression, config);
71
+ result = yield evaluate(expression, childConfig);
66
72
  }
67
73
  catch (err) {
68
74
  result = (0, helpers_1.fallbackOrError)(yield (0, exports.evaluatorFunction)(expression.fallback, config), `Operator: ${operator}\n${(0, helpers_1.errorMessage)(err)}`, returnErrorAsString);
69
75
  }
70
- const outputType = (_c = expression === null || expression === void 0 ? void 0 : expression.outputType) !== null && _c !== void 0 ? _c : expression === null || expression === void 0 ? void 0 : expression.type;
76
+ const outputType = (_d = expression === null || expression === void 0 ? void 0 : expression.outputType) !== null && _d !== void 0 ? _d : expression === null || expression === void 0 ? void 0 : expression.type;
71
77
  if (!outputType)
72
78
  return result;
73
79
  const evaluatedOutputType = (yield (0, exports.evaluatorFunction)(outputType, config));
@@ -34,6 +34,7 @@ export declare const fallbackOrError: (fallback: any, errorMessage: string, retu
34
34
  export declare const mapPropertyAliases: (propertyAliases: {
35
35
  [key: string]: string;
36
36
  }, expression: CombinedOperatorNode) => CombinedOperatorNode;
37
+ export declare const isAliasString: (value: string) => boolean;
37
38
  export declare const evaluateNodeAliases: (expression: OperatorNodeUnion, config: FigTreeConfig) => Promise<{
38
39
  [x: string]: EvaluatorOutput;
39
40
  }>;
package/build/helpers.js CHANGED
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.evaluateObject = exports.isObject = exports.errorMessage = exports.convertOutputMethods = exports.mergeOptions = exports.checkRequiredNodes = exports.replaceAliasNodeValues = exports.evaluateNodeAliases = exports.mapPropertyAliases = exports.fallbackOrError = exports.truncateString = exports.filterOperators = exports.getOperatorName = exports.isFragmentNode = exports.isOperatorNode = exports.parseIfJson = void 0;
12
+ exports.evaluateObject = exports.isObject = exports.errorMessage = exports.convertOutputMethods = exports.mergeOptions = exports.checkRequiredNodes = exports.replaceAliasNodeValues = exports.evaluateNodeAliases = exports.isAliasString = exports.mapPropertyAliases = exports.fallbackOrError = exports.truncateString = exports.filterOperators = exports.getOperatorName = exports.isFragmentNode = exports.isOperatorNode = exports.parseIfJson = void 0;
13
13
  const change_case_1 = require("change-case");
14
14
  const evaluate_1 = require("./evaluate");
15
15
  const _operatorUtils_1 = require("./operators/_operatorUtils");
@@ -65,8 +65,9 @@ const mapObjectKeys = (inputObj, mapFunction) => {
65
65
  return Object.fromEntries(mappedKeys);
66
66
  };
67
67
  const isAliasString = (value) => /^\$.+/.test(value);
68
+ exports.isAliasString = isAliasString;
68
69
  const evaluateNodeAliases = (expression, config) => __awaiter(void 0, void 0, void 0, function* () {
69
- const aliasKeys = Object.keys(expression).filter(isAliasString);
70
+ const aliasKeys = Object.keys(expression).filter(exports.isAliasString);
70
71
  if (aliasKeys.length === 0)
71
72
  return {};
72
73
  const evaluations = [];
@@ -76,7 +77,7 @@ const evaluateNodeAliases = (expression, config) => __awaiter(void 0, void 0, vo
76
77
  exports.evaluateNodeAliases = evaluateNodeAliases;
77
78
  const replaceAliasNodeValues = (value, { resolvedAliasNodes }) => {
78
79
  var _a;
79
- if (typeof value !== 'string' || !isAliasString(value))
80
+ if (typeof value !== 'string' || !(0, exports.isAliasString)(value))
80
81
  return value;
81
82
  return (_a = resolvedAliasNodes === null || resolvedAliasNodes === void 0 ? void 0 : resolvedAliasNodes[value]) !== null && _a !== void 0 ? _a : value;
82
83
  };
@@ -129,7 +130,7 @@ const evaluateObject = (input, config) => __awaiter(void 0, void 0, void 0, func
129
130
  const newObjectEntries = [];
130
131
  const newAliases = [];
131
132
  Object.entries(input).forEach(([key, value]) => {
132
- if (isAliasString(key)) {
133
+ if ((0, exports.isAliasString)(key)) {
133
134
  newAliases.push(key, (0, evaluate_1.evaluatorFunction)(value, config));
134
135
  delete input[key];
135
136
  }
@@ -0,0 +1,5 @@
1
+ import { OperatorData } from '../../types';
2
+ export declare const requiredProperties: string[];
3
+ export declare const propertyAliases: Record<string, string>;
4
+ declare const operatorData: OperatorData;
5
+ export default operatorData;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.propertyAliases = exports.requiredProperties = void 0;
4
+ const _operatorUtils_1 = require("../_operatorUtils");
5
+ const description = 'Logical AND';
6
+ const aliases = ['and', '&', '&&'];
7
+ const parameters = [
8
+ {
9
+ name: 'values',
10
+ description: 'Returns true if all values are true',
11
+ aliases: [],
12
+ required: true,
13
+ type: 'array',
14
+ },
15
+ ];
16
+ exports.requiredProperties = (0, _operatorUtils_1.getRequiredProperties)(parameters);
17
+ exports.propertyAliases = (0, _operatorUtils_1.getPropertyAliases)(parameters);
18
+ const operatorData = {
19
+ description,
20
+ aliases,
21
+ parameters,
22
+ };
23
+ exports.default = operatorData;
@@ -0,0 +1 @@
1
+ export { AND, type BasicExtendedNode } from './operator';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AND = void 0;
4
+ var operator_1 = require("./operator");
5
+ Object.defineProperty(exports, "AND", { enumerable: true, get: function () { return operator_1.AND; } });
@@ -1,8 +1,7 @@
1
- import { EvaluatorNode, BaseOperatorNode, CombinedOperatorNode, OperatorObject } from '../types';
2
- declare const requiredProperties: readonly ["values"];
1
+ import { EvaluatorNode, BaseOperatorNode, CombinedOperatorNode, OperatorObject } from '../../types';
2
+ import { requiredProperties } from './data';
3
3
  export type BasicExtendedNode = {
4
4
  [key in typeof requiredProperties[number]]: EvaluatorNode[];
5
5
  } & BaseOperatorNode;
6
6
  export declare const parseChildren: (expression: CombinedOperatorNode) => BasicExtendedNode;
7
7
  export declare const AND: OperatorObject;
8
- export {};
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.AND = exports.parseChildren = void 0;
36
+ const _operatorUtils_1 = require("../_operatorUtils");
37
+ const data_1 = __importStar(require("./data"));
38
+ const evaluate = (expression, config) => __awaiter(void 0, void 0, void 0, function* () {
39
+ const values = (yield (0, _operatorUtils_1.evaluateArray)(expression.values, config));
40
+ config.typeChecker(...(0, _operatorUtils_1.getTypeCheckInput)(data_1.default.parameters, { values }));
41
+ return values.reduce((acc, val) => acc && !!val, true);
42
+ });
43
+ const parseChildren = (expression) => {
44
+ const values = expression.children;
45
+ return Object.assign(Object.assign({}, expression), { values });
46
+ };
47
+ exports.parseChildren = parseChildren;
48
+ exports.AND = {
49
+ requiredProperties: data_1.requiredProperties,
50
+ propertyAliases: data_1.propertyAliases,
51
+ operatorData: data_1.default,
52
+ evaluate,
53
+ parseChildren: exports.parseChildren,
54
+ };
@@ -0,0 +1,5 @@
1
+ import { OperatorData } from '../../types';
2
+ export declare const requiredProperties: string[];
3
+ export declare const propertyAliases: Record<string, string>;
4
+ declare const operatorData: OperatorData;
5
+ export default operatorData;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.propertyAliases = exports.requiredProperties = void 0;
4
+ const _operatorUtils_1 = require("../_operatorUtils");
5
+ const aliases = ['buildObject', 'build', 'object'];
6
+ const description = 'Construct an object using objects defining keys and values';
7
+ const parameters = [
8
+ {
9
+ name: 'properties',
10
+ description: 'An array of objects, each with a "key" property and a "value" property',
11
+ aliases: ['values', 'keyValPairs', 'keyValuePairs'],
12
+ required: true,
13
+ type: 'array',
14
+ },
15
+ ];
16
+ exports.requiredProperties = (0, _operatorUtils_1.getRequiredProperties)(parameters);
17
+ exports.propertyAliases = (0, _operatorUtils_1.getPropertyAliases)(parameters);
18
+ const operatorData = {
19
+ description,
20
+ aliases,
21
+ parameters,
22
+ };
23
+ exports.default = operatorData;
@@ -0,0 +1 @@
1
+ export * from './operator';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./operator"), exports);
@@ -1,7 +1,6 @@
1
- import { BaseOperatorNode, EvaluatorNode, OperatorObject } from '../types';
2
- declare const requiredProperties: readonly ["properties"];
1
+ import { BaseOperatorNode, EvaluatorNode, OperatorObject } from '../../types';
3
2
  export type BuildObjectNode = {
4
- [key in typeof requiredProperties[number]]: BuildObjectElement[];
3
+ properties: BuildObjectElement[];
5
4
  } & BaseOperatorNode;
6
5
  type BuildObjectElement = {
7
6
  key: EvaluatorNode;