fig-tree-evaluator 2.23.1 → 3.0.0-preview.1
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 +214 -142
- package/build/chunks/shared.js +1 -0
- package/build/editor-hints/index.d.ts +35 -0
- package/build/editor-hints/index.js +1 -0
- package/build/format/index.d.ts +31 -0
- package/build/format/index.js +1 -0
- package/build/index.d.ts +741 -240
- package/build/index.js +1 -0
- package/build/migrate/index.d.ts +55 -0
- package/build/migrate/index.js +1 -0
- package/package.json +97 -50
- package/CHANGELOG.md +0 -128
- package/build/index.cjs.js +0 -1
- package/build/index.esm.js +0 -1
package/README.md
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
**FigTree
|
|
5
|
+
_This is an early preview release for **FigTree v3**. Full documentation will be added as we get closer to release, but for those interested, look in the repo on the `v3.0-dev` branch, under `/docs-artifacts`, for an explanation and reference._
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
_The documentation below is still for `v2.x`_
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
**FigTree Evaluator** is a module to evaluate JSON-structured expression trees.
|
|
12
|
+
|
|
13
|
+
A typical use case would be for evaluating **configuration** files, where you need to store dynamic values or arbitrary logic without allowing users to inject executable code (perhaps in a .json file, say). Examples could include:
|
|
8
14
|
|
|
9
15
|
- a [form-builder app](https://github.com/openmsupply/conforma-web-app) might need to allow complex conditional logic for form element visibility based on previous responses, or for validation beyond what is available in standard validation libraries.
|
|
10
16
|
- configure a [decision tree](https://en.wikipedia.org/wiki/Decision_tree) to implement branching logic. (See implementation in `20_match.test.ts`)
|
|
@@ -12,13 +18,14 @@ A typical use case would be for evaluating **configuration** files, where you ne
|
|
|
12
18
|
|
|
13
19
|
A range of built-in operators are available, from simple logic, arithmetic and string manipulation, to data fetching from local sources or remote APIs. Plus, you can extend functionality with your own [custom operators](#custom-functionsoperators)
|
|
14
20
|
|
|
15
|
-
<!-- omit in toc -->
|
|
16
|
-
## [Try the Demo/Playground](https://carlosnz.github.io/fig-tree-evaluator/)
|
|
21
|
+
## [Try the Demo/Playground](https://carlosnz.github.io/fig-tree-evaluator/) <!-- omit in toc -->
|
|
17
22
|
|
|
18
23
|
The demo is powered by [fig-tree-editor-react](https://github.com/CarlosNZ/fig-tree-editor-react), a React component for editing FigTree expressions.
|
|
19
24
|
|
|
20
25
|
## Contents <!-- omit in toc -->
|
|
26
|
+
|
|
21
27
|
<!-- TOC -->
|
|
28
|
+
|
|
22
29
|
- [The basics](#the-basics)
|
|
23
30
|
- [Install](#install)
|
|
24
31
|
- [Usage](#usage)
|
|
@@ -68,6 +75,7 @@ The demo is powered by [fig-tree-editor-react](https://github.com/CarlosNZ/fig-t
|
|
|
68
75
|
- [Credit](#credit)
|
|
69
76
|
|
|
70
77
|
<!-- /TOC -->
|
|
78
|
+
|
|
71
79
|
## The basics
|
|
72
80
|
|
|
73
81
|
Fig-tree evaluates expressions structured in a JSON/Javascript object [expression tree](https://www.geeksforgeeks.org/expression-tree/). A single "node" of the tree consists of an **Operator**, with associated parameters (or child nodes), each of which can itself be another Operator node -- i.e. a recursive tree structure of arbitrary depth and complexity.
|
|
@@ -85,6 +93,7 @@ For example:
|
|
|
85
93
|
```
|
|
86
94
|
|
|
87
95
|
Or, with a deeper structure that results in the same final output:
|
|
96
|
+
|
|
88
97
|
```js
|
|
89
98
|
{
|
|
90
99
|
operator: '+',
|
|
@@ -132,19 +141,22 @@ or\
|
|
|
132
141
|
import { FigTreeEvaluator } from 'fig-tree-evaluator'
|
|
133
142
|
|
|
134
143
|
// New evaluator instance
|
|
135
|
-
const fig = new FigTreeEvaluator([
|
|
144
|
+
const fig = new FigTreeEvaluator([options]) // See available options below
|
|
136
145
|
|
|
137
146
|
// Evaluate expressions
|
|
138
|
-
fig
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
147
|
+
fig
|
|
148
|
+
.evaluate(expression, [options]) // Options over-ride initial options for this evaluation
|
|
149
|
+
.then((result) => {
|
|
150
|
+
// "evaluate" is async method
|
|
151
|
+
// Do something with result
|
|
152
|
+
})
|
|
142
153
|
|
|
143
154
|
// Or within async function:
|
|
144
155
|
const result = await fig.evaluate(expression, [options])
|
|
145
156
|
```
|
|
146
157
|
|
|
147
158
|
FigTreeEvaluator is written in **Typescript**, and the following types are available to import from the package:
|
|
159
|
+
|
|
148
160
|
- `FigTreeOptions`: `options` object, as per [options](#available-options) below
|
|
149
161
|
- `Operator`: string literal canonical [Operator](#operator-nodes) names (`AND`, `OR`, `EQUAL`, etc.)
|
|
150
162
|
- `EvaluatorNode`: Evaluator input
|
|
@@ -154,20 +166,20 @@ FigTreeEvaluator is written in **Typescript**, and the following types are avail
|
|
|
154
166
|
|
|
155
167
|
The `options` parameter is an object with the following available properties (all optional):
|
|
156
168
|
|
|
157
|
-
- `data` -- a single object containing any
|
|
158
|
-
- `functions` -- a single object containing any
|
|
169
|
+
- `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.
|
|
170
|
+
- `functions` -- a single object containing any _custom functions_ available for use by [custom functions/operators](#custom_functions).
|
|
159
171
|
- `fragments` -- commonly-used expressions (with optional parameters) that can be re-used in any other expression. See [Fragments](#fragments)
|
|
160
|
-
- `httpClient` -- pass your http client in here in order to use the HTTP-based operators ([`GET`](#get), [`POST`](#post), [`GraphQL`](#graphql)) (uses browser's native `fetch` by default).
|
|
172
|
+
- `httpClient` -- pass your http client in here in order to use the HTTP-based operators ([`GET`](#get), [`POST`](#post), [`GraphQL`](#graphql)) (uses browser's native `fetch` by default).
|
|
161
173
|
- `graphQLConnection` -- a GraphQL connection object, if using the [`graphQL` operator](#graphql). See operator details below.
|
|
162
174
|
- `sqlConnection` -- if you wish to make calls to an SQL database using the [`SQL` operator](#sql), pass a connection to the database here. See operator details below.
|
|
163
175
|
- `baseEndpoint` -- If specified, any partial urls specified in the http-based operators (`GET`, `POST`) will be relative to to this base. Useful if you expect most http requests to be to the same server.
|
|
164
|
-
- `headers` -- A general http headers object that will be passed to
|
|
176
|
+
- `headers` -- A general http headers object that will be passed to _all_ http-based operators (`GET`, `POST`, `GraphQL`). Useful for authentication headers, for example. Each operator and instance can have its own headers, though, so see specific operator reference for details.
|
|
165
177
|
- `returnErrorAsString` -- by default the evaluator will throw errors with invalid evaluation expressions (with helpful error messages indicating the node which threw the error and what the problem was). But if you have `returnErrorAsString: true` set, the evaluator will never throw, but instead return error messages as a valid string output. (See also the [`fallback`](#other-common-properties) parameter below). See [Error handling](#error-handling) section for more detail.
|
|
166
178
|
- `allowJSONStringInput` -- the evaluator is expecting the input expression to be a javascript object. However, it will also accept JSON strings if this option is set to `true`. We have to perform additional logic on every evaluation input to determine if a string is a JSON expression or a standard string, so this is skipped by default for performance reasons. However, if you want to send (for example) user input directly to the evaluator without running it through your own `JSON.parse()`, then enable this option.
|
|
167
179
|
- `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`)
|
|
168
|
-
- `caseInsensitive` -- this only affects the [`equal`/`notEqual` operators](#equal) (see there for more detail).
|
|
169
|
-
- `nullEqualsUndefined` -- this only affects the [`equal`/`notEqual` operators](#equal) (see there for more detail).
|
|
170
|
-
- `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
|
|
180
|
+
- `caseInsensitive` -- this only affects the [`equal`/`notEqual` operators](#equal) (see there for more detail).
|
|
181
|
+
- `nullEqualsUndefined` -- this only affects the [`equal`/`notEqual` operators](#equal) (see there for more detail).
|
|
182
|
+
- `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 within the object tree.
|
|
171
183
|
- `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).
|
|
172
184
|
- `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
|
|
173
185
|
- `maxCacheSize` -- the maximum number of results that will be held in the aforementioned cache (default: `50`)
|
|
@@ -188,7 +200,7 @@ It's also possible to run one-off evaluations by importing the evaluation method
|
|
|
188
200
|
import { evaluateExpression } from 'fig-tree-evaluator'
|
|
189
201
|
|
|
190
202
|
evaluateExpression(expression, [options]).then((result) => {
|
|
191
|
-
|
|
203
|
+
// Do something with result
|
|
192
204
|
})
|
|
193
205
|
```
|
|
194
206
|
|
|
@@ -232,12 +244,12 @@ Most of the time named properties would be preferable; however there are occasio
|
|
|
232
244
|
|
|
233
245
|
In each operator node, as well as the operator-specific properties, the following three optional properties can be provided:
|
|
234
246
|
|
|
235
|
-
- `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.
|
|
236
|
-
See [Error handling](#error-handling)
|
|
247
|
+
- `fallback`: if the operation throws an error, the `fallback` value will be returned instead. The `fallback` property can be provided at any level of the expression tree and bubbled up from where errors are caught to parent nodes. _Fallbacks are strongly recommended if there is any chance of an error (e.g. a network "GET" request that doesn't yet have its parameters defined)._
|
|
248
|
+
See [Error handling](#error-handling)
|
|
237
249
|
- `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.
|
|
238
250
|
- `useCache`: Overrides the global `useCache` value (from [options](#available-options)) for this node only. See [Caching/Memoization](#caching-memoization) below for more info.
|
|
239
251
|
|
|
240
|
-
Remember that
|
|
252
|
+
Remember that _all_ operator node properties can themselves be operator nodes, _including_ the `fallback` and `outputType` properties.
|
|
241
253
|
|
|
242
254
|
e.g.
|
|
243
255
|
|
|
@@ -275,7 +287,7 @@ The full list of available operators and their associated properties:
|
|
|
275
287
|
|
|
276
288
|
### AND
|
|
277
289
|
|
|
278
|
-
|
|
290
|
+
_Logical AND_
|
|
279
291
|
|
|
280
292
|
Aliases: `and`, `&`, `&&`
|
|
281
293
|
|
|
@@ -284,6 +296,7 @@ Aliases: `and`, `&`, `&&`
|
|
|
284
296
|
- `values`<sup>*</sup>: (array) -- any number of elements; will be compared using Javascript `&&` operator
|
|
285
297
|
|
|
286
298
|
e.g.
|
|
299
|
+
|
|
287
300
|
```js
|
|
288
301
|
{
|
|
289
302
|
operator: '&',
|
|
@@ -294,10 +307,11 @@ e.g.
|
|
|
294
307
|
|
|
295
308
|
`children` array: `[...values]`
|
|
296
309
|
|
|
297
|
-
|
|
310
|
+
---
|
|
311
|
+
|
|
298
312
|
### OR
|
|
299
313
|
|
|
300
|
-
|
|
314
|
+
_Logical OR_
|
|
301
315
|
|
|
302
316
|
Aliases: `or`, `|`, `||`
|
|
303
317
|
|
|
@@ -306,6 +320,7 @@ Aliases: `or`, `|`, `||`
|
|
|
306
320
|
- `values`<sup>*</sup>: (array) -- any number of elements; will be compared using Javascript `||` operator
|
|
307
321
|
|
|
308
322
|
e.g.
|
|
323
|
+
|
|
309
324
|
```js
|
|
310
325
|
{
|
|
311
326
|
operator: 'or',
|
|
@@ -323,10 +338,11 @@ e.g.
|
|
|
323
338
|
|
|
324
339
|
`children` array: `[...values]`
|
|
325
340
|
|
|
326
|
-
|
|
341
|
+
---
|
|
342
|
+
|
|
327
343
|
### EQUAL
|
|
328
344
|
|
|
329
|
-
|
|
345
|
+
_Equality_
|
|
330
346
|
|
|
331
347
|
Aliases: `=`, `eq`, `equal`, `equals`
|
|
332
348
|
|
|
@@ -337,6 +353,7 @@ Aliases: `=`, `eq`, `equal`, `equals`
|
|
|
337
353
|
- `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.
|
|
338
354
|
|
|
339
355
|
e.g.
|
|
356
|
+
|
|
340
357
|
```js
|
|
341
358
|
{
|
|
342
359
|
operator: '=',
|
|
@@ -347,10 +364,11 @@ e.g.
|
|
|
347
364
|
|
|
348
365
|
`children` array: `[...values]`
|
|
349
366
|
|
|
350
|
-
|
|
367
|
+
---
|
|
368
|
+
|
|
351
369
|
### NOT_EQUAL
|
|
352
370
|
|
|
353
|
-
|
|
371
|
+
_Non-equality_
|
|
354
372
|
|
|
355
373
|
Aliases: `!=`, `!`, `ne`, `notEqual`
|
|
356
374
|
|
|
@@ -361,6 +379,7 @@ Aliases: `!=`, `!`, `ne`, `notEqual`
|
|
|
361
379
|
- `nullEqualsUndefined`: (boolean, default `false`) -- as [above](#equal)
|
|
362
380
|
|
|
363
381
|
e.g.
|
|
382
|
+
|
|
364
383
|
```js
|
|
365
384
|
{
|
|
366
385
|
operator: '=',
|
|
@@ -371,19 +390,21 @@ e.g.
|
|
|
371
390
|
|
|
372
391
|
`children` array: `[...values]`
|
|
373
392
|
|
|
374
|
-
|
|
393
|
+
---
|
|
394
|
+
|
|
375
395
|
### PLUS
|
|
376
396
|
|
|
377
|
-
|
|
397
|
+
_Addition, concatenation, merging_
|
|
378
398
|
|
|
379
399
|
Aliases: `+`, `add`, `concat`, `join`, `merge`
|
|
380
400
|
|
|
381
401
|
#### Properties
|
|
382
402
|
|
|
383
403
|
- `values`<sup>*</sup>: (array) -- any number of elements. Will be added (numbers), concatenated (strings, arrays) or merged (objects) according their type.
|
|
384
|
-
- `type`: (`'string' | 'array'`) -- if specified, operator will treat the `values` as though they were this type. E.g. if `string`, it will concatenate the values, even if they're all numbers. The difference between this property and the common [`outputType` property](#other-common-properties) is that `outputType` converts the result, whereas this `type` property converts each element
|
|
404
|
+
- `type`: (`'string' | 'array'`) -- if specified, operator will treat the `values` as though they were this type. E.g. if `string`, it will concatenate the values, even if they're all numbers. The difference between this property and the common [`outputType` property](#other-common-properties) is that `outputType` converts the result, whereas this `type` property converts each element _before_ the "PLUS" operation.
|
|
385
405
|
|
|
386
406
|
e.g.
|
|
407
|
+
|
|
387
408
|
```js
|
|
388
409
|
{
|
|
389
410
|
operator: '+',
|
|
@@ -421,10 +442,11 @@ e.g.
|
|
|
421
442
|
|
|
422
443
|
`children` array: `[...values]`
|
|
423
444
|
|
|
424
|
-
|
|
445
|
+
---
|
|
446
|
+
|
|
425
447
|
### SUBTRACT
|
|
426
448
|
|
|
427
|
-
|
|
449
|
+
_Subtraction_
|
|
428
450
|
|
|
429
451
|
Aliases: `-`, `subtract`, `minus`, `takeaway`
|
|
430
452
|
|
|
@@ -433,6 +455,7 @@ Aliases: `-`, `subtract`, `minus`, `takeaway`
|
|
|
433
455
|
- `values`<sup>*</sup>: (array) -- exactly 2 numerical elements; the second will be subtracted from the first. (If non-numerical elements are provided, the operator will return `NaN`)
|
|
434
456
|
|
|
435
457
|
e.g.
|
|
458
|
+
|
|
436
459
|
```js
|
|
437
460
|
{
|
|
438
461
|
operator: '-',
|
|
@@ -455,18 +478,20 @@ e.g.
|
|
|
455
478
|
|
|
456
479
|
`children` array: `[originalValue, valueToSubtract]` (same as `values`)
|
|
457
480
|
|
|
458
|
-
|
|
481
|
+
---
|
|
482
|
+
|
|
459
483
|
### MULTIPLY
|
|
460
484
|
|
|
461
|
-
|
|
485
|
+
_Multiplication_
|
|
462
486
|
|
|
463
487
|
Aliases: `*`, `x`, `multiply`, `times`
|
|
464
488
|
|
|
465
489
|
#### Properties
|
|
466
490
|
|
|
467
|
-
- `values`<sup>*</sup>: (array) -- any number of numerical elements. Returns the product of all elements.
|
|
491
|
+
- `values`<sup>*</sup>: (array) -- any number of numerical elements. Returns the product of all elements. (If non-numerical elements are provided, the operator will return `NaN`)
|
|
468
492
|
|
|
469
493
|
e.g.
|
|
494
|
+
|
|
470
495
|
```js
|
|
471
496
|
{
|
|
472
497
|
operator: '*',
|
|
@@ -489,23 +514,25 @@ e.g.
|
|
|
489
514
|
|
|
490
515
|
`children` array: `[...values]`
|
|
491
516
|
|
|
492
|
-
|
|
517
|
+
---
|
|
518
|
+
|
|
493
519
|
### DIVIDE
|
|
494
520
|
|
|
495
|
-
|
|
521
|
+
_Division_
|
|
496
522
|
|
|
497
523
|
Aliases: `/`, `divide`, `÷`
|
|
498
524
|
|
|
499
525
|
#### Properties
|
|
500
526
|
|
|
501
|
-
- `values`: (array) -- exactly 2 numerical elements; the first will be divided by the second.
|
|
527
|
+
- `values`: (array) -- exactly 2 numerical elements; the first will be divided by the second. (If non-numerical elements are provided, the operator will return `NaN`)
|
|
502
528
|
- `dividend` (or `divide`): (number) -- the number that will be divided
|
|
503
529
|
- `divisor` (or `by`): (number) -- the number to divide `dividend` by
|
|
504
|
-
- `output` (`'quotient' | 'remainder'
|
|
530
|
+
- `output` (`'quotient' | 'remainder'`) -- by default, the operator returns a floating point value. However, if `quotient` is specified, it will return the integer part of the result; if `remainder` is specified, it will return the remainder after division (i.e. `value1 % value2`)
|
|
505
531
|
|
|
506
|
-
Note that the input values can be provided as
|
|
532
|
+
Note that the input values can be provided as _either_ a `values` array _or_ `dividend`/`divisor` properties. If both are provided, `values` takes precedence.
|
|
507
533
|
|
|
508
534
|
e.g.
|
|
535
|
+
|
|
509
536
|
```js
|
|
510
537
|
{
|
|
511
538
|
operator: '/',
|
|
@@ -517,7 +544,7 @@ e.g.
|
|
|
517
544
|
operator: '/',
|
|
518
545
|
divide: 20,
|
|
519
546
|
by: 3,
|
|
520
|
-
output: 'quotient'
|
|
547
|
+
output: 'quotient'
|
|
521
548
|
}
|
|
522
549
|
// => 6
|
|
523
550
|
|
|
@@ -532,10 +559,11 @@ e.g.
|
|
|
532
559
|
|
|
533
560
|
`children` array: `[dividend, divisor]` (same as `values`)
|
|
534
561
|
|
|
535
|
-
|
|
562
|
+
---
|
|
563
|
+
|
|
536
564
|
### GREATER_THAN
|
|
537
565
|
|
|
538
|
-
|
|
566
|
+
_Greater than (or equal to)_
|
|
539
567
|
|
|
540
568
|
Aliases: `>`, `greaterThan`, `higher`, `larger`
|
|
541
569
|
|
|
@@ -545,6 +573,7 @@ Aliases: `>`, `greaterThan`, `higher`, `larger`
|
|
|
545
573
|
- `strict`: (boolean, default `false`) -- if `true`, value 1 must be strictly greater than value 2 (i.e. `>`). Otherwise it will be compared with "greater than or equal to" (i.e. `>=`)
|
|
546
574
|
|
|
547
575
|
e.g.
|
|
576
|
+
|
|
548
577
|
```js
|
|
549
578
|
{
|
|
550
579
|
operator: '>',
|
|
@@ -568,10 +597,11 @@ e.g.
|
|
|
568
597
|
|
|
569
598
|
`children` array: `[firstValue, secondValue]` (same as `values`)
|
|
570
599
|
|
|
571
|
-
|
|
600
|
+
---
|
|
601
|
+
|
|
572
602
|
### LESS_THAN
|
|
573
603
|
|
|
574
|
-
|
|
604
|
+
_Less than (or equal to)_
|
|
575
605
|
|
|
576
606
|
Aliases: `<`, `lessThan`, `lower`, `smaller`
|
|
577
607
|
|
|
@@ -581,6 +611,7 @@ Aliases: `<`, `lessThan`, `lower`, `smaller`
|
|
|
581
611
|
- `strict`: (boolean, default `false`) -- if `true`, value 1 must be strictly lower than value 2 (i.e. `<`). Otherwise it will be compared with "less than or equal to" (i.e. `<=`)
|
|
582
612
|
|
|
583
613
|
e.g.
|
|
614
|
+
|
|
584
615
|
```js
|
|
585
616
|
{
|
|
586
617
|
operator: '<',
|
|
@@ -604,10 +635,11 @@ e.g.
|
|
|
604
635
|
|
|
605
636
|
`children` array: `[firstValue, secondValue]` (same as `values`)
|
|
606
637
|
|
|
607
|
-
|
|
638
|
+
---
|
|
639
|
+
|
|
608
640
|
### COUNT
|
|
609
641
|
|
|
610
|
-
|
|
642
|
+
_Count elements in array_
|
|
611
643
|
|
|
612
644
|
Aliases: `count`, `length`
|
|
613
645
|
|
|
@@ -616,6 +648,7 @@ Aliases: `count`, `length`
|
|
|
616
648
|
- `values`<sup>*</sup>: (array) -- any number of elements. Returns `array.length`
|
|
617
649
|
|
|
618
650
|
e.g.
|
|
651
|
+
|
|
619
652
|
```js
|
|
620
653
|
{
|
|
621
654
|
operator: 'count',
|
|
@@ -626,10 +659,11 @@ e.g.
|
|
|
626
659
|
|
|
627
660
|
`children` array: `[...values]`
|
|
628
661
|
|
|
629
|
-
|
|
662
|
+
---
|
|
663
|
+
|
|
630
664
|
### CONDITIONAL
|
|
631
665
|
|
|
632
|
-
|
|
666
|
+
_Return different values depending on a condition expression_
|
|
633
667
|
|
|
634
668
|
Aliases: `?`, `conditional`, `ifThen`
|
|
635
669
|
|
|
@@ -640,6 +674,7 @@ Aliases: `?`, `conditional`, `ifThen`
|
|
|
640
674
|
- `valueIfFalse` (or `ifFalse`)<sup>*</sup>: the value returned if `condition` is `false`
|
|
641
675
|
|
|
642
676
|
e.g.
|
|
677
|
+
|
|
643
678
|
```js
|
|
644
679
|
{
|
|
645
680
|
operator: '?',
|
|
@@ -661,12 +696,13 @@ e.g.
|
|
|
661
696
|
|
|
662
697
|
`children` array: `[condition, valueIfTrue, valueIfFalse]`
|
|
663
698
|
|
|
664
|
-
**Note**:
|
|
699
|
+
**Note**: _For more complex branching logic, the ["match" operator](#match) can be used (it matches more than just a boolean condition)_
|
|
700
|
+
|
|
701
|
+
---
|
|
665
702
|
|
|
666
|
-
----
|
|
667
703
|
### REGEX
|
|
668
704
|
|
|
669
|
-
|
|
705
|
+
_Compares an input string against a [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) pattern_
|
|
670
706
|
|
|
671
707
|
Aliases: `regex`, `patternMatch`, `regexp`, `matchPattern`
|
|
672
708
|
|
|
@@ -678,6 +714,7 @@ Aliases: `regex`, `patternMatch`, `regexp`, `matchPattern`
|
|
|
678
714
|
Returns `true` (match found) or `false` (no match)
|
|
679
715
|
|
|
680
716
|
e.g.
|
|
717
|
+
|
|
681
718
|
```js
|
|
682
719
|
{
|
|
683
720
|
operator: 'regex',
|
|
@@ -689,10 +726,11 @@ e.g.
|
|
|
689
726
|
|
|
690
727
|
`children` array: `[testString, pattern]`
|
|
691
728
|
|
|
692
|
-
|
|
729
|
+
---
|
|
730
|
+
|
|
693
731
|
### OBJECT_PROPERTIES
|
|
694
732
|
|
|
695
|
-
|
|
733
|
+
_Extracts values from data objects in your application_
|
|
696
734
|
|
|
697
735
|
Aliases: `objectProperties`, `dataProperties`,`data`, `getData`, `objProps`, `getProperty`, `getObjProp`
|
|
698
736
|
|
|
@@ -703,7 +741,7 @@ Aliases: `objectProperties`, `dataProperties`,`data`, `getData`, `objProps`, `ge
|
|
|
703
741
|
|
|
704
742
|
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.)
|
|
705
743
|
|
|
706
|
-
For example, consider a `user` object and an fig-tree evaluator instance:
|
|
744
|
+
For example, consider a `user` object and an fig-tree evaluator instance:
|
|
707
745
|
|
|
708
746
|
```js
|
|
709
747
|
const user = {
|
|
@@ -745,18 +783,19 @@ Here is the result of various values of `expression:`
|
|
|
745
783
|
}
|
|
746
784
|
// => ["The Vulture", "Green Goblin"]
|
|
747
785
|
```
|
|
786
|
+
|
|
748
787
|
Notice the last example pulls multiple values out of an array of objects, in this case the "name". This is essentially a shorthand for:
|
|
749
788
|
|
|
750
789
|
```js
|
|
751
|
-
fig
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
).map((e) => e.name)
|
|
790
|
+
fig
|
|
791
|
+
.evaluate({ operator: 'getProperty', path: 'user.enemies' }, { data: { user } })
|
|
792
|
+
.map((e) => e.name)
|
|
755
793
|
```
|
|
756
794
|
|
|
757
795
|
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.
|
|
758
796
|
|
|
759
797
|
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:
|
|
798
|
+
|
|
760
799
|
```js
|
|
761
800
|
{
|
|
762
801
|
operator: 'objectProperties',
|
|
@@ -768,7 +807,6 @@ The "objectProperties" operator will throw an error if an invalid path is provid
|
|
|
768
807
|
|
|
769
808
|
`children` array: `[property]`
|
|
770
809
|
|
|
771
|
-
|
|
772
810
|
Example using "data" passed in dynamically as part of expression:
|
|
773
811
|
|
|
774
812
|
```js
|
|
@@ -785,10 +823,11 @@ Example using "data" passed in dynamically as part of expression:
|
|
|
785
823
|
// => "Frodo"
|
|
786
824
|
```
|
|
787
825
|
|
|
788
|
-
|
|
826
|
+
---
|
|
827
|
+
|
|
789
828
|
### STRING_SUBSTITUTION
|
|
790
829
|
|
|
791
|
-
|
|
830
|
+
_Replace values in a string using simple parameter (positional or named properties) substitution_
|
|
792
831
|
|
|
793
832
|
Aliases: `stringSubstitution`, `substitute`, `stringSub`, `replace`
|
|
794
833
|
|
|
@@ -807,6 +846,7 @@ Substitution can be done using either **positional** replacement, or with **name
|
|
|
807
846
|
The values in the `substitutions` array are replaced in the original `string` by matching their order to the numerical order of the parameters.
|
|
808
847
|
|
|
809
848
|
e.g.
|
|
849
|
+
|
|
810
850
|
```js
|
|
811
851
|
{
|
|
812
852
|
operator: 'stringSubstitution',
|
|
@@ -866,6 +906,7 @@ e.g.
|
|
|
866
906
|
(`trimWhiteSpace` and `substitutionCharacter` not available, since `substitutions` can be an arbitrary number of items)
|
|
867
907
|
|
|
868
908
|
e.g.
|
|
909
|
+
|
|
869
910
|
```js
|
|
870
911
|
{
|
|
871
912
|
operator: 'replace',
|
|
@@ -890,6 +931,7 @@ Replacement tokens can be indicated in the main string with a named value, using
|
|
|
890
931
|
}
|
|
891
932
|
// => "Your name is Steve Rogers and your best friend is Bucky Barnes"
|
|
892
933
|
```
|
|
934
|
+
|
|
893
935
|
Note the use of `nested.properties` as per [objectProperties](#object_properties).
|
|
894
936
|
|
|
895
937
|
Additionally, the substitutions can actually be provided directly in the associated `data` object and the operator will search for them there if not found in the `substitutions` property. This could be achieved simply by nesting a [`getData` node](#object_properties) inside the `substitutions` property, but because this is a very common scenario (the values provided will normally be dynamic based on application state), this shorthand is provided as a convenience.
|
|
@@ -918,13 +960,12 @@ Additionally, the substitutions can actually be provided directly in the associa
|
|
|
918
960
|
// => "The rain in Spain falls mainly on the plain"
|
|
919
961
|
```
|
|
920
962
|
|
|
921
|
-
|
|
922
|
-
|
|
923
963
|
#### Number mapping
|
|
924
964
|
|
|
925
965
|
If the replacement values are numbers, we can extend this functionality with a special `numberMapping` object, which allows for different replacements depending on the value, which is handy for pluralisation, for example.
|
|
926
966
|
|
|
927
967
|
The syntax for the `numberMapping` property is:
|
|
968
|
+
|
|
928
969
|
```js
|
|
929
970
|
{
|
|
930
971
|
propertyName1: {
|
|
@@ -938,9 +979,11 @@ The syntax for the `numberMapping` property is:
|
|
|
938
979
|
propertyName2: { ...etc }
|
|
939
980
|
}
|
|
940
981
|
```
|
|
982
|
+
|
|
941
983
|
The number map can have as few or as many match options as desired -- if no match is found (or if no `numberMapping` property at all), the number will be returned as-is.
|
|
942
984
|
|
|
943
985
|
e.g.
|
|
986
|
+
|
|
944
987
|
```js
|
|
945
988
|
{
|
|
946
989
|
operator: 'stringSubstitution',
|
|
@@ -976,26 +1019,26 @@ e.g.
|
|
|
976
1019
|
|
|
977
1020
|
**Note**: `children` array not available for named properties
|
|
978
1021
|
|
|
979
|
-
|
|
980
|
-
----
|
|
1022
|
+
---
|
|
981
1023
|
|
|
982
1024
|
### SPLIT
|
|
983
1025
|
|
|
984
|
-
|
|
1026
|
+
_Split strings into arrays_
|
|
985
1027
|
|
|
986
1028
|
Aliases: `split`, `arraySplit`
|
|
987
1029
|
|
|
988
1030
|
#### Properties
|
|
989
1031
|
|
|
990
1032
|
- `value` (or `string`)<sup>*</sup>: (string) -- string to be split
|
|
991
|
-
- `delimiter` (or `separator`): (string) -- substring to split `value` on (Default: `" "` (space))
|
|
992
|
-
- `trimWhiteSpace` (or `trimWhitespace`, `trim`): (boolean, default `true`) -- strips whitespace from the beginning or end of resulting substrings
|
|
1033
|
+
- `delimiter` (or `separator`): (string) -- substring to split `value` on (Default: `" "` (space))
|
|
1034
|
+
- `trimWhiteSpace` (or `trimWhitespace`, `trim`): (boolean, default `true`) -- strips whitespace from the beginning or end of resulting substrings
|
|
993
1035
|
- `excludeTrailing` (or `removeTrailing`, `excludeTrailingDelimiter`): (boolean, default `true`) -- if `false`, if the input string ends with the delimiter, the last member of the output array will be an empty string.
|
|
994
1036
|
i.e. `this, that, another,` (delimiter `","`) => `["this", "that", "another", ""]`
|
|
995
1037
|
|
|
996
|
-
The last two parameters (`timeWhiteSpace` and `excludeTrailing`) should
|
|
1038
|
+
The last two parameters (`timeWhiteSpace` and `excludeTrailing`) should _rarely_ be needed to be changed from their default values.
|
|
997
1039
|
|
|
998
1040
|
e.g.
|
|
1041
|
+
|
|
999
1042
|
```js
|
|
1000
1043
|
{
|
|
1001
1044
|
operator: 'split',
|
|
@@ -1012,7 +1055,7 @@ e.g.
|
|
|
1012
1055
|
|
|
1013
1056
|
### HTTP requests
|
|
1014
1057
|
|
|
1015
|
-
The following three operators (`GET`, `POST`, `GraphQL`) make http requests, so require an http client. If using fig-tree in the browser, it will use the native `fetch()` method by default,
|
|
1058
|
+
The following three operators (`GET`, `POST`, `GraphQL`) make http requests, so require an http client. If using fig-tree in the browser, it will use the native `fetch()` method by default, _so no configuration is required_. However, if using in `node`, or you wish to use a different http client (if your project is already using [`axios`](https://www.npmjs.com/package/axios), say), you can specify it with the `httpClient` option.
|
|
1016
1059
|
|
|
1017
1060
|
The `httpClient` object is an abstraction around an http package in order to standardise the implementation for use in fig-tree. Two such "wrappers" are provided in the FigTree package, for:
|
|
1018
1061
|
|
|
@@ -1029,7 +1072,7 @@ import { FigTreeEvaluator, AxiosClient } from 'fig-tree-evaluator'
|
|
|
1029
1072
|
|
|
1030
1073
|
const fig = new FigTreeEvaluator({
|
|
1031
1074
|
httpClient: AxiosClient(axios),
|
|
1032
|
-
...otherOptions
|
|
1075
|
+
...otherOptions,
|
|
1033
1076
|
})
|
|
1034
1077
|
```
|
|
1035
1078
|
|
|
@@ -1041,7 +1084,7 @@ import { FigTreeEvaluator, FetchClient } from 'fig-tree-evaluator'
|
|
|
1041
1084
|
|
|
1042
1085
|
const fig = new FigTreeEvaluator({
|
|
1043
1086
|
httpClient: FetchClient(fetch),
|
|
1044
|
-
...otherOptions
|
|
1087
|
+
...otherOptions,
|
|
1045
1088
|
})
|
|
1046
1089
|
```
|
|
1047
1090
|
|
|
@@ -1072,21 +1115,19 @@ import { someClient } from 'some-library'
|
|
|
1072
1115
|
|
|
1073
1116
|
const fig = new FigTreeEvaluator({
|
|
1074
1117
|
httpClient: MyHttpWrapper(someClient),
|
|
1075
|
-
...otherOptions
|
|
1118
|
+
...otherOptions,
|
|
1076
1119
|
})
|
|
1077
|
-
|
|
1078
1120
|
```
|
|
1079
1121
|
|
|
1080
1122
|
See the implementation for `axios` and `node-fetch` [in the repo](https://github.com/CarlosNZ/fig-tree-evaluator/blob/main/src/httpClients.ts) for specific details.
|
|
1081
1123
|
|
|
1082
|
-
|
|
1083
1124
|
### GET
|
|
1084
1125
|
|
|
1085
|
-
|
|
1126
|
+
_Http GET request_
|
|
1086
1127
|
|
|
1087
1128
|
Aliases: `get`, `api`
|
|
1088
1129
|
|
|
1089
|
-
|
|
1130
|
+
_Note: if used in `node`, or you're using an http client other than `fetch`, you will need to explicitly provide an `httpClient` option. [See details](#http-requests)_
|
|
1090
1131
|
|
|
1091
1132
|
#### Properties
|
|
1092
1133
|
|
|
@@ -1098,6 +1139,7 @@ Aliases: `get`, `api`
|
|
|
1098
1139
|
As mentioned in the [options reference](#available-options) above, a `baseEndpoint` string and `headers` object can be provided in the constructor. These are applied to all subsequent requests to save having to specify them in every evaluation. (Additional/override `headers` can always be added to a specific evaluation, too.)
|
|
1099
1140
|
|
|
1100
1141
|
e.g.
|
|
1142
|
+
|
|
1101
1143
|
```js
|
|
1102
1144
|
{
|
|
1103
1145
|
operator: 'GET',
|
|
@@ -1128,6 +1170,7 @@ e.g.
|
|
|
1128
1170
|
- `returnProperty` (optional): as above
|
|
1129
1171
|
|
|
1130
1172
|
e.g.
|
|
1173
|
+
|
|
1131
1174
|
```js
|
|
1132
1175
|
{
|
|
1133
1176
|
operator: 'get',
|
|
@@ -1143,16 +1186,17 @@ e.g.
|
|
|
1143
1186
|
// => "🇨🇺"
|
|
1144
1187
|
```
|
|
1145
1188
|
|
|
1146
|
-
|
|
1189
|
+
---
|
|
1190
|
+
|
|
1147
1191
|
### POST
|
|
1148
1192
|
|
|
1149
|
-
|
|
1193
|
+
_Http POST request_
|
|
1150
1194
|
|
|
1151
1195
|
Aliases: `post`
|
|
1152
1196
|
|
|
1153
1197
|
The "POST" operator is basically structurally the same as [GET](#get).
|
|
1154
1198
|
|
|
1155
|
-
|
|
1199
|
+
_Note: if used in `node`, or you're using an http client other than `fetch`, you will need to explicitly provide an `httpClient` option. [See details](#http-requests)_
|
|
1156
1200
|
|
|
1157
1201
|
#### Properties
|
|
1158
1202
|
|
|
@@ -1160,6 +1204,7 @@ The "POST" operator is basically structurally the same as [GET](#get).
|
|
|
1160
1204
|
- `parameters` (or `bodyJson`, `data`) -- passed to the Post request as body JSON rather than url query parameters (hence the different aliases)
|
|
1161
1205
|
|
|
1162
1206
|
e.g.
|
|
1207
|
+
|
|
1163
1208
|
```js
|
|
1164
1209
|
{
|
|
1165
1210
|
operator: "post",
|
|
@@ -1177,17 +1222,17 @@ e.g.
|
|
|
1177
1222
|
|
|
1178
1223
|
`children` array: `[urlObject, parameterKeys, ...values, returnProperty]` (same as "GET")
|
|
1179
1224
|
|
|
1180
|
-
|
|
1225
|
+
---
|
|
1181
1226
|
|
|
1182
1227
|
### GRAPHQL
|
|
1183
1228
|
|
|
1184
|
-
|
|
1229
|
+
_Http GraphQL request (using POST)_
|
|
1185
1230
|
|
|
1186
1231
|
Aliases: `graphQL`, `graphQl`, `graphql`, `gql`
|
|
1187
1232
|
|
|
1188
1233
|
This operator is essentially a special case of the "POST" operator, but structured specifically for [GraphQL](https://graphql.org/) requests.
|
|
1189
1234
|
|
|
1190
|
-
|
|
1235
|
+
_Note: if used in `node`, or you're using an http client other than `fetch`, you will need to explicitly provide an `httpClient` option. [See details](#http-requests)_
|
|
1191
1236
|
|
|
1192
1237
|
#### Properties
|
|
1193
1238
|
|
|
@@ -1202,6 +1247,7 @@ As mentioned in the [options reference](#available-options) above, a `headers` o
|
|
|
1202
1247
|
Often, GraphQL queries will be to a single endpoint and only the query/variables will differ. In that case, it is recommended to pass a GraphQL connection object into the FigTreeEvaluator constructor [options](#available-options).
|
|
1203
1248
|
|
|
1204
1249
|
The required connection object is:
|
|
1250
|
+
|
|
1205
1251
|
```ts
|
|
1206
1252
|
{
|
|
1207
1253
|
endpoint: string // url
|
|
@@ -1210,6 +1256,7 @@ The required connection object is:
|
|
|
1210
1256
|
```
|
|
1211
1257
|
|
|
1212
1258
|
The following example expression uses the GraphQL connection (specified in constructor options): `{endpoint: 'https://countries.trevorblades.com/'}`
|
|
1259
|
+
|
|
1213
1260
|
```js
|
|
1214
1261
|
{
|
|
1215
1262
|
operator: 'graphQL',
|
|
@@ -1235,6 +1282,7 @@ The following example expression uses the GraphQL connection (specified in const
|
|
|
1235
1282
|
- `returnNode` (optional): the return property, as per "GET" and "POST" operators
|
|
1236
1283
|
|
|
1237
1284
|
e.g.
|
|
1285
|
+
|
|
1238
1286
|
```js
|
|
1239
1287
|
{
|
|
1240
1288
|
operator: 'GraphQL',
|
|
@@ -1255,10 +1303,11 @@ e.g.
|
|
|
1255
1303
|
// => "🇨🇺"
|
|
1256
1304
|
```
|
|
1257
1305
|
|
|
1258
|
-
|
|
1306
|
+
---
|
|
1307
|
+
|
|
1259
1308
|
### SQL
|
|
1260
1309
|
|
|
1261
|
-
|
|
1310
|
+
_Query an SQL database_
|
|
1262
1311
|
|
|
1263
1312
|
Aliases: `sql`, `pgSql`, `postgres`, `pg`, `sqlLite`, `sqlite`, `mySql`
|
|
1264
1313
|
|
|
@@ -1266,7 +1315,7 @@ Aliases: `sql`, `pgSql`, `postgres`, `pg`, `sqlLite`, `sqlite`, `mySql`
|
|
|
1266
1315
|
|
|
1267
1316
|
- `query`<sup>*</sup>: (string) -- SQL query string, with parameterised replacements (i.e. `$1`, `$2`, etc)
|
|
1268
1317
|
- `values` (or `replacements`): (array / object) -- replacements for the `query` parameters
|
|
1269
|
-
- `single` (or `singleRecord`): (boolean) -- by default, results are returned as an array of objects. However, if your query is expected to just return a single record, you can set `single: true` and just the record object will be returned (i.e. not in an array). Note that if the query
|
|
1318
|
+
- `single` (or `singleRecord`): (boolean) -- by default, results are returned as an array of objects. However, if your query is expected to just return a single record, you can set `single: true` and just the record object will be returned (i.e. not in an array). Note that if the query _does_ fetch multiple records, only the first will be returned.
|
|
1270
1319
|
- `flatten` (or `flat`): (boolean) -- Instead of returning an object, `flatten: true` will just return an array of values. e.g, instead of `{name: "Tom", age: 49}`, it will return `["Tom", 49]`. This would usually be used in conjunction with the `single` property -- if not, it will return an array of flattened arrays.
|
|
1271
1320
|
|
|
1272
1321
|
#### Examples
|
|
@@ -1327,7 +1376,7 @@ const pgConfig = {
|
|
|
1327
1376
|
host: 'localhost',
|
|
1328
1377
|
database: 'northwind',
|
|
1329
1378
|
port: 5432,
|
|
1330
|
-
...etc
|
|
1379
|
+
...etc,
|
|
1331
1380
|
}
|
|
1332
1381
|
|
|
1333
1382
|
const pgConnect = new Client(pgConfig)
|
|
@@ -1336,21 +1385,21 @@ pgConnect.connect()
|
|
|
1336
1385
|
|
|
1337
1386
|
const fig = new FigTreeEvaluator({
|
|
1338
1387
|
sqlConnection: SQLNodePostgres(pgConnect),
|
|
1339
|
-
...otherOptions
|
|
1388
|
+
...otherOptions,
|
|
1340
1389
|
})
|
|
1341
1390
|
|
|
1342
|
-
fig
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1391
|
+
fig
|
|
1392
|
+
.evaluate({
|
|
1393
|
+
operator: 'SQL',
|
|
1394
|
+
query: "SELECT contact_name FROM customers where customer_id = 'FAMIA';",
|
|
1395
|
+
single: true,
|
|
1396
|
+
flatten: true,
|
|
1397
|
+
})
|
|
1398
|
+
.then((result) => console.log(result)) // => "Aria Cruz"
|
|
1349
1399
|
```
|
|
1350
1400
|
|
|
1351
1401
|
##### SQLite
|
|
1352
1402
|
|
|
1353
|
-
|
|
1354
1403
|
```js
|
|
1355
1404
|
import sqlite3 from 'sqlite3'
|
|
1356
1405
|
import { open, Database } from 'sqlite'
|
|
@@ -1391,10 +1440,11 @@ You then implement in FigTree options the same way as the two described above.
|
|
|
1391
1440
|
|
|
1392
1441
|
Check out `SQLNodePostgres` and `SQLite` [in the repo](https://github.com/CarlosNZ/fig-tree-evaluator/blob/main/src/databaseConnections.ts) for specific details.
|
|
1393
1442
|
|
|
1394
|
-
|
|
1443
|
+
---
|
|
1444
|
+
|
|
1395
1445
|
### BUILD_OBJECT
|
|
1396
1446
|
|
|
1397
|
-
|
|
1447
|
+
_Return an object constructed by separate keys and values_
|
|
1398
1448
|
|
|
1399
1449
|
Aliases: `buildObject`, `build`, `object`
|
|
1400
1450
|
|
|
@@ -1403,18 +1453,20 @@ The "buildObject" operator would primarily be used to construct an object input
|
|
|
1403
1453
|
#### Properties
|
|
1404
1454
|
|
|
1405
1455
|
- `properties` (or `values`, `keyValPairs`, `keyValuePairs`)<sup>*</sup>: (array) -- array of either:
|
|
1406
|
-
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1456
|
+
- objects of the following shape:
|
|
1457
|
+
|
|
1458
|
+
```ts
|
|
1459
|
+
{
|
|
1460
|
+
key: string
|
|
1461
|
+
value: any
|
|
1462
|
+
}
|
|
1463
|
+
```
|
|
1413
1464
|
- key/value pairs in sequence, e.g. `[ "key1", "value1", "key2", "value2", ... ]`
|
|
1414
1465
|
|
|
1415
1466
|
Each object or pair of elements provides one key-value pair in the output object
|
|
1416
1467
|
|
|
1417
1468
|
e.g.
|
|
1469
|
+
|
|
1418
1470
|
```js
|
|
1419
1471
|
{
|
|
1420
1472
|
operator: 'buildObject',
|
|
@@ -1446,11 +1498,11 @@ e.g.
|
|
|
1446
1498
|
|
|
1447
1499
|
`children`: `[...properties]` (same as properties array above)
|
|
1448
1500
|
|
|
1449
|
-
|
|
1501
|
+
---
|
|
1450
1502
|
|
|
1451
1503
|
### MATCH
|
|
1452
1504
|
|
|
1453
|
-
|
|
1505
|
+
_Return different values depending on a matching expression_
|
|
1454
1506
|
|
|
1455
1507
|
Aliases: `match`, `switch`
|
|
1456
1508
|
|
|
@@ -1459,10 +1511,11 @@ The "match" operator is equivalent to a "switch"/"case" in Javascript. It is sim
|
|
|
1459
1511
|
#### Properties
|
|
1460
1512
|
|
|
1461
1513
|
- `matchExpression` (or `matchValue`)<sup>*</sup>: (string | number | boolean) -- a node that returns a value to be compared against possible cases.
|
|
1462
|
-
- `branches` (or `arms` or `cases`): (object) -- an object whose
|
|
1514
|
+
- `branches` (or `arms` or `cases`): (object) -- an object whose _keys_ are compared against the `matchExpression`. The _value_ of the matching key is returned.
|
|
1463
1515
|
- `...branches` -- as an alternative to the `branches` object, matching key/values can be placed at the root of the node (see example)
|
|
1464
1516
|
|
|
1465
1517
|
e.g.
|
|
1518
|
+
|
|
1466
1519
|
```js
|
|
1467
1520
|
// Simple decision tree
|
|
1468
1521
|
{
|
|
@@ -1498,6 +1551,7 @@ e.g.
|
|
|
1498
1551
|
```
|
|
1499
1552
|
|
|
1500
1553
|
This expression could also be written as (with branch/case keys at the root level)"
|
|
1554
|
+
|
|
1501
1555
|
```js
|
|
1502
1556
|
{
|
|
1503
1557
|
operator: 'match',
|
|
@@ -1533,10 +1587,11 @@ The pairs of `key`/`value`s are constructed into the `branches` object, the same
|
|
|
1533
1587
|
|
|
1534
1588
|
For an example of a complex decision tree implementation, which includes [aliases](#alias-nodes), [fallbacks](#other-common-properties) and a range of operators, see the "match" test case file (`20_match.test.ts`).
|
|
1535
1589
|
|
|
1536
|
-
|
|
1590
|
+
---
|
|
1591
|
+
|
|
1537
1592
|
### PASSTHRU
|
|
1538
1593
|
|
|
1539
|
-
|
|
1594
|
+
_Pass-thru (does nothing)_
|
|
1540
1595
|
|
|
1541
1596
|
Aliases: `passThru`, `_`, `pass`, `ignore`, `coerce`, `convert`
|
|
1542
1597
|
|
|
@@ -1547,6 +1602,7 @@ This operator simply returns its input. Its purpose is to allow an additional ty
|
|
|
1547
1602
|
- `value` (or `_`, `data`)<sup>*</sup>: (any) -- the value that is returned
|
|
1548
1603
|
|
|
1549
1604
|
e.g.
|
|
1605
|
+
|
|
1550
1606
|
```js
|
|
1551
1607
|
{
|
|
1552
1608
|
operator: 'pass',
|
|
@@ -1556,7 +1612,7 @@ e.g.
|
|
|
1556
1612
|
// => ["500"]
|
|
1557
1613
|
```
|
|
1558
1614
|
|
|
1559
|
-
|
|
1615
|
+
---
|
|
1560
1616
|
|
|
1561
1617
|
## Custom Functions/Operators
|
|
1562
1618
|
|
|
@@ -1569,7 +1625,7 @@ const fig = new FigTreeEvaluator({
|
|
|
1569
1625
|
functions: {
|
|
1570
1626
|
double: (x) => x * 2,
|
|
1571
1627
|
getCurrentYear: () => new Date().toLocaleString('en', { year: 'numeric' }),
|
|
1572
|
-
changeCase: ({string, toCase}) => toCase === "upper" ?
|
|
1628
|
+
changeCase: ({string, toCase}) => toCase === "upper" ?
|
|
1573
1629
|
string.toUpperCase() : string.toLowerCase()
|
|
1574
1630
|
average: (...numbers) => (numbers.reduce(
|
|
1575
1631
|
(a, n) => a + n, 0)) / numbers.length,
|
|
@@ -1577,7 +1633,7 @@ const fig = new FigTreeEvaluator({
|
|
|
1577
1633
|
})
|
|
1578
1634
|
```
|
|
1579
1635
|
|
|
1580
|
-
|
|
1636
|
+
_You can also define functions with extended metadata -- see [Metadata](#metadata) for more on that._
|
|
1581
1637
|
|
|
1582
1638
|
### The CUSTOM_FUNCTIONS operator
|
|
1583
1639
|
|
|
@@ -1585,11 +1641,12 @@ Aliases: `customFunctions`, `customFunction`, `functions`, `function`, `runFunct
|
|
|
1585
1641
|
|
|
1586
1642
|
#### Properties
|
|
1587
1643
|
|
|
1588
|
-
- `functionPath` (or `functionsPath`, `functionName`, `funcPath`<sup>*</sup>): (string) -- name of the function in the
|
|
1644
|
+
- `functionPath` (or `functionsPath`, `functionName`, `funcPath`<sup>*</sup>): (string) -- name of the function in the `options.functions` object
|
|
1589
1645
|
- `args` (or `arguments`, `variables`): (array | any) -- input arguments for the function. If an array, will be passed in as multiple arguments.
|
|
1590
1646
|
- `input`: Input argument if function only takes a single argument. Note that, if your function takes a single array as its argument, you'll need to pass it in the `input` property -- the `args` property will spread the array into multiple arguments.
|
|
1591
1647
|
|
|
1592
1648
|
Here is the result of various expressions:
|
|
1649
|
+
|
|
1593
1650
|
```js
|
|
1594
1651
|
{
|
|
1595
1652
|
operator: 'functions',
|
|
@@ -1673,12 +1730,12 @@ Converting the above examples to this format:
|
|
|
1673
1730
|
|
|
1674
1731
|
These custom operators even support the [shorthand syntax](#shorthand-syntax) -- they should behave just like standard operators.
|
|
1675
1732
|
|
|
1676
|
-
|
|
1677
1733
|
## Alias Nodes
|
|
1678
1734
|
|
|
1679
1735
|
If you have a node that is used more than once in a complex expression, it's possible to just evaluate the repeated node once, and refer to it throughout using an "alias" reference. This allows for a simpler expression (reduces code duplication) as well as a performance improvement, since the aliased node is only evaluated once, providing a simple [memoization](https://en.wikipedia.org/wiki/Memoization) mechanism. (See also [Caching/Memoization](#caching-memoization))
|
|
1680
1736
|
|
|
1681
1737
|
For example, if you have the expression:
|
|
1738
|
+
|
|
1682
1739
|
```js
|
|
1683
1740
|
{
|
|
1684
1741
|
operator: "?",
|
|
@@ -1713,6 +1770,7 @@ For example, if you have the expression:
|
|
|
1713
1770
|
The `GET` operation is used twice -- once to compare it for non-equality with `null`, and once again to return its value if `true`. This is particularly wasteful since it is a network request.
|
|
1714
1771
|
|
|
1715
1772
|
We can create an alias for this whole node, resulting in this equivalent expression:
|
|
1773
|
+
|
|
1716
1774
|
```js
|
|
1717
1775
|
{
|
|
1718
1776
|
$getCountry: {
|
|
@@ -1748,6 +1806,7 @@ Like all expression nodes, alias nodes can themselves contain complex expression
|
|
|
1748
1806
|
## Fragments
|
|
1749
1807
|
|
|
1750
1808
|
You may find that the expressions you are building for your configuration files often use very similar sub-expressions (but perhaps with only some input values that differ). For example, your expressions might be regularly looking up a "countries" database and fetching the capital city, such as:
|
|
1809
|
+
|
|
1751
1810
|
```js
|
|
1752
1811
|
{
|
|
1753
1812
|
operator: 'GET',
|
|
@@ -1776,21 +1835,22 @@ const fig = new FigTreeEvaluator({
|
|
|
1776
1835
|
url: {
|
|
1777
1836
|
operator: 'stringSubstitution',
|
|
1778
1837
|
string: 'https://restcountries.com/v3.1/name/%1',
|
|
1779
|
-
replacements: [
|
|
1838
|
+
replacements: ['$country'],
|
|
1780
1839
|
},
|
|
1781
1840
|
returnProperty: '[0].capital',
|
|
1782
1841
|
outputType: 'string',
|
|
1783
1842
|
metadata: {
|
|
1784
1843
|
// Not required, but useful for external consumers -- see Metadata below
|
|
1785
|
-
description:
|
|
1844
|
+
description: 'Fetches the capital city of a country',
|
|
1786
1845
|
parameters: { $country: { type: 'string', required: true } },
|
|
1787
|
-
}
|
|
1846
|
+
},
|
|
1788
1847
|
},
|
|
1789
1848
|
},
|
|
1790
1849
|
})
|
|
1791
1850
|
```
|
|
1792
1851
|
|
|
1793
1852
|
Then any subsequent expressions can use this fragment by specifying a special "Fragment Node", which contains the `fragment` and (optionally) `parameters` fields:
|
|
1853
|
+
|
|
1794
1854
|
```js
|
|
1795
1855
|
{
|
|
1796
1856
|
fragment: "getCapital",
|
|
@@ -1801,6 +1861,7 @@ Then any subsequent expressions can use this fragment by specifying a special "F
|
|
|
1801
1861
|
```
|
|
1802
1862
|
|
|
1803
1863
|
Like the `branches` field in the ["Match" operator](#match), the properties of the `parameters` field can be specified at the root level as well -- it just depends on whichever is most appropriate for your use case. So the following is equivalent to the previous fragment node:
|
|
1864
|
+
|
|
1804
1865
|
```js
|
|
1805
1866
|
{
|
|
1806
1867
|
fragment: "getCapital",
|
|
@@ -1810,21 +1871,24 @@ Like the `branches` field in the ["Match" operator](#match), the properties of t
|
|
|
1810
1871
|
|
|
1811
1872
|
See `22_fragments.test.ts` for more complex examples.
|
|
1812
1873
|
|
|
1813
|
-
Unlike Alias Nodes, which are evaluated
|
|
1874
|
+
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.
|
|
1814
1875
|
|
|
1815
1876
|
## Shorthand syntax
|
|
1816
1877
|
|
|
1817
1878
|
It's possible to express FigTree expressions in a more compact syntax, as follows:
|
|
1818
1879
|
|
|
1819
|
-
- 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:
|
|
1880
|
+
- 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:
|
|
1881
|
+
|
|
1820
1882
|
```js
|
|
1821
1883
|
{
|
|
1822
1884
|
operator: 'regex',
|
|
1823
1885
|
string: "home@myplace.com",
|
|
1824
|
-
pattern: '^[A-Za-z0-9.]+@[A-Za-z0-9]+\\.[A-Za-z0-9.]+$'
|
|
1886
|
+
pattern: '^[A-Za-z0-9.]+@[A-Za-z0-9]+\\.[A-Za-z0-9.]+$'
|
|
1825
1887
|
}
|
|
1826
1888
|
```
|
|
1889
|
+
|
|
1827
1890
|
can be written as:
|
|
1891
|
+
|
|
1828
1892
|
```js
|
|
1829
1893
|
{
|
|
1830
1894
|
$regex:
|
|
@@ -1835,14 +1899,15 @@ It's possible to express FigTree expressions in a more compact syntax, as follow
|
|
|
1835
1899
|
}
|
|
1836
1900
|
```
|
|
1837
1901
|
|
|
1838
|
-
- 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:
|
|
1902
|
+
- 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:
|
|
1903
|
+
|
|
1839
1904
|
```js
|
|
1840
1905
|
{
|
|
1841
|
-
$regex: [
|
|
1906
|
+
$regex: ['home@myplace.com', '^[A-Za-z0-9.]+@[A-Za-z0-9]+\\.[A-Za-z0-9.]+$']
|
|
1842
1907
|
}
|
|
1843
1908
|
```
|
|
1844
1909
|
|
|
1845
|
-
- Operator nodes with a single parameter can just be placed directly as the single property value. For example:
|
|
1910
|
+
- Operator nodes with a single parameter can just be placed directly as the single property value. For example:
|
|
1846
1911
|
```js
|
|
1847
1912
|
{
|
|
1848
1913
|
operator: "getData",
|
|
@@ -1851,12 +1916,13 @@ It's possible to express FigTree expressions in a more compact syntax, as follow
|
|
|
1851
1916
|
```
|
|
1852
1917
|
can become, simply:
|
|
1853
1918
|
```js
|
|
1854
|
-
{
|
|
1919
|
+
{
|
|
1920
|
+
$getData: 'user.firstName'
|
|
1921
|
+
}
|
|
1855
1922
|
```
|
|
1856
1923
|
|
|
1857
1924
|
For more examples, see `23_shorthand.test.ts`, or have a play with the [demo app](https://carlosnz.github.io/fig-tree-evaluator/) app.
|
|
1858
1925
|
|
|
1859
|
-
|
|
1860
1926
|
## Caching (Memoization)
|
|
1861
1927
|
|
|
1862
1928
|
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 with each result being valid for half and hour, but these values can be modified using the `maxCacheSize` and `maxCacheTime` [options](#available-options).
|
|
@@ -1864,14 +1930,15 @@ FigTree Evaluator has basic [memoization](https://en.wikipedia.org/wiki/Memoizat
|
|
|
1864
1930
|
Currently, caching is only implemented for the following operators, since they perform requests to external resources, which are inherently slow:
|
|
1865
1931
|
|
|
1866
1932
|
- GET (`useCache` default: `true`)
|
|
1867
|
-
- POST (`useCache` default: `false`)
|
|
1933
|
+
- POST (`useCache` default: `false`)
|
|
1868
1934
|
- PG_SQL (`useCache` default: `true`)
|
|
1869
1935
|
- GRAPH_QL (`useCache` default: `true`)
|
|
1870
1936
|
- CUSTOM_FUNCTIONS (`useCache` default: `false`)
|
|
1871
1937
|
|
|
1872
1938
|
This is different to the memoization provided by [Alias Nodes](#alias-nodes):
|
|
1873
|
-
|
|
1874
|
-
-
|
|
1939
|
+
|
|
1940
|
+
- Alias nodes are still evaluated once for every evaluation -- they're more for re-use _within_ a complex expression.
|
|
1941
|
+
- Cached nodes will persist _between_ different evaluations as long as the input values are the same as a previously evaluated node.
|
|
1875
1942
|
|
|
1876
1943
|
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.
|
|
1877
1944
|
|
|
@@ -1901,7 +1968,7 @@ interface FigTreeError extends Error {
|
|
|
1901
1968
|
There are two alternatives to throwing:
|
|
1902
1969
|
|
|
1903
1970
|
1. **Fallback**: If the `fallback` property is specified in the expression, this will be returned whenever an error occurs at that node (or below). See [Fallback option](#other-common-properties).
|
|
1904
|
-
2. **Formatted string**: By using the `returnErrorAsString: true` option, FigTree will return a nicely formatted string describing the error.
|
|
1971
|
+
2. **Formatted string**: By using the `returnErrorAsString: true` option, FigTree will return a nicely formatted string describing the error. _This is the same string returned in the `prettyPrint` property of the FigTreeError._ The formatted string will be structured like so:
|
|
1905
1972
|
|
|
1906
1973
|
```
|
|
1907
1974
|
Operator: <OPERATOR_NAME>: - <error.name (if specific)>
|
|
@@ -1910,9 +1977,11 @@ Operator: <OPERATOR_NAME>: - <error.name (if specific)>
|
|
|
1910
1977
|
...errorData
|
|
1911
1978
|
}
|
|
1912
1979
|
```
|
|
1980
|
+
|
|
1913
1981
|
`errorData` is specific data returned by the error thrown by an internal process (such as a network request using [fetch](#http-requests)).
|
|
1914
1982
|
|
|
1915
1983
|
For example, a 403 (forbidden) error thrown by the `GET` operator (with Axios HTTP client) would return a string like:
|
|
1984
|
+
|
|
1916
1985
|
```
|
|
1917
1986
|
Operator: GET - AxiosError
|
|
1918
1987
|
Request failed with status code 403
|
|
@@ -1928,6 +1997,7 @@ Request failed with status code 403
|
|
|
1928
1997
|
```
|
|
1929
1998
|
|
|
1930
1999
|
And, if thrown, the error object would contain:
|
|
2000
|
+
|
|
1931
2001
|
```js
|
|
1932
2002
|
{
|
|
1933
2003
|
name: "AxiosError",
|
|
@@ -1995,7 +2065,7 @@ const fig = new FigTreeEvaluator({
|
|
|
1995
2065
|
argsDefault: [1, 2, 3, 4]
|
|
1996
2066
|
},
|
|
1997
2067
|
changeCase: {
|
|
1998
|
-
function: ({string, toCase}) => toCase === "upper" ?
|
|
2068
|
+
function: ({string, toCase}) => toCase === "upper" ?
|
|
1999
2069
|
string.toUpperCase() : string.toLowerCase(),
|
|
2000
2070
|
description: "Convert a string to either upper or lower case",
|
|
2001
2071
|
inputDefault: {string: "New string", toCase: "upper"}
|
|
@@ -2069,6 +2139,7 @@ This will return something like:
|
|
|
2069
2139
|
#### Retrieve customFunction info
|
|
2070
2140
|
|
|
2071
2141
|
Similarly, we can fetch basic info about custom functions in the current FigTree instance, although with more limited detail:
|
|
2142
|
+
|
|
2072
2143
|
```js
|
|
2073
2144
|
fig.getCustomFunctions()
|
|
2074
2145
|
```
|
|
@@ -2076,19 +2147,19 @@ fig.getCustomFunctions()
|
|
|
2076
2147
|
Returns:
|
|
2077
2148
|
|
|
2078
2149
|
```js
|
|
2079
|
-
[
|
|
2150
|
+
;[
|
|
2080
2151
|
{
|
|
2081
2152
|
name: 'doubleArray',
|
|
2082
2153
|
numRequiredArgs: 1,
|
|
2083
2154
|
description: 'Double each item in an array',
|
|
2084
|
-
argsDefault: [
|
|
2155
|
+
argsDefault: [1, 2, 3, 4],
|
|
2085
2156
|
},
|
|
2086
2157
|
{
|
|
2087
2158
|
name: 'changeCase',
|
|
2088
2159
|
numRequiredArgs: 1,
|
|
2089
2160
|
description: 'Convert a string to either upper or lower case',
|
|
2090
|
-
inputDefault: { string: 'New string', toCase: 'upper' }
|
|
2091
|
-
}
|
|
2161
|
+
inputDefault: { string: 'New string', toCase: 'upper' },
|
|
2162
|
+
},
|
|
2092
2163
|
]
|
|
2093
2164
|
```
|
|
2094
2165
|
|
|
@@ -2098,14 +2169,14 @@ Returns:
|
|
|
2098
2169
|
fig.isFigTreeExpression(value)
|
|
2099
2170
|
```
|
|
2100
2171
|
|
|
2101
|
-
When building a UI, you often need to decide whether a given value should be treated as an evaluable FigTree expression or as plain data. This method returns `true` only for values that
|
|
2172
|
+
When building a UI, you often need to decide whether a given value should be treated as an evaluable FigTree expression or as plain data. This method returns `true` only for values that _this_ instance would actually process as an expression. It is **registry-aware** — `$`-prefixed [shorthand](#shorthand-syntax) and keys are validated against the instance's registered operators, [fragments](#fragments) and [custom functions](#custom_functions) — and it follows the instance's [`evaluateFullObject`](#available-options) and `noShorthand` settings.
|
|
2102
2173
|
|
|
2103
2174
|
```js
|
|
2104
2175
|
fig.isFigTreeExpression({ operator: '+', values: [1, 2] }) // true
|
|
2105
|
-
fig.isFigTreeExpression({ $getData: 'user.name' })
|
|
2106
|
-
fig.isFigTreeExpression({ name: 'Steve', age: 30 })
|
|
2107
|
-
fig.isFigTreeExpression({ $somethingUnknown: 1 })
|
|
2108
|
-
fig.isFigTreeExpression('$myAlias')
|
|
2176
|
+
fig.isFigTreeExpression({ $getData: 'user.name' }) // true (registered shorthand)
|
|
2177
|
+
fig.isFigTreeExpression({ name: 'Steve', age: 30 }) // false (plain data)
|
|
2178
|
+
fig.isFigTreeExpression({ $somethingUnknown: 1 }) // false (not a registered operator/fragment/function)
|
|
2179
|
+
fig.isFigTreeExpression('$myAlias') // false (alias reference with no definition in scope)
|
|
2109
2180
|
```
|
|
2110
2181
|
|
|
2111
2182
|
The last two cases are why this is preferable to a purely structural check: an object whose only `$`-prefixed key doesn't correspond to anything the evaluator recognises (e.g. `{ "$somethingUnknown": 1 }`) is plain data, not an expression, and won't be flagged here.
|
|
@@ -2147,4 +2218,5 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
|
|
|
2147
2218
|
See [here](https://github.com/CarlosNZ/fig-tree-evaluator/blob/main/CHANGELOG.md)
|
|
2148
2219
|
|
|
2149
2220
|
## Credit
|
|
2150
|
-
|
|
2221
|
+
|
|
2222
|
+
Icon: Tree by ka reemov from <a href="https://thenounproject.com/icon/tree-2665898/" target="_blank" title="Tree Icon">Noun Project</a>
|