fig-tree-evaluator 2.8.6 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -10
- package/build/FigTreeEvaluator.d.ts +3 -0
- package/build/FigTreeEvaluator.js +5 -1
- package/build/cache.d.ts +16 -1
- package/build/cache.js +20 -5
- package/build/operators/STRING_SUBSTITUTION/data.js +23 -2
- package/build/operators/STRING_SUBSTITUTION/operator.js +60 -7
- package/build/types.d.ts +2 -1
- package/build/version.d.ts +1 -1
- package/build/version.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -115,9 +115,9 @@ A playground for building and testing expressions is available [here](https://ca
|
|
|
115
115
|
|
|
116
116
|
## Install
|
|
117
117
|
|
|
118
|
-
`npm install fig-tree`\
|
|
118
|
+
`npm install fig-tree-evaluator`\
|
|
119
119
|
or\
|
|
120
|
-
`yarn add fig-tree`
|
|
120
|
+
`yarn add fig-tree-evaluator`
|
|
121
121
|
|
|
122
122
|
## Usage
|
|
123
123
|
|
|
@@ -162,6 +162,7 @@ The `options` parameter is an object with the following available properties (al
|
|
|
162
162
|
- `excludeOperators` -- an array of operator names (or [aliases](#operator--property-aliases)) to prohibit from being used in expressions. You may wish to restrict (for example) database access via FigTree configurations, in which case these exclusions can be defined when instantiating the FigTree instance (or updated on the fly).
|
|
163
163
|
- `useCache` -- caches the results from certain operators to avoid repeated network requests with the same input values. By default, this is set to `true`, and it can be overridden for specific nodes. See [Memoization/Caching section](#caching-memoization) for more detail
|
|
164
164
|
- `maxCacheSize` -- the maximum number of results that will be held in the aforementioned cache (default: `50`)
|
|
165
|
+
- `maxCacheTime` -- the maximum time (in seconds) that a result will be cached since last access (default: `1800` (30 minutes))
|
|
165
166
|
- `noShorthand` -- there is a [shorthand syntax](#shorthand-syntax) available for writing expressions. Internally, this is pre-processed into the standard expression form before evaluation. If you have no use for this and you'd rather all expressions were written with full verbosity, set `noShorthand: true` to save a small amount in performance by skipping internal pre-processing.
|
|
166
167
|
|
|
167
168
|
As mentioned above, `options` can be provided as part of the constructor as part of each separate evaluation. You can also change the options permanently for a given evaluator instance with:
|
|
@@ -775,14 +776,21 @@ Example using "data" passed in dynamically as part of expression:
|
|
|
775
776
|
----
|
|
776
777
|
### STRING_SUBSTITUTION
|
|
777
778
|
|
|
778
|
-
*Replace values in a string using simple parameter substitution*
|
|
779
|
+
*Replace values in a string using simple parameter (positional or named properties) substitution*
|
|
779
780
|
|
|
780
781
|
Aliases: `stringSubstitution`, `substitute`, `stringSub`, `replace`
|
|
781
782
|
|
|
782
783
|
#### Properties
|
|
783
784
|
|
|
784
785
|
- `string`<sup>*</sup>: (string) -- a parameterized (`%1`, `%2`) string, where the parameters are to be replaced by dynamic values. E.g. `"My name is %1 (age %2)"`
|
|
785
|
-
- `substitutions` (or `replacements`)<sup>*</sup>: (array) -- the values to be substituted into `string
|
|
786
|
+
- `substitutions` (or `replacements`, `values`)<sup>*</sup>: (array | object) -- the values to be substituted into `string`. Will be either an array or object depending on whether you're using positional replacements or named properties (see [below](#positional-replacement)).
|
|
787
|
+
- `trimWhiteSpace` (or `trimWhitespace`, `trim`): (boolean, default `true`) -- strips whitespace from the beginning or end of the substitution values
|
|
788
|
+
- `substitutionCharacter` (or `subCharacter`, `subChar`): (`"%"` or `"$"`) -- by default, when using positional replacement, it looks for the `%` token (i.e `%1, %2, etc`), but this can be changed to `$` (i.e. `$1, $2, $3, etc`) by setting this property to `$`.
|
|
789
|
+
- `numberMapping` (or `numMap`, `numberMap`, `pluralisation`, `pluralization`, `plurals`): (object) -- when replacing with named properties and you have replacement values that are numbers, it's possible to map values or ranges to specific string outputs. This can be used to produce correct pluralisation, for example. [See below](#named-property-replacement) for more details.
|
|
790
|
+
|
|
791
|
+
Substitution can be done using either **positional** replacement, or with **named properties**:
|
|
792
|
+
|
|
793
|
+
#### Positional replacement
|
|
786
794
|
|
|
787
795
|
The values in the `substitutions` array are replaced in the original `string` by matching their order to the numerical order of the parameters.
|
|
788
796
|
|
|
@@ -803,7 +811,9 @@ e.g.
|
|
|
803
811
|
|
|
804
812
|
{
|
|
805
813
|
operator: 'replace',
|
|
806
|
-
|
|
814
|
+
// Using $1, $2 instead of %1, %2 this time:
|
|
815
|
+
string: '$1 is actually $2 $3',
|
|
816
|
+
substitutionCharacter: "$",
|
|
807
817
|
substitutions: [
|
|
808
818
|
// Using the 'user' object from above (OBJECT_PROPERTIES operator)
|
|
809
819
|
{
|
|
@@ -829,9 +839,19 @@ e.g.
|
|
|
829
839
|
substitutions: ['bird', 'Tweet!'],
|
|
830
840
|
}
|
|
831
841
|
// => 'A bird says: "Tweet! Tweet! Tweet!"'
|
|
842
|
+
|
|
843
|
+
// Replacement tokens can be escaped using the standard "\" escape character:
|
|
844
|
+
{
|
|
845
|
+
operator: 'stringSubstitution',
|
|
846
|
+
string: 'The price of $1 is \$5',
|
|
847
|
+
subChar: '$',
|
|
848
|
+
substitutions: [ 'a coffee', 'not used' ],
|
|
849
|
+
}
|
|
850
|
+
// => The price of a coffee is $5
|
|
832
851
|
```
|
|
833
852
|
|
|
834
|
-
`children` array: `[string, ...substitutions]`
|
|
853
|
+
`children` array: `[string, ...substitutions]`
|
|
854
|
+
(`trimWhiteSpace` and `substitutionCharacter` not available, since `substitutions` can be an arbitrary number of items)
|
|
835
855
|
|
|
836
856
|
e.g.
|
|
837
857
|
```js
|
|
@@ -842,6 +862,77 @@ e.g.
|
|
|
842
862
|
// => "I am Iron Man"
|
|
843
863
|
```
|
|
844
864
|
|
|
865
|
+
#### Named property replacement
|
|
866
|
+
|
|
867
|
+
Replacement tokens can be indicated in the main string with a named value, using `{{<name>}}` syntax, e.g. `"Your name is {{firstName}} {{lastName}}"`. Then the `substitutions` property must be an object with those property names:
|
|
868
|
+
|
|
869
|
+
```js
|
|
870
|
+
{
|
|
871
|
+
operator: 'stringSubstitution',
|
|
872
|
+
string: 'Your name is {{firstName}} {{lastName}}',
|
|
873
|
+
substitutions: {
|
|
874
|
+
firstName: 'Steve',
|
|
875
|
+
lastName: "Rogers"
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
// => "Your name is Steve Rogers"
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
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.
|
|
882
|
+
|
|
883
|
+
The syntax for the `numberMapping` property is:
|
|
884
|
+
```js
|
|
885
|
+
{
|
|
886
|
+
propertyName1: {
|
|
887
|
+
1: "Output if value is {}",
|
|
888
|
+
2: "Output if value is 2",
|
|
889
|
+
">5": "Output if value is greater than 5",
|
|
890
|
+
"<0": "Output if value is less than 0"
|
|
891
|
+
"other": "Fallback output if none of the others match: {} count"
|
|
892
|
+
// {} is a replacement for the numerical value itself
|
|
893
|
+
},
|
|
894
|
+
propertyName2: { ...etc }
|
|
895
|
+
}
|
|
896
|
+
```
|
|
897
|
+
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.
|
|
898
|
+
|
|
899
|
+
e.g.
|
|
900
|
+
```js
|
|
901
|
+
{
|
|
902
|
+
operator: 'stringSubstitution',
|
|
903
|
+
string: 'Hi {{name}}, we have {{count}} attending this event.',
|
|
904
|
+
values: {
|
|
905
|
+
name: "Tatiana",
|
|
906
|
+
count: {operator: "getData", property: "numOfPeople" }
|
|
907
|
+
},
|
|
908
|
+
numberMap: {
|
|
909
|
+
count: {
|
|
910
|
+
0: "no one",
|
|
911
|
+
1: "just one person",
|
|
912
|
+
">10": "too many people",
|
|
913
|
+
"other": "{} people"
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
// Output with varying values for "numOfPeople" passed into evaluation
|
|
918
|
+
// "data" object:
|
|
919
|
+
|
|
920
|
+
// { numOfPeople: 5 }
|
|
921
|
+
// => "Hi Tatiana, we have 5 people attending this event."
|
|
922
|
+
|
|
923
|
+
// { numOfPeople: 0 }
|
|
924
|
+
// => "Hi Tatiana, we have no one attending this event."
|
|
925
|
+
|
|
926
|
+
// { numOfPeople: 100 }
|
|
927
|
+
// => "Hi Tatiana, we have too many people attending this event."
|
|
928
|
+
|
|
929
|
+
// { numOfPeople: 1 }
|
|
930
|
+
// => "Hi Tatiana, we have just one person attending this event.
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
**Note**: `children` array not available for named properties
|
|
934
|
+
|
|
935
|
+
|
|
845
936
|
----
|
|
846
937
|
|
|
847
938
|
### SPLIT
|
|
@@ -1527,7 +1618,7 @@ For more examples, see `23_shorthand.test.ts`, or have a play with the [demo app
|
|
|
1527
1618
|
|
|
1528
1619
|
## Caching (Memoization)
|
|
1529
1620
|
|
|
1530
|
-
FigTree Evaluator has basic [memoization](https://en.wikipedia.org/wiki/Memoization) functionality for certain nodes to speed up re-evaluation when the input parameters haven't changed. There is a single cache store per FigTree instance which persists for the lifetime of the instance. By default, it remembers the last 50 results, but
|
|
1621
|
+
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).
|
|
1531
1622
|
|
|
1532
1623
|
Currently, caching is only implemented for the following operators, since they perform requests to external resources, which are inherently slow:
|
|
1533
1624
|
|
|
@@ -1543,6 +1634,17 @@ This is different to the memoization provided by [Alias Nodes](#alias-nodes):
|
|
|
1543
1634
|
|
|
1544
1635
|
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.
|
|
1545
1636
|
|
|
1637
|
+
It's possible to manually set/get the cache store, which can be used (for example) to save the cache in local storage to persist it between page reloads or new FigTree instances.
|
|
1638
|
+
|
|
1639
|
+
Use the methods:
|
|
1640
|
+
|
|
1641
|
+
```js
|
|
1642
|
+
fig.getCache() // returns key-value store
|
|
1643
|
+
|
|
1644
|
+
fig.setCache(cache) // where "cache" is the object retrieved by .getCache()
|
|
1645
|
+
```
|
|
1646
|
+
|
|
1647
|
+
|
|
1546
1648
|
## Metadata
|
|
1547
1649
|
|
|
1548
1650
|
Evaluator expression can be configured by hand, with [aliases](#alias-nodes), [fragments](#fragments) and [shorthand](#shorthand-syntax) available to make the job easier.
|
|
@@ -1691,13 +1793,18 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
|
|
|
1691
1793
|
|
|
1692
1794
|
*Trivial upgrades (e.g. documentation, small re-factors, types, etc.) not included*
|
|
1693
1795
|
|
|
1796
|
+
- **v2.10.0**: Extended stringSubstitution functionality to included named
|
|
1797
|
+
property substitution, trim whitespace option, and pluralisation (#97)
|
|
1798
|
+
- **v2.9.0**: Added ability to invalidate cache by time (#94)
|
|
1694
1799
|
- **v2.8.6**: Small bug fix where `options` object would be mutated instead of replaced
|
|
1695
1800
|
- **v2.8.5**: Small bug fix in [COUNT](#count) operator
|
|
1696
1801
|
- **v2.8.4**: Refactor types, better compliance with [ESLint](https://eslint.org/) rules, add more tests
|
|
1697
1802
|
- **v2.8.0**:
|
|
1698
1803
|
- **[Shorthand syntax](#shorthand-syntax)** (#80)
|
|
1699
|
-
- **Methods to retrieve [metadata](#metadata)** about operators, fragments and
|
|
1700
|
-
|
|
1804
|
+
- **Methods to retrieve [metadata](#metadata)** about operators, fragments and
|
|
1805
|
+
functions (#82)
|
|
1806
|
+
- **v2.7.0**: **Add `excludeOperators` option** to allow certain operators to be
|
|
1807
|
+
prohibited (e.g. database lookups) (#54)
|
|
1701
1808
|
- **v2.6.0**: Resolve alias nodes that are not part of an Operator node when `evaluateFullObject` is enabled (#78)
|
|
1702
1809
|
- **v2.5.0**:
|
|
1703
1810
|
- Bug fixes for edge cases (mainly related to backwards compatibility)
|
|
@@ -1719,7 +1826,8 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
|
|
|
1719
1826
|
- **v2.0.0**: Re-write as stand-alone package. Major improvements include:
|
|
1720
1827
|
- more [operators](#operator-reference)
|
|
1721
1828
|
- operator (and property) [aliases](#operator--property-aliases)
|
|
1722
|
-
- more appropriately-named properties associated with each operator (as
|
|
1829
|
+
- more appropriately-named properties associated with each operator (as
|
|
1830
|
+
opposed to a single `children` array)
|
|
1723
1831
|
- class-based Evaluator instances
|
|
1724
1832
|
- runtime type-checking
|
|
1725
1833
|
- better error handling and error reporting
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EvaluatorNode, FigTreeOptions, OperatorMetadata, FragmentMetadata } from './types';
|
|
2
|
+
import { Store } from './cache';
|
|
2
3
|
declare class FigTreeEvaluator {
|
|
3
4
|
private options;
|
|
4
5
|
private operators;
|
|
@@ -9,6 +10,8 @@ declare class FigTreeEvaluator {
|
|
|
9
10
|
evaluate(expression: EvaluatorNode, options?: FigTreeOptions): Promise<import("./types").EvaluatorOutput>;
|
|
10
11
|
getOptions(): FigTreeOptions;
|
|
11
12
|
updateOptions(options: FigTreeOptions): void;
|
|
13
|
+
getCache: () => Store;
|
|
14
|
+
setCache: (cache: Store) => Store;
|
|
12
15
|
getOperators(): readonly OperatorMetadata[];
|
|
13
16
|
getFragments(): readonly (FragmentMetadata & {
|
|
14
17
|
name: string;
|
|
@@ -54,17 +54,21 @@ class FigTreeEvaluator {
|
|
|
54
54
|
return;
|
|
55
55
|
throw new Error(result);
|
|
56
56
|
};
|
|
57
|
+
this.getCache = () => this.cache.getCache();
|
|
58
|
+
this.setCache = (cache) => this.cache.setCache(cache);
|
|
57
59
|
this.getVersion = () => version_1.version;
|
|
58
60
|
this.options = standardiseOptionNames(options);
|
|
59
61
|
this.operators = (0, helpers_1.filterOperators)(operators, (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.excludeOperators) !== null && _b !== void 0 ? _b : [], operatorAliases);
|
|
60
62
|
this.operatorAliases = operatorAliases;
|
|
61
|
-
this.cache = new cache_1.default(options.maxCacheSize);
|
|
63
|
+
this.cache = new cache_1.default({ maxSize: options.maxCacheSize, maxTime: options.maxCacheTime });
|
|
62
64
|
}
|
|
63
65
|
evaluate(expression, options = {}) {
|
|
64
66
|
return __awaiter(this, void 0, void 0, function* () {
|
|
65
67
|
const currentOptions = (0, helpers_1.mergeOptions)(this.options, standardiseOptionNames(options));
|
|
66
68
|
if (currentOptions.maxCacheSize && currentOptions.maxCacheSize !== this.cache.getMax())
|
|
67
69
|
this.cache.setMax(currentOptions.maxCacheSize);
|
|
70
|
+
if (currentOptions.maxCacheTime && currentOptions.maxCacheTime !== this.cache.getMaxTime())
|
|
71
|
+
this.cache.setMaxTime(currentOptions.maxCacheTime);
|
|
68
72
|
return yield (0, evaluate_1.evaluatorFunction)(expression, {
|
|
69
73
|
options: currentOptions,
|
|
70
74
|
operators: options.excludeOperators
|
package/build/cache.d.ts
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import { EvaluatorOutput, UnknownFunction } from './types';
|
|
2
|
+
interface CacheOptions {
|
|
3
|
+
maxSize?: number;
|
|
4
|
+
maxTime?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface Store {
|
|
7
|
+
[key: string]: {
|
|
8
|
+
result: EvaluatorOutput;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
2
12
|
declare class FigTreeCache {
|
|
3
13
|
private store;
|
|
4
14
|
private queue;
|
|
5
15
|
private maxSize;
|
|
6
|
-
|
|
16
|
+
private maxTime;
|
|
17
|
+
constructor(options?: CacheOptions);
|
|
7
18
|
useCache: (shouldUseCache: boolean, action: UnknownFunction, ...args: unknown[]) => Promise<EvaluatorOutput>;
|
|
8
19
|
getMax: () => number;
|
|
9
20
|
setMax: (size: number) => void;
|
|
21
|
+
getMaxTime: () => number;
|
|
22
|
+
setMaxTime: (time: number) => void;
|
|
23
|
+
getCache: () => Store;
|
|
24
|
+
setCache: (cache: Store) => Store;
|
|
10
25
|
private resizeCache;
|
|
11
26
|
}
|
|
12
27
|
export default FigTreeCache;
|
package/build/cache.js
CHANGED
|
@@ -10,19 +10,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const MAX_CACHED_ITEMS = 50;
|
|
13
|
+
const MAX_CACHE_TIME = 1800;
|
|
13
14
|
class FigTreeCache {
|
|
14
|
-
constructor(
|
|
15
|
+
constructor(options) {
|
|
16
|
+
var _a, _b;
|
|
15
17
|
this.useCache = (shouldUseCache, action, ...args) => __awaiter(this, void 0, void 0, function* () {
|
|
16
18
|
if (!shouldUseCache)
|
|
17
19
|
return yield action(...args);
|
|
18
20
|
const key = stringifyInput(args);
|
|
19
|
-
|
|
21
|
+
const keyExists = key in this.store;
|
|
22
|
+
const newTimestamp = Date.now();
|
|
23
|
+
const isExpired = keyExists && newTimestamp - this.store[key].timestamp > this.maxTime * 1000;
|
|
24
|
+
if (keyExists) {
|
|
20
25
|
this.queue = this.queue.filter((val) => val !== key);
|
|
21
26
|
this.queue.unshift(key);
|
|
22
|
-
|
|
27
|
+
this.store[key].timestamp = newTimestamp;
|
|
28
|
+
if (isExpired)
|
|
29
|
+
this.store[key].result = yield action(...args);
|
|
30
|
+
return this.store[key].result;
|
|
23
31
|
}
|
|
24
32
|
const result = yield action(...args);
|
|
25
|
-
this.store[key] = result;
|
|
33
|
+
this.store[key] = { result, timestamp: newTimestamp };
|
|
26
34
|
this.queue.unshift(key);
|
|
27
35
|
this.resizeCache();
|
|
28
36
|
return result;
|
|
@@ -32,6 +40,12 @@ class FigTreeCache {
|
|
|
32
40
|
this.maxSize = size;
|
|
33
41
|
this.resizeCache();
|
|
34
42
|
};
|
|
43
|
+
this.getMaxTime = () => this.maxTime;
|
|
44
|
+
this.setMaxTime = (time) => {
|
|
45
|
+
this.maxTime = time;
|
|
46
|
+
};
|
|
47
|
+
this.getCache = () => this.store;
|
|
48
|
+
this.setCache = (cache) => (this.store = cache);
|
|
35
49
|
this.resizeCache = () => {
|
|
36
50
|
if (this.queue.length > this.maxSize) {
|
|
37
51
|
const keysToRemove = this.queue.slice(this.maxSize);
|
|
@@ -41,7 +55,8 @@ class FigTreeCache {
|
|
|
41
55
|
};
|
|
42
56
|
this.store = {};
|
|
43
57
|
this.queue = [];
|
|
44
|
-
this.maxSize = maxSize !== null &&
|
|
58
|
+
this.maxSize = (_a = options === null || options === void 0 ? void 0 : options.maxSize) !== null && _a !== void 0 ? _a : MAX_CACHED_ITEMS;
|
|
59
|
+
this.maxTime = (_b = options === null || options === void 0 ? void 0 : options.maxTime) !== null && _b !== void 0 ? _b : MAX_CACHE_TIME;
|
|
45
60
|
}
|
|
46
61
|
}
|
|
47
62
|
exports.default = FigTreeCache;
|
|
@@ -15,9 +15,30 @@ const parameters = [
|
|
|
15
15
|
{
|
|
16
16
|
name: 'substitutions',
|
|
17
17
|
description: 'An array of substitution values for the parameterised string',
|
|
18
|
-
aliases: ['replacements'],
|
|
18
|
+
aliases: ['replacements', 'values'],
|
|
19
19
|
required: true,
|
|
20
|
-
type: 'array',
|
|
20
|
+
type: ['array', 'object'],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'trimWhiteSpace',
|
|
24
|
+
description: 'Whether or not to trim white space from either end of the substituted strings (default: true)',
|
|
25
|
+
aliases: ['trim', 'trimWhitespace'],
|
|
26
|
+
required: false,
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'substitutionCharacter',
|
|
31
|
+
description: 'Which character to search for in original string for replacement -- can be "%" or "$" (default: "%")',
|
|
32
|
+
aliases: ['subCharacter', 'subChar'],
|
|
33
|
+
required: false,
|
|
34
|
+
type: 'string',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'numberMapping',
|
|
38
|
+
description: 'Rules for mapping number values to text strings, such as pluralisation.',
|
|
39
|
+
aliases: ['numMap', 'numberMap', 'pluralisation', 'pluralization', 'plurals'],
|
|
40
|
+
required: false,
|
|
41
|
+
type: 'object',
|
|
21
42
|
},
|
|
22
43
|
];
|
|
23
44
|
exports.propertyAliases = (0, _operatorUtils_1.getPropertyAliases)(parameters);
|
|
@@ -36,15 +36,42 @@ exports.STRING_SUBSTITUTION = void 0;
|
|
|
36
36
|
const _operatorUtils_1 = require("../_operatorUtils");
|
|
37
37
|
const data_1 = __importStar(require("./data"));
|
|
38
38
|
const evaluate = (expression, config) => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
-
const [string, substitutions] = (yield (0, _operatorUtils_1.evaluateArray)([
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
const [string, substitutions, trimWhiteSpace = true, substitutionCharacter = '%', numberMapping = {},] = (yield (0, _operatorUtils_1.evaluateArray)([
|
|
40
|
+
expression.string,
|
|
41
|
+
expression.substitutions,
|
|
42
|
+
expression.trimWhiteSpace,
|
|
43
|
+
expression.substitutionCharacter,
|
|
44
|
+
expression.numberMapping,
|
|
45
|
+
], config));
|
|
46
|
+
config.typeChecker((0, _operatorUtils_1.getTypeCheckInput)(data_1.default.parameters, {
|
|
47
|
+
string,
|
|
48
|
+
substitutions,
|
|
49
|
+
trimWhiteSpace,
|
|
50
|
+
substitutionCharacter,
|
|
51
|
+
numberMapping,
|
|
52
|
+
}));
|
|
53
|
+
if (Array.isArray(substitutions)) {
|
|
54
|
+
const subChar = substitutionCharacter === '$' ? '$' : '%';
|
|
55
|
+
const patternString = `(?<!\\\\)(${subChar === '%' ? '%' : '\\$'}[\\d]+)`;
|
|
56
|
+
const parameterPattern = new RegExp(patternString, 'g');
|
|
57
|
+
const parameters = (string.match(parameterPattern) || []).sort((a, b) => Number(a.slice(1)) - Number(b.slice(1)));
|
|
58
|
+
const uniqueParameters = new Set(parameters);
|
|
59
|
+
const replacementsObj = (0, _operatorUtils_1.zipArraysToObject)(Array.from(uniqueParameters), substitutions.map((sub) => (trimWhiteSpace ? String(sub).trim() : sub)));
|
|
60
|
+
return (string
|
|
61
|
+
.split(parameterPattern)
|
|
62
|
+
.map((fragment) => (fragment in replacementsObj ? replacementsObj[fragment] : fragment))
|
|
63
|
+
.join('')
|
|
64
|
+
.replace(`\\${subChar}`, subChar));
|
|
65
|
+
}
|
|
66
|
+
const parameterPattern = /(?<!\\)({{[A-z0-9_]+}})/g;
|
|
45
67
|
return string
|
|
46
68
|
.split(parameterPattern)
|
|
47
|
-
.map((fragment) =>
|
|
69
|
+
.map((fragment) => {
|
|
70
|
+
if (!/(?<!\\){{(.+)}}/.exec(fragment))
|
|
71
|
+
return fragment.replace('\\{{', '{{');
|
|
72
|
+
const replacement = getReplacement(fragment, substitutions, numberMapping);
|
|
73
|
+
return trimWhiteSpace ? String(replacement).trim() : replacement;
|
|
74
|
+
})
|
|
48
75
|
.join('');
|
|
49
76
|
});
|
|
50
77
|
const parseChildren = (expression) => {
|
|
@@ -57,3 +84,29 @@ exports.STRING_SUBSTITUTION = {
|
|
|
57
84
|
evaluate,
|
|
58
85
|
parseChildren,
|
|
59
86
|
};
|
|
87
|
+
const getReplacement = (fragment, replacements, numberMaps) => {
|
|
88
|
+
var _a, _b;
|
|
89
|
+
const key = fragment.replace(/{{(.+)}}/, '$1');
|
|
90
|
+
const value = (_a = replacements === null || replacements === void 0 ? void 0 : replacements[key]) !== null && _a !== void 0 ? _a : '';
|
|
91
|
+
if (typeof value !== 'number')
|
|
92
|
+
return (_b = replacements === null || replacements === void 0 ? void 0 : replacements[key]) !== null && _b !== void 0 ? _b : '';
|
|
93
|
+
if (!(key in numberMaps))
|
|
94
|
+
return value;
|
|
95
|
+
const numMap = numberMaps[key];
|
|
96
|
+
if (value in numMap)
|
|
97
|
+
return numMap[value].replace('{}', String(value));
|
|
98
|
+
const numberKeys = Object.keys(numberMaps[key]);
|
|
99
|
+
const greaterThanKey = numberKeys.find((key) => key.startsWith('>'));
|
|
100
|
+
if (greaterThanKey) {
|
|
101
|
+
const num = Number(greaterThanKey.slice(1));
|
|
102
|
+
if (value > num)
|
|
103
|
+
return numMap === null || numMap === void 0 ? void 0 : numMap[greaterThanKey];
|
|
104
|
+
}
|
|
105
|
+
const lessThanKey = numberKeys.find((key) => key.startsWith('<'));
|
|
106
|
+
if (lessThanKey) {
|
|
107
|
+
const num = Number(lessThanKey.slice(1));
|
|
108
|
+
if (value < num)
|
|
109
|
+
return numMap === null || numMap === void 0 ? void 0 : numMap[lessThanKey];
|
|
110
|
+
}
|
|
111
|
+
return numMap.other ? numMap.other.replace('{}', String(value)) : value;
|
|
112
|
+
};
|
package/build/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { GraphQLConnection } from './operators';
|
|
|
4
4
|
import operatorAliases from './operators/_operatorAliases.json';
|
|
5
5
|
import { ExpectedType, TypeCheckInput } from './typeCheck';
|
|
6
6
|
export declare const Operators: readonly ["AND", "OR", "EQUAL", "NOT_EQUAL", "PLUS", "SUBTRACT", "MULTIPLY", "DIVIDE", "GREATER_THAN", "LESS_THAN", "CONDITIONAL", "REGEX", "OBJECT_PROPERTIES", "STRING_SUBSTITUTION", "SPLIT", "COUNT", "GET", "POST", "PG_SQL", "GRAPHQL", "BUILD_OBJECT", "MATCH", "CUSTOM_FUNCTIONS", "PASSTHRU"];
|
|
7
|
-
export type Operator = typeof Operators[number];
|
|
7
|
+
export type Operator = (typeof Operators)[number];
|
|
8
8
|
export type OperatorAlias = keyof typeof operatorAliases;
|
|
9
9
|
export type OperatorAliases = Record<OperatorAlias, Operator>;
|
|
10
10
|
export type Fragments = Record<string, Fragment>;
|
|
@@ -29,6 +29,7 @@ export interface FigTreeOptions {
|
|
|
29
29
|
excludeOperators?: OperatorAlias[];
|
|
30
30
|
useCache?: boolean;
|
|
31
31
|
maxCacheSize?: number;
|
|
32
|
+
maxCacheTime?: number;
|
|
32
33
|
supportDeprecatedValueNodes?: boolean;
|
|
33
34
|
}
|
|
34
35
|
export interface FigTreeConfig {
|
package/build/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.
|
|
1
|
+
export declare const version = "2.10.0";
|
package/build/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fig-tree-evaluator",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "Module to evaluate JSON-structured expression trees",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"@typescript-eslint/parser": "^5.57.0",
|
|
21
21
|
"dotenv": "^16.0.1",
|
|
22
22
|
"eslint": "^8.37.0",
|
|
23
|
-
"jest": "^
|
|
23
|
+
"jest": "^29.6.2",
|
|
24
24
|
"pg": "^8.7.3",
|
|
25
25
|
"rimraf": "^3.0.2",
|
|
26
|
-
"ts-jest": "^
|
|
26
|
+
"ts-jest": "^29.1.1",
|
|
27
27
|
"ts-node": "^10.8.0",
|
|
28
28
|
"typescript": "^4.7.2"
|
|
29
29
|
},
|