@pawel-up/jexl
Version:
Javascript Expression Language: Powerful context-based expression parser and evaluator
344 lines (307 loc) • 11.2 kB
text/typescript
/**
* Jexl common operators as functions.
* These functions provide additional operators that can be used in Jexl expressions.
*/
/**
* Logical AND operator.
* @param a - First value.
* @param b - Second value.
* @returns True if both values are truthy, false otherwise.
*/
export const and = (a: unknown, b: unknown) => Boolean(a) && Boolean(b)
/**
* Logical OR operator.
* @param a - First value.
* @param b - Second value.
* @returns True if either value is truthy, false otherwise.
*/
export const or = (a: unknown, b: unknown) => Boolean(a) || Boolean(b)
/**
* Logical NOT operator.
* @param a - The value to negate.
* @returns True if the value is falsy, false otherwise.
*/
export const not = (a: unknown) => !a
/**
* Logical XOR operator.
* @param a - First value.
* @param b - Second value.
* @returns True if exactly one value is truthy, false otherwise.
*/
export const xor = (a: unknown, b: unknown) => Boolean(a) !== Boolean(b)
/**
* Equality operator (loose equality).
* @param a - First value.
* @param b - Second value.
* @returns True if values are equal, false otherwise.
*/
export const eq = (a: unknown, b: unknown) => a == b
/**
* Strict equality operator.
* @param a - First value.
* @param b - Second value.
* @returns True if values are strictly equal, false otherwise.
*/
export const strictEq = (a: unknown, b: unknown) => a === b
/**
* Inequality operator (loose inequality).
* @param a - First value.
* @param b - Second value.
* @returns True if values are not equal, false otherwise.
*/
export const ne = (a: unknown, b: unknown) => a != b
/**
* Strict inequality operator.
* @param a - First value.
* @param b - Second value.
* @returns True if values are not strictly equal, false otherwise.
*/
export const strictNe = (a: unknown, b: unknown) => a !== b
/**
* Greater than operator.
* @param a - First value.
* @param b - Second value.
* @returns True if a is greater than b, false otherwise.
*/
export const gt = (a: number | string, b: number | string) => a > b
/**
* Greater than or equal operator.
* @param a - First value.
* @param b - Second value.
* @returns True if a is greater than or equal to b, false otherwise.
*/
export const gte = (a: number | string, b: number | string) => a >= b
/**
* Less than operator.
* @param a - First value.
* @param b - Second value.
* @returns True if a is less than b, false otherwise.
*/
export const lt = (a: number | string, b: number | string) => a < b
/**
* Less than or equal operator.
* @param a - First value.
* @param b - Second value.
* @returns True if a is less than or equal to b, false otherwise.
*/
export const lte = (a: number | string, b: number | string) => a <= b
/**
* Between operator (inclusive).
* @param value - The value to check.
* @param min - The minimum value.
* @param max - The maximum value.
* @returns True if value is between min and max (inclusive), false otherwise.
*/
export const between = (value: number, min: number, max: number) => value >= min && value <= max
/**
* In operator - checks if a value exists in an array or string.
* @param value - The value to search for.
* @param collection - The array or string to search in.
* @returns True if the value exists in the collection, false otherwise.
*/
export const inOp = (value: unknown, collection: unknown[] | string) => {
if (typeof collection === 'string') {
return collection.includes(String(value))
}
return Array.isArray(collection) && collection.includes(value)
}
/**
* Not in operator - checks if a value does not exist in an array or string.
* @param value - The value to search for.
* @param collection - The array or string to search in.
* @returns True if the value does not exist in the collection, false otherwise.
*/
export const notIn = (value: unknown, collection: unknown[] | string) => {
return !inOp(value, collection)
}
/**
* Like operator - performs pattern matching (case-insensitive).
* @param str - The string to test.
* @param pattern - The pattern to match (supports % as wildcard).
* @returns True if the string matches the pattern, false otherwise.
*/
export const like = (str: string, pattern: string) => {
const regexPattern = pattern
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // Escape regex special chars
.replace(/%/g, '.*') // Replace % with .*
const regex = new RegExp(`^${regexPattern}$`, 'i')
return regex.test(str)
}
/**
* Not like operator - performs negative pattern matching.
* @param str - The string to test.
* @param pattern - The pattern to match (supports % as wildcard).
* @returns True if the string does not match the pattern, false otherwise.
*/
export const notLike = (str: string, pattern: string) => {
return !like(str, pattern)
}
/**
* Regex match operator.
* @param str - The string to test.
* @param pattern - The regex pattern.
* @param flags - Optional regex flags (default: 'g').
* @returns True if the string matches the regex, false otherwise.
*/
export const regex = (str: string, pattern: string, flags = 'g') => {
const regex = new RegExp(pattern, flags)
return regex.test(str)
}
/**
* Is null or undefined operator.
* @param value - The value to check.
* @returns True if the value is null or undefined, false otherwise.
*/
export const isNull = (value: unknown) => value === null || value === undefined
/**
* Is not null or undefined operator.
* @param value - The value to check.
* @returns True if the value is not null or undefined, false otherwise.
*/
export const isNotNull = (value: unknown) => value !== null && value !== undefined
/**
* Is empty operator - checks if value is empty (null, undefined, empty string, empty array, empty object).
* @param value - The value to check.
* @returns True if the value is empty, false otherwise.
*/
export const isEmpty = (value: unknown) => {
if (value === null || value === undefined) return true
if (typeof value === 'string') return value.length === 0
if (Array.isArray(value)) return value.length === 0
if (typeof value === 'object') return Object.keys(value).length === 0
return false
}
/**
* Is not empty operator.
* @param value - The value to check.
* @returns True if the value is not empty, false otherwise.
*/
export const isNotEmpty = (value: unknown) => !isEmpty(value)
/**
* Type check operator.
* @param value - The value to check.
* @param type - The expected type ('string', 'number', 'boolean', 'object', 'array', 'function').
* @returns True if the value is of the specified type, false otherwise.
*/
export const isType = (value: unknown, type: string) => {
switch (type.toLowerCase()) {
case 'string':
return typeof value === 'string'
case 'number':
return typeof value === 'number' && !isNaN(value)
case 'boolean':
return typeof value === 'boolean'
case 'object':
return typeof value === 'object' && value !== null && !Array.isArray(value)
case 'array':
return Array.isArray(value)
case 'function':
return typeof value === 'function'
case 'null':
return value === null || value === undefined
case 'undefined':
return value === undefined
default:
return false
}
}
/**
* Conditional operator (ternary).
* @param condition - The condition to evaluate.
* @param trueValue - Value to return if condition is truthy.
* @param falseValue - Value to return if condition is falsy.
* @returns The appropriate value based on the condition.
*/
export const ifElse = (condition: unknown, trueValue: unknown, falseValue: unknown) => {
return condition ? trueValue : falseValue
}
/**
* Null coalescing operator.
* @param value - The value to check.
* @param defaultValue - The default value to return if value is null/undefined.
* @returns The value if not null/undefined, otherwise the default value.
*/
export const coalesce = (value: unknown, defaultValue: unknown) => {
return value !== null && value !== undefined ? value : defaultValue
}
/**
* Safe navigation operator - safely accesses nested properties.
* @param obj - The object to navigate.
* @param path - The property path (e.g., 'user.profile.name').
* @returns The value at the path, or undefined if any part is null/undefined.
*/
export const safeGet = (obj: unknown, path: string) => {
if (!obj || typeof obj !== 'object') return undefined
const keys = path.split('.')
let current: Record<string, unknown> = obj as Record<string, unknown>
for (const key of keys) {
if (current === null || current === undefined) return undefined
current = current[key] as Record<string, unknown>
}
return current
}
/**
* Default operator - returns the first non-null, non-undefined value.
* @param args - Values to check.
* @returns The first non-null, non-undefined value, or undefined if all are null/undefined.
*/
export const defaultTo = (...args: unknown[]) => {
for (const arg of args) {
if (arg !== null && arg !== undefined) return arg
}
return undefined
}
/**
* Range check operator.
* @param value - The value to check.
* @param ranges - Array of [min, max] ranges.
* @returns True if the value falls within any of the ranges, false otherwise.
*/
export const inRange = (value: number, ...ranges: [number, number][]) => {
return ranges.some(([min, max]) => value >= min && value <= max)
}
/**
* Multiple equality operator - checks if value equals any of the provided values.
* @param value - The value to check.
* @param values - Values to compare against.
* @returns True if value equals any of the provided values, false otherwise.
*/
export const equalsAny = (value: unknown, ...values: unknown[]) => {
return values.includes(value)
}
/**
* Multiple inequality operator - checks if value does not equal any of the provided values.
* @param value - The value to check.
* @param values - Values to compare against.
* @returns True if value does not equal any of the provided values, false otherwise.
*/
export const notEqualsAny = (value: unknown, ...values: unknown[]) => {
return !values.includes(value)
}
/**
* All equality operator - checks if value equals all of the provided values.
* @param value - The value to check.
* @param values - Values to compare against.
* @returns True if value equals all of the provided values, false otherwise.
*/
export const equalsAll = (value: unknown, ...values: unknown[]) => {
return values.every((v) => v === value)
}
/**
* Has property operator - checks if an object has a specific property.
* @param obj - The object to check.
* @param property - The property name to check for.
* @returns True if the object has the property, false otherwise.
*/
export const hasProperty = (obj: unknown, property: string) => {
return typeof obj === 'object' && obj !== null && property in obj
}
/**
* Instance of operator - checks if an object is an instance of a constructor.
* @param obj - The object to check.
* @param constructor - The constructor function.
* @returns True if the object is an instance of the constructor, false otherwise.
*/
export const instanceOf = (obj: unknown, constructor: new (...args: unknown[]) => unknown) => {
return obj instanceof constructor
}