/** * A simple, safe, JavaScript expression engine, allowing end-users to enter arbitrary expressions without p0wning you. * * @example * // Input from user (e.g. search filter) * let expression = 'transactions <= 5 and abs(profit) > 20.5'; * * // Compile expression to executable function * let myfilter = compileExpression(expression); * * // Execute function * myfilter({transactions: 3, profit:-40.5}); // returns 1 * myfilter({transactions: 3, profit:-14.5}); // returns 0 * * @param expression * The expression to be parsed. Under the hood, the expression gets compiled to a clean and fast JavaScript function. * There are only 2 types: numbers and strings. Numbers may be floating point or integers. Boolean logic is applied * on the truthy value of values (e.g. any non-zero number is true, any non-empty string is true, otherwise false). * Examples of numbers: `43`, `-1.234`; example of a string: `"hello"`; example of external data variable: `foo`, `a.b.c`, * `'foo-bar'`. * You can use the following operations: * * `x + y` Add * * `x - y` Subtract * * `x * y` Multiply * * `x / y` Divide * * `x ^ y` Power * * `x mod y` Modulo * * `x == y` Equals * * `x < y` Less than * * `x <= y` Less than or equal to * * `x > y` Greater than * * `x >= y` Greater than or equal to * * `x == y <= z` Chained relation, equivalent to `(x == y and y <= z)` * * `x of y` Get property x of object y * * `x in (a, b, c)` Equivalent to `(x == a or x == b or x == c)` * * `x not in (a, b, c)` Equivalent to `(x != a and x != b and x != c)` * * `x or y` Boolean or * * `x and y` Boolean and * * `not x` Boolean not * * `if x then y else z` If boolean x is true, return value y, else return z * * `( x )` Explicity operator precedence * * `( x, y, z )` Array of elements x, y and z * * `abs(x)` Absolute value * * `ceil(x)` Round floating point up * * `floor(x)` Round floating point down * * `log(x)` Natural logarithm * * `log2(x)` Binary logarithm * * `log10(x)` Decadic logarithm * * `max(a, b, c...)` Max value (variable length of args) * * `min(a, b, c...)` Min value (variable length of args) * * `round(x)` Round floating point * * `sqrt(x)` Square root * * `exists(x)` True if `x` is neither `undefined` nor `null` * * `empty(x)` True if `x` doesn't exist, it is an empty string or empty array * * `myFooBarFunction(x)` Custom function defined in `options.extraFunctions` */ export function compileExpression( expression: string, options?: Options, ): (obj: any) => any; export interface Options { /** * When integrating in to your application, you can add your own custom functions. * These functions will be available in the expression in the same way as `sqrt(x)` and `round(x)`. */ extraFunctions?: { [T: string]: Function; }; /** * Pass constants like `pi` or `true` to the expression without having to modify data. * These constants will shadow identically named properties on the data object. In order * to access `data.pi` instead of `constants.pi`, for example, use a single-quoted * symbol in your expression, ie. `'pi'` instead of just `pi`. */ constants?: { [T: string]: any; }; /** * If you want to do some more magic with your expression, you can supply a custom function * that will resolve the identifiers used in the expression and assign them a value yourself. * * **Safety note**: The `get` function returns `undefined` for properties that are defined on * the object's prototype, not on the object itself. This is important, because otherwise the user * could access things like `toString.constructor` and maybe do some nasty things with it. Bear * this in mind if you decide not to use `get` and access the properties yourself. * * @param name - name of the property being accessed * @param get - safe getter that retrieves the property from obj * @param obj - the object passed to compiled expression * * @example * function containsWord(string, word) { * // your optimized code * } * * let myfilter = compileExpression( * 'Bob and Alice or Cecil', {}, * (word, _, string) => containsWord(string, word) * ); * * myfilter("Bob is boring"); // returns false * myfilter("Bob met Alice"); // returns true * myfilter("Cecil is cool"); // returns true */ customProp?: ( name: string, get: (name: string) => any, object: any, type: "unescaped" | "single-quoted", ) => any; /** * This option lets you override operators like `+` and `>=` with custom functions. */ operators?: Operators; } export interface Operators { "+"?: (a: any, b: any) => any; "-"?: (a: any, b?: any) => any; "*"?: (a: any, b: any) => any; "/"?: (a: any, b: any) => any; "%"?: (a: any, b: any) => any; "^"?: (a: any, b: any) => any; "==": (a: any, b: any) => boolean; "!=": (a: any, b: any) => boolean; "<"?: (a: any, b: any) => boolean; ">="?: (a: any, b: any) => boolean; "<="?: (a: any, b: any) => boolean; ">"?: (a: any, b: any) => boolean; "~="?: (a: any, b: any) => boolean; } /** * A custom prop function which doesn't throw an UnknownPropertyError * if the user tries to access a property of `undefined` and `null`, * but instead returns `unknown` or `null`. This effectively turns * `a of b` into `b.?a`. You can use this function using the following * code: * ``` * import { * compileExpression, * useOptionalChaining * } from 'filtrex' * * const expr = "foo of bar" * * const fn = compileExpression(expr, { * customProp: useOptionalChaining * }); * * fn({ bar: null }) // → null * ``` */ export function useOptionalChaining( name: string, get: (name: string) => any, object: any, type: "unescaped" | "single-quoted", ); /** * A custom prop function which treats dots inside a symbol * as property accessors. If you want to use the `foo.bar` * syntax to access properties instead of the default * `bar of foo`, you can use this function using the following * code: * ``` * import { * compileExpression, * useDotAccessOperator * } from 'filtrex' * * const expr = "foo.bar" * * const fn = compileExpression(expr, { * customProp: useDotAccessOperator * }); * * fn({ foo: { bar: 42 } }) // → 42 * ``` */ export function useDotAccessOperator( name: string, get: (name: string) => any, object: any, type: "unescaped" | "single-quoted", ); /** * A custom prop function which combines `useOptionalChaining` and `useDotAccessOperator`. * The user can use both `foo of bar` and `bar.foo`, both have optional chaining. * You can use this function using the following code: * ``` * import { * compileExpression, * useDotAccessOperatorAndOptionalChaining * } from 'filtrex' * * const expr = "foo.bar" * * const fn = compileExpression(expr, { * customProp: useDotAccessOperatorAndOptionalChaining * }); * * fn({ foo: null }) // → null * ``` */ export function useDotAccessOperatorAndOptionalChaining( name: string, get: (name: string) => any, object: any, type: "unescaped" | "single-quoted", );