@briza/illogical 2.2.3 → 2.2.4

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.cjs CHANGED
@@ -1304,7 +1304,7 @@ class Present extends Comparison {
1304
1304
  }
1305
1305
 
1306
1306
  // Operator key
1307
- const OPERATOR$6 = Symbol('PREFIX');
1307
+ const OPERATOR$6 = Symbol('SUFFIX');
1308
1308
 
1309
1309
  /**
1310
1310
  * Suffix comparison expression
@@ -2498,7 +2498,9 @@ function emitExpression(raw, state) {
2498
2498
  return;
2499
2499
  }
2500
2500
  if (operator === maps.orOp) {
2501
- const orAnd2 = detectOrAndIn2Pattern(arr, state);
2501
+ // Skip the merge when compiling for simplify so the nested structure can
2502
+ // be reconstructed verbatim (the merge collapses branches by ref1 value).
2503
+ const orAnd2 = state.simplify ? null : detectOrAndIn2Pattern(arr, state);
2502
2504
  if (orAnd2 !== null) {
2503
2505
  const {
2504
2506
  ref1Raw,
@@ -2775,7 +2777,7 @@ function emitExpression(raw, state) {
2775
2777
  * Compile a raw ExpressionInput into bytecode.
2776
2778
  * The result should be cached and reused across evaluate() calls.
2777
2779
  */
2778
- function compile(raw, opts) {
2780
+ function compile(raw, opts, simplify = false) {
2779
2781
  const maps = buildOperatorMaps(opts);
2780
2782
  const state = {
2781
2783
  bytecode: [],
@@ -2790,7 +2792,8 @@ function compile(raw, opts) {
2790
2792
  consts: [],
2791
2793
  constIndex: new Map(),
2792
2794
  overlapRefsEntries: [],
2793
- directionEntries: []
2795
+ directionEntries: [],
2796
+ simplify
2794
2797
  };
2795
2798
  emitExpression(raw, state);
2796
2799
 
@@ -3250,7 +3253,8 @@ function interpret(compiled, ctx) {
3250
3253
  }
3251
3254
  case OP_OR_AND_IN_CONST_2:
3252
3255
  {
3253
- // bytecode layout: ref1Idx, ref2Idx, M, (v0, setBIdx0, ref1Op0, ref2Op0), (v1, setBIdx1, ref1Op1, ref2Op1), ...
3256
+ // bytecode layout: ref1Idx, ref2Idx, M,
3257
+ // (aVal0, setBIdx0, ref1Op0, ref2Op0), (aVal1, setBIdx1, ref1Op1, ref2Op1), ...
3254
3258
  // ref1Op/ref2Op: 0 for 'eq', 1 for 'in' (unused at runtime, kept for simplifier)
3255
3259
  // constSets[setBIdx] is pre-built at first interpret() call — plain Set.has lookup.
3256
3260
  const ref1Idx = numAt$1(bytecode[++i]);
@@ -3369,8 +3373,14 @@ function interpret(compiled, ctx) {
3369
3373
  break;
3370
3374
  }
3371
3375
  const values = new Array(n);
3376
+ // Fixed destination slot for the single result value: the bottom-most
3377
+ // operand slot. The operands occupy [stackTop - n + 1 .. stackTop].
3378
+ // Writing here (instead of to stack[++stackTop]) keeps the result in a
3379
+ // stable slot even when the loop below breaks early on a missing
3380
+ // operand, so the next opcode never reads a stale operand value.
3381
+ const resultSlot = stackTop$1 - n + 1;
3372
3382
  let hasNull = false;
3373
- const isDateArithmetic = !isNaN(toDateNumber(stack$1[stackTop$1 - n + 1])) && (op === OP_SUM || op === OP_SUBTRACT);
3383
+ const isDateArithmetic = !isNaN(toDateNumber(stack$1[resultSlot])) && (op === OP_SUM || op === OP_SUBTRACT);
3374
3384
  for (let j = n - 1; j >= 0; j--) {
3375
3385
  const v = stack$1[stackTop$1--];
3376
3386
  if (v === null || v === undefined) {
@@ -3391,7 +3401,9 @@ function interpret(compiled, ctx) {
3391
3401
  values[j] = v;
3392
3402
  }
3393
3403
  }
3394
- stack$1[++stackTop$1] = hasNull ? false : isDateArithmetic && (op === OP_SUM || op === OP_SUBTRACT) && values.every(v => isString(v)) ? dateArithmeticReduce$1(values, op) : values.every(v => isNumber(v)) ? arithmeticReduce$1(values, op) : false;
3404
+ const result = hasNull ? false : isDateArithmetic && (op === OP_SUM || op === OP_SUBTRACT) && values.every(v => isString(v)) ? dateArithmeticReduce$1(values, op) : values.every(v => isNumber(v)) ? arithmeticReduce$1(values, op) : false;
3405
+ stack$1[resultSlot] = result;
3406
+ stackTop$1 = resultSlot;
3395
3407
  break;
3396
3408
  }
3397
3409
 
@@ -5245,6 +5257,11 @@ class Engine {
5245
5257
  _defineProperty(this, "parser", void 0);
5246
5258
  _defineProperty(this, "evaluator", void 0);
5247
5259
  _defineProperty(this, "bytecodeCache", new WeakMap());
5260
+ // Separate cache for the simplify compiler output. Simplify must preserve the
5261
+ // nested structure verbatim, so it compiles with the OR_AND_IN merge disabled
5262
+ // (see `compile`'s `simplify` flag), which yields different bytecode than the
5263
+ // evaluate path and therefore cannot share `bytecodeCache`.
5264
+ _defineProperty(this, "simplifyCache", new WeakMap());
5248
5265
  this.parser = new Parser(options);
5249
5266
  this.evaluator = options?.evaluator ?? 'oop';
5250
5267
  }
@@ -5258,6 +5275,20 @@ class Engine {
5258
5275
  return compiled;
5259
5276
  }
5260
5277
 
5278
+ /**
5279
+ * Compile for simplify: preserves the original nested structure so the
5280
+ * simplify interpreter can reproduce the input verbatim (no OR_AND_IN merge).
5281
+ */
5282
+ getSimplifiedCompiled(exp) {
5283
+ let compiled = this.simplifyCache.get(exp);
5284
+ if (compiled === undefined) {
5285
+ this.parser.parse(exp); // validates root operator and expression structure
5286
+ compiled = compile(exp, this.parser.options, true);
5287
+ this.simplifyCache.set(exp, compiled);
5288
+ }
5289
+ return compiled;
5290
+ }
5291
+
5261
5292
  /**
5262
5293
  * Evaluate the expression.
5263
5294
  * @param {ExpressionInput} exp Raw expression.
@@ -5312,7 +5343,7 @@ class Engine {
5312
5343
  */
5313
5344
  simplify(exp, context, strictKeys, optionalKeys) {
5314
5345
  if (this.evaluator === 'bytecode') {
5315
- return interpretSimplify(this.getCompiled(exp), context, strictKeys, optionalKeys);
5346
+ return interpretSimplify(this.getSimplifiedCompiled(exp), context, strictKeys, optionalKeys);
5316
5347
  }
5317
5348
  const result = this.parser.parse(exp).simplify(context, strictKeys, optionalKeys);
5318
5349
  if (isEvaluable(result)) {
@@ -1300,7 +1300,7 @@ class Present extends Comparison {
1300
1300
  }
1301
1301
 
1302
1302
  // Operator key
1303
- const OPERATOR$6 = Symbol('PREFIX');
1303
+ const OPERATOR$6 = Symbol('SUFFIX');
1304
1304
 
1305
1305
  /**
1306
1306
  * Suffix comparison expression
@@ -2494,7 +2494,9 @@ function emitExpression(raw, state) {
2494
2494
  return;
2495
2495
  }
2496
2496
  if (operator === maps.orOp) {
2497
- const orAnd2 = detectOrAndIn2Pattern(arr, state);
2497
+ // Skip the merge when compiling for simplify so the nested structure can
2498
+ // be reconstructed verbatim (the merge collapses branches by ref1 value).
2499
+ const orAnd2 = state.simplify ? null : detectOrAndIn2Pattern(arr, state);
2498
2500
  if (orAnd2 !== null) {
2499
2501
  const {
2500
2502
  ref1Raw,
@@ -2771,7 +2773,7 @@ function emitExpression(raw, state) {
2771
2773
  * Compile a raw ExpressionInput into bytecode.
2772
2774
  * The result should be cached and reused across evaluate() calls.
2773
2775
  */
2774
- function compile(raw, opts) {
2776
+ function compile(raw, opts, simplify = false) {
2775
2777
  const maps = buildOperatorMaps(opts);
2776
2778
  const state = {
2777
2779
  bytecode: [],
@@ -2786,7 +2788,8 @@ function compile(raw, opts) {
2786
2788
  consts: [],
2787
2789
  constIndex: new Map(),
2788
2790
  overlapRefsEntries: [],
2789
- directionEntries: []
2791
+ directionEntries: [],
2792
+ simplify
2790
2793
  };
2791
2794
  emitExpression(raw, state);
2792
2795
 
@@ -3246,7 +3249,8 @@ function interpret(compiled, ctx) {
3246
3249
  }
3247
3250
  case OP_OR_AND_IN_CONST_2:
3248
3251
  {
3249
- // bytecode layout: ref1Idx, ref2Idx, M, (v0, setBIdx0, ref1Op0, ref2Op0), (v1, setBIdx1, ref1Op1, ref2Op1), ...
3252
+ // bytecode layout: ref1Idx, ref2Idx, M,
3253
+ // (aVal0, setBIdx0, ref1Op0, ref2Op0), (aVal1, setBIdx1, ref1Op1, ref2Op1), ...
3250
3254
  // ref1Op/ref2Op: 0 for 'eq', 1 for 'in' (unused at runtime, kept for simplifier)
3251
3255
  // constSets[setBIdx] is pre-built at first interpret() call — plain Set.has lookup.
3252
3256
  const ref1Idx = numAt$1(bytecode[++i]);
@@ -3365,8 +3369,14 @@ function interpret(compiled, ctx) {
3365
3369
  break;
3366
3370
  }
3367
3371
  const values = new Array(n);
3372
+ // Fixed destination slot for the single result value: the bottom-most
3373
+ // operand slot. The operands occupy [stackTop - n + 1 .. stackTop].
3374
+ // Writing here (instead of to stack[++stackTop]) keeps the result in a
3375
+ // stable slot even when the loop below breaks early on a missing
3376
+ // operand, so the next opcode never reads a stale operand value.
3377
+ const resultSlot = stackTop$1 - n + 1;
3368
3378
  let hasNull = false;
3369
- const isDateArithmetic = !isNaN(toDateNumber(stack$1[stackTop$1 - n + 1])) && (op === OP_SUM || op === OP_SUBTRACT);
3379
+ const isDateArithmetic = !isNaN(toDateNumber(stack$1[resultSlot])) && (op === OP_SUM || op === OP_SUBTRACT);
3370
3380
  for (let j = n - 1; j >= 0; j--) {
3371
3381
  const v = stack$1[stackTop$1--];
3372
3382
  if (v === null || v === undefined) {
@@ -3387,7 +3397,9 @@ function interpret(compiled, ctx) {
3387
3397
  values[j] = v;
3388
3398
  }
3389
3399
  }
3390
- stack$1[++stackTop$1] = hasNull ? false : isDateArithmetic && (op === OP_SUM || op === OP_SUBTRACT) && values.every(v => isString(v)) ? dateArithmeticReduce$1(values, op) : values.every(v => isNumber(v)) ? arithmeticReduce$1(values, op) : false;
3400
+ const result = hasNull ? false : isDateArithmetic && (op === OP_SUM || op === OP_SUBTRACT) && values.every(v => isString(v)) ? dateArithmeticReduce$1(values, op) : values.every(v => isNumber(v)) ? arithmeticReduce$1(values, op) : false;
3401
+ stack$1[resultSlot] = result;
3402
+ stackTop$1 = resultSlot;
3391
3403
  break;
3392
3404
  }
3393
3405
 
@@ -5241,6 +5253,11 @@ class Engine {
5241
5253
  _defineProperty(this, "parser", void 0);
5242
5254
  _defineProperty(this, "evaluator", void 0);
5243
5255
  _defineProperty(this, "bytecodeCache", new WeakMap());
5256
+ // Separate cache for the simplify compiler output. Simplify must preserve the
5257
+ // nested structure verbatim, so it compiles with the OR_AND_IN merge disabled
5258
+ // (see `compile`'s `simplify` flag), which yields different bytecode than the
5259
+ // evaluate path and therefore cannot share `bytecodeCache`.
5260
+ _defineProperty(this, "simplifyCache", new WeakMap());
5244
5261
  this.parser = new Parser(options);
5245
5262
  this.evaluator = options?.evaluator ?? 'oop';
5246
5263
  }
@@ -5254,6 +5271,20 @@ class Engine {
5254
5271
  return compiled;
5255
5272
  }
5256
5273
 
5274
+ /**
5275
+ * Compile for simplify: preserves the original nested structure so the
5276
+ * simplify interpreter can reproduce the input verbatim (no OR_AND_IN merge).
5277
+ */
5278
+ getSimplifiedCompiled(exp) {
5279
+ let compiled = this.simplifyCache.get(exp);
5280
+ if (compiled === undefined) {
5281
+ this.parser.parse(exp); // validates root operator and expression structure
5282
+ compiled = compile(exp, this.parser.options, true);
5283
+ this.simplifyCache.set(exp, compiled);
5284
+ }
5285
+ return compiled;
5286
+ }
5287
+
5257
5288
  /**
5258
5289
  * Evaluate the expression.
5259
5290
  * @param {ExpressionInput} exp Raw expression.
@@ -5308,7 +5339,7 @@ class Engine {
5308
5339
  */
5309
5340
  simplify(exp, context, strictKeys, optionalKeys) {
5310
5341
  if (this.evaluator === 'bytecode') {
5311
- return interpretSimplify(this.getCompiled(exp), context, strictKeys, optionalKeys);
5342
+ return interpretSimplify(this.getSimplifiedCompiled(exp), context, strictKeys, optionalKeys);
5312
5343
  }
5313
5344
  const result = this.parser.parse(exp).simplify(context, strictKeys, optionalKeys);
5314
5345
  if (isEvaluable(result)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@briza/illogical",
3
- "version": "2.2.3",
3
+ "version": "2.2.4",
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
  "type": "module",
6
6
  "main": "./lib/illogical.cjs",
@@ -24,10 +24,9 @@
24
24
  "build:types": "tsc --project tsconfig.build.json --emitDeclarationOnly",
25
25
  "build:js": "rollup -c",
26
26
  "build": "rm -rf lib types && npm run build:types && npm run build:js",
27
- "docs": "typedoc src && git checkout docs/.nojekyll",
28
- "test": "node --import tsx --test \"src/**/*.test.ts\"",
27
+ "test": "bash -c 'node --import tsx --test \"${@:-src/__test__/unit/**/*.test.ts}\"' --",
29
28
  "test:sample-conditions": "node --import tsx --test --test-reporter=spec \"src/__test__/unit/sample-conditions.test.ts\"",
30
- "test:coverage": "node --import tsx --test --experimental-test-coverage --test-reporter=spec --test-reporter-destination=stdout --test-reporter=lcov --test-reporter-destination=coverage.lcov \"src/**/*.test.ts\"",
29
+ "test:coverage": "node --import tsx --test --experimental-test-coverage --test-reporter=spec --test-reporter-destination=stdout --test-reporter=lcov --test-reporter-destination=coverage.lcov \"src/__test__/unit/**/*.test.ts\"",
31
30
  "lint": "eslint --max-warnings 0 \"src/**/*.{ts,js}\"",
32
31
  "lint:fix": "eslint --max-warnings 0 \"src/**/*.{ts,js}\" --fix",
33
32
  "prepublishOnly": "npm run test && npm run build",
@@ -53,8 +52,11 @@
53
52
  "bench:synthetic:report:simplify": "node --import tsx src/benchmark/report.ts benchmark/results-synthetic-simplify-oop.json benchmark/results-synthetic-simplify-bytecode.json --op simplify --out benchmark/report-synthetic-simplify.md",
54
53
  "bench:sample:report:full:simplify": "node --import tsx src/benchmark/report.ts benchmark/results-sample-simplify-oop.json benchmark/results-sample-simplify-bytecode.json --op simplify --full --out benchmark/report-sample-simplify-full.md",
55
54
  "bench:synthetic:report:full:simplify": "node --import tsx src/benchmark/report.ts benchmark/results-synthetic-simplify-oop.json benchmark/results-synthetic-simplify-bytecode.json --op simplify --full --out benchmark/report-synthetic-simplify-full.md",
55
+ "bench:full:report:test-case": "sh -c 'FILTER=$(echo \"$@\" | sed -n \"s/.*--filter \\([^ ]*\\).*/\\1/p\"); if [ -z \"$FILTER\" ]; then FILTER=\"tmp\"; fi; node --import tsx src/benchmark/simplify.ts --cases conditions/sample-conditions \"$@\" --out benchmark/results-simplify-${FILTER}-oop.json && node --import tsx src/benchmark/simplify.ts --cases conditions/sample-conditions \"$@\" --options '\\''{\"evaluator\":\"bytecode\"}'\\'' --out benchmark/results-simplify-${FILTER}-bytecode.json && node --import tsx src/benchmark/report.ts benchmark/results-simplify-${FILTER}-oop.json benchmark/results-simplify-${FILTER}-bytecode.json --op simplify --out benchmark/report-simplify-${FILTER}.md && echo \"\\nReport written to benchmark/report-simplify-${FILTER}.md\" && node --import tsx src/benchmark/evaluate.ts --cases conditions/sample-conditions \"$@\" --out benchmark/results-evaluate-${FILTER}-oop.json && node --import tsx src/benchmark/evaluate.ts --cases conditions/sample-conditions \"$@\" --options '\\''{\"evaluator\":\"bytecode\"}'\\'' --out benchmark/results-evaluate-${FILTER}-bytecode.json && node --import tsx src/benchmark/report.ts benchmark/results-evaluate-${FILTER}-oop.json benchmark/results-evaluate-${FILTER}-bytecode.json --op evaluate --out benchmark/report-evaluate-${FILTER}.md && echo \"\\nReport written to benchmark/report-evaluate-${FILTER}.md\"' -- ",
56
56
  "get-bytecode": "node --import tsx src/bytecode/get-bytecode.ts",
57
- "debug-bytecode": "node --import tsx src/tools/debugger.ts"
57
+ "debug-bytecode": "node --import tsx src/tools/debugger.ts",
58
+ "test:fuzz": "node --import tsx src/tools/fuzz-runner.ts",
59
+ "test:fuzz:deep": "FUZZ_RUNS=100000 npm run test:fuzz"
58
60
  },
59
61
  "repository": {
60
62
  "type": "git",
@@ -92,12 +94,12 @@
92
94
  "eslint-plugin-prettier": "^5.5.5",
93
95
  "eslint-plugin-promise": "^7.2.1",
94
96
  "eslint-plugin-simple-import-sort": "^12.1.1",
97
+ "fast-check": "^4.8.0",
95
98
  "license-checker": "^25.0.1",
96
99
  "prettier": "^3.6.2",
97
100
  "rollup": "^4.52.5",
98
101
  "tinybench": "^6.0.0",
99
102
  "tsx": "^4.19.1",
100
- "typedoc": "^0.28.18",
101
103
  "typescript": "^6.0.2"
102
104
  }
103
105
  }
package/readme.md CHANGED
@@ -28,6 +28,8 @@
28
28
 
29
29
  Get up and running with illogical in just a few steps.
30
30
 
31
+ Read the [Background](#background) section to quickly understand what illogical is and why it exists.
32
+
31
33
  ### Installation
32
34
 
33
35
  ```sh
@@ -69,6 +71,18 @@ engine.evaluate(['AND', ['>', '$age', 20], ['==', '$name', 'peter']]) // true
69
71
 
70
72
  ## 📚 Documentation
71
73
 
74
+ ### Background
75
+
76
+ **illogical** is a JSON DSL (domain-specific language) for expressing and evaluating business rules in the insurance industry.
77
+
78
+ Domain-specific languages exist to streamline work in a given domain by providing the means of performing tasks in a way that is quicker to learn and implement. This lets you optimize your custom solution and tailor it for use by distinct user groups or within unique contexts.
79
+
80
+ Developers of software for insurance underwriters can enable illogical quickly and easily to make the underwriter's task of writing question sets for programmatic use more efficient by eliminating the need to learn and understand the programming language(s) in use.
81
+
82
+ The way it works is that underwriters use the illogical JSON DSL to express their underwriting models, specifically their question sets and business rules. Then, the JavaScript functions in illogical parse what has been written for use within an application.
83
+
84
+ Another way to think about this is that illogical is used to define machine-readable business rules for underwriting models and question sets that brokers and distribution partners can easily consume. This is much more efficient than the traditional use of spreadsheets, documents, PDFs, email exchanges, and other non-machine-readable formats that then must be translated before being used. illogical speeds up the integration process, gets your rules to market faster, and does so with significantly reduced submission errors and a lower total cost of ownership.
85
+
72
86
  ### Core Concepts
73
87
 
74
88
  Explore the supported expressions and their usage:
@@ -90,10 +104,7 @@ Learn how to use the engine and its methods:
90
104
 
91
105
  ### Customization
92
106
 
93
- Customize the engine and the documentation:
94
-
95
- - [Engine Options](./specs/engine.md)
96
- - [Code Documentation](https://briza-insurance.github.io/illogical/index.html)
107
+ Customize the [engine options](./specs/engine.md).
97
108
 
98
109
  ### Development Tools
99
110
 
@@ -10,6 +10,63 @@ import { ArrayInput, ExpressionInput, Input } from '../parser/index.js';
10
10
  import { Options } from '../parser/options.js';
11
11
  import { CompactRef } from './refs.js';
12
12
  export type Bytecode = (number | Result)[];
13
+ interface OperatorMaps {
14
+ binary: Record<string, number>;
15
+ arithmetic: Record<string, number>;
16
+ presentOp: string;
17
+ undefinedOp: string;
18
+ andOp: string;
19
+ orOp: string;
20
+ norOp: string;
21
+ notOp: string;
22
+ xorOp: string;
23
+ inOp: string;
24
+ notInOp: string;
25
+ overlapOp: string;
26
+ eqOp: string;
27
+ }
28
+ export interface CompilerState {
29
+ bytecode: Bytecode;
30
+ refs: CompactRef[];
31
+ refIndex: Map<string, number>;
32
+ refRawKeys: string[];
33
+ refKeys: string[];
34
+ opts: Options;
35
+ maps: OperatorMaps;
36
+ collectionCse: Map<string, number>;
37
+ numLocals: number;
38
+ consts: ArrayInput[];
39
+ constIndex: Map<string, number>;
40
+ overlapRefsEntries: Array<{
41
+ pos: number;
42
+ refIdxs: number[];
43
+ }>;
44
+ directionEntries: Array<{
45
+ pos: number;
46
+ dir: 0 | 1;
47
+ }>;
48
+ simplify: boolean;
49
+ }
50
+ /**
51
+ * Check whether an OR expression matches the pattern:
52
+ * OR( AND(IN-like(ref1, set1), IN-like(ref2, set2)), ... )
53
+ * where IN-like is either IN(ref, staticSet) or ==(ref, scalar),
54
+ * and every branch uses the exact same two refs in the same order.
55
+ *
56
+ * Builds an inverted index: for each unique value in any setA, union-merges all
57
+ * setB values across branches where that setA value appears, and emits one
58
+ * (literal value, mergedSetBIdx) entry per distinct setA value.
59
+ *
60
+ * This lets the interpreter do a single O(1) Map lookup on ref1 to find all
61
+ * relevant setB indices, instead of a linear scan through N setA Sets.
62
+ * Returns null if the pattern does not match.
63
+ */
64
+ export declare function detectOrAndIn2Pattern(arr: ArrayInput, state: CompilerState): {
65
+ ref1Raw: string;
66
+ ref2Raw: string;
67
+ entries: Array<[Result, number]>;
68
+ entryOperators: Array<['eq' | 'in', 'eq' | 'in']>;
69
+ } | null;
13
70
  export interface CompiledExpression {
14
71
  bytecode: Bytecode;
15
72
  refs: CompactRef[];
@@ -26,4 +83,5 @@ export interface CompiledExpression {
26
83
  * Compile a raw ExpressionInput into bytecode.
27
84
  * The result should be cached and reused across evaluate() calls.
28
85
  */
29
- export declare function compile(raw: ExpressionInput, opts: Options): CompiledExpression;
86
+ export declare function compile(raw: ExpressionInput, opts: Options, simplify?: boolean): CompiledExpression;
87
+ export {};
@@ -3,7 +3,7 @@ import { Options } from '../parser/options.js';
3
3
  /**
4
4
  * Valid types for context members
5
5
  */
6
- type ContextValue = Record<string, unknown> | string | number | boolean | null | undefined | ContextValue[];
6
+ export type ContextValue = Record<string, unknown> | string | number | boolean | null | undefined | ContextValue[];
7
7
  /**
8
8
  * Evaluation Context
9
9
  * Holds references used during the evaluation process.
@@ -56,4 +56,3 @@ export interface Evaluable {
56
56
  toString(): string;
57
57
  }
58
58
  export type SimplifyArgs = Parameters<Evaluable['simplify']>;
59
- export {};
package/types/index.d.ts CHANGED
@@ -39,12 +39,18 @@ declare class Engine {
39
39
  private readonly parser;
40
40
  private readonly evaluator;
41
41
  private readonly bytecodeCache;
42
+ private readonly simplifyCache;
42
43
  /**
43
44
  * @constructor
44
45
  * @param {Options?} options Parser options.
45
46
  */
46
47
  constructor(options?: Partial<Options>);
47
48
  private getCompiled;
49
+ /**
50
+ * Compile for simplify: preserves the original nested structure so the
51
+ * simplify interpreter can reproduce the input verbatim (no OR_AND_IN merge).
52
+ */
53
+ private getSimplifiedCompiled;
48
54
  /**
49
55
  * Evaluate the expression.
50
56
  * @param {ExpressionInput} exp Raw expression.