npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@stackone/expressions

v0.21.0

Published

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

Downloads

5,913

Readme

@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 Path | Description | | ------------------ | ------------------------------------------------------------------ | | $ | 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 (e.g., $.a[b,c] for multiple properties) | | [start:end:step] | Array slice operator | | ?(expression) | Filter expression | | () | Script expression |

Examples:

// Given the context: { user: { name: "John", age: 30 }, "info/email": "[email protected]" }
"$.user.name"; // Returns "John"
"$.user.age"; // Returns 30
"$.user[*]"; // Returns ["John", 30]
"$.user[name,age]"; // Returns ["John", 30] (union operator)
'$["info/email"]'; // Returns "[email protected]"

For more information on JSON Path syntax, refer to the JSONPath Plus documentation and the original JSON Path specification.

JEXL Expressions

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

Operators

| Operator | Description | | -------- | ------------------------------ | | ! | 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 | | in | Element 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)}}";
String Functions
capitalize(value, mode?)

Capitalizes characters in a string.

  • value (string): The string to capitalize
  • mode (string, optional): 'first' to capitalize first character only, 'each' to capitalize each word (default: 'first')
  • Returns: capitalized string, or empty string for invalid input
// Capitalize first character (default)
"{{capitalize('hello')}}"; // Returns "Hello"
"{{capitalize('hello world')}}"; // Returns "Hello world"

// Capitalize each word
"{{capitalize('hello world', 'each')}}"; // Returns "Hello World"
"{{capitalize('the great gatsby', 'each')}}"; // Returns "The Great Gatsby"

// Capitalize from context variable
"{{capitalize($.name)}}";
"{{capitalize($.title, 'each')}}";
decodeBase64(encodedValue)

Decodes a Base64 encoded string and returns the decoded result.

  • encodedValue (string): The Base64 encoded string to decode
  • Returns: decoded string, or empty string if input is invalid
// Decode a base64 string directly
"{{decodeBase64("SGVsbG8gV29ybGQ")}}" // Returns "Hello World"

// Decode from context variable
"{{decodeBase64($.encodedValue)}}" // Decodes the encodedValue from context

// Handles invalid cases gracefully
"{{decodeBase64(null)}}" // Returns ""
"{{decodeBase64("!")}}" // Returns ""
"{{decodeBase64(123)}}" // Returns ""
encodeBase64(value)

Encodes a string to Base64 and returns the encoded result.

  • value (string): The string to encode
  • Returns: encoded string, or empty string if input is invalid
// Encode a string directly
"{{encodeBase64("Hello World")}}" // Returns "SGVsbG8gV29ybGQ"

// Encode from context variable
"{{encodeBase64($.value)}}" // Encodes the value from context

// Handles invalid cases gracefully
"{{encodeBase64(null)}}" // Returns ""
"{{encodeBase64(123)}}" // Returns ""
"{{encodeBase64("")}}" // Returns ""
truncate(value, maxLength, suffix?)

Truncates a string to a specified maximum length, optionally appending a suffix.

  • value (string): The string to truncate
  • maxLength (number): Maximum length of the result (including suffix)
  • suffix (string, optional): Suffix to append when truncating, defaults to "..."
  • Returns: truncated string, or original string if shorter than maxLength
// Truncate with default suffix
"{{truncate('Hello World', 8)}}"; // Returns "Hello..."

// Truncate with custom suffix
"{{truncate('Hello World', 8, '…')}}"; // Returns "Hello W…"

// Truncate with no suffix
"{{truncate('Hello World', 5, '')}}"; // Returns "Hello"

// No truncation needed
"{{truncate('Hi', 10)}}"; // Returns "Hi"

// Truncate from context variable
"{{truncate($.description, 100)}}";
padStart(value, targetLength, padString?)

Pads the start of a string with another string until it reaches the target length.

  • value (string | number): The value to pad (numbers are converted to strings)
  • targetLength (number): The target length of the resulting string
  • padString (string, optional): The string to pad with, defaults to space " "
  • Returns: padded string, or original if already at or beyond target length
// Pad with zeros (common for formatting numbers)
"{{padStart('5', 3, '0')}}"; // Returns "005"
"{{padStart(5, 3, '0')}}"; // Returns "005" (numbers work too)

// Pad with default space
"{{padStart('hello', 10)}}"; // Returns "     hello"

// Pad with custom character
"{{padStart('abc', 6, '*')}}"; // Returns "***abc"

// No padding needed if already long enough
"{{padStart('hello', 3)}}"; // Returns "hello"

// Pad from context variable
"{{padStart($.id, 8, '0')}}";
regexMatch(value, pattern, groupIndex?)

Extracts a value from a string using a regular expression pattern. Returns the specified capture group, or null if no match is found.

  • value (string): The string to search in
  • pattern (string): The regex pattern as a string (without delimiters)
  • groupIndex (number, optional): Capture group index to return (default: 1 for first capture group, 0 for full match)
  • Returns: matched string from the specified group, or null if no match or group doesn't exist
// Extract parameter from URL or header
"{{regexMatch('<https://api.com?after=abc123&limit=2>; rel=\"next\"', 'after=([^&>]+)', 1)}}"; // Returns "abc123"

// Extract full match (group 0)
"{{regexMatch('Hello World', 'World', 0)}}"; // Returns "World"

// Extract with capture group
"{{regexMatch('user_id=12345', 'user_id=(\\d+)', 1)}}"; // Returns "12345"

// No match returns null
"{{regexMatch('Hello World', 'foo', 1)}}"; // Returns null

// Extract from context variable
"{{regexMatch($.linkHeader, 'after=([^&>]+)', 1)}}";

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"