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 CHANGED
@@ -2,9 +2,15 @@
2
2
 
3
3
  ![Logo](images/FigTreeEvaluator_logo_1000.png)
4
4
 
5
- **FigTree Evaluator** is a module to evaluate JSON-structured expression trees.
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
- 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:
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([ options ]) // See available options below
144
+ const fig = new FigTreeEvaluator([options]) // See available options below
136
145
 
137
146
  // Evaluate expressions
138
- fig.evaluate(expression, [options]) // Options over-ride initial options for this evaluation
139
- .then((result) => { // "evaluate" is async method
140
- // Do something with result
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 *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.
158
- - `functions` -- a single object containing any *custom functions* available for use by [custom functions/operators](#custom_functions).
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 *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.
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 *any* operator nodes within the structure and evaluate them within the object tree.
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
- // Do something with result
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. *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).*
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 *all* operator node properties can themselves be operator nodes, *including* the `fallback` and `outputType` properties.
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
- *Logical AND*
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
- *Logical OR*
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
- *Equality*
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
- *Non-equality*
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
- *Addition, concatenation, merging*
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 *before* the "PLUS" operation.
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
- *Subtraction*
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
- *Multiplication*
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. (If non-numerical elements are provided, the operator will return `NaN`)
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
- *Division*
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. (If non-numerical elements are provided, the operator will return `NaN`)
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' | 'decimal'`) -- by default (or if `decimal` is specified), the operator returns a floating point value. 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`)
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 *either* a `values` array *or* `dividend`/`divisor` properties. If both are provided, `values` takes precedence.
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
- *Greater than (or equal to)*
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
- *Less than (or equal to)*
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
- *Count elements in array*
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
- *Return different values depending on a condition expression*
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**: *For more complex branching logic, the ["match" operator](#match) can be used (it matches more than just a boolean condition)*
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
- *Compares an input string against a [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) pattern*
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
- *Extracts values from data objects in your application*
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.evaluate(
752
- { operator: 'getProperty', path: 'user.enemies' },
753
- { data: { user } }
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
- *Replace values in a string using simple parameter (positional or named properties) substitution*
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
- *Split strings into arrays*
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)). The whitespace escape sequences `\n`, `\t` and `\r` may be written literally (e.g. `"\n"` to split on line breaks), which is convenient when authoring the delimiter in an input field that can't hold a real control character.
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 *rarely* be needed to be changed from their default values.
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, *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.
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
- *Http GET request*
1126
+ _Http GET request_
1086
1127
 
1087
1128
  Aliases: `get`, `api`
1088
1129
 
1089
- *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)*
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
- *Http POST request*
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
- *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)*
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
- *Http GraphQL request (using POST)*
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
- *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)*
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
- *Query an SQL database*
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 *does* fetch multiple records, only the first will be returned.
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.evaluate({
1343
- operator: "SQL",
1344
- query: "SELECT contact_name FROM customers where customer_id = 'FAMIA';",
1345
- single: true,
1346
- flatten: true
1347
- })
1348
- .then((result) => console.log(result)) // => "Aria Cruz"
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
- *Return an object constructed by separate keys and values*
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
- - objects of the following shape:
1407
- ```ts
1408
- {
1409
- key: string
1410
- value: any
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
- *Return different values depending on a matching expression*
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 *keys* are compared against the `matchExpression`. The *value* of the matching key is returned.
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
- *Pass-thru (does nothing)*
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
- *You can also define functions with extended metadata -- see [Metadata](#metadata) for more on that.*
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 `options.functions` object
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: [ "$country" ],
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: "Fetches the capital city of a country",
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 *once* and then the result re-used whenever the alias is referenced, Fragments are evaluated every time, as the input parameters may differ.
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: [ 'home@myplace.com', '^[A-Za-z0-9.]+@[A-Za-z0-9]+\\.[A-Za-z0-9.]+$' ]
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
- { $getData: "user.firstName" }
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
- - Alias nodes are still evaluated once for every evaluation -- they're more for re-use *within* a complex expression.
1874
- - Cached nodes will persist *between* different evaluations as long as the input values are the same as a previously evaluated node.
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. *This is the same string returned in the `prettyPrint` property of the FigTreeError.* The formatted string will be structured like so:
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: [ 1, 2, 3, 4 ]
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 *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.
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' }) // true (registered shorthand)
2106
- fig.isFigTreeExpression({ name: 'Steve', age: 30 }) // false (plain data)
2107
- fig.isFigTreeExpression({ $somethingUnknown: 1 }) // false (not a registered operator/fragment/function)
2108
- fig.isFigTreeExpression('$myAlias') // false (alias reference with no definition in scope)
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
- Icon: Tree by ka reemov from <a href="https://thenounproject.com/icon/tree-2665898/" target="_blank" title="Tree Icon">Noun Project</a>
2221
+
2222
+ Icon: Tree by ka reemov from <a href="https://thenounproject.com/icon/tree-2665898/" target="_blank" title="Tree Icon">Noun Project</a>