@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.
- package/README.md +225 -18
- package/cjs/errors/JSONNormalizedPathError.cjs +8 -0
- package/cjs/errors/JSONPathError.cjs +1 -1
- package/cjs/errors/{JSONPathCompileError.cjs → JSONPathEvaluateError.cjs} +2 -2
- package/cjs/evaluate/evaluators/comparable.cjs +44 -0
- package/cjs/evaluate/evaluators/comparison-expr.cjs +37 -0
- package/cjs/evaluate/evaluators/filter-query.cjs +182 -0
- package/cjs/evaluate/evaluators/function-expr.cjs +106 -0
- package/cjs/evaluate/evaluators/literal.cjs +25 -0
- package/cjs/evaluate/evaluators/logical-expr.cjs +96 -0
- package/cjs/evaluate/evaluators/singular-query.cjs +103 -0
- package/cjs/evaluate/functions/count.cjs +35 -0
- package/cjs/evaluate/functions/index.cjs +15 -0
- package/cjs/evaluate/functions/length.cjs +42 -0
- package/cjs/evaluate/functions/match.cjs +49 -0
- package/cjs/evaluate/functions/search.cjs +49 -0
- package/cjs/evaluate/functions/value.cjs +36 -0
- package/cjs/evaluate/index.cjs +182 -0
- package/cjs/evaluate/realms/EvaluationRealm.cjs +154 -0
- package/cjs/evaluate/realms/json/index.cjs +246 -0
- package/cjs/evaluate/utils/guards.cjs +129 -0
- package/cjs/evaluate/utils/i-regexp.cjs +118 -0
- package/cjs/evaluate/visitors/bracketed-selection.cjs +35 -0
- package/cjs/evaluate/visitors/filter-selector.cjs +43 -0
- package/cjs/evaluate/visitors/index-selector.cjs +55 -0
- package/cjs/evaluate/visitors/name-selector.cjs +38 -0
- package/cjs/evaluate/visitors/segment.cjs +99 -0
- package/cjs/evaluate/visitors/selector.cjs +47 -0
- package/cjs/evaluate/visitors/slice-selector.cjs +115 -0
- package/cjs/evaluate/visitors/wildcard-selector.cjs +32 -0
- package/cjs/index.cjs +16 -7
- package/cjs/normalized-path.cjs +145 -0
- package/cjs/parse/callbacks/cst.cjs +2 -4
- package/cjs/parse/index.cjs +3 -1
- package/cjs/parse/translators/ASTTranslator/index.cjs +1 -1
- package/cjs/parse/translators/ASTTranslator/transformers.cjs +246 -5
- package/cjs/parse/translators/CSTOptimizedTranslator.cjs +1 -3
- package/cjs/parse/translators/CSTTranslator.cjs +1 -2
- package/cjs/test/index.cjs +4 -2
- package/es/errors/JSONNormalizedPathError.mjs +3 -0
- package/es/errors/JSONPathError.mjs +1 -1
- package/es/errors/JSONPathEvaluateError.mjs +3 -0
- package/es/evaluate/evaluators/comparable.mjs +38 -0
- package/es/evaluate/evaluators/comparison-expr.mjs +31 -0
- package/es/evaluate/evaluators/filter-query.mjs +175 -0
- package/es/evaluate/evaluators/function-expr.mjs +99 -0
- package/es/evaluate/evaluators/literal.mjs +21 -0
- package/es/evaluate/evaluators/logical-expr.mjs +89 -0
- package/es/evaluate/evaluators/singular-query.mjs +97 -0
- package/es/evaluate/functions/count.mjs +30 -0
- package/es/evaluate/functions/index.mjs +13 -0
- package/es/evaluate/functions/length.mjs +37 -0
- package/es/evaluate/functions/match.mjs +44 -0
- package/es/evaluate/functions/search.mjs +44 -0
- package/es/evaluate/functions/value.mjs +31 -0
- package/es/evaluate/index.mjs +174 -0
- package/es/evaluate/realms/EvaluationRealm.mjs +148 -0
- package/es/evaluate/realms/json/index.mjs +240 -0
- package/es/evaluate/utils/guards.mjs +114 -0
- package/es/evaluate/utils/i-regexp.mjs +113 -0
- package/es/evaluate/visitors/bracketed-selection.mjs +29 -0
- package/es/evaluate/visitors/filter-selector.mjs +37 -0
- package/es/evaluate/visitors/index-selector.mjs +51 -0
- package/es/evaluate/visitors/name-selector.mjs +34 -0
- package/es/evaluate/visitors/segment.mjs +91 -0
- package/es/evaluate/visitors/selector.mjs +41 -0
- package/es/evaluate/visitors/slice-selector.mjs +111 -0
- package/es/evaluate/visitors/wildcard-selector.mjs +28 -0
- package/es/index.mjs +7 -3
- package/es/normalized-path.mjs +136 -0
- package/es/parse/callbacks/cst.mjs +2 -4
- package/es/parse/index.mjs +3 -1
- package/es/parse/translators/ASTTranslator/index.mjs +1 -1
- package/es/parse/translators/ASTTranslator/transformers.mjs +246 -5
- package/es/parse/translators/CSTOptimizedTranslator.mjs +1 -3
- package/es/parse/translators/CSTTranslator.mjs +1 -2
- package/es/test/index.mjs +4 -2
- package/package.json +4 -2
- package/types/index.d.ts +114 -11
- package/cjs/compile.cjs +0 -50
- package/cjs/escape.cjs +0 -59
- package/es/compile.mjs +0 -45
- package/es/errors/JSONPathCompileError.mjs +0 -3
- package/es/escape.mjs +0 -55
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://npm.runkit.com/@swaggerexpert/jsonpath)
|
|
8
8
|
[](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 **
|
|
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
|
-
- [
|
|
47
|
-
- [
|
|
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`
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
235
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
|
453
|
+
enabling precise error handling across JSONPath operations, including parsing, compilation, and evaluation.
|
|
262
454
|
|
|
263
455
|
```js
|
|
264
|
-
import { JSONPathError, JSONPathParseError,
|
|
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.
|
|
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
|
|
8
|
-
var _default = exports.default =
|
|
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;
|