@swaggerexpert/jsonpath 3.2.5 → 4.0.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 (84) hide show
  1. package/README.md +225 -18
  2. package/cjs/errors/JSONNormalizedPathError.cjs +8 -0
  3. package/cjs/errors/JSONPathError.cjs +1 -1
  4. package/cjs/errors/{JSONPathCompileError.cjs → JSONPathEvaluateError.cjs} +2 -2
  5. package/cjs/evaluate/evaluators/comparable.cjs +44 -0
  6. package/cjs/evaluate/evaluators/comparison-expr.cjs +37 -0
  7. package/cjs/evaluate/evaluators/filter-query.cjs +182 -0
  8. package/cjs/evaluate/evaluators/function-expr.cjs +106 -0
  9. package/cjs/evaluate/evaluators/literal.cjs +25 -0
  10. package/cjs/evaluate/evaluators/logical-expr.cjs +96 -0
  11. package/cjs/evaluate/evaluators/singular-query.cjs +103 -0
  12. package/cjs/evaluate/functions/count.cjs +35 -0
  13. package/cjs/evaluate/functions/index.cjs +15 -0
  14. package/cjs/evaluate/functions/length.cjs +42 -0
  15. package/cjs/evaluate/functions/match.cjs +49 -0
  16. package/cjs/evaluate/functions/search.cjs +49 -0
  17. package/cjs/evaluate/functions/value.cjs +36 -0
  18. package/cjs/evaluate/index.cjs +182 -0
  19. package/cjs/evaluate/realms/EvaluationRealm.cjs +154 -0
  20. package/cjs/evaluate/realms/json/index.cjs +246 -0
  21. package/cjs/evaluate/utils/guards.cjs +129 -0
  22. package/cjs/evaluate/utils/i-regexp.cjs +118 -0
  23. package/cjs/evaluate/visitors/bracketed-selection.cjs +35 -0
  24. package/cjs/evaluate/visitors/filter-selector.cjs +43 -0
  25. package/cjs/evaluate/visitors/index-selector.cjs +55 -0
  26. package/cjs/evaluate/visitors/name-selector.cjs +38 -0
  27. package/cjs/evaluate/visitors/segment.cjs +99 -0
  28. package/cjs/evaluate/visitors/selector.cjs +47 -0
  29. package/cjs/evaluate/visitors/slice-selector.cjs +115 -0
  30. package/cjs/evaluate/visitors/wildcard-selector.cjs +32 -0
  31. package/cjs/index.cjs +16 -7
  32. package/cjs/normalized-path.cjs +145 -0
  33. package/cjs/parse/callbacks/cst.cjs +2 -4
  34. package/cjs/parse/index.cjs +3 -1
  35. package/cjs/parse/translators/ASTTranslator/index.cjs +1 -1
  36. package/cjs/parse/translators/ASTTranslator/transformers.cjs +246 -5
  37. package/cjs/parse/translators/CSTOptimizedTranslator.cjs +1 -3
  38. package/cjs/parse/translators/CSTTranslator.cjs +1 -2
  39. package/cjs/test/index.cjs +4 -2
  40. package/es/errors/JSONNormalizedPathError.mjs +3 -0
  41. package/es/errors/JSONPathError.mjs +1 -1
  42. package/es/errors/JSONPathEvaluateError.mjs +3 -0
  43. package/es/evaluate/evaluators/comparable.mjs +38 -0
  44. package/es/evaluate/evaluators/comparison-expr.mjs +31 -0
  45. package/es/evaluate/evaluators/filter-query.mjs +175 -0
  46. package/es/evaluate/evaluators/function-expr.mjs +99 -0
  47. package/es/evaluate/evaluators/literal.mjs +21 -0
  48. package/es/evaluate/evaluators/logical-expr.mjs +89 -0
  49. package/es/evaluate/evaluators/singular-query.mjs +97 -0
  50. package/es/evaluate/functions/count.mjs +30 -0
  51. package/es/evaluate/functions/index.mjs +13 -0
  52. package/es/evaluate/functions/length.mjs +37 -0
  53. package/es/evaluate/functions/match.mjs +44 -0
  54. package/es/evaluate/functions/search.mjs +44 -0
  55. package/es/evaluate/functions/value.mjs +31 -0
  56. package/es/evaluate/index.mjs +174 -0
  57. package/es/evaluate/realms/EvaluationRealm.mjs +148 -0
  58. package/es/evaluate/realms/json/index.mjs +240 -0
  59. package/es/evaluate/utils/guards.mjs +114 -0
  60. package/es/evaluate/utils/i-regexp.mjs +113 -0
  61. package/es/evaluate/visitors/bracketed-selection.mjs +29 -0
  62. package/es/evaluate/visitors/filter-selector.mjs +37 -0
  63. package/es/evaluate/visitors/index-selector.mjs +51 -0
  64. package/es/evaluate/visitors/name-selector.mjs +34 -0
  65. package/es/evaluate/visitors/segment.mjs +91 -0
  66. package/es/evaluate/visitors/selector.mjs +41 -0
  67. package/es/evaluate/visitors/slice-selector.mjs +111 -0
  68. package/es/evaluate/visitors/wildcard-selector.mjs +28 -0
  69. package/es/index.mjs +7 -3
  70. package/es/normalized-path.mjs +136 -0
  71. package/es/parse/callbacks/cst.mjs +2 -4
  72. package/es/parse/index.mjs +3 -1
  73. package/es/parse/translators/ASTTranslator/index.mjs +1 -1
  74. package/es/parse/translators/ASTTranslator/transformers.mjs +246 -5
  75. package/es/parse/translators/CSTOptimizedTranslator.mjs +1 -3
  76. package/es/parse/translators/CSTTranslator.mjs +1 -2
  77. package/es/test/index.mjs +4 -2
  78. package/package.json +4 -2
  79. package/types/index.d.ts +114 -11
  80. package/cjs/compile.cjs +0 -50
  81. package/cjs/escape.cjs +0 -59
  82. package/es/compile.mjs +0 -45
  83. package/es/errors/JSONPathCompileError.mjs +0 -3
  84. package/es/escape.mjs +0 -55
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![try on RunKit](https://img.shields.io/badge/try%20on-RunKit-brightgreen.svg?style=flat)](https://npm.runkit.com/@swaggerexpert/jsonpath)
8
8
  [![Tidelift](https://tidelift.com/badges/package/npm/@swaggerexpert%2Fjsonpath)](https://tidelift.com/subscription/pkg/npm-.swaggerexpert-jsonpath?utm_source=npm-swaggerexpert-jsonpath&utm_medium=referral&utm_campaign=readme)
9
9
 
10
- `@swaggerexpert/jsonpath` is a **parser** and **validator** for [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535) Query Expressions for JSON - **JSONPath**.
10
+ `@swaggerexpert/jsonpath` is a **parser**, **validator**, **compiler**, and **evaluator** for [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535) Query Expressions for JSON - **JSONPath**.
11
11
 
12
12
  The development of this library contributed to the identification and formal submission of following **erratas** against the RFC 9535:
13
13
  - [Errata ID: 8343](https://www.rfc-editor.org/errata/eid8343)
@@ -15,6 +15,8 @@ The development of this library contributed to the identification and formal sub
15
15
  - [Errata ID: 8353](https://www.rfc-editor.org/errata/eid8353)
16
16
  - [Errata ID: 8354](https://www.rfc-editor.org/errata/eid8354)
17
17
 
18
+ This library is **100% compliant** with the [JSONPath Compliance Test Suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite).
19
+
18
20
  <table>
19
21
  <tr>
20
22
  <td align="right" valign="middle">
@@ -43,11 +45,16 @@ The development of this library contributed to the identification and formal sub
43
45
  - [Statistics](#statistics)
44
46
  - [Tracing](#tracing)
45
47
  - [Validation](#validation)
46
- - [Compilation](#compilation)
47
- - [Escaping](#escaping)
48
+ - [Normalized paths](#normalized-paths-1)
49
+ - [Evaluation](#evaluation)
50
+ - [Collecting normalized paths](#collecting-normalized-paths)
51
+ - [Built-in functions](#built-in-functions)
52
+ - [Custom functions](#custom-functions)
53
+ - [Evaluation realms](#evaluation-realms)
48
54
  - [Errors](#errors)
49
55
  - [Grammar](#grammar)
50
56
  - [More about JSONPath](#more-about-jsonpath)
57
+ - [Setting up for development](#setting-up-for-development)
51
58
  - [License](#license)
52
59
 
53
60
  ## Getting started
@@ -62,8 +69,8 @@ You can install `@swaggerexpert/jsonpath` using `npm`:
62
69
 
63
70
  ### Usage
64
71
 
65
- `@swaggerexpert/jsonpath` currently supports **parsing** and **validation**.
66
- Both parser and validator are based on a superset of [ABNF](https://www.rfc-editor.org/rfc/rfc5234) ([SABNF](https://cs.github.com/ldthomas/apg-js2/blob/master/SABNF.md))
72
+ `@swaggerexpert/jsonpath` supports **parsing**, **validation**, **compilation**, and **evaluation** of JSONPath expressions.
73
+ The parser and validator are based on a superset of [ABNF](https://www.rfc-editor.org/rfc/rfc5234) ([SABNF](https://cs.github.com/ldthomas/apg-js2/blob/master/SABNF.md))
67
74
  and use [apg-lite](https://github.com/ldthomas/apg-lite) parser generator.
68
75
 
69
76
  #### Parsing
@@ -229,39 +236,224 @@ test("$['a']", { normalized: true }); // => true
229
236
  test('$.store.book[0].title', { normalized: true }); // => false
230
237
  ```
231
238
 
232
- #### Compilation
239
+ **Well-typedness validation** is enabled by default and validates the JSONPath expression against [RFC 9535 type system rules](https://www.rfc-editor.org/rfc/rfc9535#section-2.4.9)
240
+ (e.g., function argument types, return type usage). Set `wellTyped` option to `false` to perform syntax-only validation.
241
+
242
+ ```js
243
+ import { test } from '@swaggerexpert/jsonpath';
244
+
245
+ test('$[?length(@.*) < 3]'); // => false (non-singular query for ValueType parameter)
246
+ test('$[?length(@.*) < 3]', { wellTyped: false }); // => true (syntax is valid)
247
+ ```
248
+
249
+ #### Normalized paths
250
+
251
+ The `NormalizedPath` namespace provides utilities for working with [Normalized Paths](https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths).
233
252
 
234
- Compilation is the process of transforming a list of selectors into a [Normalized Path](https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths).
235
- During compilation, name selectors are automatically escaped before being compiled.
253
+ ##### NormalizedPath.test
254
+
255
+ Tests if a string is a valid normalized JSONPath. This is a convenience wrapper around `test(path, { normalized: true })`.
236
256
 
237
257
  ```js
238
- import { compile } from '@swaggerexpert/jsonpath';
258
+ import { NormalizedPath } from '@swaggerexpert/jsonpath';
259
+
260
+ NormalizedPath.test("$['a']"); // => true
261
+ NormalizedPath.test('$[0]'); // => true
262
+ NormalizedPath.test("$['store']['book'][0]['title']"); // => true
263
+ NormalizedPath.test('$.a'); // => false (dot notation not allowed)
264
+ NormalizedPath.test('$["a"]'); // => false (double quotes not allowed)
265
+ NormalizedPath.test('$[*]'); // => false (wildcard not allowed)
266
+ ```
239
267
 
240
- compile(['store', 'book', 0, 'title']); // => "$['store']['book'][0]['title']"
268
+ ##### NormalizedPath.from
269
+
270
+ Creates a normalized path string from a list of selectors. Name selectors are automatically escaped.
271
+
272
+ ```js
273
+ import { NormalizedPath } from '@swaggerexpert/jsonpath';
274
+
275
+ NormalizedPath.from(['store', 'book', 0, 'title']); // => "$['store']['book'][0]['title']"
276
+ NormalizedPath.from(["it's"]); // => "$['it\\'s']"
241
277
  ```
242
278
 
243
- #### Escaping
279
+ ##### NormalizedPath.to
280
+
281
+ Parses a normalized path string and returns a list of selectors.
282
+
283
+ ```js
284
+ import { NormalizedPath } from '@swaggerexpert/jsonpath';
285
+
286
+ NormalizedPath.to("$['store']['book'][0]['title']"); // => ['store', 'book', 0, 'title']
287
+ NormalizedPath.to("$['it\\'s']"); // => ["it's"]
288
+ ```
244
289
 
245
- Certain characters within name selectors in Normalized Paths require escaping.
290
+ ##### NormalizedPath.escape
291
+
292
+ Escapes special characters in name selectors for use in normalized paths.
246
293
  The apostrophe (`'`) and backslash (`\`) characters must be escaped.
247
294
  Control characters (U+0000 through U+001F) are escaped using specific escape sequences
248
295
  (`\b`, `\t`, `\n`, `\f`, `\r`) or Unicode escape sequences (`\uXXXX`).
249
296
 
250
297
  ```js
251
- import { escape } from '@swaggerexpert/jsonpath';
298
+ import { NormalizedPath } from '@swaggerexpert/jsonpath';
299
+
300
+ NormalizedPath.escape("it's"); // => "it\\'s"
301
+ NormalizedPath.escape('back\\slash'); // => "back\\\\slash"
302
+ NormalizedPath.escape('line\nfeed'); // => "line\\nfeed"
303
+ ```
304
+
305
+ #### Evaluation
306
+
307
+ Evaluation is the process of applying a JSONPath expression against a JSON value to produce a nodelist (array of matched values).
308
+ Per [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535#section-2.1), evaluation begins with the root value (`$`) and processes each
309
+ segment sequentially, producing a nodelist of all matching values.
310
+
311
+ ```js
312
+ import { evaluate } from '@swaggerexpert/jsonpath';
313
+
314
+ const value = {
315
+ store: {
316
+ book: [
317
+ { title: 'A', price: 10 },
318
+ { title: 'B', price: 20 }
319
+ ]
320
+ }
321
+ };
322
+
323
+ evaluate(value, '$.store.book[*].title'); // => ['A', 'B']
324
+ evaluate(value, '$.store.book[0]'); // => [{ title: 'A', price: 10 }]
325
+ evaluate(value, '$.store.book[?@.price > 15]'); // => [{ title: 'B', price: 20 }]
326
+ ```
327
+
328
+ ##### Collecting normalized paths
329
+
330
+ Use the `callback` option to collect [normalized paths](https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths) alongside matched values:
331
+
332
+ ```js
333
+ import { evaluate } from '@swaggerexpert/jsonpath';
334
+
335
+ const value = { store: { book: [{ title: 'A' }, { title: 'B' }] } };
336
+ const paths = [];
337
+
338
+ evaluate(value, '$.store.book[*].title', {
339
+ callback: (v, path) => paths.push(path)
340
+ });
341
+ // paths => ["$['store']['book'][0]['title']", "$['store']['book'][1]['title']"]
342
+ ```
343
+
344
+ ##### Built-in functions
345
+
346
+ The evaluation engine includes all [RFC 9535 Section 2.4](https://www.rfc-editor.org/rfc/rfc9535#section-2.4) functions:
347
+
348
+ | Function | Description |
349
+ |----------|-------------|
350
+ | `length(value)` | Returns the length of a string, array, or object |
351
+ | `count(nodelist)` | Returns the number of nodes in a nodelist |
352
+ | `match(string, regex)` | Tests if a string matches an I-Regexp pattern (full match) |
353
+ | `search(string, regex)` | Tests if a string contains an I-Regexp pattern |
354
+ | `value(nodelist)` | Extracts a single value from a nodelist |
355
+
356
+ ```js
357
+ import { evaluate } from '@swaggerexpert/jsonpath';
358
+
359
+ const value = { items: ['short', 'very long string', 'medium'] };
360
+
361
+ evaluate(value, '$.items[?length(@) > 5]'); // => ['very long string', 'medium']
362
+ evaluate(value, '$.items[?match(@, "^v.*")]'); // => ['very long string']
363
+ ```
252
364
 
253
- escape("it's"); // => "it\\'s"
254
- escape('back\\slash'); // => "back\\\\slash"
255
- escape('line\nfeed'); // => "line\\nfeed"
365
+ ##### Custom functions
366
+
367
+ Extend or override built-in functions via the `functions` option:
368
+
369
+ ```js
370
+ import { evaluate, functions } from '@swaggerexpert/jsonpath';
371
+
372
+ const customFunctions = {
373
+ ...functions,
374
+ min: (ctx, args) => {
375
+ const values = args[0];
376
+ if (!Array.isArray(values)) return undefined;
377
+ return Math.min(...values);
378
+ }
379
+ };
380
+
381
+ const value = { numbers: [3, 1, 4, 1, 5] };
382
+ evaluate(value, '$[?min($.numbers) == 1]', { functions: customFunctions });
383
+ ```
384
+
385
+ ##### Evaluation realms
386
+
387
+ Evaluation realms provide an abstraction layer for accessing different data structures during JSONPath evaluation.
388
+ By default, the evaluation engine uses the **JSON Evaluation Realm** which works with plain JavaScript objects and arrays.
389
+
390
+ ###### JSON Evaluation Realm
391
+
392
+ By default, the evaluation operates under the **JSON realm**, which assumes that:
393
+
394
+ - **Arrays** are indexed numerically
395
+ - **Objects** (plain JavaScript objects) are accessed by string keys
396
+
397
+ ```js
398
+ import { evaluate } from '@swaggerexpert/jsonpath';
399
+
400
+ const value = { store: { book: [{ title: 'A' }] } };
401
+ evaluate(value, '$.store.book[*].title'); // => ['A']
402
+ ```
403
+
404
+ The default realm can also be specified explicitly:
405
+
406
+ ```js
407
+ import { evaluate, JSONEvaluationRealm } from '@swaggerexpert/jsonpath';
408
+
409
+ const value = { store: { book: [{ title: 'A' }] } };
410
+ evaluate(value, '$.store.book[*].title', { realm: new JSONEvaluationRealm() }); // => ['A']
411
+ ```
412
+
413
+ ###### Custom Evaluation Realms
414
+
415
+ You can create custom evaluation realms to work with different data structures (e.g., Immutable.js, ApiDOM elements).
416
+ Extend the `EvaluationRealm` base class and implement the required methods:
417
+
418
+ | Method | Description |
419
+ |--------|-------------|
420
+ | `isObject(value)` | Check if value is an object (has named properties) |
421
+ | `isArray(value)` | Check if value is an array (has indexed elements) |
422
+ | `isString(value)` | Check if value is a string |
423
+ | `isNumber(value)` | Check if value is a number |
424
+ | `isBoolean(value)` | Check if value is a boolean |
425
+ | `isNull(value)` | Check if value is null |
426
+ | `getString(value)` | Get raw string value for regex operations |
427
+ | `getProperty(value, key)` | Get property by name from object |
428
+ | `hasProperty(value, key)` | Check if object has property |
429
+ | `getElement(value, index)` | Get element by index from array |
430
+ | `getKeys(value)` | Get all keys of object |
431
+ | `getLength(value)` | Get length of string, array, or object |
432
+ | `entries(value)` | Iterate over [key/index, value] pairs |
433
+ | `compare(left, op, right)` | Compare two values using operator (==, !=, <, <=, >, >=) |
434
+
435
+ ```js
436
+ import { evaluate, EvaluationRealm } from '@swaggerexpert/jsonpath';
437
+
438
+ class CustomRealm extends EvaluationRealm {
439
+ name = 'custom';
440
+
441
+ isObject(value) { /* ... */ }
442
+ isArray(value) { /* ... */ }
443
+ // ... implement all required methods
444
+ }
445
+
446
+ const value = { /* ... */ };
447
+ evaluate(value, '$.path', { realm: new CustomRealm() });
256
448
  ```
257
449
 
258
450
  #### Errors
259
451
 
260
452
  `@swaggerexpert/jsonpath` provides a structured error class hierarchy,
261
- enabling precise error handling across JSONPath operations, including parsing and compilation.
453
+ enabling precise error handling across JSONPath operations, including parsing, compilation, and evaluation.
262
454
 
263
455
  ```js
264
- import { JSONPathError, JSONPathParseError, JSONPathCompileError } from '@swaggerexpert/jsonpath';
456
+ import { JSONPathError, JSONPathParseError, JSONNormalizedPathError, JSONPathEvaluateError } from '@swaggerexpert/jsonpath';
265
457
  ```
266
458
 
267
459
  **JSONPathError** is the base class for all JSONPath errors.
@@ -530,6 +722,21 @@ disjunction = "||"
530
722
  conjunction = "&&"
531
723
  ```
532
724
 
725
+ ## Setting up for development
726
+
727
+ ```sh
728
+ # Clone with submodules (recommended)
729
+ $ git clone --recurse-submodules https://github.com/swaggerexpert/jsonpath.git
730
+
731
+ # Or initialize submodules after cloning
732
+ $ git submodule update --init
733
+
734
+ # Install dependencies
735
+ $ npm install
736
+ ```
737
+
738
+ The project uses the [JSONPath Compliance Test Suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite) as a git submodule for testing.
739
+
533
740
  ## License
534
741
 
535
742
  `@swaggerexpert/jsonpath` is licensed under [Apache 2.0 license](https://github.com/swaggerexpert/jsonpath/blob/main/LICENSE).
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.default = void 0;
5
+ var _JSONPathError = _interopRequireDefault(require("./JSONPathError.cjs"));
6
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
7
+ class JSONNormalizedPathError extends _JSONPathError.default {}
8
+ var _default = exports.default = JSONNormalizedPathError;
@@ -19,7 +19,7 @@ class JSONPathError extends Error {
19
19
  * This needs to stay here until our minimum supported version of Node.js is >= 16.9.0.
20
20
  * Node.js is >= 16.9.0 supports error causes natively.
21
21
  */
22
- if (options != null && typeof options === 'object' && Object.prototype.hasOwnProperty.call(options, 'cause') && !('cause' in this)) {
22
+ if (options != null && typeof options === 'object' && Object.hasOwn(options, 'cause') && !('cause' in this)) {
23
23
  const {
24
24
  cause
25
25
  } = options;
@@ -4,5 +4,5 @@ exports.__esModule = true;
4
4
  exports.default = void 0;
5
5
  var _JSONPathError = _interopRequireDefault(require("./JSONPathError.cjs"));
6
6
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
7
- class JSONPathCompileError extends _JSONPathError.default {}
8
- var _default = exports.default = JSONPathCompileError;
7
+ class JSONPathEvaluateError extends _JSONPathError.default {}
8
+ var _default = exports.default = JSONPathEvaluateError;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.default = void 0;
5
+ var _literal = _interopRequireDefault(require("./literal.cjs"));
6
+ var _singularQuery = require("./singular-query.cjs");
7
+ var _functionExpr = _interopRequireDefault(require("./function-expr.cjs"));
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ /**
10
+ * Comparable evaluator.
11
+ *
12
+ * Evaluates comparables which can be:
13
+ * - Literal (string, number, boolean, null)
14
+ * - RelSingularQuery (@.path)
15
+ * - AbsSingularQuery ($.path)
16
+ * - FunctionExpr (function call)
17
+ *
18
+ * @see https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.2.2
19
+ */
20
+
21
+ /**
22
+ * Evaluate a comparable expression.
23
+ *
24
+ * @param {object} ctx - Evaluation context
25
+ * @param {unknown} root - Root value ($)
26
+ * @param {unknown} current - Current value (@)
27
+ * @param {object} node - Comparable AST node
28
+ * @returns {unknown} - Evaluated value
29
+ */
30
+ const evaluateComparable = (ctx, root, current, node) => {
31
+ switch (node.type) {
32
+ case 'Literal':
33
+ return (0, _literal.default)(ctx, root, current, node);
34
+ case 'RelSingularQuery':
35
+ return (0, _singularQuery.evaluateRelSingularQuery)(ctx, root, current, node);
36
+ case 'AbsSingularQuery':
37
+ return (0, _singularQuery.evaluateAbsSingularQuery)(ctx, root, current, node);
38
+ case 'FunctionExpr':
39
+ return (0, _functionExpr.default)(ctx, root, current, node);
40
+ default:
41
+ return undefined;
42
+ }
43
+ };
44
+ var _default = exports.default = evaluateComparable;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.default = void 0;
5
+ var _comparable = _interopRequireDefault(require("./comparable.cjs"));
6
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
7
+ /**
8
+ * Comparison expression evaluator.
9
+ *
10
+ * Evaluates comparison expressions like @.price < 10, @.name == "foo", etc.
11
+ *
12
+ * @see https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.2.3
13
+ */
14
+
15
+ /**
16
+ * Evaluate a comparison expression.
17
+ *
18
+ * @param {object} ctx - Evaluation context
19
+ * @param {unknown} root - Root value ($)
20
+ * @param {unknown} current - Current value (@)
21
+ * @param {object} node - ComparisonExpr AST node
22
+ * @param {object} node.left - Left comparable
23
+ * @param {string} node.op - Comparison operator (==, !=, <, <=, >, >=)
24
+ * @param {object} node.right - Right comparable
25
+ * @returns {boolean} - Comparison result
26
+ */
27
+ const evaluateComparisonExpr = (ctx, root, current, node) => {
28
+ const {
29
+ left,
30
+ op,
31
+ right
32
+ } = node;
33
+ const leftValue = (0, _comparable.default)(ctx, root, current, left);
34
+ const rightValue = (0, _comparable.default)(ctx, root, current, right);
35
+ return ctx.realm.compare(leftValue, op, rightValue);
36
+ };
37
+ var _default = exports.default = evaluateComparisonExpr;
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.evaluateRelQuery = exports.evaluateJsonPathQuery = exports.default = void 0;
5
+ var _selector = _interopRequireDefault(require("../visitors/selector.cjs"));
6
+ var _bracketedSelection = _interopRequireDefault(require("../visitors/bracketed-selection.cjs"));
7
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
8
+ /**
9
+ * Filter query evaluator.
10
+ *
11
+ * Evaluates FilterQuery expressions which contain either:
12
+ * - RelQuery (@.path) - relative to current node
13
+ * - JsonPathQuery ($.path) - absolute from root
14
+ *
15
+ * Returns a nodelist (array of matched values).
16
+ *
17
+ * @see https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.2.1
18
+ */
19
+
20
+ /**
21
+ * Apply a segment to a value and collect all results.
22
+ *
23
+ * @param {object} ctx - Evaluation context
24
+ * @param {unknown} value - Current value
25
+ * @param {object} segment - Segment AST node
26
+ * @returns {unknown[]} - Array of matched values
27
+ */
28
+ const applySegment = (ctx, value, segment) => {
29
+ const results = [];
30
+ const emit = selectedValue => {
31
+ results.push(selectedValue);
32
+ };
33
+ const {
34
+ selector
35
+ } = segment;
36
+ switch (selector.type) {
37
+ case 'BracketedSelection':
38
+ (0, _bracketedSelection.default)(ctx, value, selector, emit);
39
+ break;
40
+ case 'NameSelector':
41
+ case 'WildcardSelector':
42
+ case 'IndexSelector':
43
+ case 'SliceSelector':
44
+ case 'FilterSelector':
45
+ (0, _selector.default)(ctx, value, selector, emit);
46
+ break;
47
+ default:
48
+ break;
49
+ }
50
+ return results;
51
+ };
52
+
53
+ /**
54
+ * Apply segments to get nodelist, supporting descendant segments.
55
+ *
56
+ * @param {object} ctx - Evaluation context
57
+ * @param {unknown} root - Root value ($)
58
+ * @param {unknown[]} values - Current nodelist
59
+ * @param {object[]} segments - Remaining segments
60
+ * @returns {unknown[]} - Result nodelist
61
+ */
62
+ const applySegments = (ctx, root, values, segments) => {
63
+ let current = values;
64
+ for (const segment of segments) {
65
+ const next = [];
66
+ if (segment.type === 'DescendantSegment') {
67
+ // Descendant segment: apply to current and all descendants
68
+ const collectDescendants = value => {
69
+ const {
70
+ realm
71
+ } = ctx;
72
+ // Apply selector at current level
73
+ const results = applySegment(ctx, value, segment);
74
+ next.push(...results);
75
+
76
+ // Recurse into children
77
+ for (const [, child] of realm.entries(value)) {
78
+ collectDescendants(child);
79
+ }
80
+ };
81
+ for (const value of current) {
82
+ collectDescendants(value);
83
+ }
84
+ } else {
85
+ // Child segment: apply to current values only
86
+ for (const value of current) {
87
+ const results = applySegment(ctx, value, segment);
88
+ next.push(...results);
89
+ }
90
+ }
91
+ current = next;
92
+ }
93
+ return current;
94
+ };
95
+
96
+ /**
97
+ * Evaluate a RelQuery (@.path).
98
+ *
99
+ * @param {object} ctx - Evaluation context
100
+ * @param {unknown} root - Root value ($)
101
+ * @param {unknown} current - Current value (@)
102
+ * @param {object} node - RelQuery AST node
103
+ * @returns {unknown[]} - Nodelist of matched values
104
+ */
105
+ const evaluateRelQuery = (ctx, root, current, node) => {
106
+ const {
107
+ segments
108
+ } = node;
109
+ if (segments.length === 0) {
110
+ // @ with no segments returns current as single-element nodelist
111
+ return [current];
112
+ }
113
+ return applySegments(ctx, root, [current], segments);
114
+ };
115
+
116
+ /**
117
+ * Evaluate a JsonPathQuery ($.path).
118
+ *
119
+ * @param {object} ctx - Evaluation context
120
+ * @param {unknown} root - Root value ($)
121
+ * @param {unknown} current - Current value (unused)
122
+ * @param {object} node - JsonPathQuery AST node
123
+ * @returns {unknown[]} - Nodelist of matched values
124
+ */
125
+ exports.evaluateRelQuery = evaluateRelQuery;
126
+ const evaluateJsonPathQuery = (ctx, root, current, node) => {
127
+ const {
128
+ segments
129
+ } = node;
130
+ if (segments.length === 0) {
131
+ // $ with no segments returns root as single-element nodelist
132
+ return [root];
133
+ }
134
+ return applySegments(ctx, root, [root], segments);
135
+ };
136
+
137
+ /**
138
+ * Mark an array as a nodelist.
139
+ * This helps functions distinguish nodelists from array values.
140
+ *
141
+ * @param {unknown[]} arr - Array to mark
142
+ * @returns {unknown[]} - Marked array
143
+ */
144
+ exports.evaluateJsonPathQuery = evaluateJsonPathQuery;
145
+ const markAsNodelist = arr => {
146
+ Object.defineProperty(arr, '_isNodelist', {
147
+ value: true,
148
+ enumerable: false,
149
+ writable: false
150
+ });
151
+ return arr;
152
+ };
153
+
154
+ /**
155
+ * Evaluate a FilterQuery.
156
+ *
157
+ * @param {object} ctx - Evaluation context
158
+ * @param {unknown} root - Root value ($)
159
+ * @param {unknown} current - Current value (@)
160
+ * @param {object} node - FilterQuery AST node
161
+ * @returns {unknown[]} - Nodelist of matched values
162
+ */
163
+ const evaluateFilterQuery = (ctx, root, current, node) => {
164
+ const {
165
+ query
166
+ } = node;
167
+ let result;
168
+ switch (query.type) {
169
+ case 'RelQuery':
170
+ result = evaluateRelQuery(ctx, root, current, query);
171
+ break;
172
+ case 'JsonPathQuery':
173
+ result = evaluateJsonPathQuery(ctx, root, current, query);
174
+ break;
175
+ default:
176
+ result = [];
177
+ }
178
+
179
+ // Mark result as a nodelist so functions can handle type coercion
180
+ return markAsNodelist(result);
181
+ };
182
+ var _default = exports.default = evaluateFilterQuery;