@jmespath-community/jmespath 1.1.4 → 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 +121 -85
  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 +121 -85
  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} +7 -2
  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} +36 -13
  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/{Scope.js → src/Scope.js} +5 -5
  23. package/dist/lib/src/Scope.js.map +1 -0
  24. package/dist/lib/{TreeInterpreter.js → src/TreeInterpreter.js} +17 -10
  25. package/dist/lib/src/TreeInterpreter.js.map +1 -0
  26. package/dist/lib/{index.js → src/index.js} +2 -4
  27. package/dist/lib/src/index.js.map +1 -0
  28. package/dist/lib/{utils → src/utils}/index.js +12 -11
  29. package/dist/lib/src/utils/index.js.map +1 -0
  30. package/dist/lib/src/utils/strings.js.map +1 -0
  31. package/dist/lib/{utils → src/utils}/text.js +1 -0
  32. package/dist/lib/src/utils/text.js.map +1 -0
  33. package/dist/types/bin/jp.d.ts +2 -0
  34. package/dist/types/{AST.type.d.ts → src/AST.type.d.ts} +7 -1
  35. package/dist/types/{Lexer.type.d.ts → src/Lexer.type.d.ts} +2 -1
  36. package/dist/types/{Runtime.d.ts → src/Runtime.d.ts} +1 -1
  37. package/dist/types/{Scope.d.ts → src/Scope.d.ts} +1 -0
  38. package/dist/types/{index.d.ts → src/index.d.ts} +4 -3
  39. package/package.json +4 -1
  40. package/dist/lib/Lexer.js.map +0 -1
  41. package/dist/lib/Lexer.type.js.map +0 -1
  42. package/dist/lib/Parser.js.map +0 -1
  43. package/dist/lib/Runtime.js.map +0 -1
  44. package/dist/lib/Scope.js.map +0 -1
  45. package/dist/lib/TreeInterpreter.js.map +0 -1
  46. package/dist/lib/index.js.map +0 -1
  47. package/dist/lib/utils/index.js.map +0 -1
  48. package/dist/lib/utils/strings.js.map +0 -1
  49. package/dist/lib/utils/text.js.map +0 -1
  50. /package/dist/lib/{AST.type.js → src/AST.type.js} +0 -0
  51. /package/dist/lib/{JSON.type.js → src/JSON.type.js} +0 -0
  52. /package/dist/lib/{Parser.type.js → src/Parser.type.js} +0 -0
  53. /package/dist/lib/{utils → src/utils}/strings.js +0 -0
  54. /package/dist/types/{JSON.type.d.ts → src/JSON.type.d.ts} +0 -0
  55. /package/dist/types/{Lexer.d.ts → src/Lexer.d.ts} +0 -0
  56. /package/dist/types/{Parser.d.ts → src/Parser.d.ts} +0 -0
  57. /package/dist/types/{Parser.type.d.ts → src/Parser.type.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,
@@ -467,7 +470,11 @@ class StreamLexer {
467
470
  this._current += 1;
468
471
  if (this._current < stream.length && stream[this._current] === peek) {
469
472
  this._current += 1;
470
- return { start: start, type: orElse, value: stream.slice(start, this._current) };
473
+ return {
474
+ start: start,
475
+ type: orElse,
476
+ value: stream.slice(start, this._current),
477
+ };
471
478
  }
472
479
  return { start: start, type: token, value: stream[start] };
473
480
  }
@@ -573,8 +580,9 @@ const bindingPower = {
573
580
  [Token.TOK_ROOT]: 0,
574
581
  [Token.TOK_ASSIGN]: 1,
575
582
  [Token.TOK_PIPE]: 1,
576
- [Token.TOK_OR]: 2,
577
- [Token.TOK_AND]: 3,
583
+ [Token.TOK_QUESTION]: 2,
584
+ [Token.TOK_OR]: 3,
585
+ [Token.TOK_AND]: 4,
578
586
  [Token.TOK_EQ]: 5,
579
587
  [Token.TOK_GT]: 5,
580
588
  [Token.TOK_LT]: 5,
@@ -665,23 +673,33 @@ class TokenParser {
665
673
  }
666
674
  case Token.TOK_MINUS: {
667
675
  const child = this.expression(bindingPower.Minus);
668
- return { type: 'Unary', operator: token.type, operand: child };
676
+ return {
677
+ type: 'Unary',
678
+ operator: token.type,
679
+ operand: child,
680
+ };
669
681
  }
670
682
  case Token.TOK_PLUS: {
671
683
  const child = this.expression(bindingPower.Plus);
672
- return { type: 'Unary', operator: token.type, operand: child };
684
+ return {
685
+ type: 'Unary',
686
+ operator: token.type,
687
+ operand: child,
688
+ };
673
689
  }
674
690
  case Token.TOK_STAR: {
675
691
  const left = { type: 'Identity' };
676
- const right = this.lookahead(0) === Token.TOK_RBRACKET ? left : this.parseProjectionRHS(bindingPower.Star);
677
- return { type: 'ValueProjection', left, right };
692
+ return { type: 'ValueProjection', left, right: this.parseProjectionRHS(bindingPower.Star) };
678
693
  }
679
694
  case Token.TOK_FILTER:
680
695
  return this.led(token.type, { type: 'Identity' });
681
696
  case Token.TOK_LBRACE:
682
697
  return this.parseMultiselectHash();
683
698
  case Token.TOK_FLATTEN: {
684
- const left = { type: 'Flatten', child: { type: 'Identity' } };
699
+ const left = {
700
+ type: 'Flatten',
701
+ child: { type: 'Identity' },
702
+ };
685
703
  const right = this.parseProjectionRHS(bindingPower.Flatten);
686
704
  return { type: 'Projection', left, right };
687
705
  }
@@ -711,12 +729,9 @@ class TokenParser {
711
729
  return { type: 'ExpressionReference', child };
712
730
  }
713
731
  case Token.TOK_LPAREN: {
714
- console.log('nud::TOK_LPAREN');
715
- const args = [];
716
- let expression = this.expression(0);
717
- args.push(expression);
732
+ const expression = this.expression(0);
718
733
  this.match(Token.TOK_RPAREN);
719
- return args[0];
734
+ return expression;
720
735
  }
721
736
  default:
722
737
  this.errorToken(token);
@@ -724,6 +739,17 @@ class TokenParser {
724
739
  }
725
740
  led(tokenName, left) {
726
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
+ }
727
753
  case Token.TOK_DOT: {
728
754
  const rbp = bindingPower.Dot;
729
755
  if (this.lookahead(0) !== Token.TOK_STAR) {
@@ -833,7 +859,11 @@ class TokenParser {
833
859
  return { type: 'Index', value };
834
860
  }
835
861
  projectIfSlice(left, right) {
836
- const indexExpr = { type: 'IndexExpression', left, right };
862
+ const indexExpr = {
863
+ type: 'IndexExpression',
864
+ left,
865
+ right,
866
+ };
837
867
  if (right.type === 'Slice') {
838
868
  return {
839
869
  left: indexExpr,
@@ -1029,6 +1059,7 @@ class Text {
1029
1059
  }
1030
1060
  get codePoints() {
1031
1061
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1062
+ // biome-ignore lint: lint/style/noNonNullAssertion
1032
1063
  const array = [...this._text].map(s => s.codePointAt(0));
1033
1064
  return array;
1034
1065
  }
@@ -1957,6 +1988,9 @@ class ScopeChain {
1957
1988
  this.inner = undefined;
1958
1989
  this.data = {};
1959
1990
  }
1991
+ get currentScopeData() {
1992
+ return this.data;
1993
+ }
1960
1994
  withScope(data) {
1961
1995
  const outer = new ScopeChain();
1962
1996
  outer.inner = this;
@@ -1964,11 +1998,8 @@ class ScopeChain {
1964
1998
  return outer;
1965
1999
  }
1966
2000
  getValue(identifier) {
1967
- if (identifier in this.data) {
1968
- const result = this.data[identifier];
1969
- if (result !== null && result !== undefined) {
1970
- return result;
1971
- }
2001
+ if (Object.prototype.hasOwnProperty.call(this.data, identifier)) {
2002
+ return this.data[identifier];
1972
2003
  }
1973
2004
  if (this.inner) {
1974
2005
  return this.inner.getValue(identifier);
@@ -1996,15 +2027,22 @@ class TreeInterpreter$1 {
1996
2027
  return this.visit(node, value);
1997
2028
  }
1998
2029
  visit(node, value) {
1999
- var _a, _b, _c, _d;
2030
+ var _a, _b, _c;
2000
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
+ }
2001
2039
  case 'Field':
2002
2040
  const identifier = node.name;
2003
- let result = null;
2004
- if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
2005
- result = (_a = value[identifier]) !== null && _a !== void 0 ? _a : null;
2041
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
2042
+ return null;
2006
2043
  }
2007
- return result;
2044
+ // return the value of the field
2045
+ return (_a = value[identifier]) !== null && _a !== void 0 ? _a : null;
2008
2046
  case 'LetExpression': {
2009
2047
  const { bindings, expression } = node;
2010
2048
  let scope = {};
@@ -2021,24 +2059,24 @@ class TreeInterpreter$1 {
2021
2059
  }
2022
2060
  case 'Variable': {
2023
2061
  const variable = node.name;
2024
- const result = (_b = this._scope.getValue(variable)) !== null && _b !== void 0 ? _b : null;
2025
- if (result === null || result === undefined) {
2062
+ if (!this._scope.getValue(variable) &&
2063
+ !Object.prototype.hasOwnProperty.call(this._scope.currentScopeData, variable)) {
2026
2064
  throw new Error(`Error referencing undefined variable ${variable}`);
2027
2065
  }
2028
- return result;
2066
+ return this._scope.getValue(variable);
2029
2067
  }
2030
2068
  case 'IndexExpression':
2031
2069
  return this.visit(node.right, this.visit(node.left, value));
2032
2070
  case 'Subexpression': {
2033
2071
  const result = this.visit(node.left, value);
2034
- return result != null ? (_c = this.visit(node.right, result)) !== null && _c !== void 0 ? _c : null : null;
2072
+ return result != null ? ((_b = this.visit(node.right, result)) !== null && _b !== void 0 ? _b : null) : null;
2035
2073
  }
2036
2074
  case 'Index': {
2037
2075
  if (!Array.isArray(value)) {
2038
2076
  return null;
2039
2077
  }
2040
2078
  const index = node.value < 0 ? value.length + node.value : node.value;
2041
- return (_d = value[index]) !== null && _d !== void 0 ? _d : null;
2079
+ return (_c = value[index]) !== null && _c !== void 0 ? _c : null;
2042
2080
  }
2043
2081
  case 'Slice': {
2044
2082
  if (!Array.isArray(value) && typeof value !== 'string') {
@@ -2299,9 +2337,7 @@ function compile(expression, options) {
2299
2337
  function tokenize(expression, options) {
2300
2338
  return Lexer.tokenize(expression, options);
2301
2339
  }
2302
- const registerFunction = (functionName,
2303
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2304
- customFunction, signature) => {
2340
+ const registerFunction = (functionName, customFunction, signature) => {
2305
2341
  TreeInterpreterInstance.runtime.registerFunction(functionName, customFunction, signature);
2306
2342
  };
2307
2343
  function search(data, expression, options) {