@stackone/expressions 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -3
- package/dist/index.es.mjs +1 -1
- package/dist/index.js +1 -1
- package/dist/types/evaluate.d.ts +2 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/utils.d.ts +4 -0
- package/dist/types/validate.d.ts +1 -2
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# @stackone/expressions
|
|
2
|
+
|
|
2
3
|
## Description
|
|
3
4
|
|
|
4
5
|
This package can be used to parse and evaluate string expressions with support for variables replacement, functions and operators.
|
|
5
6
|
|
|
6
|
-
This package uses the tech stack provided by the **Connect Monorepo** global setup. Please check the [root README](../../README.md) for more information.
|
|
7
|
-
|
|
8
7
|
## Requirements
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
Node.js 20+ is required to run this project.
|
|
11
10
|
|
|
12
11
|
## Installation
|
|
13
12
|
|
|
@@ -47,3 +46,169 @@ $ npm run lint
|
|
|
47
46
|
# run linter and try to fix any error
|
|
48
47
|
$ npm run lint:fix
|
|
49
48
|
```
|
|
49
|
+
|
|
50
|
+
## API Reference
|
|
51
|
+
|
|
52
|
+
### evaluate(expression: string, context?: object)
|
|
53
|
+
|
|
54
|
+
Evaluates the given expression using the provided context.
|
|
55
|
+
|
|
56
|
+
- Returns the evaluated result
|
|
57
|
+
- Throws an error if the expression is invalid or evaluation fails
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
evaluate("$.user.name", { user: { name: "John" } }); // Returns "John"
|
|
61
|
+
evaluate("x + y", { x: 1, y: 2 }); // Returns 3
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### isValidExpression(expression: string)
|
|
65
|
+
|
|
66
|
+
Checks if the given expression is valid.
|
|
67
|
+
|
|
68
|
+
- Returns `true` if the expression is valid
|
|
69
|
+
- Returns `false` otherwise
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
isValidExpression("$.user.name"); // Returns true
|
|
73
|
+
isValidExpression("invalid $$$ expression"); // Returns false
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### safeEvaluate(expression: string, context?: object)
|
|
77
|
+
|
|
78
|
+
Safely evaluates the expression without throwing errors.
|
|
79
|
+
|
|
80
|
+
- Returns the evaluated result if successful
|
|
81
|
+
- Returns `null` if evaluation fails or the expression is invalid
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
safeEvaluate("$.user.name", { user: { name: "John" } }); // Returns "John"
|
|
85
|
+
safeEvaluate("$ invalid expression", {}); // Returns null
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Expression language syntax
|
|
89
|
+
|
|
90
|
+
There are three types of expressions supported:
|
|
91
|
+
|
|
92
|
+
### JSON Path Expressions
|
|
93
|
+
|
|
94
|
+
When the expression starts with `$`, it is treated as a JSON Path expression and will be evaluated as such.
|
|
95
|
+
|
|
96
|
+
#### JSON Path Syntax
|
|
97
|
+
|
|
98
|
+
| JSON Path | Description |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `$` | The root object |
|
|
101
|
+
| `.` | Child operator |
|
|
102
|
+
| `@` | The current object |
|
|
103
|
+
| `*` | Wildcard. All elements in an array, or all properties of an object |
|
|
104
|
+
| `..` | Recursive descent |
|
|
105
|
+
| `[]` | Subscript operator |
|
|
106
|
+
| `[,]` | Union operator |
|
|
107
|
+
| `[start:end:step]` | Array slice operator |
|
|
108
|
+
| `?(expression)` | Filter expression |
|
|
109
|
+
| `()` | Script expression |
|
|
110
|
+
|
|
111
|
+
Examples:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
// Given the context: { user: { name: "John", age: 30 }, "info/email": "info@email.com" }
|
|
115
|
+
'$.user.name' // Returns "John"
|
|
116
|
+
'$.user.age' // Returns 30
|
|
117
|
+
'$.user[*]' // Returns ["John", 30]
|
|
118
|
+
'$["info/email"]' // Returns "info@email.com"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
For more information on JSON Path syntax, refer to the original [JSON Path documentation](https://goessner.net/articles/JsonPath/).
|
|
122
|
+
|
|
123
|
+
### JEXL Expressions
|
|
124
|
+
|
|
125
|
+
This kind of expression is enclosed in double brackets `{{expression}}`. It supports variables and operators.
|
|
126
|
+
|
|
127
|
+
#### Operators
|
|
128
|
+
|
|
129
|
+
| Operator | Description |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| `!` | Logical NOT |
|
|
132
|
+
| `+` | Addition, string concatenation |
|
|
133
|
+
| `-` | Subtraction |
|
|
134
|
+
| `*` | Multiplication |
|
|
135
|
+
| `/` | Division |
|
|
136
|
+
| `//` | Floor division |
|
|
137
|
+
| `%` | Modulus |
|
|
138
|
+
| `^` | Exponentiation |
|
|
139
|
+
| `&&` | Logical AND |
|
|
140
|
+
| `\|\|` | Logical OR |
|
|
141
|
+
| `==` | Equal |
|
|
142
|
+
| `!=` | Not equal |
|
|
143
|
+
| `>` | Greater than |
|
|
144
|
+
| `>=` | Greater than or equal |
|
|
145
|
+
| `<` | Less than |
|
|
146
|
+
| `<=` | Less than or equal |
|
|
147
|
+
| `in` | Element of string or array |
|
|
148
|
+
| `? :` | Ternary operator |
|
|
149
|
+
|
|
150
|
+
Examples:
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
// Given the context: { x: 10, y: 5 }
|
|
154
|
+
'{{x + y}}' // Returns 15
|
|
155
|
+
'{{x * 2}}' // Returns 20
|
|
156
|
+
'{{x > y}}' // Returns true
|
|
157
|
+
'{{x == 10 ? "yes" : "no"}}' // Returns "yes"
|
|
158
|
+
'{{x in [1, 2, 3]}}' // Returns false
|
|
159
|
+
'{{x != y}}' // Returns true
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Identifiers
|
|
163
|
+
|
|
164
|
+
Identifiers can be used to reference variables in the context.
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
// Given the context:
|
|
168
|
+
// {
|
|
169
|
+
// name: {
|
|
170
|
+
// first: "John",
|
|
171
|
+
// last: "Smith"
|
|
172
|
+
// },
|
|
173
|
+
// jobs: ["Developer", "Designer"]
|
|
174
|
+
// }
|
|
175
|
+
`{{name.first}}` // Returns "John"
|
|
176
|
+
`{{jobs[1]}}` // Returns "Designer"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### Collections
|
|
180
|
+
|
|
181
|
+
Collections, or arrays of objects, can be filtered by including a filter expression in brackets.
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
// Given the context:
|
|
185
|
+
// {
|
|
186
|
+
// users: [
|
|
187
|
+
// { name: "John", age: 30 },
|
|
188
|
+
// { name: "Jane", age: 25 }
|
|
189
|
+
// ]
|
|
190
|
+
// }
|
|
191
|
+
`{{users[.name == "John"].age}}` // Returns 30
|
|
192
|
+
`{{users[.age > 25].name}}` // Returns ["John"]
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### String Interpolation
|
|
196
|
+
|
|
197
|
+
To simplify strings usage, a more straightforward syntax is provided for string interpolation of variables using the `${var}` syntax.
|
|
198
|
+
|
|
199
|
+
Examples:
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
// Given the context: { name: "John", age: 30 }
|
|
203
|
+
"Hello ${name}" // Returns "Hello John"
|
|
204
|
+
"User is ${age}" // Returns "User is 30"
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Note: If the expression is a string without any of the patterns described above, it will be returned as is.
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
// Given the context: { name: "John", age: 30 }
|
|
211
|
+
"Hello world" // Returns "Hello world"
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
For more information on the JEXL syntax, refer to the [JEXL Syntax documentation](https://commons.apache.org/proper/commons-jexl/reference/syntax.html).
|
package/dist/index.es.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{notMissing as r,isObject as t}from"@stackone/utils";import*as e from"jexl";import n from"jsonpath";import o from"lodash.get";const a=/\${([^}]+)}/g,s=(s,i)=>{const c=s?.trim();if(null==c||""===c)throw new Error("Empty expression");const l=t(i)?i:{},p=(r=>{if(r.startsWith("{{")&&r.endsWith("}}"))return r.slice(2,-2)})(c),u=p??c,h=((r=()=>new e.Jexl)=>r())(),f=(r=>r.replace(/\$\./g,"").trim())(u),m=((t,e)=>{const n=t.match(a)?.map((r=>({toReplace:r,path:r.slice(2,-1)})));if(!n)return;let s=String(t);for(const t of n){const n=o(e,t.path);r(n)&&(s=s.replace(t.toReplace,`${String(n)}`))}return s})(f,l);if(m)return m;try{if(!p&&c.startsWith("$"))return((r,t)=>(n.parse(r),n.query(t,r)[0]))(c,l)}catch(r){throw new Error(`Invalid JSON path: "${c}"`)}if(!m&&!p)return s;const w=((r,t)=>{try{return r.compile(t)}catch(r){return null}})(h,f);if(!w)throw new Error(`Invalid expression: "${c}"`);try{return w.evalSync(l)}catch(r){throw new Error(`Invalid expression: "${c}"`)}},i=(r,t)=>{try{return s(r,t)}catch(r){return null}},c=r=>{try{return s(r),!0}catch(r){return!1}};export{s as evaluate,c as isValidExpression,i as safeEvaluate};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var r=require("@stackone/utils"),e=require("jexl"),t=require("jsonpath"),n=require("lodash.get");function i(r){var e=Object.create(null);return r&&Object.keys(r).forEach((function(t){if("default"!==t){var n=Object.getOwnPropertyDescriptor(r,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:function(){return r[t]}})}})),e.default=r,Object.freeze(e)}var c=i(e);const a=/\${([^}]+)}/g,s=(e,i)=>{const s=e?.trim();if(null==s||""===s)throw new Error("Empty expression");const o=r.isObject(i)?i:{},u=(r=>{if(r.startsWith("{{")&&r.endsWith("}}"))return r.slice(2,-2)})(s),l=u??s,p=((r=()=>new c.Jexl)=>r())(),f=(r=>r.replace(/\$\./g,"").trim())(l),h=((e,t)=>{const i=e.match(a)?.map((r=>({toReplace:r,path:r.slice(2,-1)})));if(!i)return;let c=String(e);for(const e of i){const i=n(t,e.path);r.notMissing(i)&&(c=c.replace(e.toReplace,`${String(i)}`))}return c})(f,o);if(h)return h;try{if(!u&&s.startsWith("$"))return((r,e)=>(t.parse(r),t.query(e,r)[0]))(s,o)}catch(r){throw new Error(`Invalid JSON path: "${s}"`)}if(!h&&!u)return e;const y=((r,e)=>{try{return r.compile(e)}catch(r){return null}})(p,f);if(!y)throw new Error(`Invalid expression: "${s}"`);try{return y.evalSync(o)}catch(r){throw new Error(`Invalid expression: "${s}"`)}};exports.evaluate=s,exports.isValidExpression=r=>{try{return s(r),!0}catch(r){return!1}},exports.safeEvaluate=(r,e)=>{try{return s(r,e)}catch(r){return null}};
|
package/dist/types/evaluate.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export declare const evaluateExpression: (expression
|
|
1
|
+
export declare const evaluateExpression: (expression?: string | null, context?: unknown) => unknown;
|
|
2
|
+
export declare const safeEvaluateExpression: (expression?: string | null, context?: unknown) => unknown;
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/utils.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import Expression from 'jexl/Expression';
|
|
2
|
+
import { ExpressionContext } from './types';
|
|
2
3
|
export declare const cleanExpression: (expression: string) => string;
|
|
3
4
|
export declare const compileExpression: (expressionHandler: {
|
|
4
5
|
compile: (expr: string) => Expression;
|
|
5
6
|
}, expression: string) => Expression | null;
|
|
7
|
+
export declare const extractExpressionBetweenDoubleCurlyBraces: (expression: string) => string | undefined;
|
|
8
|
+
export declare const evaluateJsonPath: (expression: string, context: unknown) => unknown;
|
|
9
|
+
export declare const evaluateStringInterpolations: (expression: string, context: ExpressionContext) => string | undefined;
|
package/dist/types/validate.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export declare const isValidExpression: (expression
|
|
2
|
-
export declare const isInterpolatedExpression: (expression: string) => boolean;
|
|
1
|
+
export declare const isValidExpression: (expression?: string | null) => boolean;
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackone/expressions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
-
"module": "dist/index.es.
|
|
6
|
+
"module": "dist/index.es.mjs",
|
|
7
7
|
"types": "dist/types/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
@@ -34,10 +34,12 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@stackone/utils": "*",
|
|
36
36
|
"jexl": "^2.3.0",
|
|
37
|
+
"jsonpath": "^1.1.1",
|
|
37
38
|
"lodash.get": "^4.4.2"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@types/jexl": "^2.3.4",
|
|
42
|
+
"@types/jsonpath": "^0.2.4",
|
|
41
43
|
"@types/lodash.get": "^4.4.9"
|
|
42
44
|
}
|
|
43
45
|
}
|