
Research
/Security News
Laravel Lang Compromised with RCE Backdoor Across 700+ Versions
Laravel Lang packages were compromised with an RCE backdoor across hundreds of versions, exposing cloud, CI/CD, and developer secrets.
@stackone/expressions
Advanced tools
This package can be used to parse and evaluate string expressions with support for variables replacement, functions and operators.
This package can be used to parse and evaluate string expressions with support for variables replacement, functions and operators.
Node.js 20+ is required to run this project.
# install dependencies
$ npm run install
# 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
Evaluates the given expression using the provided context.
evaluate("$.user.name", { user: { name: "John" } }); // Returns "John"
evaluate("x + y", { x: 1, y: 2 }); // Returns 3
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"
Checks if the given expression is valid.
true if the expression is validfalse otherwiseisValidExpression("$.user.name"); // Returns true
isValidExpression("invalid $$$ expression"); // Returns false
Safely evaluates the expression without throwing errors.
null if evaluation fails or the expression is invalidsafeEvaluate("$.user.name", { user: { name: "John" } }); // Returns "John"
safeEvaluate("$ invalid expression", {}); // Returns null
There are three types of expressions supported:
When the expression starts with $, it is treated as a JSON Path expression and will be evaluated as such.
| 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": "info@email.com" }
"$.user.name"; // Returns "John"
"$.user.age"; // Returns 30
"$.user[*]"; // Returns ["John", 30]
"$.user[name,age]"; // Returns ["John", 30] (union operator)
'$["info/email"]'; // Returns "info@email.com"
For more information on JSON Path syntax, refer to the JSONPath Plus documentation and the original JSON Path specification.
This kind of expression is enclosed in double brackets {{expression}}. It supports variables and 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 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, 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"]
The expression handler provides several built-in functions you can use in your expressions:
Calculates the next anniversary date for a given date.
initialDate (string): The initial date stringformat (string): The format of the date string (uses date-fns format)// 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)
Calculates the number of complete years elapsed between two dates.
startDate (string): The start date stringformat (string): The format of the date string (uses date-fns format)endDate (string, optional): The end date string, defaults to current date if omitted// 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
Determines if a given date (optionally with added years) has passed.
date (string): The date to checkformat (string): The format of the date string (uses date-fns format)yearsToAdd (number, optional): Number of years to add to the date before comparing// 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
Returns the current date and time.
// Get the current date and time
"{{now()}}"; // Returns "2025-04-10T12:00:00.000Z" (example)
Checks if an array includes a specific value or all values from another array.
array (array): The array to checkvalue (any | array): The value(s) to search for in the array// 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
Checks if an array includes at least one value from another array or a single value.
array (array): The array to checkvalue (any | array): The value(s) to search for in the array// 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)}}";
Groups an array of objects by the value of a specified key. Items with missing or null values for the key are collected under "__missing__".
array (array): Array of objects to groupkey (string): The object key to group by// Group employees by department
"{{groupBy($.employees, 'dept')}}";
// { eng: [{dept: "eng", ...}, ...], hr: [{dept: "hr", ...}] }
// Group tickets by status
"{{groupBy($.tickets, 'status')}}";
// { open: [...], closed: [...] }
Applies an aggregate operation to an array. When field is specified, that field is extracted from each object before applying the operation.
array (array): The array to reduceoperation (string): One of "sum", "avg", "count", "min", "max", "concat", "flatten"field (string, optional): Object key to extract values from before reducing// Sum numbers
"{{reduce([1, 2, 3], 'sum')}}"; // Returns 6
// Sum a field from objects
"{{reduce($.orders, 'sum', 'total')}}"; // Returns sum of all order totals
// Average scores
"{{reduce($.reviews, 'avg', 'rating')}}"; // Returns average rating
// Count items
"{{reduce($.users, 'count')}}"; // Returns number of users
// Concatenate arrays from a field
"{{reduce($.users, 'concat', 'permissions')}}"; // Returns all permissions merged
Combines two parallel arrays into an object by pairing keys[i] with values[i]. If the arrays differ in length, the shorter length is used. Non-string keys are skipped.
keys (array): Array of string keysvalues (array): Array of values to pair with each key// Combine field names and values into an object
"{{zipObject(['name', 'email'], ['John', 'john@example.com'])}}"; // Returns { name: "John", email: "john@example.com" }
// Numeric values
"{{zipObject(['count', 'total'], [5, 100])}}"; // Returns { count: 5, total: 100 }
// From context variables
"{{zipObject($.fieldNames, $.fieldValues)}}";
Removes duplicate entries from an array, returning a new array with unique values. Primitives are compared by type and value (so 1 and "1" are distinct). Objects are compared by their contents with keys sorted, so {a:1,b:2} and {b:2,a:1} are treated as duplicates.
array (array): The array to deduplicate// Dedupe numbers
"{{dedupe([1, 2, 2, 3, 1])}}"; // Returns [1, 2, 3]
// Dedupe strings
"{{dedupe(['a', 'b', 'a', 'c'])}}"; // Returns ["a", "b", "c"]
// Dedupe objects by value (key order doesn't matter)
"{{dedupe([{id: 1}, {id: 2}, {id: 1}])}}"; // Returns [{id: 1}, {id: 2}]
"{{dedupe([{a: 1, b: 2}, {b: 2, a: 1}])}}"; // Returns [{a: 1, b: 2}]
// Dedupe from context variable
"{{dedupe($.tags)}}";
Joins array elements into a string with a specified separator. Null and undefined values in the array are filtered out.
array (array): The array to joinseparator (string, optional): The string to use between elements (default: ',')// Join with default comma separator
"{{join(['a', 'b', 'c'])}}"; // Returns "a,b,c"
// Join with custom separator
"{{join(['a', 'b', 'c'], ' - ')}}"; // Returns "a - b - c"
"{{join([1, 2, 3], '|')}}"; // Returns "1|2|3"
// Join with empty string (concatenate)
"{{join(['a', 'b', 'c'], '')}}"; // Returns "abc"
// Null/undefined values are filtered out
"{{join(['a', null, 'b', undefined, 'c'])}}"; // Returns "a,b,c"
// Join from context variable
"{{join($.tags, ', ')}}";
"{{join(data.items, ' | ')}}";
Checks if an object is present (not null or undefined).
object (object): The object to check// 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
Checks if an object is missing (null or undefined).
object (object): The object to check// 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
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// Get keys from an object
"{{keys({ a: 1, b: 2 })}}"; // Returns ["a", "b"]
// Get keys from a context variable
"{{keys($.user)}}";
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// Get values from an object
"{{values({ a: 1, b: 2 })}}"; // Returns [1, 2]
// Get values from a context variable
"{{values($.user)}}";
Returns the smallest value from the provided numbers.
values (number | number[]): Numbers to compare (individual values or an array)// Multiple arguments
"{{min(3, 1, 2)}}"; // Returns 1
// Array of numbers
"{{min([5, 2, 8])}}"; // Returns 2
// Mixed positive and negative
"{{min(-10, 0, 10)}}"; // Returns -10
// From context variables
"{{min(a, b, c)}}";
"{{min($.values)}}";
// Invalid input returns null
"{{min([])}}"; // Returns null
Converts a value to its string representation.
value (any): The value to convert// Convert numbers
"{{asString(42)}}" // Returns "42"
"{{asString(3.14)}}" // Returns "3.14"
// Convert booleans
"{{asString(true)}}" // Returns "true"
"{{asString(false)}}" // Returns "false"
// Strings pass through unchanged
"{{asString('hello')}}" // Returns "hello"
// Arrays and objects are JSON-stringified
"{{asString([1, 2, 3])}}" // Returns "[1,2,3]"
"{{asString({a: 1})}}" // Returns '{"a":1}'
// Null/undefined returns empty string
"{{asString(null)}}" // Returns ""
// From context variable
"{{asString($.userId)}}"
Capitalizes characters in a string.
value (string): The string to capitalizemode (string, optional): 'first' to capitalize first character only, 'each' to capitalize each word (default: 'first')// 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')}}";
Decodes a Base64 encoded string and returns the decoded result.
encodedValue (string): The Base64 encoded string to decode// 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 ""
Encodes a string to Base64 and returns the encoded result.
value (string): The string to encode// 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 ""
Decodes a URI-encoded string, converting percent-encoded characters back to their original form. Useful for handling pre-encoded values (like pagination cursors) that would otherwise be double-encoded when passed as query parameters.
value (string): The URI-encoded string to decode// Decode basic percent-encoded characters
"{{decodeUri('hello%20world')}}" // Returns "hello world"
"{{decodeUri('cursor%3Dabc%26page%3D2')}}" // Returns "cursor=abc&page=2"
// Decode Base64 padding characters (common in cursors)
"{{decodeUri('eyJpZCI6MTIzfQ%3D%3D')}}" // Returns "eyJpZCI6MTIzfQ=="
// Decode special characters
"{{decodeUri('%2B%2F%3D')}}" // Returns "+/="
// Already decoded strings are returned unchanged
"{{decodeUri('already decoded')}}" // Returns "already decoded"
// Handles invalid cases gracefully
"{{decodeUri(null)}}" // Returns ""
"{{decodeUri(123)}}" // Returns ""
// Malformed encoding returns original string
"{{decodeUri('invalid%ZZ')}}" // Returns "invalid%ZZ"
// Decode from context variable (Confluence pagination example)
"{{decodeUri($.response.cursor)}}"
Truncates a string to a specified maximum length, optionally appending a suffix.
value (string): The string to truncatemaxLength (number): Maximum length of the result (including suffix)suffix (string, optional): Suffix to append when truncating, defaults to "..."// 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)}}";
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 stringpadString (string, optional): The string to pad with, defaults to space " "// 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')}}";
Replaces occurrences of a search string with a replacement string.
value (string): The string to perform replacement onsearch (string): The substring to search forreplacement (string): The string to replace matches withreplaceAll (boolean, optional): If true, replaces all occurrences; otherwise replaces only the first (default: false)// Replace first occurrence (default)
"{{replace('hello world', 'world', 'there')}}"; // Returns "hello there"
"{{replace('hello hello', 'hello', 'hi')}}"; // Returns "hi hello"
// Replace all occurrences
"{{replace('hello hello', 'hello', 'hi', true)}}"; // Returns "hi hi"
"{{replace('foo-bar-baz', '-', '_', true)}}"; // Returns "foo_bar_baz"
// Remove characters by replacing with empty string
"{{replace('a-b-c', '-', '', true)}}"; // Returns "abc"
// Replace from context variable
"{{replace($.text, ' ', '-', true)}}";
Splits a string into an array of substrings using a specified separator.
value (string): The string to splitseparator (string, optional): The string used to separate the value (default: ',')// Split with default comma separator
"{{split('a,b,c')}}"; // Returns ["a", "b", "c"]
// Split with custom separator
"{{split('a-b-c', '-')}}"; // Returns ["a", "b", "c"]
"{{split('hello world', ' ')}}"; // Returns ["hello", "world"]
"{{split('a|b|c', '|')}}"; // Returns ["a", "b", "c"]
// Split with multi-character separator
"{{split('a AND b AND c', ' AND ')}}"; // Returns ["a", "b", "c"]
// Split into characters with empty separator
"{{split('hello', '')}}"; // Returns ["h", "e", "l", "l", "o"]
// Split from context variable
"{{split($.tags, ',')}}";
"{{split(data.csv, ',')}}";
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 inpattern (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)// 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)}}";
Extracts a query parameter value from a URL string. Useful for parsing pagination URLs where the next page token is embedded in a full URL.
url (string): The URL string to parseparamName (string): The name of the query parameter to extract// Extract page number from pagination URL
"{{urlParam('https://api.example.com/v1/items?page=2&limit=10', 'page')}}"; // Returns "2"
// Extract cursor from next page URL
"{{urlParam('https://api.example.com/v1/items?cursor=eyJpZCI6MTIzfQ==&limit=50', 'cursor')}}"; // Returns "eyJpZCI6MTIzfQ=="
// Extract offset parameter
"{{urlParam('https://api.example.com/v1/items?offset=100&limit=25', 'offset')}}"; // Returns "100"
// Returns null when parameter doesn't exist
"{{urlParam('https://api.example.com?page=1', 'missing')}}"; // Returns null
// From context variable (common pagination use case)
"{{urlParam($.pagination.next_page, 'page')}}";
// Use in conditional for pagination
"{{urlParam(nextUrl, 'page') != null ? urlParam(nextUrl, 'page') : null}}";
Computes the SHA-256 hash of a string value.
value (string): The string to hashencoding (string, optional): Output encoding — 'hex' (default) or 'base64'"{{sha256('hello')}}" // Returns "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
"{{sha256('hello', 'base64')}}" // Returns "LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
// Hash from context variable
"{{sha256($.body)}}"
Computes an HMAC-SHA256 of a value using a secret key. Useful for webhook signature verification and API request signing.
value (string): The string to signkey (string): The secret keyencoding (string, optional): Output encoding — 'hex' (default) or 'base64'"{{hmacSha256('hello', 'secret')}}" // Returns "88aab3ede8d3adf94d26ab90d3bafd4a2083070c3bcce9c014ee04a443847c0b"
"{{hmacSha256('hello', 'secret', 'base64')}}" // Returns base64-encoded HMAC
// Sign from context variables
"{{hmacSha256($.body, $.signingSecret)}}"
Computes the MD5 hash of a string value.
value (string): The string to hashencoding (string, optional): Output encoding — 'hex' (default) or 'base64'"{{md5('hello')}}" // Returns "5d41402abc4b2a76b9719d911017c592"
"{{md5('hello', 'base64')}}" // Returns "XUFAKrxLKna5cZ2REBfFkg=="
// Hash from context variable
"{{md5($.body, 'base64')}}"
For more information on the JEXL syntax, refer to the JEXL Syntax documentation.
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"
FAQs
This package can be used to parse and evaluate string expressions with support for variables replacement, functions and operators.
We found that @stackone/expressions demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Laravel Lang packages were compromised with an RCE backdoor across hundreds of versions, exposing cloud, CI/CD, and developer secrets.

Security News
Socket found a malicious postinstall hook across 700+ GitHub repos, including PHP packages on Packagist and Node.js project repositories.

Security News
Vibe coding at scale is reshaping how packages are created, contributed, and selected across the software supply chain