@jmespath-community/jmespath 1.1.5 → 1.2.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.
Files changed (61) hide show
  1. package/README.md +107 -72
  2. package/dist/jmespath.esm.js +87 -69
  3. package/dist/jmespath.esm.min.js +1 -1
  4. package/dist/jmespath.esm.min.js.map +1 -1
  5. package/dist/jmespath.umd.js +87 -69
  6. package/dist/jmespath.umd.min.js +1 -1
  7. package/dist/jmespath.umd.min.js.map +1 -1
  8. package/dist/lib/bin/jp.js +127 -0
  9. package/dist/lib/bin/jp.js.map +1 -0
  10. package/dist/lib/package.json +70 -0
  11. package/dist/lib/{AST.type.js.map → src/AST.type.js.map} +1 -1
  12. package/dist/lib/{JSON.type.js.map → src/JSON.type.js.map} +1 -1
  13. package/dist/lib/{Lexer.js → src/Lexer.js} +2 -1
  14. package/dist/lib/src/Lexer.js.map +1 -0
  15. package/dist/lib/{Lexer.type.js → src/Lexer.type.js} +1 -0
  16. package/dist/lib/src/Lexer.type.js.map +1 -0
  17. package/dist/lib/{Parser.js → src/Parser.js} +17 -8
  18. package/dist/lib/src/Parser.js.map +1 -0
  19. package/dist/lib/{Parser.type.js.map → src/Parser.type.js.map} +1 -1
  20. package/dist/lib/{Runtime.js → src/Runtime.js} +1 -1
  21. package/dist/lib/src/Runtime.js.map +1 -0
  22. package/dist/lib/src/Scope.js.map +1 -0
  23. package/dist/lib/{TreeInterpreter.js → src/TreeInterpreter.js} +12 -5
  24. package/dist/lib/src/TreeInterpreter.js.map +1 -0
  25. package/dist/lib/{index.js → src/index.js} +2 -4
  26. package/dist/lib/src/index.js.map +1 -0
  27. package/dist/lib/{utils → src/utils}/index.js +12 -11
  28. package/dist/lib/src/utils/index.js.map +1 -0
  29. package/dist/lib/src/utils/strings.js.map +1 -0
  30. package/dist/lib/{utils → src/utils}/text.js +1 -0
  31. package/dist/lib/src/utils/text.js.map +1 -0
  32. package/dist/types/bin/jp.d.ts +2 -0
  33. package/dist/types/{AST.type.d.ts → src/AST.type.d.ts} +7 -1
  34. package/dist/types/{Lexer.type.d.ts → src/Lexer.type.d.ts} +2 -1
  35. package/dist/types/{Runtime.d.ts → src/Runtime.d.ts} +1 -1
  36. package/dist/types/{index.d.ts → src/index.d.ts} +3 -3
  37. package/package.json +4 -1
  38. package/dist/lib/Lexer.js.map +0 -1
  39. package/dist/lib/Lexer.type.js.map +0 -1
  40. package/dist/lib/Parser.js.map +0 -1
  41. package/dist/lib/Runtime.js.map +0 -1
  42. package/dist/lib/Scope.js.map +0 -1
  43. package/dist/lib/TreeInterpreter.js.map +0 -1
  44. package/dist/lib/index.js.map +0 -1
  45. package/dist/lib/utils/index.js.map +0 -1
  46. package/dist/lib/utils/strings.js.map +0 -1
  47. package/dist/lib/utils/text.js.map +0 -1
  48. /package/dist/lib/{AST.type.js → src/AST.type.js} +0 -0
  49. /package/dist/lib/{JSON.type.js → src/JSON.type.js} +0 -0
  50. /package/dist/lib/{Parser.type.js → src/Parser.type.js} +0 -0
  51. /package/dist/lib/{Scope.js → src/Scope.js} +0 -0
  52. /package/dist/lib/{utils → src/utils}/strings.js +0 -0
  53. /package/dist/types/{JSON.type.d.ts → src/JSON.type.d.ts} +0 -0
  54. /package/dist/types/{Lexer.d.ts → src/Lexer.d.ts} +0 -0
  55. /package/dist/types/{Parser.d.ts → src/Parser.d.ts} +0 -0
  56. /package/dist/types/{Parser.type.d.ts → src/Parser.type.d.ts} +0 -0
  57. /package/dist/types/{Scope.d.ts → src/Scope.d.ts} +0 -0
  58. /package/dist/types/{TreeInterpreter.d.ts → src/TreeInterpreter.d.ts} +0 -0
  59. /package/dist/types/{utils → src/utils}/index.d.ts +0 -0
  60. /package/dist/types/{utils → src/utils}/strings.d.ts +0 -0
  61. /package/dist/types/{utils → src/utils}/text.d.ts +0 -0
package/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  # @jmespath-community/jmespath
4
4
 
5
-
6
5
  @jmespath-community/jmespath is a **TypeScript** implementation of the [JMESPath](https://jmespath.site/) spec.
7
6
 
8
7
  JMESPath is a query language for JSON. It will take a JSON document
@@ -20,12 +19,9 @@ npm install @jmespath-community/jmespath
20
19
  ### `search(data: JSONValue, expression: string): JSONValue`
21
20
 
22
21
  ```javascript
23
- import { search } from '@jmespath-community/jmespath';
22
+ import { search } from "@jmespath-community/jmespath";
24
23
 
25
- search(
26
- {foo: {bar: {baz: [0, 1, 2, 3, 4]}}},
27
- "foo.bar.baz[2]"
28
- );
24
+ search({ foo: { bar: { baz: [0, 1, 2, 3, 4] } } }, "foo.bar.baz[2]");
29
25
 
30
26
  // OUTPUTS: 2
31
27
  ```
@@ -35,50 +31,42 @@ In the example we gave the `search` function input data of
35
31
  expression `foo.bar.baz[2]`, and the `search` function evaluated
36
32
  the expression against the input data to produce the result `2`.
37
33
 
38
- The JMESPath language can do *a lot* more than select an element
39
- from a list. Here are a few more examples:
34
+ The JMESPath language can do _a lot_ more than select an element
35
+ from a list. Here are a few more examples:
40
36
 
41
37
  ```javascript
42
- import { search } from '@jmespath-community/jmespath';
38
+ import { search } from "@jmespath-community/jmespath";
43
39
 
44
40
  /* --- EXAMPLE 1 --- */
45
41
 
46
42
  let JSON_DOCUMENT = {
47
43
  foo: {
48
44
  bar: {
49
- baz: [0, 1, 2, 3, 4]
50
- }
51
- }
45
+ baz: [0, 1, 2, 3, 4],
46
+ },
47
+ },
52
48
  };
53
49
 
54
50
  search(JSON_DOCUMENT, "foo.bar");
55
51
  // OUTPUTS: { baz: [ 0, 1, 2, 3, 4 ] }
56
52
 
57
-
58
53
  /* --- EXAMPLE 2 --- */
59
54
 
60
55
  JSON_DOCUMENT = {
61
- "foo": [
62
- {"first": "a", "last": "b"},
63
- {"first": "c", "last": "d"}
64
- ]
56
+ foo: [
57
+ { first: "a", last: "b" },
58
+ { first: "c", last: "d" },
59
+ ],
65
60
  };
66
61
 
67
- search(JSON_DOCUMENT, "foo[*].first")
62
+ search(JSON_DOCUMENT, "foo[*].first");
68
63
  // OUTPUTS: [ 'a', 'c' ]
69
64
 
70
-
71
65
  /* --- EXAMPLE 3 --- */
72
66
 
73
67
  JSON_DOCUMENT = {
74
- "foo": [
75
- {"age": 20},
76
- {"age": 25},
77
- {"age": 30},
78
- {"age": 35},
79
- {"age": 40}
80
- ]
81
- }
68
+ foo: [{ age: 20 }, { age: 25 }, { age: 30 }, { age: 35 }, { age: 40 }],
69
+ };
82
70
 
83
71
  search(JSON_DOCUMENT, "foo[?age > `30`]");
84
72
  // OUTPUTS: [ { age: 35 }, { age: 40 } ]
@@ -86,91 +74,138 @@ search(JSON_DOCUMENT, "foo[?age > `30`]");
86
74
 
87
75
  ### `compile(expression: string): ExpressionNodeTree`
88
76
 
89
- You can precompile all your expressions ready for use later on. the `compile`
77
+ You can precompile all your expressions ready for use later on. The `compile`
90
78
  function takes a JMESPath expression and returns an abstract syntax tree that
91
79
  can be used by the TreeInterpreter function
92
80
 
93
81
  ```javascript
94
- import { compile, TreeInterpreter } from '@jmespath-community/jmespath';
82
+ import { compile, TreeInterpreter } from "@jmespath-community/jmespath";
95
83
 
96
- const ast = compile('foo.bar');
84
+ const ast = compile("foo.bar");
97
85
 
98
- TreeInterpreter.search(ast, {foo: {bar: 'BAZ'}})
86
+ TreeInterpreter.search(ast, { foo: { bar: "BAZ" } });
99
87
  // RETURNS: "BAZ"
100
-
101
88
  ```
102
89
 
103
90
  ---
91
+
104
92
  ## EXTENSIONS TO ORIGINAL SPEC
105
93
 
106
94
  1. ### Register you own custom functions
107
95
 
108
- #### `registerFunction(functionName: string, customFunction: RuntimeFunction, signature: InputSignature[]): void`
109
-
110
- Extend the list of built in JMESpath expressions with your own functions.
111
-
112
- ```javascript
113
- import {search, registerFunction, TYPE_NUMBER} from '@jmespath-community/jmespath'
114
-
96
+ #### `registerFunction(functionName: string, customFunction: RuntimeFunction, signature: InputSignature[]): void`
115
97
 
116
- search({ foo: 60, bar: 10 }, 'divide(foo, bar)')
117
- // THROWS ERROR: Error: Unknown function: divide()
98
+ Extend the list of built in JMESpath expressions with your own functions.
118
99
 
119
- registerFunction(
120
- 'divide', // FUNCTION NAME
121
- (resolvedArgs) => { // CUSTOM FUNCTION
122
- const [dividend, divisor] = resolvedArgs;
123
- return dividend / divisor;
124
- },
125
- [{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER] }] //SIGNATURE
126
- );
100
+ ```javascript
101
+ import { search, registerFunction, TYPE_NUMBER } from "@jmespath-community/jmespath";
127
102
 
128
- search({ foo: 60,bar: 10 }, 'divide(foo, bar)');
129
- // OUTPUTS: 6
103
+ search({ foo: 60, bar: 10 }, "divide(foo, bar)");
104
+ // THROWS ERROR: Error: Unknown function: divide()
130
105
 
131
- ```
106
+ registerFunction(
107
+ "divide", // FUNCTION NAME
108
+ (resolvedArgs) => {
109
+ // CUSTOM FUNCTION
110
+ const [dividend, divisor] = resolvedArgs;
111
+ return dividend / divisor;
112
+ },
113
+ [{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER] }], //SIGNATURE
114
+ );
132
115
 
133
- Optional arguments are supported by setting `{..., optional: true}` in argument signatures
116
+ search({ foo: 60, bar: 10 }, "divide(foo, bar)");
117
+ // OUTPUTS: 6
118
+ ```
134
119
 
120
+ Optional arguments are supported by setting `{..., optional: true}` in argument signatures
135
121
 
136
- ```javascript
122
+ ```javascript
123
+ registerFunction(
124
+ "divide",
125
+ (resolvedArgs) => {
126
+ const [dividend, divisor] = resolvedArgs;
127
+ return dividend / divisor ?? 1; //OPTIONAL DIVISOR THAT DEFAULTS TO 1
128
+ },
129
+ [{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER], optional: true }], //SIGNATURE
130
+ );
137
131
 
138
- registerFunction(
139
- 'divide',
140
- (resolvedArgs) => {
141
- const [dividend, divisor] = resolvedArgs;
142
- return dividend / divisor ?? 1; //OPTIONAL DIVISOR THAT DEFAULTS TO 1
143
- },
144
- [{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER], optional: true }] //SIGNATURE
145
- );
146
-
147
- search({ foo: 60, bar: 10 }, 'divide(foo)');
148
- // OUTPUTS: 60
149
-
150
- ```
132
+ search({ foo: 60, bar: 10 }, "divide(foo)");
133
+ // OUTPUTS: 60
134
+ ```
151
135
 
152
136
  2. ### Root value access with `$` symbol
153
137
 
154
138
  ```javascript
155
-
156
- search({foo: {bar: 999}, baz: [1, 2, 3]}, '$.baz[*].[@, $.foo.bar]')
139
+ search({ foo: { bar: 999 }, baz: [1, 2, 3] }, "$.baz[*].[@, $.foo.bar]");
157
140
 
158
141
  // OUTPUTS:
159
142
  // [ [ 1, 999 ], [ 2, 999 ], [ 3, 999 ] ]
160
143
  ```
161
144
 
162
-
163
145
  ## More Resources
164
146
 
165
147
  The example above only show a small amount of what
166
148
  a JMESPath expression can do. If you want to take a
167
- tour of the language, the *best* place to go is the
149
+ tour of the language, the _best_ place to go is the
168
150
  [JMESPath Tutorial](http://jmespath.site/main#tutorial).
169
151
 
170
152
  One of the best things about JMESPath is that it is
171
153
  implemented in many different programming languages including
172
- python, ruby, php, lua, etc. To see a complete list of libraries,
154
+ python, ruby, php, lua, etc. To see a complete list of libraries,
173
155
  check out the [JMESPath libraries page](http://jmespath.site/main#libraries).
174
156
 
175
157
  And finally, the full JMESPath specification can be found
176
158
  on the [JMESPath site](https://jmespath.site/main/#specification).
159
+
160
+ ## Experimental Features
161
+
162
+ ### Ternary Operations (`? :`)
163
+
164
+ **Supported Version:** 1.1.6
165
+
166
+ Experimental support for [ternary operations](https://github.com/jmespath-community/jmespath.spec/discussions/179) has been added, allowing for conditional logic within your JMESPath expressions. The syntax is `condition ? value_if_true : value_if_false`.
167
+
168
+ - **Condition:** The expression before the `?`. JMESPath determines truthiness based on the evaluated value:
169
+ - `true` is truthy.
170
+ - Any non-empty object, array, or string is truthy.
171
+ - Any non-zero number is truthy.
172
+ - `false`, `null`, empty objects `{}`, empty arrays `[]`, and empty strings `''` are falsy.
173
+ - **Value if true:** The expression between the `?` and `:`. This is evaluated and returned if the condition is truthy.
174
+ - **Value if false:** The expression after the `:`. This is evaluated and returned if the condition is falsy.
175
+
176
+ **Examples:**
177
+
178
+ Basic usage:
179
+
180
+ ```javascript
181
+ search({ is_active: true, user: "Alice" }, "is_active ? user : 'Guest'");
182
+ // OUTPUTS: "Alice"
183
+
184
+ search({ is_active: false, user: "Bob" }, "is_active ? user : 'Guest'");
185
+ // OUTPUTS: "Guest"
186
+ ```
187
+
188
+ Truthiness with different types:
189
+
190
+ ```javascript
191
+ search({ data: [1, 2] }, "data ? 'has_data' : 'no_data'");
192
+ // OUTPUTS: "has_data"
193
+
194
+ search({ data: [] }, "data ? 'has_data' : 'no_data'");
195
+ // OUTPUTS: "no_data"
196
+
197
+ search({ count: 5 }, "count ? 'count_present' : 'no_count'");
198
+ // OUTPUTS: "count_present"
199
+
200
+ search({ count: 0 }, "count ? 'count_present' : 'no_count'");
201
+ // OUTPUTS: "no_count"
202
+ ```
203
+
204
+ Nested Ternaries:
205
+
206
+ ```javascript
207
+ search({ a: true, b: false, val1: 10, val2: 20, val3: 30 }, "a ? (b ? val1 : val2) : val3");
208
+ // OUTPUTS: 20
209
+ ```
210
+
211
+ This feature is currently experimental and its syntax or behavior might change in future releases.
@@ -1,3 +1,46 @@
1
+ var Token;
2
+ (function (Token) {
3
+ Token["TOK_EOF"] = "EOF";
4
+ Token["TOK_VARIABLE"] = "Variable";
5
+ Token["TOK_ASSIGN"] = "Assign";
6
+ Token["TOK_UNQUOTEDIDENTIFIER"] = "UnquotedIdentifier";
7
+ Token["TOK_QUOTEDIDENTIFIER"] = "QuotedIdentifier";
8
+ Token["TOK_RBRACKET"] = "Rbracket";
9
+ Token["TOK_RPAREN"] = "Rparen";
10
+ Token["TOK_COMMA"] = "Comma";
11
+ Token["TOK_COLON"] = "Colon";
12
+ Token["TOK_RBRACE"] = "Rbrace";
13
+ Token["TOK_NUMBER"] = "Number";
14
+ Token["TOK_CURRENT"] = "Current";
15
+ Token["TOK_ROOT"] = "Root";
16
+ Token["TOK_EXPREF"] = "Expref";
17
+ Token["TOK_PIPE"] = "Pipe";
18
+ Token["TOK_OR"] = "Or";
19
+ Token["TOK_AND"] = "And";
20
+ Token["TOK_EQ"] = "EQ";
21
+ Token["TOK_GT"] = "GT";
22
+ Token["TOK_LT"] = "LT";
23
+ Token["TOK_GTE"] = "GTE";
24
+ Token["TOK_LTE"] = "LTE";
25
+ Token["TOK_NE"] = "NE";
26
+ Token["TOK_PLUS"] = "Plus";
27
+ Token["TOK_MINUS"] = "Minus";
28
+ Token["TOK_MULTIPLY"] = "Multiply";
29
+ Token["TOK_DIVIDE"] = "Divide";
30
+ Token["TOK_MODULO"] = "Modulo";
31
+ Token["TOK_DIV"] = "Div";
32
+ Token["TOK_FLATTEN"] = "Flatten";
33
+ Token["TOK_STAR"] = "Star";
34
+ Token["TOK_FILTER"] = "Filter";
35
+ Token["TOK_DOT"] = "Dot";
36
+ Token["TOK_NOT"] = "Not";
37
+ Token["TOK_LBRACE"] = "Lbrace";
38
+ Token["TOK_LBRACKET"] = "Lbracket";
39
+ Token["TOK_LPAREN"] = "Lparen";
40
+ Token["TOK_LITERAL"] = "Literal";
41
+ Token["TOK_QUESTION"] = "Question";
42
+ })(Token || (Token = {}));
43
+
1
44
  const isObject = (obj) => {
2
45
  return obj !== null && Object.prototype.toString.call(obj) === '[object Object]';
3
46
  };
@@ -36,31 +79,32 @@ const strictDeepEqual = (first, second) => {
36
79
  return false;
37
80
  };
38
81
  const isFalse = (obj) => {
82
+ // falsey values
83
+ if (obj === null || obj === undefined || obj === false) {
84
+ return true;
85
+ }
86
+ if (typeof obj === 'string') {
87
+ return obj === '';
88
+ }
39
89
  if (typeof obj === 'object') {
40
- if (obj === null) {
41
- return true;
42
- }
43
90
  if (Array.isArray(obj)) {
44
91
  return obj.length === 0;
45
92
  }
46
- // eslint-disable-next-line @typescript-eslint/naming-convention
47
- for (const _key in obj) {
48
- return false;
93
+ if (obj === null) {
94
+ return true;
49
95
  }
50
- return true;
96
+ // check if object is empty
97
+ return Object.keys(obj).length === 0;
51
98
  }
52
- return !(typeof obj === 'number' || obj);
99
+ return false;
53
100
  };
54
101
  const isAlpha = (ch) => {
55
- // tslint:disable-next-line: strict-comparisons
56
102
  return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '_';
57
103
  };
58
104
  const isNum = (ch) => {
59
- // tslint:disable-next-line: strict-comparisons
60
105
  return (ch >= '0' && ch <= '9') || ch === '-';
61
106
  };
62
107
  const isAlphaNum = (ch) => {
63
- // tslint:disable-next-line: strict-comparisons
64
108
  return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch === '_';
65
109
  };
66
110
  const ensureInteger = (value) => {
@@ -201,48 +245,6 @@ const trimImpl = (subject, regExper, chars) => {
201
245
  };
202
246
  const upper = (subject) => subject.toUpperCase();
203
247
 
204
- var Token;
205
- (function (Token) {
206
- Token["TOK_EOF"] = "EOF";
207
- Token["TOK_VARIABLE"] = "Variable";
208
- Token["TOK_ASSIGN"] = "Assign";
209
- Token["TOK_UNQUOTEDIDENTIFIER"] = "UnquotedIdentifier";
210
- Token["TOK_QUOTEDIDENTIFIER"] = "QuotedIdentifier";
211
- Token["TOK_RBRACKET"] = "Rbracket";
212
- Token["TOK_RPAREN"] = "Rparen";
213
- Token["TOK_COMMA"] = "Comma";
214
- Token["TOK_COLON"] = "Colon";
215
- Token["TOK_RBRACE"] = "Rbrace";
216
- Token["TOK_NUMBER"] = "Number";
217
- Token["TOK_CURRENT"] = "Current";
218
- Token["TOK_ROOT"] = "Root";
219
- Token["TOK_EXPREF"] = "Expref";
220
- Token["TOK_PIPE"] = "Pipe";
221
- Token["TOK_OR"] = "Or";
222
- Token["TOK_AND"] = "And";
223
- Token["TOK_EQ"] = "EQ";
224
- Token["TOK_GT"] = "GT";
225
- Token["TOK_LT"] = "LT";
226
- Token["TOK_GTE"] = "GTE";
227
- Token["TOK_LTE"] = "LTE";
228
- Token["TOK_NE"] = "NE";
229
- Token["TOK_PLUS"] = "Plus";
230
- Token["TOK_MINUS"] = "Minus";
231
- Token["TOK_MULTIPLY"] = "Multiply";
232
- Token["TOK_DIVIDE"] = "Divide";
233
- Token["TOK_MODULO"] = "Modulo";
234
- Token["TOK_DIV"] = "Div";
235
- Token["TOK_FLATTEN"] = "Flatten";
236
- Token["TOK_STAR"] = "Star";
237
- Token["TOK_FILTER"] = "Filter";
238
- Token["TOK_DOT"] = "Dot";
239
- Token["TOK_NOT"] = "Not";
240
- Token["TOK_LBRACE"] = "Lbrace";
241
- Token["TOK_LBRACKET"] = "Lbracket";
242
- Token["TOK_LPAREN"] = "Lparen";
243
- Token["TOK_LITERAL"] = "Literal";
244
- })(Token || (Token = {}));
245
-
246
248
  const basicTokens = {
247
249
  '(': Token.TOK_LPAREN,
248
250
  ')': Token.TOK_RPAREN,
@@ -256,6 +258,7 @@ const basicTokens = {
256
258
  '}': Token.TOK_RBRACE,
257
259
  '+': Token.TOK_PLUS,
258
260
  '%': Token.TOK_MODULO,
261
+ '?': Token.TOK_QUESTION,
259
262
  '\u2212': Token.TOK_MINUS,
260
263
  '\u00d7': Token.TOK_MULTIPLY,
261
264
  '\u00f7': Token.TOK_DIVIDE,
@@ -577,8 +580,9 @@ const bindingPower = {
577
580
  [Token.TOK_ROOT]: 0,
578
581
  [Token.TOK_ASSIGN]: 1,
579
582
  [Token.TOK_PIPE]: 1,
580
- [Token.TOK_OR]: 2,
581
- [Token.TOK_AND]: 3,
583
+ [Token.TOK_QUESTION]: 2,
584
+ [Token.TOK_OR]: 3,
585
+ [Token.TOK_AND]: 4,
582
586
  [Token.TOK_EQ]: 5,
583
587
  [Token.TOK_GT]: 5,
584
588
  [Token.TOK_LT]: 5,
@@ -685,8 +689,7 @@ class TokenParser {
685
689
  }
686
690
  case Token.TOK_STAR: {
687
691
  const left = { type: 'Identity' };
688
- const right = this.lookahead(0) === Token.TOK_RBRACKET ? left : this.parseProjectionRHS(bindingPower.Star);
689
- return { type: 'ValueProjection', left, right };
692
+ return { type: 'ValueProjection', left, right: this.parseProjectionRHS(bindingPower.Star) };
690
693
  }
691
694
  case Token.TOK_FILTER:
692
695
  return this.led(token.type, { type: 'Identity' });
@@ -726,11 +729,9 @@ class TokenParser {
726
729
  return { type: 'ExpressionReference', child };
727
730
  }
728
731
  case Token.TOK_LPAREN: {
729
- const args = [];
730
- let expression = this.expression(0);
731
- args.push(expression);
732
+ const expression = this.expression(0);
732
733
  this.match(Token.TOK_RPAREN);
733
- return args[0];
734
+ return expression;
734
735
  }
735
736
  default:
736
737
  this.errorToken(token);
@@ -738,6 +739,17 @@ class TokenParser {
738
739
  }
739
740
  led(tokenName, left) {
740
741
  switch (tokenName) {
742
+ case Token.TOK_QUESTION: {
743
+ const trueExpr = this.expression(0);
744
+ this.match(Token.TOK_COLON);
745
+ const falseExpr = this.expression(0);
746
+ return {
747
+ type: 'Ternary',
748
+ condition: left,
749
+ trueExpr,
750
+ falseExpr,
751
+ };
752
+ }
741
753
  case Token.TOK_DOT: {
742
754
  const rbp = bindingPower.Dot;
743
755
  if (this.lookahead(0) !== Token.TOK_STAR) {
@@ -1047,6 +1059,7 @@ class Text {
1047
1059
  }
1048
1060
  get codePoints() {
1049
1061
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1062
+ // biome-ignore lint: lint/style/noNonNullAssertion
1050
1063
  const array = [...this._text].map(s => s.codePointAt(0));
1051
1064
  return array;
1052
1065
  }
@@ -2016,13 +2029,20 @@ class TreeInterpreter$1 {
2016
2029
  visit(node, value) {
2017
2030
  var _a, _b, _c;
2018
2031
  switch (node.type) {
2032
+ case 'Ternary': {
2033
+ const condition = this.visit(node.condition, value);
2034
+ if (!isFalse(condition)) {
2035
+ return this.visit(node.trueExpr, value);
2036
+ }
2037
+ return this.visit(node.falseExpr, value);
2038
+ }
2019
2039
  case 'Field':
2020
2040
  const identifier = node.name;
2021
- let result = null;
2022
- if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
2023
- result = (_a = value[identifier]) !== null && _a !== void 0 ? _a : null;
2041
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
2042
+ return null;
2024
2043
  }
2025
- return result;
2044
+ // return the value of the field
2045
+ return (_a = value[identifier]) !== null && _a !== void 0 ? _a : null;
2026
2046
  case 'LetExpression': {
2027
2047
  const { bindings, expression } = node;
2028
2048
  let scope = {};
@@ -2049,7 +2069,7 @@ class TreeInterpreter$1 {
2049
2069
  return this.visit(node.right, this.visit(node.left, value));
2050
2070
  case 'Subexpression': {
2051
2071
  const result = this.visit(node.left, value);
2052
- return result != null ? (_b = this.visit(node.right, result)) !== null && _b !== void 0 ? _b : null : null;
2072
+ return result != null ? ((_b = this.visit(node.right, result)) !== null && _b !== void 0 ? _b : null) : null;
2053
2073
  }
2054
2074
  case 'Index': {
2055
2075
  if (!Array.isArray(value)) {
@@ -2317,9 +2337,7 @@ function compile(expression, options) {
2317
2337
  function tokenize(expression, options) {
2318
2338
  return Lexer.tokenize(expression, options);
2319
2339
  }
2320
- const registerFunction = (functionName,
2321
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2322
- customFunction, signature) => {
2340
+ const registerFunction = (functionName, customFunction, signature) => {
2323
2341
  TreeInterpreterInstance.runtime.registerFunction(functionName, customFunction, signature);
2324
2342
  };
2325
2343
  function search(data, expression, options) {