@briza/illogical 1.7.0 → 1.7.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/changelog.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # illogical changelog
2
2
 
3
+ ## 1.7.1
4
+
5
+ - Adding Reference cache and other performance improvements to condition parsing.
6
+
3
7
  ## 1.7.0
4
8
 
5
9
  - Introducing unsafeSimplify which simplifies the condition without fully parsing it beforehand.
@@ -472,29 +472,33 @@ const toDateNumber = value => {
472
472
 
473
473
  const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
474
474
  const arrayIndexRegex = /\[(\d+)]/g;
475
- const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
476
- const parseKey = key => {
477
- const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
478
- return !keys ? [] : keys.flatMap(key => {
479
- const unwrappedKey = parseBacktickWrappedKey(key);
480
- const keys = [];
481
- const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
482
- if (parseResult) {
483
- var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
484
- const extractedKey = parseBacktickWrappedKey((_parseResult$groups$c = parseResult === null || parseResult === void 0 || (_parseResult$groups = parseResult.groups) === null || _parseResult$groups === void 0 ? void 0 : _parseResult$groups.currentKey) !== null && _parseResult$groups$c !== void 0 ? _parseResult$groups$c : unwrappedKey);
485
- keys.push(extractedKey);
486
- const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
487
- if (rawIndexes) {
488
- for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
489
- keys.push(parseInt(indexResult[1]));
490
- }
475
+ function parseBacktickWrappedKey(key) {
476
+ return key[0] === '`' && key[key.length - 1] === '`' ? key.slice(1, -1) : key;
477
+ }
478
+ function parseKeyComponents(key) {
479
+ const unwrappedKey = parseBacktickWrappedKey(key);
480
+ const keys = [];
481
+ const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
482
+ if (parseResult) {
483
+ var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
484
+ const extractedKey = parseBacktickWrappedKey((_parseResult$groups$c = parseResult === null || parseResult === void 0 || (_parseResult$groups = parseResult.groups) === null || _parseResult$groups === void 0 ? void 0 : _parseResult$groups.currentKey) !== null && _parseResult$groups$c !== void 0 ? _parseResult$groups$c : unwrappedKey);
485
+ keys.push(extractedKey);
486
+ const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
487
+ if (rawIndexes) {
488
+ for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
489
+ keys.push(parseInt(indexResult[1]));
491
490
  }
492
- } else {
493
- keys.push(unwrappedKey);
494
491
  }
495
- return keys;
496
- });
497
- };
492
+ } else {
493
+ keys.push(unwrappedKey);
494
+ }
495
+ return keys;
496
+ }
497
+ const parseKeyRegex = /(`[^[\]]+`(\[\d+\])*|[^`.]+)/g;
498
+ function parseKey(key) {
499
+ const keys = key.match(parseKeyRegex);
500
+ return !keys ? [] : keys.flatMap(parseKeyComponents);
501
+ }
498
502
  const complexKeyExpression = /{([^{}]+)}/;
499
503
  function extractComplexKeys(ctx, key) {
500
504
  // Resolve complex keys
@@ -551,6 +555,7 @@ let DataType = /*#__PURE__*/function (DataType) {
551
555
  // Equivalent to /^.+\.\((Number|String)\)$/
552
556
  const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
553
557
  const isComplexKey = key => key.indexOf('{') > -1;
558
+ const castingRegex = /\.\(.+\)$/;
554
559
 
555
560
  /**
556
561
  * Reference operand resolved within the context
@@ -573,9 +578,7 @@ class Reference extends Operand {
573
578
  const dataTypeMatch = dataTypeRegex.exec(this.key);
574
579
  if (dataTypeMatch) {
575
580
  this.dataType = DataType[dataTypeMatch[1]];
576
- }
577
- if (this.key.match(/.\(.+\)$/)) {
578
- this.key = this.key.replace(/.\(.+\)$/, '');
581
+ this.key = this.key.replace(castingRegex, '');
579
582
  }
580
583
  if (isComplexKey(this.key)) {
581
584
  this.valueLookup = context => complexValueLookup(context, this.key);
@@ -1690,7 +1693,7 @@ class Collection extends Operand {
1690
1693
  * @return {boolean}
1691
1694
  */
1692
1695
  function defaultReferencePredicate(key) {
1693
- return isString(key) && key.startsWith('$');
1696
+ return key[0] === '$';
1694
1697
  }
1695
1698
 
1696
1699
  /**
@@ -1748,6 +1751,7 @@ class Parser {
1748
1751
  _defineProperty(this, "opts", void 0);
1749
1752
  _defineProperty(this, "expectedRootOperators", void 0);
1750
1753
  _defineProperty(this, "unexpectedRootSymbols", new Set([OPERATOR$i, OPERATOR$j, OPERATOR$k, OPERATOR$l]));
1754
+ _defineProperty(this, "referenceCache", new Map());
1751
1755
  this.opts = {
1752
1756
  ...defaultOptions
1753
1757
  };
@@ -1769,6 +1773,18 @@ class Parser {
1769
1773
  get options() {
1770
1774
  return this.opts;
1771
1775
  }
1776
+ getReference(key) {
1777
+ const cached = this.referenceCache.get(key);
1778
+ if (cached !== undefined) {
1779
+ return cached;
1780
+ }
1781
+ const reference = new Reference(this.opts.referenceTransform(key));
1782
+ this.referenceCache.set(key, reference);
1783
+ return reference;
1784
+ }
1785
+ resolve(raw) {
1786
+ return isString(raw) && this.opts.referencePredicate(raw) ? this.getReference(raw) : new Value(raw);
1787
+ }
1772
1788
 
1773
1789
  /**
1774
1790
  * Parse raw expression into evaluable expression.
@@ -1927,11 +1943,14 @@ class Parser {
1927
1943
  * @param raw Raw data
1928
1944
  */
1929
1945
  getOperand(raw) {
1930
- const resolve = raw => this.opts.referencePredicate(raw) ? new Reference(this.opts.referenceTransform(raw)) : new Value(raw);
1931
1946
  if (Array.isArray(raw)) {
1932
- return new Collection(raw.map(item => resolve(item)));
1947
+ const collectionItems = [];
1948
+ for (const item of raw) {
1949
+ collectionItems.push(this.resolve(item));
1950
+ }
1951
+ return new Collection(collectionItems);
1933
1952
  }
1934
- return resolve(raw);
1953
+ return this.resolve(raw);
1935
1954
  }
1936
1955
  }
1937
1956
 
package/lib/illogical.js CHANGED
@@ -476,29 +476,33 @@ const toDateNumber = value => {
476
476
 
477
477
  const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
478
478
  const arrayIndexRegex = /\[(\d+)]/g;
479
- const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
480
- const parseKey = key => {
481
- const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
482
- return !keys ? [] : keys.flatMap(key => {
483
- const unwrappedKey = parseBacktickWrappedKey(key);
484
- const keys = [];
485
- const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
486
- if (parseResult) {
487
- var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
488
- const extractedKey = parseBacktickWrappedKey((_parseResult$groups$c = parseResult === null || parseResult === void 0 || (_parseResult$groups = parseResult.groups) === null || _parseResult$groups === void 0 ? void 0 : _parseResult$groups.currentKey) !== null && _parseResult$groups$c !== void 0 ? _parseResult$groups$c : unwrappedKey);
489
- keys.push(extractedKey);
490
- const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
491
- if (rawIndexes) {
492
- for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
493
- keys.push(parseInt(indexResult[1]));
494
- }
479
+ function parseBacktickWrappedKey(key) {
480
+ return key[0] === '`' && key[key.length - 1] === '`' ? key.slice(1, -1) : key;
481
+ }
482
+ function parseKeyComponents(key) {
483
+ const unwrappedKey = parseBacktickWrappedKey(key);
484
+ const keys = [];
485
+ const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
486
+ if (parseResult) {
487
+ var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
488
+ const extractedKey = parseBacktickWrappedKey((_parseResult$groups$c = parseResult === null || parseResult === void 0 || (_parseResult$groups = parseResult.groups) === null || _parseResult$groups === void 0 ? void 0 : _parseResult$groups.currentKey) !== null && _parseResult$groups$c !== void 0 ? _parseResult$groups$c : unwrappedKey);
489
+ keys.push(extractedKey);
490
+ const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
491
+ if (rawIndexes) {
492
+ for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
493
+ keys.push(parseInt(indexResult[1]));
495
494
  }
496
- } else {
497
- keys.push(unwrappedKey);
498
495
  }
499
- return keys;
500
- });
501
- };
496
+ } else {
497
+ keys.push(unwrappedKey);
498
+ }
499
+ return keys;
500
+ }
501
+ const parseKeyRegex = /(`[^[\]]+`(\[\d+\])*|[^`.]+)/g;
502
+ function parseKey(key) {
503
+ const keys = key.match(parseKeyRegex);
504
+ return !keys ? [] : keys.flatMap(parseKeyComponents);
505
+ }
502
506
  const complexKeyExpression = /{([^{}]+)}/;
503
507
  function extractComplexKeys(ctx, key) {
504
508
  // Resolve complex keys
@@ -555,6 +559,7 @@ let DataType = /*#__PURE__*/function (DataType) {
555
559
  // Equivalent to /^.+\.\((Number|String)\)$/
556
560
  const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
557
561
  const isComplexKey = key => key.indexOf('{') > -1;
562
+ const castingRegex = /\.\(.+\)$/;
558
563
 
559
564
  /**
560
565
  * Reference operand resolved within the context
@@ -577,9 +582,7 @@ class Reference extends Operand {
577
582
  const dataTypeMatch = dataTypeRegex.exec(this.key);
578
583
  if (dataTypeMatch) {
579
584
  this.dataType = DataType[dataTypeMatch[1]];
580
- }
581
- if (this.key.match(/.\(.+\)$/)) {
582
- this.key = this.key.replace(/.\(.+\)$/, '');
585
+ this.key = this.key.replace(castingRegex, '');
583
586
  }
584
587
  if (isComplexKey(this.key)) {
585
588
  this.valueLookup = context => complexValueLookup(context, this.key);
@@ -1694,7 +1697,7 @@ class Collection extends Operand {
1694
1697
  * @return {boolean}
1695
1698
  */
1696
1699
  function defaultReferencePredicate(key) {
1697
- return isString(key) && key.startsWith('$');
1700
+ return key[0] === '$';
1698
1701
  }
1699
1702
 
1700
1703
  /**
@@ -1752,6 +1755,7 @@ class Parser {
1752
1755
  _defineProperty(this, "opts", void 0);
1753
1756
  _defineProperty(this, "expectedRootOperators", void 0);
1754
1757
  _defineProperty(this, "unexpectedRootSymbols", new Set([OPERATOR$i, OPERATOR$j, OPERATOR$k, OPERATOR$l]));
1758
+ _defineProperty(this, "referenceCache", new Map());
1755
1759
  this.opts = {
1756
1760
  ...defaultOptions
1757
1761
  };
@@ -1773,6 +1777,18 @@ class Parser {
1773
1777
  get options() {
1774
1778
  return this.opts;
1775
1779
  }
1780
+ getReference(key) {
1781
+ const cached = this.referenceCache.get(key);
1782
+ if (cached !== undefined) {
1783
+ return cached;
1784
+ }
1785
+ const reference = new Reference(this.opts.referenceTransform(key));
1786
+ this.referenceCache.set(key, reference);
1787
+ return reference;
1788
+ }
1789
+ resolve(raw) {
1790
+ return isString(raw) && this.opts.referencePredicate(raw) ? this.getReference(raw) : new Value(raw);
1791
+ }
1776
1792
 
1777
1793
  /**
1778
1794
  * Parse raw expression into evaluable expression.
@@ -1931,11 +1947,14 @@ class Parser {
1931
1947
  * @param raw Raw data
1932
1948
  */
1933
1949
  getOperand(raw) {
1934
- const resolve = raw => this.opts.referencePredicate(raw) ? new Reference(this.opts.referenceTransform(raw)) : new Value(raw);
1935
1950
  if (Array.isArray(raw)) {
1936
- return new Collection(raw.map(item => resolve(item)));
1951
+ const collectionItems = [];
1952
+ for (const item of raw) {
1953
+ collectionItems.push(this.resolve(item));
1954
+ }
1955
+ return new Collection(collectionItems);
1937
1956
  }
1938
- return resolve(raw);
1957
+ return this.resolve(raw);
1939
1958
  }
1940
1959
  }
1941
1960
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@briza/illogical",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "A micro conditional javascript engine used to parse the raw logical and comparison expressions, evaluate the expression in the given data context, and provide access to a text form of the given expressions.",
5
5
  "main": "lib/illogical.js",
6
6
  "module": "lib/illogical.esm.js",
package/readme.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  A micro conditional javascript engine used to parse the raw logical and comparison expressions, evaluate the expression in the given data context, and provide access to a text form of the given expressions.
4
4
 
5
- > Revision: March 22, 2022.
6
-
7
5
  ## About
8
6
 
9
7
  This project has been developed to provide a shared conditional logic between front-end and back-end code, stored in JSON or in any other data serialization format.
@@ -10,6 +10,7 @@ export declare class Parser {
10
10
  private readonly opts;
11
11
  private readonly expectedRootOperators;
12
12
  private readonly unexpectedRootSymbols;
13
+ private readonly referenceCache;
13
14
  /**
14
15
  * @constructor
15
16
  * @param {Options?} options Parser options.
@@ -20,6 +21,8 @@ export declare class Parser {
20
21
  * @type {Options}
21
22
  */
22
23
  get options(): Options;
24
+ private getReference;
25
+ private resolve;
23
26
  /**
24
27
  * Parse raw expression into evaluable expression.
25
28
  * @param {ExpressionInput} raw Raw expression.