@briza/illogical 1.5.8 → 1.6.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
@@ -2,6 +2,14 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ function _defineProperty(e, r, t) {
6
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
7
+ value: t,
8
+ enumerable: !0,
9
+ configurable: !0,
10
+ writable: !0
11
+ }) : e[r] = t, e;
12
+ }
5
13
  function _toPrimitive(t, r) {
6
14
  if ("object" != typeof t || !t) return t;
7
15
  var e = t[Symbol.toPrimitive];
@@ -14,21 +22,7 @@ function _toPrimitive(t, r) {
14
22
  }
15
23
  function _toPropertyKey(t) {
16
24
  var i = _toPrimitive(t, "string");
17
- return "symbol" == typeof i ? i : String(i);
18
- }
19
- function _defineProperty(obj, key, value) {
20
- key = _toPropertyKey(key);
21
- if (key in obj) {
22
- Object.defineProperty(obj, key, {
23
- value: value,
24
- enumerable: true,
25
- configurable: true,
26
- writable: true
27
- });
28
- } else {
29
- obj[key] = value;
30
- }
31
- return obj;
25
+ return "symbol" == typeof i ? i : i + "";
32
26
  }
33
27
 
34
28
  /**
@@ -36,7 +30,14 @@ function _defineProperty(obj, key, value) {
36
30
  * @param value Tested value.
37
31
  */
38
32
  function isNumber(value) {
39
- return typeof value === 'number' && isFinite(value);
33
+ return typeof value === 'number';
34
+ }
35
+ /**
36
+ * Is number predicate.
37
+ * @param value Tested value.
38
+ */
39
+ function isInfinite(value) {
40
+ return typeof value === 'number' && !isFinite(value);
40
41
  }
41
42
 
42
43
  /**
@@ -58,50 +59,573 @@ function isObject(value) {
58
59
  if (typeof value !== 'object' || (value === null || value === void 0 ? void 0 : value.constructor) !== Object) {
59
60
  return false;
60
61
  }
61
- return true;
62
- }
63
-
64
- /**
65
- * Is Boolean predicate.
66
- * @param value tested value.
67
- * @return result of the test
68
- */
69
- function isBoolean(value) {
70
- return typeof value === 'boolean';
71
- }
62
+ return true;
63
+ }
64
+
65
+ /**
66
+ * Is Boolean predicate.
67
+ * @param value tested value.
68
+ * @return result of the test
69
+ */
70
+ function isBoolean(value) {
71
+ return typeof value === 'boolean';
72
+ }
73
+
74
+ /**
75
+ * Check if a value is a an Evaluable
76
+ * @param {Result | Evaluable} value value to check if is Evaluable
77
+ * @returns {Evaluable}
78
+ */
79
+ function isEvaluable(value) {
80
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && typeof value.evaluate === 'function' && typeof value.simplify === 'function' && typeof value.serialize === 'function' && typeof value.toString === 'function';
81
+ }
82
+
83
+ /**
84
+ * Ensures all values are results.
85
+ * @param {(Result | Evaluable)[]} values results or evaluables
86
+ * @returns {boolean} type guard
87
+ */
88
+ function areAllResults(values) {
89
+ return values.every(value => !isEvaluable(value));
90
+ }
91
+
92
+ /**
93
+ * Ensures all values are numbers.
94
+ * @param {Result[]} results results or evaluables
95
+ * @returns {boolean} type guard
96
+ */
97
+ function areAllNumbers(results) {
98
+ return results.every(isNumber);
99
+ }
100
+
101
+ /**
102
+ * Valid types for context members
103
+ */
104
+
105
+ /**
106
+ * Evaluation Context
107
+ * Holds references used during the evaluation process.
108
+ * Format: key: value.
109
+ */
110
+
111
+ /**
112
+ * Evaluation result
113
+ */
114
+
115
+ let EvaluableType = /*#__PURE__*/function (EvaluableType) {
116
+ EvaluableType["Operand"] = "Operand";
117
+ EvaluableType["Expression"] = "Expression";
118
+ return EvaluableType;
119
+ }({});
120
+
121
+ /**
122
+ * Evaluable
123
+ */
124
+
125
+ /**
126
+ * Abstract arithmetic expression
127
+ */
128
+ class Arithmetic {
129
+ /**
130
+ * @constructor
131
+ * @param {string} operator String representation of the operator.
132
+ * @param {symbol} operatorSymbol Operator symbol.
133
+ * @param {Operand[]} operands Operands.
134
+ */
135
+ constructor(operator, operatorSymbol, operands) {
136
+ this.operator = operator;
137
+ this.operatorSymbol = operatorSymbol;
138
+ this.operands = operands;
139
+ _defineProperty(this, "type", EvaluableType.Expression);
140
+ }
141
+
142
+ /**
143
+ * Performs the arithmetic operation on the operands evaluated values.
144
+ * @param {Result[]} results Operand result values.
145
+ * @returns {Result}
146
+ */
147
+
148
+ /**
149
+ * {@link Evaluable.evaluate}
150
+ */
151
+ evaluate(ctx) {
152
+ return this.operate(this.operands.map(operand => operand.evaluate(ctx)));
153
+ }
154
+
155
+ /**
156
+ * {@link Evaluable.toString}
157
+ */
158
+ toString() {
159
+ return `(${this.operands.map(operand => operand.toString()).join(` ${this.operator} `)})`;
160
+ }
161
+
162
+ /**
163
+ * {@link Evaluable.simplify}
164
+ */
165
+ simplify(...args) {
166
+ const results = this.operands.map(operand => operand.simplify(...args));
167
+ if (areAllResults(results)) {
168
+ return this.operate(results);
169
+ }
170
+ return this;
171
+ }
172
+
173
+ /**
174
+ * {@link Evaluable.serialize}
175
+ */
176
+ serialize(options) {
177
+ const {
178
+ operatorMapping
179
+ } = options;
180
+ const operator = operatorMapping.get(this.operatorSymbol);
181
+ if (operator === undefined) {
182
+ throw new Error(`missing operator ${this.operatorSymbol.toString()}`);
183
+ }
184
+ return [operator, ...this.operands.map(operand => operand.serialize(options))];
185
+ }
186
+ }
187
+
188
+ // Operator key
189
+ const OPERATOR$l = Symbol('DIVIDE');
190
+
191
+ /**
192
+ * Divide operation expression
193
+ */
194
+ class Divide extends Arithmetic {
195
+ /**
196
+ * @constructor Generic constructor
197
+ * @param {Evaluable[]} args
198
+ */
199
+
200
+ /**
201
+ * @constructor
202
+ * @param {Operand[]} operands Operands.
203
+ */
204
+ constructor(...operands) {
205
+ if (operands.length < 2) {
206
+ throw new Error('divide expression requires at least 2 operands');
207
+ }
208
+ super('/', OPERATOR$l, operands);
209
+ }
210
+ operate(results) {
211
+ if (!areAllNumbers(results)) {
212
+ throw new Error('operands must be numbers for divide');
213
+ }
214
+ const result = results.reduce((acc, result) => acc / result);
215
+ return result;
216
+ }
217
+ }
218
+
219
+ const getNumDecimals = num => {
220
+ const numberSplit = num.toString().split('.');
221
+ return numberSplit.length == 2 ? numberSplit[1].length : 0;
222
+ };
223
+ const operateWithExpectedDecimals = operation => (first, second) => {
224
+ const numDecimals1 = getNumDecimals(first);
225
+ const numDecimals2 = getNumDecimals(second);
226
+ const maxDecimals = operation === 'multiply' ? numDecimals1 + numDecimals2 : numDecimals1 > numDecimals2 ? numDecimals1 : numDecimals2;
227
+ return operation === 'sum' ? Number((first + second).toFixed(maxDecimals)) : operation === 'subtract' ? Number((first - second).toFixed(maxDecimals)) : Number((first * second).toFixed(maxDecimals));
228
+ };
229
+
230
+ // Operator key
231
+ const OPERATOR$k = Symbol('MULTIPLY');
232
+ const multiplyWithExpectedDecimals = operateWithExpectedDecimals('multiply');
233
+
234
+ /**
235
+ * Multiply operation expression
236
+ */
237
+ class Multiply extends Arithmetic {
238
+ /**
239
+ * @constructor Generic constructor
240
+ * @param {Evaluable[]} args
241
+ */
242
+
243
+ /**
244
+ * @constructor
245
+ * @param {Operand[]} operands Operands.
246
+ */
247
+ constructor(...operands) {
248
+ if (operands.length < 2) {
249
+ throw new Error('multiply expression requires at least 2 operands');
250
+ }
251
+ super('*', OPERATOR$k, operands);
252
+ }
253
+ operate(results) {
254
+ if (!areAllNumbers(results)) {
255
+ throw new Error('operands must be numbers for multiply');
256
+ }
257
+ return results.reduce((acc, result) => multiplyWithExpectedDecimals(acc, result));
258
+ }
259
+ }
260
+
261
+ // Operator key
262
+ const OPERATOR$j = Symbol('SUBTRACT');
263
+ const subtractWithExpectedDecimals = operateWithExpectedDecimals('subtract');
264
+
265
+ /**
266
+ * Subtract operation expression
267
+ */
268
+ class Subtract extends Arithmetic {
269
+ /**
270
+ * @constructor Generic constructor
271
+ * @param {Evaluable[]} args
272
+ */
273
+
274
+ /**
275
+ * @constructor
276
+ * @param {Operand[]} operands Operands.
277
+ */
278
+ constructor(...operands) {
279
+ if (operands.length < 2) {
280
+ throw new Error('subtract expression requires at least 2 operands');
281
+ }
282
+ super('-', OPERATOR$j, operands);
283
+ }
284
+ operate(results) {
285
+ if (!areAllNumbers(results)) {
286
+ throw new Error('operands must be numbers for subtract');
287
+ }
288
+ return results.reduce((acc, result) => subtractWithExpectedDecimals(acc, result));
289
+ }
290
+ }
291
+
292
+ // Operator key
293
+ const OPERATOR$i = Symbol('SUM');
294
+ const addWithExpectedDecimals = operateWithExpectedDecimals('sum');
295
+
296
+ /**
297
+ * Sum operation expression
298
+ */
299
+ class Sum extends Arithmetic {
300
+ /**
301
+ * @constructor Generic constructor
302
+ * @param {Evaluable[]} args
303
+ */
304
+
305
+ /**
306
+ * @constructor
307
+ * @param {Operand[]} operands Operands.
308
+ */
309
+ constructor(...operands) {
310
+ if (operands.length < 2) {
311
+ throw new Error('sum expression requires at least 2 operands');
312
+ }
313
+ super('+', OPERATOR$i, operands);
314
+ }
315
+ operate(results) {
316
+ if (!areAllNumbers(results)) {
317
+ throw new Error('operands must be numbers for sum');
318
+ }
319
+ return results.reduce((acc, result) => addWithExpectedDecimals(acc, result));
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Abstract operand
325
+ */
326
+ class Operand {
327
+ constructor() {
328
+ _defineProperty(this, "type", EvaluableType.Operand);
329
+ }
330
+ /**
331
+ * {@link Evaluable.evaluate}
332
+ */
333
+ /**
334
+ * {@link Evaluable.simplify}
335
+ */
336
+ /**
337
+ * {@link Evaluable.serialize}
338
+ */
339
+ /**
340
+ * Get the strict representation.
341
+ */
342
+ toString() {
343
+ throw new Error('not implemented exception');
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Print value as string
349
+ * @param {Result} value
350
+ * @return {string}
351
+ */
352
+ function printValue(value) {
353
+ if (isString(value)) {
354
+ return `"${value}"`;
355
+ }
356
+ return `${value}`;
357
+ }
358
+
359
+ /**
360
+ * Static value operand
361
+ */
362
+ class Value extends Operand {
363
+ /**
364
+ * @constructor
365
+ * @param {Result} value Constant value.
366
+ */
367
+ constructor(value) {
368
+ if (Array.isArray(value)) {
369
+ throw new Error('deprecated direct usage of array, please use Collection operand');
370
+ }
371
+ super();
372
+ _defineProperty(this, "value", void 0);
373
+ this.value = value;
374
+ }
375
+
376
+ /**
377
+ * {@link Evaluable.evaluate}
378
+ */
379
+ evaluate() {
380
+ return this.value;
381
+ }
382
+
383
+ /**
384
+ * {@link Evaluable.simplify}
385
+ */
386
+ simplify() {
387
+ return this.value;
388
+ }
389
+
390
+ /**
391
+ * {@link Evaluable.serialize}
392
+ */
393
+ serialize() {
394
+ return this.value;
395
+ }
396
+
397
+ /**
398
+ * Get the strict representation of the operand.
399
+ * @return {string}
400
+ */
401
+ toString() {
402
+ return printValue(this.value);
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Convert a value to number if possible, otherwise return undefined
408
+ * @param value value to be converted to number
409
+ */
410
+ const toNumber = value => {
411
+ const isValueNumber = isNumber(value);
412
+ if (isValueNumber) {
413
+ return value;
414
+ } else if (isString(value)) {
415
+ if (value.match(/^\d+\.\d+$/)) {
416
+ return parseFloat(value);
417
+ } else if (value.match(/^0$|^[1-9]\d*$/)) {
418
+ return parseInt(value);
419
+ }
420
+ }
421
+ return undefined;
422
+ };
423
+
424
+ /**
425
+ * Convert a value to string if possible, otherwise return undefined
426
+ * @param value value to be converted to string
427
+ */
428
+ const toString = value => {
429
+ if (isNumber(value)) {
430
+ return `${value}`;
431
+ } else if (isString(value)) {
432
+ return value;
433
+ }
434
+ return undefined;
435
+ };
436
+ /**
437
+ * Convert a value to number if it's type is string, otherwise return NaN
438
+ * @param value value to be converted to number
439
+ */
440
+ const toDateNumber = value => {
441
+ if (isString(value)) {
442
+ return Date.parse(value);
443
+ }
444
+ return NaN;
445
+ };
446
+
447
+ const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
448
+ const arrayIndexRegex = /\[(\d+)]/g;
449
+ const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
450
+ const parseKey = key => {
451
+ const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
452
+ return !keys ? [] : keys.flatMap(key => {
453
+ const unwrappedKey = parseBacktickWrappedKey(key);
454
+ const keys = [];
455
+ const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
456
+ if (parseResult) {
457
+ var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
458
+ 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);
459
+ keys.push(extractedKey);
460
+ const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
461
+ if (rawIndexes) {
462
+ for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
463
+ keys.push(parseInt(indexResult[1]));
464
+ }
465
+ }
466
+ } else {
467
+ keys.push(unwrappedKey);
468
+ }
469
+ return keys;
470
+ });
471
+ };
472
+ const complexKeyExpression = /{([^{}]+)}/;
473
+ function extractComplexKeys(ctx, key) {
474
+ // Resolve complex keys
475
+ let complexKeyMatches = complexKeyExpression.exec(key);
476
+ while (complexKeyMatches) {
477
+ const resolvedValue = complexValueLookup(ctx, complexKeyMatches[1]);
478
+ if (resolvedValue === undefined) {
479
+ return undefined;
480
+ }
481
+ key = key.replace(complexKeyExpression, `${resolvedValue}`);
482
+ complexKeyMatches = complexKeyExpression.exec(key);
483
+ }
484
+ return parseKey(key);
485
+ }
486
+ const isContext = value => isObject(value);
487
+ const simpleValueLookup = keys => ctx => {
488
+ let pointer = ctx;
489
+ for (const key of keys) {
490
+ if (typeof key === 'number') {
491
+ if (!Array.isArray(pointer)) {
492
+ return undefined;
493
+ }
494
+ pointer = pointer[key];
495
+ } else if (!isContext(pointer)) {
496
+ return undefined;
497
+ } else {
498
+ pointer = pointer[key];
499
+ }
500
+ }
501
+ return pointer;
502
+ };
503
+
504
+ /**
505
+ * Lookup for the reference in the context.
506
+ * The nested context value is annotated with "." delimiter.
507
+ * @example address.city
508
+ * @param {Context} ctx
509
+ * @param {string} key Context lookup key.
510
+ * @return {Result}
511
+ */
512
+ function complexValueLookup(ctx, key) {
513
+ const keys = extractComplexKeys(ctx, key);
514
+ if (!keys) {
515
+ return undefined;
516
+ }
517
+ return simpleValueLookup(keys !== null && keys !== void 0 ? keys : [])(ctx);
518
+ }
519
+ let DataType = /*#__PURE__*/function (DataType) {
520
+ DataType["Number"] = "Number";
521
+ DataType["String"] = "String";
522
+ return DataType;
523
+ }({});
524
+
525
+ // Equivalent to /^.+\.\((Number|String)\)$/
526
+ const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
527
+ const isComplexKey = key => key.indexOf('{') > -1;
528
+
529
+ /**
530
+ * Reference operand resolved within the context
531
+ */
532
+ class Reference extends Operand {
533
+ /**
534
+ * @constructor
535
+ * @param {string} key Context key.
536
+ */
537
+ constructor(key) {
538
+ if (key.trim() === '') {
539
+ throw new Error('invalid reference key');
540
+ }
541
+ super();
542
+ _defineProperty(this, "key", void 0);
543
+ _defineProperty(this, "dataType", void 0);
544
+ _defineProperty(this, "valueLookup", void 0);
545
+ _defineProperty(this, "getKeys", void 0);
546
+ this.key = key;
547
+ const dataTypeMatch = dataTypeRegex.exec(this.key);
548
+ if (dataTypeMatch) {
549
+ this.dataType = DataType[dataTypeMatch[1]];
550
+ }
551
+ if (this.key.match(/.\(.+\)$/)) {
552
+ this.key = this.key.replace(/.\(.+\)$/, '');
553
+ }
554
+ if (isComplexKey(this.key)) {
555
+ this.valueLookup = context => complexValueLookup(context, this.key);
556
+ this.getKeys = context => extractComplexKeys(context, this.key);
557
+ } else {
558
+ const keys = parseKey(this.key);
559
+ this.valueLookup = simpleValueLookup(keys);
560
+ this.getKeys = () => keys;
561
+ }
562
+ }
72
563
 
73
- /**
74
- * Check if a value is a an Evaluable
75
- * @param {Result | Evaluable} value value to check if is Evaluable
76
- * @returns {Evaluable}
77
- */
78
- function isEvaluable(value) {
79
- return typeof value === 'object' && value !== null && !Array.isArray(value) && typeof value.evaluate === 'function' && typeof value.simplify === 'function' && typeof value.serialize === 'function' && typeof value.toString === 'function';
80
- }
564
+ /**
565
+ * Evaluate in the given context.
566
+ * @param {Context} ctx
567
+ * @return {boolean}
568
+ */
569
+ evaluate(ctx) {
570
+ return this.toDataType(this.valueLookup(ctx));
571
+ }
81
572
 
82
- /**
83
- * Valid types for context members
84
- */
573
+ /**
574
+ * {@link Evaluable.simplify}
575
+ */
576
+ simplify(ctx, strictKeys, optionalKeys) {
577
+ var _this$getKeys;
578
+ const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
579
+ if (ctx[key] !== undefined) {
580
+ return this.evaluate(ctx);
581
+ }
582
+ if (!key || typeof key === 'number') {
583
+ return this;
584
+ }
585
+ return strictKeys && strictKeys.includes(key) || optionalKeys && !optionalKeys.includes(key) ? undefined : this;
586
+ }
85
587
 
86
- /**
87
- * Evaluation Context
88
- * Holds references used during the evaluation process.
89
- * Format: key: value.
90
- */
588
+ /**
589
+ * {@link Evaluable.serialize}
590
+ */
591
+ serialize({
592
+ referenceSerialization
593
+ }) {
594
+ const key = this.dataType ? `${this.key}.(${this.dataType})` : this.key;
595
+ return referenceSerialization(key);
596
+ }
91
597
 
92
- /**
93
- * Evaluation result
94
- */
598
+ /**
599
+ * Get the strict representation of the operand.
600
+ * @return {string}
601
+ */
602
+ toString() {
603
+ return `{${this.key}}`;
604
+ }
95
605
 
96
- let EvaluableType = /*#__PURE__*/function (EvaluableType) {
97
- EvaluableType["Operand"] = "Operand";
98
- EvaluableType["Expression"] = "Expression";
99
- return EvaluableType;
100
- }({});
606
+ /**
607
+ * Converts a value to a specified data type
608
+ * Silently returns original value if data type conversion has not been implemented.
609
+ * @param value value to cast as data type
610
+ */
611
+ toDataType(value) {
612
+ let result = value;
613
+ switch (this.dataType) {
614
+ case DataType.Number:
615
+ result = toNumber(value);
616
+ break;
617
+ case DataType.String:
618
+ result = toString(value);
619
+ break;
620
+ }
621
+ if (value && result === undefined) {
622
+ console.warn(`Casting ${value} to ${this.dataType} resulted in ${result}`);
623
+ }
624
+ return result;
625
+ }
626
+ }
101
627
 
102
- /**
103
- * Evaluable
104
- */
628
+ const isSimplifiedArithmeticExpression = (operand, result) => operand instanceof Arithmetic && !isEvaluable(result) && !isInfinite(result) && !(operand instanceof Reference);
105
629
 
106
630
  /**
107
631
  * Abstract comparison expression
@@ -152,6 +676,12 @@ class Comparison {
152
676
  if (!isEvaluable(left) && !isEvaluable(right)) {
153
677
  return this.comparison(left, right);
154
678
  }
679
+ if (isEvaluable(left) && isSimplifiedArithmeticExpression(this.right, right)) {
680
+ return Reflect.construct(this.constructor, [left, new Value(right)]);
681
+ }
682
+ if (isEvaluable(right) && isSimplifiedArithmeticExpression(this.left, left)) {
683
+ return Reflect.construct(this.constructor, [new Value(left), right]);
684
+ }
155
685
  return this;
156
686
  }
157
687
 
@@ -198,47 +728,6 @@ class Equal extends Comparison {
198
728
  }
199
729
  }
200
730
 
201
- /**
202
- * Convert a value to number if possible, otherwise return undefined
203
- * @param value value to be converted to number
204
- */
205
- const toNumber = value => {
206
- const isValueNumber = isNumber(value);
207
- if (isValueNumber) {
208
- return value;
209
- } else if (isString(value)) {
210
- if (value.match(/^\d+\.\d+$/)) {
211
- return parseFloat(value);
212
- } else if (value.match(/^0$|^[1-9]\d*$/)) {
213
- return parseInt(value);
214
- }
215
- }
216
- return undefined;
217
- };
218
-
219
- /**
220
- * Convert a value to string if possible, otherwise return undefined
221
- * @param value value to be converted to string
222
- */
223
- const toString = value => {
224
- if (isNumber(value)) {
225
- return `${value}`;
226
- } else if (isString(value)) {
227
- return value;
228
- }
229
- return undefined;
230
- };
231
- /**
232
- * Convert a value to number if it's type is string, otherwise return NaN
233
- * @param value value to be converted to number
234
- */
235
- const toDateNumber = value => {
236
- if (isString(value)) {
237
- return Date.parse(value);
238
- }
239
- return NaN;
240
- };
241
-
242
731
  // Operator key
243
732
  const OPERATOR$g = Symbol('GE');
244
733
 
@@ -553,141 +1042,61 @@ class Overlap extends Comparison {
553
1042
  }
554
1043
  const leftArray = left;
555
1044
  const rightArray = right;
556
- return leftArray.some(element => rightArray.includes(element));
557
- }
558
-
559
- /**
560
- * Get the strict representation of the expression.
561
- * @return {string}
562
- */
563
- toString() {
564
- const left = this.left.toString();
565
- const right = this.right.toString();
566
- return `(${left} ${this.operator} ${right})`;
567
- }
568
- }
569
-
570
- // Operator key
571
- const OPERATOR$8 = Symbol('PREFIX');
572
-
573
- /**
574
- * Prefix comparison expression
575
- */
576
- class Prefix extends Comparison {
577
- /**
578
- * @constructor
579
- * @param {Evaluable} left Left operand.
580
- * @param {Evaluable} right Right operand.
581
- */
582
-
583
- constructor(left, right) {
584
- if (arguments.length !== 2) {
585
- throw new Error('comparison expression expects left and right operands');
586
- }
587
- super('prefix', OPERATOR$8, left, right);
588
- }
589
-
590
- /**
591
- * {@link Comparison.comparison}
592
- */
593
- comparison(left, right) {
594
- if (isString(left) === false || isString(right) === false) {
595
- return false;
596
- }
597
- return right.startsWith(left);
598
- }
599
-
600
- /**
601
- * Get the strict representation of the expression.
602
- * @return {string}
603
- */
604
- toString() {
605
- const left = this.left.toString();
606
- const right = this.right.toString();
607
- return `(<${left}>${right})`;
608
- }
609
- }
610
-
611
- /**
612
- * Abstract operand
613
- */
614
- class Operand {
615
- constructor() {
616
- _defineProperty(this, "type", EvaluableType.Operand);
617
- }
618
- /**
619
- * {@link Evaluable.evaluate}
620
- */
621
- /**
622
- * {@link Evaluable.simplify}
623
- */
624
- /**
625
- * {@link Evaluable.serialize}
626
- */
627
- /**
628
- * Get the strict representation.
629
- */
630
- toString() {
631
- throw new Error('not implemented exception');
632
- }
633
- }
634
-
635
- /**
636
- * Print value as string
637
- * @param {Result} value
638
- * @return {string}
639
- */
640
- function printValue(value) {
641
- if (isString(value)) {
642
- return `"${value}"`;
643
- }
644
- return `${value}`;
645
- }
646
-
647
- /**
648
- * Static value operand
649
- */
650
- class Value extends Operand {
651
- /**
652
- * @constructor
653
- * @param {Result} value Constant value.
654
- */
655
- constructor(value) {
656
- if (Array.isArray(value)) {
657
- throw new Error('deprecated direct usage of array, please use Collection operand');
658
- }
659
- super();
660
- _defineProperty(this, "value", void 0);
661
- this.value = value;
1045
+ if (leftArray.length === 0 && rightArray.length === 0) {
1046
+ return true;
1047
+ }
1048
+ return leftArray.some(element => rightArray.includes(element));
662
1049
  }
663
1050
 
664
1051
  /**
665
- * {@link Evaluable.evaluate}
1052
+ * Get the strict representation of the expression.
1053
+ * @return {string}
666
1054
  */
667
- evaluate() {
668
- return this.value;
1055
+ toString() {
1056
+ const left = this.left.toString();
1057
+ const right = this.right.toString();
1058
+ return `(${left} ${this.operator} ${right})`;
669
1059
  }
1060
+ }
670
1061
 
1062
+ // Operator key
1063
+ const OPERATOR$8 = Symbol('PREFIX');
1064
+
1065
+ /**
1066
+ * Prefix comparison expression
1067
+ */
1068
+ class Prefix extends Comparison {
671
1069
  /**
672
- * {@link Evaluable.simplify}
1070
+ * @constructor
1071
+ * @param {Evaluable} left Left operand.
1072
+ * @param {Evaluable} right Right operand.
673
1073
  */
674
- simplify() {
675
- return this.value;
1074
+
1075
+ constructor(left, right) {
1076
+ if (arguments.length !== 2) {
1077
+ throw new Error('comparison expression expects left and right operands');
1078
+ }
1079
+ super('prefix', OPERATOR$8, left, right);
676
1080
  }
677
1081
 
678
1082
  /**
679
- * {@link Evaluable.serialize}
1083
+ * {@link Comparison.comparison}
680
1084
  */
681
- serialize() {
682
- return this.value;
1085
+ comparison(left, right) {
1086
+ if (isString(left) === false || isString(right) === false) {
1087
+ return false;
1088
+ }
1089
+ return right.startsWith(left);
683
1090
  }
684
1091
 
685
1092
  /**
686
- * Get the strict representation of the operand.
1093
+ * Get the strict representation of the expression.
687
1094
  * @return {string}
688
1095
  */
689
1096
  toString() {
690
- return printValue(this.value);
1097
+ const left = this.left.toString();
1098
+ const right = this.right.toString();
1099
+ return `(<${left}>${right})`;
691
1100
  }
692
1101
  }
693
1102
 
@@ -1234,187 +1643,6 @@ class Collection extends Operand {
1234
1643
  }
1235
1644
  }
1236
1645
 
1237
- const keyWithArrayIndexRegex = /^(?<currentKey>[^[\]]+?)(?<indexes>(?:\[\d+])+)?$/;
1238
- const arrayIndexRegex = /\[(\d+)]/g;
1239
- const parseBacktickWrappedKey = key => key.startsWith('`') && key.endsWith('`') ? key.slice(1, -1) : key;
1240
- const parseKey = key => {
1241
- const keys = key.match(/(`[^[\]]+`(\[\d+\])*|[^`.]+)/g);
1242
- return !keys ? [] : keys.flatMap(key => {
1243
- const unwrappedKey = parseBacktickWrappedKey(key);
1244
- const keys = [];
1245
- const parseResult = keyWithArrayIndexRegex.exec(unwrappedKey);
1246
- if (parseResult) {
1247
- var _parseResult$groups$c, _parseResult$groups, _parseResult$groups2;
1248
- 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);
1249
- keys.push(extractedKey);
1250
- const rawIndexes = parseResult === null || parseResult === void 0 || (_parseResult$groups2 = parseResult.groups) === null || _parseResult$groups2 === void 0 ? void 0 : _parseResult$groups2.indexes;
1251
- if (rawIndexes) {
1252
- for (const indexResult of rawIndexes.matchAll(arrayIndexRegex)) {
1253
- keys.push(parseInt(indexResult[1]));
1254
- }
1255
- }
1256
- } else {
1257
- keys.push(unwrappedKey);
1258
- }
1259
- return keys;
1260
- });
1261
- };
1262
- const complexKeyExpression = /{([^{}]+)}/;
1263
- function extractComplexKeys(ctx, key) {
1264
- // Resolve complex keys
1265
- let complexKeyMatches = complexKeyExpression.exec(key);
1266
- while (complexKeyMatches) {
1267
- const resolvedValue = complexValueLookup(ctx, complexKeyMatches[1]);
1268
- if (resolvedValue === undefined) {
1269
- return undefined;
1270
- }
1271
- key = key.replace(complexKeyExpression, `${resolvedValue}`);
1272
- complexKeyMatches = complexKeyExpression.exec(key);
1273
- }
1274
- return parseKey(key);
1275
- }
1276
- const isContext = value => isObject(value);
1277
- const simpleValueLookup = keys => ctx => {
1278
- let pointer = ctx;
1279
- for (const key of keys) {
1280
- if (typeof key === 'number') {
1281
- if (!Array.isArray(pointer)) {
1282
- return undefined;
1283
- }
1284
- pointer = pointer[key];
1285
- } else if (!isContext(pointer)) {
1286
- return undefined;
1287
- } else {
1288
- pointer = pointer[key];
1289
- }
1290
- }
1291
- return pointer;
1292
- };
1293
-
1294
- /**
1295
- * Lookup for the reference in the context.
1296
- * The nested context value is annotated with "." delimiter.
1297
- * @example address.city
1298
- * @param {Context} ctx
1299
- * @param {string} key Context lookup key.
1300
- * @return {Result}
1301
- */
1302
- function complexValueLookup(ctx, key) {
1303
- const keys = extractComplexKeys(ctx, key);
1304
- if (!keys) {
1305
- return undefined;
1306
- }
1307
- return simpleValueLookup(keys !== null && keys !== void 0 ? keys : [])(ctx);
1308
- }
1309
- let DataType = /*#__PURE__*/function (DataType) {
1310
- DataType["Number"] = "Number";
1311
- DataType["String"] = "String";
1312
- return DataType;
1313
- }({});
1314
-
1315
- // Equivalent to /^.+\.\((Number|String)\)$/
1316
- const dataTypeRegex = new RegExp(`^.+\\.\\((${Object.keys(DataType).join('|')})\\)$`);
1317
- const isComplexKey = key => key.indexOf('{') > -1;
1318
-
1319
- /**
1320
- * Reference operand resolved within the context
1321
- */
1322
- class Reference extends Operand {
1323
- /**
1324
- * @constructor
1325
- * @param {string} key Context key.
1326
- */
1327
- constructor(key) {
1328
- if (key.trim() === '') {
1329
- throw new Error('invalid reference key');
1330
- }
1331
- super();
1332
- _defineProperty(this, "key", void 0);
1333
- _defineProperty(this, "dataType", void 0);
1334
- _defineProperty(this, "valueLookup", void 0);
1335
- _defineProperty(this, "getKeys", void 0);
1336
- this.key = key;
1337
- const dataTypeMatch = dataTypeRegex.exec(this.key);
1338
- if (dataTypeMatch) {
1339
- this.dataType = DataType[dataTypeMatch[1]];
1340
- }
1341
- if (this.key.match(/.\(.+\)$/)) {
1342
- this.key = this.key.replace(/.\(.+\)$/, '');
1343
- }
1344
- if (isComplexKey(this.key)) {
1345
- this.valueLookup = context => complexValueLookup(context, this.key);
1346
- this.getKeys = context => extractComplexKeys(context, this.key);
1347
- } else {
1348
- const keys = parseKey(this.key);
1349
- this.valueLookup = simpleValueLookup(keys);
1350
- this.getKeys = () => keys;
1351
- }
1352
- }
1353
-
1354
- /**
1355
- * Evaluate in the given context.
1356
- * @param {Context} ctx
1357
- * @return {boolean}
1358
- */
1359
- evaluate(ctx) {
1360
- return this.toDataType(this.valueLookup(ctx));
1361
- }
1362
-
1363
- /**
1364
- * {@link Evaluable.simplify}
1365
- */
1366
- simplify(ctx, strictKeys, optionalKeys) {
1367
- var _this$getKeys;
1368
- const [key] = (_this$getKeys = this.getKeys(ctx)) !== null && _this$getKeys !== void 0 ? _this$getKeys : [];
1369
- if (ctx[key] !== undefined) {
1370
- return this.evaluate(ctx);
1371
- }
1372
- if (!key || typeof key === 'number') {
1373
- return this;
1374
- }
1375
- return strictKeys && strictKeys.includes(key) || optionalKeys && !optionalKeys.includes(key) ? undefined : this;
1376
- }
1377
-
1378
- /**
1379
- * {@link Evaluable.serialize}
1380
- */
1381
- serialize({
1382
- referenceSerialization
1383
- }) {
1384
- const key = this.dataType ? `${this.key}.(${this.dataType})` : this.key;
1385
- return referenceSerialization(key);
1386
- }
1387
-
1388
- /**
1389
- * Get the strict representation of the operand.
1390
- * @return {string}
1391
- */
1392
- toString() {
1393
- return `{${this.key}}`;
1394
- }
1395
-
1396
- /**
1397
- * Converts a value to a specified data type
1398
- * Silently returns original value if data type conversion has not been implemented.
1399
- * @param value value to cast as data type
1400
- */
1401
- toDataType(value) {
1402
- let result = value;
1403
- switch (this.dataType) {
1404
- case DataType.Number:
1405
- result = toNumber(value);
1406
- break;
1407
- case DataType.String:
1408
- result = toString(value);
1409
- break;
1410
- }
1411
- if (value && result === undefined) {
1412
- console.warn(`Casting ${value} to ${this.dataType} resulted in ${result}`);
1413
- }
1414
- return result;
1415
- }
1416
- }
1417
-
1418
1646
  // Option value whitelist
1419
1647
 
1420
1648
  // Parser options
@@ -1450,7 +1678,9 @@ const defaultOperatorMapping = new Map([
1450
1678
  // Comparison
1451
1679
  [OPERATOR$h, '=='], [OPERATOR$b, '!='], [OPERATOR$f, '>'], [OPERATOR$g, '>='], [OPERATOR$c, '<'], [OPERATOR$d, '<='], [OPERATOR$e, 'IN'], [OPERATOR$a, 'NOT IN'], [OPERATOR$8, 'PREFIX'], [OPERATOR$6, 'SUFFIX'], [OPERATOR$9, 'OVERLAP'], [OPERATOR$5, 'UNDEFINED'], [OPERATOR$7, 'PRESENT'],
1452
1680
  // Logical
1453
- [OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT']]);
1681
+ [OPERATOR$4, 'AND'], [OPERATOR$1, 'OR'], [OPERATOR$2, 'NOR'], [OPERATOR, 'XOR'], [OPERATOR$3, 'NOT'],
1682
+ // Arithmetic
1683
+ [OPERATOR$i, '+'], [OPERATOR$j, '-'], [OPERATOR$k, '*'], [OPERATOR$l, '/']]);
1454
1684
 
1455
1685
  /**
1456
1686
  * Default parser options
@@ -1464,6 +1694,14 @@ const defaultOptions = {
1464
1694
 
1465
1695
  // Input types
1466
1696
 
1697
+ const invalidExpression = 'invalid expression';
1698
+ const logicalIfValidOperands = (operands, logical) => {
1699
+ if (operands.every(operand => operand instanceof Logical || operand instanceof Comparison)) {
1700
+ return logical;
1701
+ }
1702
+ throw new Error(invalidExpression);
1703
+ };
1704
+
1467
1705
  /**
1468
1706
  * Parser of raw expressions into Evaluable expression
1469
1707
  */
@@ -1474,7 +1712,8 @@ class Parser {
1474
1712
  */
1475
1713
  constructor(options) {
1476
1714
  _defineProperty(this, "opts", void 0);
1477
- _defineProperty(this, "expectedOperators", void 0);
1715
+ _defineProperty(this, "expectedRootOperators", void 0);
1716
+ _defineProperty(this, "unexpectedRootSymbols", new Set([OPERATOR$i, OPERATOR$j, OPERATOR$k, OPERATOR$l]));
1478
1717
  this.opts = {
1479
1718
  ...defaultOptions
1480
1719
  };
@@ -1486,7 +1725,7 @@ class Parser {
1486
1725
  }
1487
1726
  }
1488
1727
  }
1489
- this.expectedOperators = new Set(this.opts.operatorMapping.values());
1728
+ this.expectedRootOperators = new Set(Array.from(this.opts.operatorMapping.entries()).filter(([symbol]) => !this.unexpectedRootSymbols.has(symbol)).map(([, operator]) => operator));
1490
1729
  }
1491
1730
 
1492
1731
  /**
@@ -1504,10 +1743,10 @@ class Parser {
1504
1743
  */
1505
1744
  parse(raw) {
1506
1745
  if (raw === undefined || raw === null || Array.isArray(raw) === false) {
1507
- throw new Error('invalid expression');
1746
+ throw new Error(invalidExpression);
1508
1747
  }
1509
- if (raw.length === 0 || !this.expectedOperators.has(`${raw[0]}`)) {
1510
- throw new Error('invalid expression');
1748
+ if (raw.length === 0 || !this.expectedRootOperators.has(`${raw[0]}`)) {
1749
+ throw new Error(invalidExpression);
1511
1750
  }
1512
1751
  return this.parseRawExp(raw);
1513
1752
  }
@@ -1545,71 +1784,103 @@ class Parser {
1545
1784
  return collapsible && operands.length === 1 ? operands[0] : undefined;
1546
1785
  };
1547
1786
  switch (operator) {
1548
- /**
1549
- * Logical
1550
- */
1787
+ // Logical
1551
1788
  case this.opts.operatorMapping.get(OPERATOR$4):
1552
- expression = operands => logicalExpressionReducer(operands, true) || new And(operands);
1789
+ expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new And(operands));
1553
1790
  operandParser = this.parseRawExp;
1554
1791
  break;
1555
1792
  case this.opts.operatorMapping.get(OPERATOR$1):
1556
- expression = operands => logicalExpressionReducer(operands, true) || new Or(operands);
1793
+ expression = operands => logicalExpressionReducer(operands, true) || logicalIfValidOperands(operands, new Or(operands));
1557
1794
  operandParser = this.parseRawExp;
1558
1795
  break;
1559
1796
  case this.opts.operatorMapping.get(OPERATOR$2):
1560
- expression = operands => logicalExpressionReducer(operands) || new Nor(operands);
1797
+ expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Nor(operands));
1561
1798
  operandParser = this.parseRawExp;
1562
1799
  break;
1563
1800
  case this.opts.operatorMapping.get(OPERATOR):
1564
- expression = operands => logicalExpressionReducer(operands) || new Xor(operands);
1801
+ expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Xor(operands));
1565
1802
  operandParser = this.parseRawExp;
1566
1803
  break;
1567
1804
  case this.opts.operatorMapping.get(OPERATOR$3):
1568
- expression = operands => logicalExpressionReducer(operands) || new Not(...operands);
1805
+ expression = operands => logicalExpressionReducer(operands) || logicalIfValidOperands(operands, new Not(...operands));
1569
1806
  operandParser = this.parseRawExp;
1570
1807
  break;
1571
- /**
1572
- * Comparison
1573
- */
1808
+
1809
+ // Comparison
1574
1810
  case this.opts.operatorMapping.get(OPERATOR$h):
1575
1811
  expression = operands => new Equal(...operands);
1812
+ operandParser = this.parseRawExp;
1576
1813
  break;
1577
1814
  case this.opts.operatorMapping.get(OPERATOR$b):
1578
1815
  expression = operands => new NotEqual(...operands);
1816
+ operandParser = this.parseRawExp;
1579
1817
  break;
1580
1818
  case this.opts.operatorMapping.get(OPERATOR$f):
1581
1819
  expression = operands => new GreaterThan(...operands);
1820
+ operandParser = this.parseRawExp;
1582
1821
  break;
1583
1822
  case this.opts.operatorMapping.get(OPERATOR$g):
1584
1823
  expression = operands => new GreaterThanOrEqual(...operands);
1824
+ operandParser = this.parseRawExp;
1585
1825
  break;
1586
1826
  case this.opts.operatorMapping.get(OPERATOR$c):
1587
1827
  expression = operands => new LessThan(...operands);
1828
+ operandParser = this.parseRawExp;
1588
1829
  break;
1589
1830
  case this.opts.operatorMapping.get(OPERATOR$d):
1590
1831
  expression = operands => new LessThanOrEqual(...operands);
1832
+ operandParser = this.parseRawExp;
1591
1833
  break;
1834
+
1835
+ // Containment
1592
1836
  case this.opts.operatorMapping.get(OPERATOR$e):
1593
1837
  expression = operands => new In(...operands);
1594
1838
  break;
1595
1839
  case this.opts.operatorMapping.get(OPERATOR$a):
1596
1840
  expression = operands => new NotIn(...operands);
1597
1841
  break;
1842
+
1843
+ // Prefix
1598
1844
  case this.opts.operatorMapping.get(OPERATOR$8):
1599
1845
  expression = operands => new Prefix(...operands);
1600
1846
  break;
1847
+
1848
+ // Suffix
1601
1849
  case this.opts.operatorMapping.get(OPERATOR$6):
1602
1850
  expression = operands => new Suffix(...operands);
1603
1851
  break;
1852
+
1853
+ // Overlap
1604
1854
  case this.opts.operatorMapping.get(OPERATOR$9):
1605
1855
  expression = operands => new Overlap(...operands);
1606
1856
  break;
1857
+
1858
+ // Presence
1607
1859
  case this.opts.operatorMapping.get(OPERATOR$5):
1608
1860
  expression = operands => new Undefined(...operands);
1609
1861
  break;
1610
1862
  case this.opts.operatorMapping.get(OPERATOR$7):
1611
1863
  expression = operands => new Present(...operands);
1612
1864
  break;
1865
+
1866
+ // Arithmetic
1867
+ case this.opts.operatorMapping.get(OPERATOR$i):
1868
+ expression = operands => new Sum(...operands);
1869
+ operandParser = this.parseRawExp;
1870
+ break;
1871
+ case this.opts.operatorMapping.get(OPERATOR$j):
1872
+ expression = operands => new Subtract(...operands);
1873
+ operandParser = this.parseRawExp;
1874
+ break;
1875
+ case this.opts.operatorMapping.get(OPERATOR$k):
1876
+ expression = operands => new Multiply(...operands);
1877
+ operandParser = this.parseRawExp;
1878
+ break;
1879
+ case this.opts.operatorMapping.get(OPERATOR$l):
1880
+ expression = operands => new Divide(...operands);
1881
+ operandParser = this.parseRawExp;
1882
+ break;
1883
+
1613
1884
  // Collection
1614
1885
  default:
1615
1886
  return this.getOperand(raw);
@@ -1630,6 +1901,8 @@ class Parser {
1630
1901
  }
1631
1902
  }
1632
1903
 
1904
+ const unexpectedResultError = 'non expression or boolean result should be returned';
1905
+
1633
1906
  /**
1634
1907
  * Condition engine
1635
1908
  */
@@ -1650,7 +1923,11 @@ class Engine {
1650
1923
  * @return {boolean}
1651
1924
  */
1652
1925
  evaluate(exp, ctx) {
1653
- return this.parse(exp).evaluate(ctx);
1926
+ const result = this.parse(exp).evaluate(ctx);
1927
+ if (isBoolean(result)) {
1928
+ return result;
1929
+ }
1930
+ throw new Error(unexpectedResultError);
1654
1931
  }
1655
1932
 
1656
1933
  /**
@@ -1694,17 +1971,19 @@ class Engine {
1694
1971
  if (isBoolean(result)) {
1695
1972
  return result;
1696
1973
  }
1697
- throw new Error('non expression or boolean result should be returned');
1974
+ throw new Error(unexpectedResultError);
1698
1975
  }
1699
1976
  }
1700
1977
 
1701
1978
  exports.OPERATOR_AND = OPERATOR$4;
1979
+ exports.OPERATOR_DIVIDE = OPERATOR$l;
1702
1980
  exports.OPERATOR_EQ = OPERATOR$h;
1703
1981
  exports.OPERATOR_GE = OPERATOR$g;
1704
1982
  exports.OPERATOR_GT = OPERATOR$f;
1705
1983
  exports.OPERATOR_IN = OPERATOR$e;
1706
1984
  exports.OPERATOR_LE = OPERATOR$d;
1707
1985
  exports.OPERATOR_LT = OPERATOR$c;
1986
+ exports.OPERATOR_MULTIPLY = OPERATOR$k;
1708
1987
  exports.OPERATOR_NE = OPERATOR$b;
1709
1988
  exports.OPERATOR_NOR = OPERATOR$2;
1710
1989
  exports.OPERATOR_NOT = OPERATOR$3;
@@ -1713,7 +1992,9 @@ exports.OPERATOR_OR = OPERATOR$1;
1713
1992
  exports.OPERATOR_OVERLAP = OPERATOR$9;
1714
1993
  exports.OPERATOR_PREFIX = OPERATOR$8;
1715
1994
  exports.OPERATOR_PRESENT = OPERATOR$7;
1995
+ exports.OPERATOR_SUBTRACT = OPERATOR$j;
1716
1996
  exports.OPERATOR_SUFFIX = OPERATOR$6;
1997
+ exports.OPERATOR_SUM = OPERATOR$i;
1717
1998
  exports.OPERATOR_UNDEFINED = OPERATOR$5;
1718
1999
  exports.OPERATOR_XOR = OPERATOR;
1719
2000
  exports.default = Engine;