@briza/illogical 1.6.1 → 1.7.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/lib/illogical.js CHANGED
@@ -5,16 +5,16 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  function _defineProperty(e, r, t) {
6
6
  return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
7
7
  value: t,
8
- enumerable: !0,
9
- configurable: !0,
10
- writable: !0
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true
11
11
  }) : e[r] = t, e;
12
12
  }
13
13
  function _toPrimitive(t, r) {
14
14
  if ("object" != typeof t || !t) return t;
15
15
  var e = t[Symbol.toPrimitive];
16
16
  if (void 0 !== e) {
17
- var i = e.call(t, r || "default");
17
+ var i = e.call(t, r);
18
18
  if ("object" != typeof i) return i;
19
19
  throw new TypeError("@@toPrimitive must return a primitive value.");
20
20
  }
@@ -94,9 +94,15 @@ function areAllResults(values) {
94
94
  * @param {Result[]} results results or evaluables
95
95
  * @returns {boolean} type guard
96
96
  */
97
- function areAllNumbers(results) {
97
+ function areAllNumbers$1(results) {
98
98
  return results.every(isNumber);
99
99
  }
100
+ function isUndefined(value) {
101
+ return value === undefined;
102
+ }
103
+ function isNull(value) {
104
+ return value === null;
105
+ }
100
106
 
101
107
  /**
102
108
  * Valid types for context members
@@ -154,7 +160,7 @@ class Arithmetic {
154
160
  if (presentValues.length !== results.length) {
155
161
  return false;
156
162
  }
157
- if (!areAllNumbers(presentValues)) {
163
+ if (!areAllNumbers$1(presentValues)) {
158
164
  throw new Error(`operands must be numbers for ${this.constructor.name}`);
159
165
  }
160
166
  return presentValues;
@@ -1617,6 +1623,14 @@ class Xor extends Logical {
1617
1623
  * Collection operand resolved containing mixture of value and references.
1618
1624
  */
1619
1625
  class Collection extends Operand {
1626
+ /**
1627
+ * Get the items in the collection.
1628
+ * @returns {Array<Value | Reference>}
1629
+ */
1630
+ getItems() {
1631
+ return this.items;
1632
+ }
1633
+
1620
1634
  /**
1621
1635
  * @constructor
1622
1636
  * @param {Operand[]} items Collection of operands.
@@ -1925,6 +1939,516 @@ class Parser {
1925
1939
  }
1926
1940
  }
1927
1941
 
1942
+ const isTrueResult = value => value === true;
1943
+ const isFalseResult = value => value === false;
1944
+ const isNonFalseResult = operand => operand !== false && !isEvaluable(operand);
1945
+ const isNonTrueResult = operand => operand !== true && !isEvaluable(operand);
1946
+ const resultToInputInternal = value => {
1947
+ if (isUndefined(value)) {
1948
+ return undefined;
1949
+ }
1950
+ if (Array.isArray(value)) {
1951
+ return undefined;
1952
+ }
1953
+ return value;
1954
+ };
1955
+ const resultToInput = value => {
1956
+ if (isUndefined(value)) {
1957
+ return undefined;
1958
+ }
1959
+ if (Array.isArray(value)) {
1960
+ const definedValues = value.map(resultToInputInternal).filter(val => !isUndefined(val));
1961
+ if (definedValues.length === 0) {
1962
+ return undefined;
1963
+ }
1964
+ return definedValues;
1965
+ }
1966
+ return value;
1967
+ };
1968
+ const areAllNumbers = results => {
1969
+ return results.every(isNumber);
1970
+ };
1971
+ const areAllInputs = values => values.every(value => !isEvaluable(value));
1972
+ const getInputValues = results => {
1973
+ const presentValues = results.filter(result => !isNull(result) && !isUndefined(result));
1974
+ // If we have missing context values the result or we still have refences
1975
+ // simplify to false.
1976
+ if (presentValues.length !== results.length || !areAllNumbers(presentValues)) {
1977
+ return false;
1978
+ }
1979
+ return presentValues;
1980
+ };
1981
+ const extractValues = input => {
1982
+ const evaluable = isEvaluable(input);
1983
+ if (!evaluable) {
1984
+ return Array.isArray(input) ? input : [input];
1985
+ }
1986
+ if (input instanceof Collection) {
1987
+ const results = input.getItems();
1988
+ const values = results.map(item => item instanceof Value ? item.evaluate() : null).filter(value => !isNull(value));
1989
+ return values;
1990
+ }
1991
+ return [];
1992
+ };
1993
+
1994
+ const simplifyAnd = simplifyInput => input => {
1995
+ const [operator, ...operands] = input;
1996
+ const simplifiedOperands = [];
1997
+ for (const operand of operands) {
1998
+ const simplification = simplifyInput(operand);
1999
+ if (isUndefined(simplification) || isBoolean(simplification) && !isTrueResult(simplification)) {
2000
+ // Short-circuit for AND
2001
+ return false;
2002
+ }
2003
+ simplifiedOperands.push(simplification);
2004
+ }
2005
+
2006
+ // Remove false operands leaving only the Inputs
2007
+ const simplified = simplifiedOperands.filter(isNonTrueResult);
2008
+ if (simplified.length === 0) {
2009
+ return true;
2010
+ }
2011
+ if (simplified.length === 1) {
2012
+ return simplified[0];
2013
+ }
2014
+ return [operator, ...simplified];
2015
+ };
2016
+
2017
+ const simplify$3 = (simplifyInput, predicate) => input => {
2018
+ const [, ...operands] = input;
2019
+ const results = operands.map(simplifyInput);
2020
+ if (areAllInputs(results)) {
2021
+ const presentValues = getInputValues(results);
2022
+ if (isFalseResult(presentValues)) {
2023
+ return false;
2024
+ }
2025
+ return presentValues.reduce(predicate);
2026
+ }
2027
+ return input;
2028
+ };
2029
+ const simplifySum = simplifyInput => input => simplify$3(simplifyInput, operateWithExpectedDecimals('sum'))(input);
2030
+ const simplifySubtract = simplifyInput => input => simplify$3(simplifyInput, operateWithExpectedDecimals('subtract'))(input);
2031
+ const simplifyMultiply = simplifyInput => input => simplify$3(simplifyInput, operateWithExpectedDecimals('multiply'))(input);
2032
+ const simplifyDivide = simplifyInput => input => simplify$3(simplifyInput, (acc, result) => acc / result)(input);
2033
+
2034
+ const simplifyComparison = predicate => (opts, simplifyInput) => input => {
2035
+ const [operator, ...operands] = input;
2036
+ const [left, right] = operands;
2037
+ const leftSimplified = simplifyInput(left);
2038
+ const rightSimplified = simplifyInput(right);
2039
+ const isLeftEvaluable = isEvaluable(leftSimplified);
2040
+ const isRightEvaluable = isEvaluable(rightSimplified);
2041
+ if (isLeftEvaluable || isRightEvaluable) {
2042
+ // If either left or right is an array, we cannot simplify further
2043
+ return [operator, isLeftEvaluable ? leftSimplified.serialize(opts) : left, isRightEvaluable ? rightSimplified.serialize(opts) : right];
2044
+ }
2045
+ if (isNumber(leftSimplified) && isNumber(rightSimplified)) {
2046
+ return predicate(leftSimplified, rightSimplified);
2047
+ }
2048
+ const leftDate = toDateNumber(leftSimplified);
2049
+ const rightDate = toDateNumber(rightSimplified);
2050
+ if (leftDate && rightDate) {
2051
+ return predicate(leftDate, rightDate);
2052
+ }
2053
+ if (Array.isArray(leftSimplified) || Array.isArray(rightSimplified)) {
2054
+ return [operator, leftSimplified, rightSimplified];
2055
+ }
2056
+ return false;
2057
+ };
2058
+ const simplifyGt = (opts, simplifyInput) => input => simplifyComparison((left, right) => left > right)(opts, simplifyInput)(input);
2059
+ const simplifyGe = (opts, simplifyInput) => input => simplifyComparison((left, right) => left >= right)(opts, simplifyInput)(input);
2060
+ const simplifyLt = (opts, simplifyInput) => input => simplifyComparison((left, right) => left < right)(opts, simplifyInput)(input);
2061
+ const simplifyLe = (opts, simplifyInput) => input => simplifyComparison((left, right) => left <= right)(opts, simplifyInput)(input);
2062
+
2063
+ const simplifyEquality = predicate => (opts, simplifyInput) => input => {
2064
+ const [operator, ...operands] = input;
2065
+ const [left, right] = operands;
2066
+ const leftSimplified = simplifyInput(left);
2067
+ const rightSimplified = simplifyInput(right);
2068
+ const isLeftEvaluable = isEvaluable(leftSimplified);
2069
+ const isRightEvaluable = isEvaluable(rightSimplified);
2070
+ if (isLeftEvaluable || isRightEvaluable) {
2071
+ // If either left or right is an array, we cannot simplify further
2072
+ return [operator, isLeftEvaluable ? leftSimplified.serialize(opts) : left, isRightEvaluable ? rightSimplified.serialize(opts) : right];
2073
+ }
2074
+
2075
+ // See Equal.comparison
2076
+ return predicate(leftSimplified, rightSimplified);
2077
+ };
2078
+ const simplifyEq = (opts, simplifyInput) => input => simplifyEquality((left, right) => left === right)(opts, simplifyInput)(input);
2079
+ const simplifyNe = (opts, simplifyInput) => input => simplifyEquality((left, right) => left !== right)(opts, simplifyInput)(input);
2080
+
2081
+ const simplify$2 = (originalInput, nonArrayInput, arrayInput) => {
2082
+ // If we don't have the non-array operand, there is nothing to do.
2083
+ if (isEvaluable(nonArrayInput)) {
2084
+ return originalInput;
2085
+ }
2086
+
2087
+ // If we don't have all the values, try with what we have for the
2088
+ // positive case, but otherwise return the original input.
2089
+ if (isEvaluable(arrayInput)) {
2090
+ const leftValues = extractValues(arrayInput);
2091
+ const isFound = leftValues.indexOf(nonArrayInput) > -1;
2092
+ return isFound ? true : originalInput;
2093
+ }
2094
+
2095
+ // If we have all the values, we can check if the non-array operand is
2096
+ // included in the array operand.
2097
+ return arrayInput.indexOf(nonArrayInput) > -1;
2098
+ };
2099
+ const simplifyIn = simplifyInput => input => {
2100
+ const [, ...operands] = input;
2101
+ const [left, right] = operands;
2102
+ if (isNull(left) || isUndefined(left) || isNull(right) || isUndefined(right)) {
2103
+ return false;
2104
+ }
2105
+ const leftSimplified = simplifyInput(left);
2106
+ const rightSimplified = simplifyInput(right);
2107
+ const isLeftArray = Array.isArray(leftSimplified) || isEvaluable(leftSimplified);
2108
+ const isRightArray = Array.isArray(rightSimplified) || isEvaluable(rightSimplified);
2109
+ if (isLeftArray) {
2110
+ return simplify$2(input, rightSimplified, leftSimplified);
2111
+ }
2112
+ if (isRightArray) {
2113
+ return simplify$2(input, leftSimplified, rightSimplified);
2114
+ }
2115
+ return false;
2116
+ };
2117
+
2118
+ const simplifyNor = (opts, simplifyInput) => input => {
2119
+ const [operator, ...operands] = input;
2120
+ const simplifiedOperands = [];
2121
+ for (const operand of operands) {
2122
+ const simplification = simplifyInput(operand);
2123
+ if (isUndefined(simplification) || isBoolean(simplification) && isTrueResult(simplification)) {
2124
+ // Short-circuit for AND
2125
+ return false;
2126
+ }
2127
+ simplifiedOperands.push(simplification);
2128
+ }
2129
+
2130
+ // Remove true operands leaving only the Inputs
2131
+ const simplified = simplifiedOperands.filter(isNonFalseResult);
2132
+ if (simplified.length === 0) {
2133
+ return true;
2134
+ }
2135
+ if (simplified.length === 1) {
2136
+ var _opts$operatorMapping;
2137
+ return [(_opts$operatorMapping = opts.operatorMapping.get(OPERATOR$3)) !== null && _opts$operatorMapping !== void 0 ? _opts$operatorMapping : 'NOT', simplified[0]];
2138
+ }
2139
+ return [operator, ...simplified];
2140
+ };
2141
+
2142
+ const simplifyNot = simplifyInput => input => {
2143
+ const [, ...operands] = input;
2144
+ const simplification = simplifyInput(operands[0]);
2145
+ if (isBoolean(simplification)) {
2146
+ return !simplification;
2147
+ }
2148
+ return input;
2149
+ };
2150
+
2151
+ const simplifyNotIn = simplifyInput => input => {
2152
+ const [, ...operands] = input;
2153
+ const [left, right] = operands;
2154
+ const leftArray = Array.isArray(left);
2155
+ const rightArray = Array.isArray(right);
2156
+ if (isNull(left) || isUndefined(left) || isNull(right) || isUndefined(right) || leftArray && rightArray || !leftArray && !rightArray) {
2157
+ return true;
2158
+ }
2159
+ if (leftArray) {
2160
+ // If any operand is still an Evaluable, we cannot simplify further
2161
+ const rightSimplified = simplifyInput(right);
2162
+ if (isEvaluable(rightSimplified)) {
2163
+ return input;
2164
+ }
2165
+ const leftSimplified = left.map(simplifyInput);
2166
+ if (leftSimplified.some(isEvaluable)) {
2167
+ return input;
2168
+ }
2169
+ return leftSimplified.indexOf(rightSimplified) === -1;
2170
+ }
2171
+ if (rightArray) {
2172
+ const leftSimplified = simplifyInput(left);
2173
+ if (isEvaluable(leftSimplified)) {
2174
+ return input;
2175
+ }
2176
+ const rightSimplified = right.map(simplifyInput);
2177
+ if (rightSimplified.some(isEvaluable)) {
2178
+ return input;
2179
+ }
2180
+ return rightSimplified.indexOf(leftSimplified) === -1;
2181
+ }
2182
+ return input;
2183
+ };
2184
+
2185
+ const simplifyOr = simplifyInput => input => {
2186
+ const [operator, ...operands] = input;
2187
+ const simplifiedOperands = [];
2188
+ for (const operand of operands) {
2189
+ const simplification = simplifyInput(operand);
2190
+ if (isBoolean(simplification) && isTrueResult(simplification)) {
2191
+ // Short-circuit for OR
2192
+ return true;
2193
+ } else if (simplification) {
2194
+ simplifiedOperands.push(simplification);
2195
+ }
2196
+ }
2197
+ const simplified = simplifiedOperands.filter(isNonFalseResult);
2198
+ if (simplified.length === 0) {
2199
+ return false;
2200
+ }
2201
+ if (simplified.length === 1) {
2202
+ return simplified[0];
2203
+ }
2204
+ return [operator, ...simplified];
2205
+ };
2206
+
2207
+ const simplifyOverlap = simplifyInput => input => {
2208
+ const [, ...operands] = input;
2209
+ const [left, right] = operands;
2210
+ const leftSimplified = simplifyInput(left);
2211
+ const rightSimplified = simplifyInput(right);
2212
+ const isLeftEvaluable = isEvaluable(leftSimplified);
2213
+ const isRightEvaluable = isEvaluable(rightSimplified);
2214
+
2215
+ // If we don't have all operands simplified, we can try the positive case
2216
+ // of the values we have satisfying the OVERLAP. But if not, we need to
2217
+ // return the original input.
2218
+ if (isLeftEvaluable || isRightEvaluable) {
2219
+ const leftValues = extractValues(leftSimplified);
2220
+ const rightValues = extractValues(rightSimplified);
2221
+ const rightSet = new Set(rightValues);
2222
+ const res = leftValues.some(element => rightSet.has(element));
2223
+ if (res) {
2224
+ return true;
2225
+ }
2226
+ return input;
2227
+ }
2228
+
2229
+ // If simplified results are not arrays, it means we had a strictKey
2230
+ // without values provided. Simplify to false.
2231
+ if (!Array.isArray(leftSimplified) || !Array.isArray(rightSimplified)) {
2232
+ return false;
2233
+ }
2234
+ const rightSet = new Set(rightSimplified);
2235
+ const res = leftSimplified.some(element => rightSet.has(element));
2236
+ if (res) {
2237
+ return true;
2238
+ }
2239
+ return false;
2240
+ };
2241
+
2242
+ const simplify$1 = (opts, simplifyInput, startsWith = true) => input => {
2243
+ const [operator, ...operands] = input;
2244
+ const [left, right] = operands;
2245
+ const leftSimplified = simplifyInput(left);
2246
+ const rightSimplified = simplifyInput(right);
2247
+ const isLeftEvaluable = isEvaluable(leftSimplified);
2248
+ const isRightEvaluable = isEvaluable(rightSimplified);
2249
+ if (isLeftEvaluable || isRightEvaluable) {
2250
+ // If either left or right is an array, we cannot simplify further
2251
+ return [operator, isLeftEvaluable ? leftSimplified.serialize(opts) : left, isRightEvaluable ? rightSimplified.serialize(opts) : right];
2252
+ }
2253
+ if (isString(leftSimplified) && isString(rightSimplified)) {
2254
+ return startsWith ? rightSimplified.startsWith(leftSimplified) : leftSimplified.endsWith(rightSimplified);
2255
+ }
2256
+ return false;
2257
+ };
2258
+ const simplifyPrefix = (opts, simplifyInput) => input => simplify$1(opts, simplifyInput)(input);
2259
+ const simplifySuffix = (opts, simplifyInput) => input => simplify$1(opts, simplifyInput, false)(input);
2260
+
2261
+ const simplify = (simplifyInput, predicate, present = true) => input => {
2262
+ const [, ...operands] = input;
2263
+ const [operand] = operands;
2264
+ const simplified = simplifyInput(operand);
2265
+ const isOperandEvaluable = isEvaluable(simplified);
2266
+ if (isOperandEvaluable) {
2267
+ return input;
2268
+ }
2269
+
2270
+ // Operand simplifies to itself when it is included in strictKeys or
2271
+ // optionalKeys, thus it was undefined.
2272
+ if (operand === simplified) {
2273
+ return !present;
2274
+ }
2275
+ return predicate(simplified);
2276
+ };
2277
+ const simplifyPresent = simplifyInput => input => simplify(simplifyInput, input => !isUndefined(input) && !isNull(input))(input);
2278
+ const simplifyUndefined = simplifyInput => input => simplify(simplifyInput, isUndefined, false)(input);
2279
+
2280
+ const simplifyXor = (opts, simplifyInput) => input => {
2281
+ const [operator, ...operands] = input;
2282
+ let trueCount = 0;
2283
+ const simplifiedOperands = [];
2284
+ for (const operand of operands) {
2285
+ const simplification = simplifyInput(operand);
2286
+ if (isBoolean(simplification) && isTrueResult(simplification)) {
2287
+ trueCount++;
2288
+ continue;
2289
+ }
2290
+ if (isBoolean(simplification) && isFalseResult(simplification)) {
2291
+ continue;
2292
+ }
2293
+ if (!isEvaluable(simplification)) {
2294
+ simplifiedOperands.push(simplification);
2295
+ }
2296
+ }
2297
+ if (trueCount > 1) {
2298
+ return false;
2299
+ }
2300
+ if (simplifiedOperands.length === 0) {
2301
+ return trueCount === 1;
2302
+ }
2303
+ if (simplifiedOperands.length === 1) {
2304
+ if (trueCount === 1) {
2305
+ var _opts$operatorMapping;
2306
+ return [(_opts$operatorMapping = opts.operatorMapping.get(OPERATOR$3)) !== null && _opts$operatorMapping !== void 0 ? _opts$operatorMapping : 'NOT', simplifiedOperands[0]];
2307
+ }
2308
+ return simplifiedOperands[0];
2309
+ }
2310
+ if (trueCount === 1) {
2311
+ var _opts$operatorMapping2;
2312
+ return [(_opts$operatorMapping2 = opts.operatorMapping.get(OPERATOR$2)) !== null && _opts$operatorMapping2 !== void 0 ? _opts$operatorMapping2 : 'NOR', ...simplifiedOperands];
2313
+ }
2314
+ return [operator, ...simplifiedOperands];
2315
+ };
2316
+
2317
+ const unsafeSimplify = (context, opts, strictKeys, optionalKeys) => {
2318
+ const simplifyInput = input => {
2319
+ // Value or Reference
2320
+ if (!Array.isArray(input)) {
2321
+ // Reference
2322
+ if (isString(input) && opts.referencePredicate(input)) {
2323
+ const result = new Reference(opts.referenceTransform(input)).simplify(context, strictKeys, optionalKeys);
2324
+ if (isEvaluable(result)) {
2325
+ return result;
2326
+ }
2327
+ const inputResult = resultToInput(result);
2328
+ if (!isUndefined(inputResult)) {
2329
+ return inputResult;
2330
+ }
2331
+ // It should have been a boolean or an Evaluable, but just in case
2332
+ // fallback to returning the input.
2333
+ }
2334
+ // Value
2335
+ return input;
2336
+ }
2337
+ const [operator] = input;
2338
+ switch (operator) {
2339
+ // Logical operators
2340
+ case opts.operatorMapping.get(OPERATOR$4):
2341
+ {
2342
+ return simplifyAnd(simplifyInput)(input);
2343
+ }
2344
+ case opts.operatorMapping.get(OPERATOR$1):
2345
+ {
2346
+ return simplifyOr(simplifyInput)(input);
2347
+ }
2348
+ case opts.operatorMapping.get(OPERATOR$2):
2349
+ {
2350
+ return simplifyNor(opts, simplifyInput)(input);
2351
+ }
2352
+ case opts.operatorMapping.get(OPERATOR):
2353
+ {
2354
+ return simplifyXor(opts, simplifyInput)(input);
2355
+ }
2356
+ case opts.operatorMapping.get(OPERATOR$3):
2357
+ {
2358
+ return simplifyNot(simplifyInput)(input);
2359
+ }
2360
+ // Comparison operators
2361
+ case opts.operatorMapping.get(OPERATOR$h):
2362
+ {
2363
+ return simplifyEq(opts, simplifyInput)(input);
2364
+ }
2365
+ case opts.operatorMapping.get(OPERATOR$b):
2366
+ {
2367
+ return simplifyNe(opts, simplifyInput)(input);
2368
+ }
2369
+ case opts.operatorMapping.get(OPERATOR$f):
2370
+ {
2371
+ return simplifyGt(opts, simplifyInput)(input);
2372
+ }
2373
+ case opts.operatorMapping.get(OPERATOR$g):
2374
+ {
2375
+ return simplifyGe(opts, simplifyInput)(input);
2376
+ }
2377
+ case opts.operatorMapping.get(OPERATOR$c):
2378
+ {
2379
+ return simplifyLt(opts, simplifyInput)(input);
2380
+ }
2381
+ case opts.operatorMapping.get(OPERATOR$d):
2382
+ {
2383
+ return simplifyLe(opts, simplifyInput)(input);
2384
+ }
2385
+ case opts.operatorMapping.get(OPERATOR$e):
2386
+ {
2387
+ return simplifyIn(simplifyInput)(input);
2388
+ }
2389
+ case opts.operatorMapping.get(OPERATOR$a):
2390
+ {
2391
+ return simplifyNotIn(simplifyInput)(input);
2392
+ }
2393
+ case opts.operatorMapping.get(OPERATOR$8):
2394
+ {
2395
+ return simplifyPrefix(opts, simplifyInput)(input);
2396
+ }
2397
+ case opts.operatorMapping.get(OPERATOR$6):
2398
+ {
2399
+ return simplifySuffix(opts, simplifyInput)(input);
2400
+ }
2401
+ case opts.operatorMapping.get(OPERATOR$9):
2402
+ {
2403
+ return simplifyOverlap(simplifyInput)(input);
2404
+ }
2405
+ case opts.operatorMapping.get(OPERATOR$5):
2406
+ {
2407
+ return simplifyUndefined(simplifyInput)(input);
2408
+ }
2409
+ case opts.operatorMapping.get(OPERATOR$7):
2410
+ {
2411
+ return simplifyPresent(simplifyInput)(input);
2412
+ }
2413
+ // Arithmetic operators
2414
+ case opts.operatorMapping.get(OPERATOR$i):
2415
+ {
2416
+ return simplifySum(simplifyInput)(input);
2417
+ }
2418
+ case opts.operatorMapping.get(OPERATOR$j):
2419
+ {
2420
+ return simplifySubtract(simplifyInput)(input);
2421
+ }
2422
+ case opts.operatorMapping.get(OPERATOR$k):
2423
+ {
2424
+ return simplifyMultiply(simplifyInput)(input);
2425
+ }
2426
+ case opts.operatorMapping.get(OPERATOR$l):
2427
+ {
2428
+ return simplifyDivide(simplifyInput)(input);
2429
+ }
2430
+ default:
2431
+ {
2432
+ // Handle as an array of References / Values if no operator matches
2433
+ const result = input.map(simplifyInput);
2434
+ if (!areAllInputs(result)) {
2435
+ return new Collection(result.map(item => {
2436
+ if (item instanceof Reference) {
2437
+ return item;
2438
+ }
2439
+ if (!isEvaluable(item)) {
2440
+ return new Value(item);
2441
+ }
2442
+ throw new Error('Unexpected expression found within a collection of values/references');
2443
+ }));
2444
+ }
2445
+ return result;
2446
+ }
2447
+ }
2448
+ };
2449
+ return simplifyInput;
2450
+ };
2451
+
1928
2452
  const unexpectedResultError = 'non expression or boolean result should be returned';
1929
2453
 
1930
2454
  /**
@@ -1997,6 +2521,18 @@ class Engine {
1997
2521
  }
1998
2522
  throw new Error(unexpectedResultError);
1999
2523
  }
2524
+ unsafeSimplify(exp, context, strictKeys, optionalKeys) {
2525
+ const result = unsafeSimplify(context, this.parser.options, strictKeys, optionalKeys)(exp);
2526
+ if (isBoolean(result)) {
2527
+ return result;
2528
+ }
2529
+
2530
+ // The unsafe implementation should not expose the Evaluable interface
2531
+ if (isEvaluable(result)) {
2532
+ throw new Error('Unexpected Evaluable not serialized result');
2533
+ }
2534
+ return result;
2535
+ }
2000
2536
  }
2001
2537
 
2002
2538
  exports.OPERATOR_AND = OPERATOR$4;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@briza/illogical",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
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",
@@ -42,7 +42,7 @@
42
42
  "rules"
43
43
  ],
44
44
  "devDependencies": {
45
- "@babel/core": "^7.25.2",
45
+ "@babel/core": "^7.27.3",
46
46
  "@babel/plugin-proposal-class-properties": "^7.18.6",
47
47
  "@babel/preset-env": "^7.25.3",
48
48
  "@babel/preset-typescript": "^7.24.7",
@@ -64,7 +64,7 @@
64
64
  "jest": "^29.7.0",
65
65
  "license-checker": "^25.0.1",
66
66
  "prettier": "^3.3.3",
67
- "rollup": "^4.20.0",
67
+ "rollup": "^4.41.1",
68
68
  "ts-jest": "^29.2.4",
69
69
  "typedoc": "^0.26.5",
70
70
  "typescript": "^5.5.4"
package/readme.md CHANGED
@@ -224,6 +224,19 @@ engine.simplify(
224
224
  ) // ['==', '$b', 20]
225
225
  ```
226
226
 
227
+ ### Unsafe Simplify
228
+
229
+ Simplifies an expression with a given context, but without parsing and ensuring the condition is
230
+ syntatically correct. This can be used in situations where you can decouple the parsing from
231
+ the simplification and extra performance is needed in runtime.
232
+
233
+ ```js
234
+ engine.unsafeSimplify(
235
+ ['OR', ['==', '$a', 10], ['==', '$b', 20], ['==', '$c', 20]],
236
+ { a: 10 }
237
+ ) // true due to $a. Expressions for $b and $c won't even be parsed.
238
+ ```
239
+
227
240
  ## Working with Expressions
228
241
 
229
242
  ### Evaluation Data Context
@@ -237,6 +250,7 @@ To reference the nested reference, please use "." delimiter, e.g.:
237
250
 
238
251
  If the key of the nested reference includes the "." delimiter, please wrap the whole key with backticks `` ` ``, e.g.:
239
252
  `` $address.`city.code` `` can reference the object
253
+
240
254
  ```javascript
241
255
  {
242
256
  address: {
@@ -245,7 +259,8 @@ If the key of the nested reference includes the "." delimiter, please wrap the w
245
259
  }
246
260
  ```
247
261
 
248
- `` $address.`city.code`[0] `` can reference the object
262
+ ``$address.`city.code`[0]`` can reference the object
263
+
249
264
  ```javascript
250
265
  {
251
266
  address: {
@@ -253,6 +268,7 @@ If the key of the nested reference includes the "." delimiter, please wrap the w
253
268
  }
254
269
  }
255
270
  ```
271
+
256
272
  when the value of the nested reference is an array.
257
273
 
258
274
  #### Accessing Array Element:
@@ -705,7 +721,7 @@ Expression format: `["/", First Operand, Second Operand, ... , Nth Operand]`.
705
721
  ```
706
722
 
707
723
  ```js
708
- engine.evaluate(["==", ["/", 100, 10], 10]) // true
724
+ engine.evaluate(['==', ['/', 100, 10], 10]) // true
709
725
  ```
710
726
 
711
727
  #### Multiplication
@@ -721,7 +737,7 @@ Expression format: `["*", First Operand, Second Operand, ... , Nth Operand]`.
721
737
  ```
722
738
 
723
739
  ```js
724
- engine.evaluate(["==", ["*", 10, 10], 100]) // true
740
+ engine.evaluate(['==', ['*', 10, 10], 100]) // true
725
741
  ```
726
742
 
727
743
  #### Subtraction
@@ -737,10 +753,9 @@ Expression format: `["-", First Operand, Second Operand, ... , Nth Operand]`.
737
753
  ```
738
754
 
739
755
  ```js
740
- engine.evaluate(["==", ["-", 20, 10], 10]) // true
756
+ engine.evaluate(['==', ['-', 20, 10], 10]) // true
741
757
  ```
742
758
 
743
-
744
759
  #### Addition
745
760
 
746
761
  The arithmetical operator for addition produces the sum of the operands.
@@ -754,10 +769,9 @@ Expression format: `["+", First Operand, Second Operand, ... , Nth Operand]`.
754
769
  ```
755
770
 
756
771
  ```js
757
- engine.evaluate(["==", ["+", 5, 5], 10]) // true
772
+ engine.evaluate(['==', ['+', 5, 5], 10]) // true
758
773
  ```
759
774
 
760
-
761
775
  ## Engine Options
762
776
 
763
777
  ### Parser Options