0.13.0 • Published 6 months ago

@stackone/expressions v0.13.0

Weekly downloads
-
License
ISC
Repository
-
Last release
6 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

6 months ago

0.12.0

6 months ago

0.11.0

6 months ago

0.10.2

6 months ago

0.9.0

6 months ago

0.8.0

7 months ago

0.7.0

9 months ago

0.6.1

10 months ago

0.6.0

10 months ago

0.5.0

10 months ago

0.4.1

10 months ago

0.4.0

10 months ago

0.3.1

11 months ago

0.3.0

11 months ago

0.2.0

11 months ago

0.1.0

11 months ago