0.13.0 • Published 3 months ago

@stackone/expressions v0.13.0

Weekly downloads
-
License
ISC
Repository
-
Last release
3 months ago

@stackone/expressions

Description

This package can be used to parse and evaluate string expressions with support for variables replacement, functions and operators.

Requirements

Node.js 20+ is required to run this project.

Installation

# install dependencies
$ npm run install

Available commands

# clean build output
$ npm run clean
# build package
$ npm run build
# run tests
$ npm run test
# run tests on watch mode
$ npm run test:watch
# run linter
$ npm run lint
# run linter and try to fix any error
$ npm run lint:fix

API Reference

evaluate(expression: string, context?: object, options?: { incrementalJsonPath?: boolean })

Evaluates the given expression using the provided context.

  • Returns the evaluated result
  • Throws an error if the expression is invalid or evaluation fails
evaluate("$.user.name", { user: { name: "John" } }); // Returns "John"
evaluate("x + y", { x: 1, y: 2 }); // Returns 3
  • Setting incrementalJsonPath iterates through any JSON Path expression to check for failure, starting at the root.
  • This exchanges performance for better error messages, useful for AI validation and self-repair.
evaluate("$.user.name", { user: { name: "John" } }, { incrementalJsonPath: true }); // Returns "John"
evaluate("$.user.details.age", { user: { details: { name: "John" } } }, { incrementalJsonPath: true }); // Throws Error: "Key 'age' not found at '$.user.details'. Available keys: name"

isValidExpression(expression: string)

Checks if the given expression is valid.

  • Returns true if the expression is valid
  • Returns false otherwise
isValidExpression("$.user.name"); // Returns true
isValidExpression("invalid $$$ expression"); // Returns false

safeEvaluate(expression: string, context?: object)

Safely evaluates the expression without throwing errors.

  • Returns the evaluated result if successful
  • Returns null if evaluation fails or the expression is invalid
safeEvaluate("$.user.name", { user: { name: "John" } }); // Returns "John"
safeEvaluate("$ invalid expression", {}); // Returns null

Expression language syntax

There are three types of expressions supported:

JSON Path Expressions

When the expression starts with $, it is treated as a JSON Path expression and will be evaluated as such.

JSON Path Syntax

JSON PathDescription
$The root object
.Child operator
@The current object
*Wildcard. All elements in an array, or all properties of an object
..Recursive descent
[]Subscript operator
[,]Union operator
[start:end:step]Array slice operator
?(expression)Filter expression
()Script expression

Examples:

// Given the context: { user: { name: "John", age: 30 }, "info/email": "info@email.com" }
'$.user.name'         // Returns "John"
'$.user.age'          // Returns 30
'$.user[*]'           // Returns ["John", 30]
'$["info/email"]'     // Returns "info@email.com"

For more information on JSON Path syntax, refer to the original JSON Path documentation.

JEXL Expressions

This kind of expression is enclosed in double brackets {{expression}}. It supports variables and operators.

Operators

OperatorDescription
!Logical NOT
+Addition, string concatenation
-Subtraction
*Multiplication
/Division
//Floor division
%Modulus
^Exponentiation
&&Logical AND
\|\|Logical OR
==Equal
!=Not equal
>Greater than
>=Greater than or equal
<Less than
<=Less than or equal
inElement of string or array
? :Ternary operator
??Nullish coalescing operator

Examples:

// Given the context: { x: 10, y: 5 }
'{{x + y}}'                  // Returns 15
'{{x * 2}}'                  // Returns 20
'{{x > y}}'                  // Returns true
'{{x == 10 ? "yes" : "no"}}' // Returns "yes"
'{{x in [1, 2, 3]}}'         // Returns false
'{{x != y}}'                 // Returns true
'{{x ?? y}}'                 // Returns 10

Identifiers

Identifiers can be used to reference variables in the context.

// Given the context:
// { 
//     name: {
//         first: "John",
//         last: "Smith"
//     },
//     jobs: ["Developer", "Designer"]
// }
`{{name.first}}`          // Returns "John"
`{{jobs[1]}}`             // Returns "Designer"

Collections

Collections, or arrays of objects, can be filtered by including a filter expression in brackets.

// Given the context:
// {
//     users: [
//         { name: "John", age: 30 },
//         { name: "Jane", age: 25 }
//     ]
// }
`{{users[.name == "John"].age}}` // Returns 30
`{{users[.age > 25].name}}`      // Returns ["John"]

Built-in Functions

The expression handler provides several built-in functions you can use in your expressions:

Date Functions
nextAnniversary(initialDate, format)

Calculates the next anniversary date for a given date.

  • initialDate (string): The initial date string
  • format (string): The format of the date string (uses date-fns format)
  • Returns: Date object or null
// If today is April 10, 2025, and a birthday is December 25, 2000
"{{nextAnniversary('25/12/2000', 'dd/MM/yyyy')}}" 
// Returns Date object for December 25, 2025 (this year's anniversary)

// If today is April 10, 2025, and a birthday is February 15, 1990
"{{nextAnniversary('15/02/1990', 'dd/MM/yyyy')}}" 
// Returns Date object for February 15, 2026 (next year's anniversary)
yearsElapsed(startDate, format, endDate?)

Calculates the number of complete years elapsed between two dates.

  • startDate (string): The start date string
  • format (string): The format of the date string (uses date-fns format)
  • endDate (string, optional): The end date string, defaults to current date if omitted
  • Returns: number of years or null
// Calculate years between two specific dates
"{{yearsElapsed('01/01/2015', 'dd/MM/yyyy', '01/01/2025')}}" // Returns 10

// Calculate years from a date to today (assuming today is April 10, 2025)
"{{yearsElapsed('01/01/2015', 'dd/MM/yyyy')}}" // Returns 10
hasPassed(date, format, yearsToAdd?)

Determines if a given date (optionally with added years) has passed.

  • date (string): The date to check
  • format (string): The format of the date string (uses date-fns format)
  • yearsToAdd (number, optional): Number of years to add to the date before comparing
  • Returns: boolean
// Check if a date has passed (assuming today is April 10, 2025)
"{{hasPassed('01/01/2020', 'dd/MM/yyyy')}}" // Returns true

// Check if a date has passed (assuming today is April 10, 2025)
"{{hasPassed('01/01/2026', 'dd/MM/yyyy')}}" // Returns false

// Check if a date + 5 years has passed (2020 + 5 = 2025)
"{{hasPassed('01/01/2020', 'dd/MM/yyyy', 5)}}" 
// Returns true if April 10, 2025 is after January 1, 2025
now()

Returns the current date and time.

  • Returns: string (ISO 8601 format)
// Get the current date and time
"{{now()}}" // Returns "2025-04-10T12:00:00.000Z" (example)
Array Functions
includes(array, value)

Checks if an array includes a specific value or all values from another array.

  • array (array): The array to check
  • value (any | array): The value(s) to search for in the array
  • Returns: boolean
// Check if an array includes a specific value
"{{includes([1, 2, 3], 2)}}" // Returns true

// Check if an array includes a specific value
"{{includes([1, 2, 3], 5)}}" // Returns false

// Can be used with context variables
"{{includes($.allowedRoles, $.currentUser.role)}}"

// Check if an array includes all of values of the second array
"{{includes([1, 2, 3, 4], [2, 4])}}" // Returns true
includesSome(array, value)

Checks if an array includes at least one value from another array or a single value.

  • array (array): The array to check
  • value (any | array): The value(s) to search for in the array
  • Returns: boolean
// Check if an array includes at least one value
"{{includesSome([1, 2, 3], 2)}}" // Returns true

// Check if an array includes at least one value from another array
"{{includesSome([1, 2, 3], [2, 5])}}" // Returns true

// Check if an array includes at least one value from another array (none match)
"{{includesSome([1, 2, 3], [4, 5])}}" // Returns false

// Can be used with context variables
"{{includesSome($.allowedRoles, $.currentUser.roles)}}"
Object Functions
present(object)

Checks if an object is present (not null or undefined).

  • object (object): The object to check
  • Returns: boolean
// Check if an object is present
"{{present({})}}" // Returns true
"{{present(null)}}" // Returns false
"{{present(undefined)}}" // Returns false
"{{present('string')}}" // Returns true
"{{present(0)}}" // Returns true
"{{present([])}}" // Returns true
missing(object)

Checks if an object is missing (null or undefined).

  • object (object): The object to check
  • Returns: boolean
// Check if an object is not present
"{{missing({})}}" // Returns false
"{{missing(null)}}" // Returns true
"{{missing(undefined)}}" // Returns true
"{{missing('string')}}" // Returns false
"{{missing(0)}}" // Returns false
"{{missing([])}}" // Returns false
keys(object)

Returns the keys of an object as an array. If the input is not an object, null, or undefined, returns an empty array.

  • object (object): The object to get keys from
  • Returns: array of keys (string[])
// Get keys from an object
"{{keys({ a: 1, b: 2 })}}" // Returns ["a", "b"]

// Get keys from a context variable
"{{keys($.user)}}"
values(object)

Returns the values of an object as an array. If the input is not an object, null, or undefined, returns an empty array.

  • object (object): The object to get values from
  • Returns: array of values (any[])
// Get values from an object
"{{values({ a: 1, b: 2 })}}" // Returns [1, 2]

// Get values from a context variable
"{{values($.user)}}"

For more information on the JEXL syntax, refer to the JEXL Syntax documentation.

String Interpolation

To simplify strings usage, a more straightforward syntax is provided for string interpolation of variables using the ${var} syntax.

Examples:

// Given the context: { name: "John", age: 30 }
"Hello ${name}"       // Returns "Hello John"
"User is ${age}"    // Returns "User is 30"
// You can also use JEXL inside string syntax
"Status: ${age > 18 ? 'Adult' : 'Minor'}" // Returns "Status: Adult"
"Age in 5 years: ${age + 5}"             // Returns "Age in 5 years: 35"

Note: If the expression is a string without any of the patterns described above, it will be returned as is.

// Given the context: { name: "John", age: 30 }
"Hello world"       // Returns "Hello world"
0.13.0

3 months ago

0.12.0

3 months ago

0.11.0

3 months ago

0.10.2

3 months ago

0.9.0

3 months ago

0.8.0

3 months ago

0.7.0

5 months ago

0.6.1

6 months ago

0.6.0

6 months ago

0.5.0

7 months ago

0.4.1

7 months ago

0.4.0

7 months ago

0.3.1

8 months ago

0.3.0

8 months ago

0.2.0

8 months ago

0.1.0

8 months ago