json-p3 1.2.0 → 1.3.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/dist/json-p3.cjs.js +255 -53
- package/dist/json-p3.esm.js +255 -53
- package/dist/json-p3.iife.js +255 -53
- package/dist/json-p3.iife.min.js +1 -1
- package/dist/json-p3.iife.min.js.map +1 -1
- package/dist/path/extra/selectors.d.ts +20 -0
- package/dist/path/index.d.ts +1 -1
- package/dist/path/lex.d.ts +1 -0
- package/dist/path/node.d.ts +1 -0
- package/dist/path/parse.d.ts +1 -1
- package/dist/path/selectors.d.ts +1 -0
- package/dist/path/token.d.ts +7 -3
- package/dist/path/types.d.ts +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/json-p3.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* json-p3 version 1.
|
|
2
|
+
* json-p3 version 1.3.0
|
|
3
3
|
* https://github.com/jg-rp/json-p3
|
|
4
4
|
*
|
|
5
5
|
* MIT License
|
|
@@ -639,6 +639,24 @@ var index$3 = /*#__PURE__*/Object.freeze({
|
|
|
639
639
|
resolve: resolve
|
|
640
640
|
});
|
|
641
641
|
|
|
642
|
+
const Nothing = Symbol.for("jsonpath.nothing");
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* ValueType for JSONPath function expression tye system.
|
|
646
|
+
*/
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Object passed to FilterExpression.evaluate().
|
|
650
|
+
*/
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* A type predicate for an object with a string property.
|
|
654
|
+
*/
|
|
655
|
+
function hasStringKey(value, key) {
|
|
656
|
+
return isObject(value) && Object.hasOwn(value, key);
|
|
657
|
+
}
|
|
658
|
+
const KEY_MARK = "\x02";
|
|
659
|
+
|
|
642
660
|
/**
|
|
643
661
|
* The pair of a JSON value and its location found in the target JSON value.
|
|
644
662
|
*/
|
|
@@ -656,7 +674,7 @@ class JSONPathNode {
|
|
|
656
674
|
get path() {
|
|
657
675
|
return (
|
|
658
676
|
// eslint-disable-next-line prefer-template
|
|
659
|
-
"$" + this.location.map(s => isString(s) ?
|
|
677
|
+
"$" + this.location.map(s => isString(s) ? this.decode_name_location(s) : `[${s}]`).join("")
|
|
660
678
|
);
|
|
661
679
|
}
|
|
662
680
|
|
|
@@ -669,6 +687,9 @@ class JSONPathNode {
|
|
|
669
687
|
}
|
|
670
688
|
return new JSONPointer(JSONPointer.encode(this.location.map(String)));
|
|
671
689
|
}
|
|
690
|
+
decode_name_location(name) {
|
|
691
|
+
return name.startsWith(KEY_MARK) ? `[~'${name.slice(1).replaceAll("'", "\\'")}']` : `['${name.replaceAll("'", "\\'")}']`;
|
|
692
|
+
}
|
|
672
693
|
}
|
|
673
694
|
|
|
674
695
|
/**
|
|
@@ -748,23 +769,6 @@ class JSONPathNodeList {
|
|
|
748
769
|
}
|
|
749
770
|
}
|
|
750
771
|
|
|
751
|
-
const Nothing = Symbol.for("jsonpath.nothing");
|
|
752
|
-
|
|
753
|
-
/**
|
|
754
|
-
* ValueType for JSONPath function expression tye system.
|
|
755
|
-
*/
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* Object passed to FilterExpression.evaluate().
|
|
759
|
-
*/
|
|
760
|
-
|
|
761
|
-
/**
|
|
762
|
-
* A type predicate for an object with a string property.
|
|
763
|
-
*/
|
|
764
|
-
function hasStringKey(value, key) {
|
|
765
|
-
return isObject(value) && Object.hasOwn(value, key);
|
|
766
|
-
}
|
|
767
|
-
|
|
768
772
|
/**
|
|
769
773
|
* Base class for all filter expressions.
|
|
770
774
|
*/
|
|
@@ -1167,7 +1171,8 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
|
|
|
1167
1171
|
TokenKind["AND"] = "TOKEN_AND";
|
|
1168
1172
|
TokenKind["COLON"] = "TOKEN_COLON";
|
|
1169
1173
|
TokenKind["COMMA"] = "TOKEN_COMMA";
|
|
1170
|
-
TokenKind["CURRENT"] = "
|
|
1174
|
+
TokenKind["CURRENT"] = "TOKEN_CURRENT_VALUE";
|
|
1175
|
+
TokenKind["CURRENT_KEY"] = "TOKEN_CURRENT_KEY";
|
|
1171
1176
|
TokenKind["DDOT"] = "TOKEN_DDOT";
|
|
1172
1177
|
TokenKind["DOT"] = "TOKEN_DOT";
|
|
1173
1178
|
TokenKind["DOUBLE_QUOTE_STRING"] = "TOKEN_DOUBLE_QUOTE_STRING";
|
|
@@ -1181,7 +1186,10 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
|
|
|
1181
1186
|
TokenKind["GT"] = "TOKEN_GT";
|
|
1182
1187
|
TokenKind["INDEX"] = "TOKEN_INDEX";
|
|
1183
1188
|
TokenKind["KEY"] = "TOKEN_KEY";
|
|
1189
|
+
TokenKind["KEY_DOUBLE_QUOTE_STRING"] = "TOKEN_KEY_DOUBLE_QUOTE_STRING";
|
|
1190
|
+
TokenKind["KEY_SINGLE_QUOTE_STRING"] = "TOKEN_KEY_SINGLE_QUOTE_STRING";
|
|
1184
1191
|
TokenKind["KEYS"] = "TOKEN_KEYS";
|
|
1192
|
+
TokenKind["KEYS_FILTER"] = "TOKEN_KEYS_FILTER";
|
|
1185
1193
|
TokenKind["LBRACKET"] = "TOKEN_LBRACKET";
|
|
1186
1194
|
TokenKind["LE"] = "TOKEN_LE";
|
|
1187
1195
|
TokenKind["LG"] = "TOKEN_LG";
|
|
@@ -1262,6 +1270,7 @@ const indexPattern = /-?\d+/y;
|
|
|
1262
1270
|
const intPattern = /-?[0-9]+/y;
|
|
1263
1271
|
const namePattern = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/y;
|
|
1264
1272
|
const whitespace = new Set([" ", "\n", "\t", "\r"]);
|
|
1273
|
+
const nameFirstPattern = /[\u0080-\uFFFFa-zA-Z_]/; // don't set sticky bit
|
|
1265
1274
|
|
|
1266
1275
|
/**
|
|
1267
1276
|
* JSONPath lexical scanner.
|
|
@@ -1334,6 +1343,11 @@ class Lexer {
|
|
|
1334
1343
|
if (ch) this.backup();
|
|
1335
1344
|
return ch;
|
|
1336
1345
|
}
|
|
1346
|
+
peekMatch(pattern) {
|
|
1347
|
+
const ch = this.next();
|
|
1348
|
+
if (ch) this.backup();
|
|
1349
|
+
return pattern.test(ch);
|
|
1350
|
+
}
|
|
1337
1351
|
accept(valid) {
|
|
1338
1352
|
const ch = this.next();
|
|
1339
1353
|
if (valid.has(ch)) return true;
|
|
@@ -1463,10 +1477,28 @@ function lexDescendantSelection(l) {
|
|
|
1463
1477
|
l.emit(TokenKind.NAME);
|
|
1464
1478
|
return lexSegment;
|
|
1465
1479
|
}
|
|
1466
|
-
if (!l.environment.strict
|
|
1467
|
-
//
|
|
1468
|
-
|
|
1469
|
-
|
|
1480
|
+
if (!l.environment.strict) {
|
|
1481
|
+
// We're effectively disabling the _key selector_ and _keys filter selector_ if a
|
|
1482
|
+
// custom _keys selector_ is set.
|
|
1483
|
+
if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
|
|
1484
|
+
l.next();
|
|
1485
|
+
if (l.peekMatch(nameFirstPattern)) {
|
|
1486
|
+
// Non-standard key selector
|
|
1487
|
+
l.ignore(); // ignore ~
|
|
1488
|
+
l.acceptMatchRun(namePattern);
|
|
1489
|
+
l.emit(TokenKind.KEY);
|
|
1490
|
+
return lexSegment;
|
|
1491
|
+
} else {
|
|
1492
|
+
// Non-standard keys selector
|
|
1493
|
+
l.emit(TokenKind.KEYS);
|
|
1494
|
+
return lexSegment;
|
|
1495
|
+
}
|
|
1496
|
+
} else if (l.acceptMatchRun(l.environment.keysPattern)) {
|
|
1497
|
+
// NOTE: A custom keys pattern does not play well with other non-standard key selectors.
|
|
1498
|
+
// We leave this here for backwards compatibility.
|
|
1499
|
+
l.emit(TokenKind.KEYS);
|
|
1500
|
+
return lexSegment;
|
|
1501
|
+
}
|
|
1470
1502
|
}
|
|
1471
1503
|
const ch = l.next();
|
|
1472
1504
|
switch (ch) {
|
|
@@ -1491,9 +1523,28 @@ function lexDotSelector(l) {
|
|
|
1491
1523
|
l.error("unexpected whitespace after dot");
|
|
1492
1524
|
return null;
|
|
1493
1525
|
}
|
|
1494
|
-
if (!l.environment.strict
|
|
1495
|
-
|
|
1496
|
-
|
|
1526
|
+
if (!l.environment.strict) {
|
|
1527
|
+
// We're effectively disabling the _key selector_ and _keys filter selector_ if a
|
|
1528
|
+
// custom _keys selector_ is set.
|
|
1529
|
+
if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
|
|
1530
|
+
l.next();
|
|
1531
|
+
if (l.peekMatch(nameFirstPattern)) {
|
|
1532
|
+
// Non-standard key selector
|
|
1533
|
+
l.ignore(); // ignore ~
|
|
1534
|
+
l.acceptMatchRun(namePattern);
|
|
1535
|
+
l.emit(TokenKind.KEY);
|
|
1536
|
+
return lexSegment;
|
|
1537
|
+
} else {
|
|
1538
|
+
// Non-standard keys selector
|
|
1539
|
+
l.emit(TokenKind.KEYS);
|
|
1540
|
+
return lexSegment;
|
|
1541
|
+
}
|
|
1542
|
+
} else if (l.acceptMatchRun(l.environment.keysPattern)) {
|
|
1543
|
+
// NOTE: A custom keys pattern does not play well with other non-standard key selectors.
|
|
1544
|
+
// We leave this here for backwards compatibility.
|
|
1545
|
+
l.emit(TokenKind.KEYS);
|
|
1546
|
+
return lexSegment;
|
|
1547
|
+
}
|
|
1497
1548
|
}
|
|
1498
1549
|
if (l.acceptMatchRun(namePattern)) {
|
|
1499
1550
|
l.emit(TokenKind.NAME);
|
|
@@ -1516,8 +1567,25 @@ function lexInsideBracketedSelection(l) {
|
|
|
1516
1567
|
continue;
|
|
1517
1568
|
}
|
|
1518
1569
|
if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
|
|
1519
|
-
l.
|
|
1520
|
-
|
|
1570
|
+
switch (l.peek()) {
|
|
1571
|
+
case "'":
|
|
1572
|
+
l.ignore(); // ~
|
|
1573
|
+
l.next();
|
|
1574
|
+
return lexSingleQuoteKeyString(l);
|
|
1575
|
+
case '"':
|
|
1576
|
+
l.ignore(); // ~
|
|
1577
|
+
l.next();
|
|
1578
|
+
return lexDoubleQuoteKeyString(l);
|
|
1579
|
+
case "?":
|
|
1580
|
+
l.ignore(); // ~
|
|
1581
|
+
l.next();
|
|
1582
|
+
l.emit(TokenKind.KEYS_FILTER);
|
|
1583
|
+
l.filterLevel += 1;
|
|
1584
|
+
return lexInsideFilter;
|
|
1585
|
+
default:
|
|
1586
|
+
l.emit(TokenKind.KEYS);
|
|
1587
|
+
continue;
|
|
1588
|
+
}
|
|
1521
1589
|
}
|
|
1522
1590
|
const ch = l.next();
|
|
1523
1591
|
switch (ch) {
|
|
@@ -1604,7 +1672,7 @@ function lexInsideFilter(l) {
|
|
|
1604
1672
|
l.emit(TokenKind.CURRENT);
|
|
1605
1673
|
return lexSegment;
|
|
1606
1674
|
case "#":
|
|
1607
|
-
l.emit(TokenKind.
|
|
1675
|
+
l.emit(TokenKind.CURRENT_KEY);
|
|
1608
1676
|
return lexSegment;
|
|
1609
1677
|
case ".":
|
|
1610
1678
|
l.backup();
|
|
@@ -1704,8 +1772,7 @@ function lexInsideFilter(l) {
|
|
|
1704
1772
|
* @param state - The state function to return control to.
|
|
1705
1773
|
* @returns String tokenizing state function.
|
|
1706
1774
|
*/
|
|
1707
|
-
function makeLexString(quote, state) {
|
|
1708
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
1775
|
+
function makeLexString(quote, state, token_kind) {
|
|
1709
1776
|
function _lexString(l) {
|
|
1710
1777
|
l.ignore();
|
|
1711
1778
|
if (l.peek() === quote) {
|
|
@@ -1731,7 +1798,7 @@ function makeLexString(quote, state) {
|
|
|
1731
1798
|
}
|
|
1732
1799
|
if (ch === quote) {
|
|
1733
1800
|
l.backup();
|
|
1734
|
-
l.emit(
|
|
1801
|
+
l.emit(token_kind);
|
|
1735
1802
|
l.next();
|
|
1736
1803
|
l.ignore();
|
|
1737
1804
|
return state;
|
|
@@ -1740,10 +1807,12 @@ function makeLexString(quote, state) {
|
|
|
1740
1807
|
}
|
|
1741
1808
|
return _lexString;
|
|
1742
1809
|
}
|
|
1743
|
-
const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection);
|
|
1744
|
-
const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection);
|
|
1745
|
-
const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter);
|
|
1746
|
-
const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter);
|
|
1810
|
+
const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection, TokenKind.SINGLE_QUOTE_STRING);
|
|
1811
|
+
const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection, TokenKind.DOUBLE_QUOTE_STRING);
|
|
1812
|
+
const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter, TokenKind.SINGLE_QUOTE_STRING);
|
|
1813
|
+
const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter, TokenKind.DOUBLE_QUOTE_STRING);
|
|
1814
|
+
const lexSingleQuoteKeyString = makeLexString("'", lexInsideBracketedSelection, TokenKind.KEY_SINGLE_QUOTE_STRING);
|
|
1815
|
+
const lexDoubleQuoteKeyString = makeLexString('"', lexInsideBracketedSelection, TokenKind.KEY_DOUBLE_QUOTE_STRING);
|
|
1747
1816
|
|
|
1748
1817
|
/**
|
|
1749
1818
|
* Base class for all JSONPath segments and selectors.
|
|
@@ -1784,7 +1853,7 @@ class NameSelector extends JSONPathSelector {
|
|
|
1784
1853
|
resolve(nodes) {
|
|
1785
1854
|
const rv = [];
|
|
1786
1855
|
for (const node of nodes) {
|
|
1787
|
-
if (hasStringKey(node.value, this.name)) {
|
|
1856
|
+
if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
|
|
1788
1857
|
rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
|
|
1789
1858
|
}
|
|
1790
1859
|
}
|
|
@@ -1792,7 +1861,7 @@ class NameSelector extends JSONPathSelector {
|
|
|
1792
1861
|
}
|
|
1793
1862
|
*lazyResolve(nodes) {
|
|
1794
1863
|
for (const node of nodes) {
|
|
1795
|
-
if (hasStringKey(node.value, this.name)) {
|
|
1864
|
+
if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
|
|
1796
1865
|
yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
|
|
1797
1866
|
}
|
|
1798
1867
|
}
|
|
@@ -1868,7 +1937,7 @@ class SliceSelector extends JSONPathSelector {
|
|
|
1868
1937
|
*lazyResolve(nodes) {
|
|
1869
1938
|
for (const node of nodes) {
|
|
1870
1939
|
if (!isArray(node.value)) continue;
|
|
1871
|
-
for (const [i, value] of this.
|
|
1940
|
+
for (const [i, value] of this.lazySlice(node.value, this.start, this.stop, this.step)) {
|
|
1872
1941
|
yield new JSONPathNode(value, node.location.concat(i), node.root);
|
|
1873
1942
|
}
|
|
1874
1943
|
}
|
|
@@ -1931,6 +2000,45 @@ class SliceSelector extends JSONPathSelector {
|
|
|
1931
2000
|
}
|
|
1932
2001
|
return slicedArray;
|
|
1933
2002
|
}
|
|
2003
|
+
|
|
2004
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
2005
|
+
*lazySlice(arr, start, stop, step) {
|
|
2006
|
+
if (!arr.length) return;
|
|
2007
|
+
|
|
2008
|
+
// Handle negative and undefined start values
|
|
2009
|
+
if (start === undefined || start === null) {
|
|
2010
|
+
start = step && step < 0 ? arr.length - 1 : 0;
|
|
2011
|
+
} else if (start < 0) {
|
|
2012
|
+
start = Math.max(arr.length + start, 0);
|
|
2013
|
+
} else {
|
|
2014
|
+
start = Math.min(start, arr.length - 1);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// Handle negative and undefined stop values
|
|
2018
|
+
if (stop === undefined || stop === null) {
|
|
2019
|
+
stop = step && step < 0 ? -1 : arr.length;
|
|
2020
|
+
} else if (stop < 0) {
|
|
2021
|
+
stop = Math.max(arr.length + stop, -1);
|
|
2022
|
+
} else {
|
|
2023
|
+
stop = Math.min(stop, arr.length);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// Perform the slice
|
|
2027
|
+
if (step === undefined) {
|
|
2028
|
+
// Default to a step of 1
|
|
2029
|
+
for (let i = start; i < stop; i += 1) {
|
|
2030
|
+
yield [i, arr[i]];
|
|
2031
|
+
}
|
|
2032
|
+
} else if (step > 0) {
|
|
2033
|
+
for (let i = start; i < stop; i += step) {
|
|
2034
|
+
yield [i, arr[i]];
|
|
2035
|
+
}
|
|
2036
|
+
} else if (step < 0) {
|
|
2037
|
+
for (let i = start; i > stop; i += step) {
|
|
2038
|
+
yield [i, arr[i]];
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
1934
2042
|
}
|
|
1935
2043
|
class WildcardSelector extends JSONPathSelector {
|
|
1936
2044
|
constructor(environment, token) {
|
|
@@ -1997,8 +2105,6 @@ class RecursiveDescentSegment extends JSONPathSelector {
|
|
|
1997
2105
|
}
|
|
1998
2106
|
}
|
|
1999
2107
|
}
|
|
2000
|
-
|
|
2001
|
-
// console.log(JSON.stringify(rv.map((n: any) => n.value)));
|
|
2002
2108
|
return this.selector.resolve(rv);
|
|
2003
2109
|
}
|
|
2004
2110
|
*lazyResolve(nodes) {
|
|
@@ -2355,6 +2461,38 @@ class CurrentKey extends FilterExpression {
|
|
|
2355
2461
|
}
|
|
2356
2462
|
}
|
|
2357
2463
|
|
|
2464
|
+
class KeySelector extends JSONPathSelector {
|
|
2465
|
+
constructor(environment, token, key) {
|
|
2466
|
+
let shorthand = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
2467
|
+
super(environment, token);
|
|
2468
|
+
this.environment = environment;
|
|
2469
|
+
this.token = token;
|
|
2470
|
+
this.key = key;
|
|
2471
|
+
this.shorthand = shorthand;
|
|
2472
|
+
}
|
|
2473
|
+
resolve(nodes) {
|
|
2474
|
+
const rv = [];
|
|
2475
|
+
for (const node of nodes) {
|
|
2476
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2477
|
+
if (isObject(node.value) && hasStringKey(node.value, this.key)) {
|
|
2478
|
+
rv.push(new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root));
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
return rv;
|
|
2482
|
+
}
|
|
2483
|
+
*lazyResolve(nodes) {
|
|
2484
|
+
for (const node of nodes) {
|
|
2485
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2486
|
+
if (isObject(node.value) && hasStringKey(node.value, this.key)) {
|
|
2487
|
+
yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
toString() {
|
|
2492
|
+
return this.shorthand ? `[~'${this.key.replaceAll("'", "\\'")}']` : `~'${this.key.replaceAll("'", "\\'")}'`;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2358
2496
|
/**
|
|
2359
2497
|
* Object property name selector or array index selector.
|
|
2360
2498
|
*/
|
|
@@ -2371,10 +2509,8 @@ class KeysSelector extends JSONPathSelector {
|
|
|
2371
2509
|
for (const node of nodes) {
|
|
2372
2510
|
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2373
2511
|
if (isObject(node.value)) {
|
|
2374
|
-
let i = 0;
|
|
2375
2512
|
for (const [key, _] of this.environment.entries(node.value)) {
|
|
2376
|
-
rv.push(new JSONPathNode(key, node.location.concat(
|
|
2377
|
-
i++;
|
|
2513
|
+
rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
|
|
2378
2514
|
}
|
|
2379
2515
|
}
|
|
2380
2516
|
}
|
|
@@ -2384,10 +2520,8 @@ class KeysSelector extends JSONPathSelector {
|
|
|
2384
2520
|
for (const node of nodes) {
|
|
2385
2521
|
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2386
2522
|
if (isObject(node.value)) {
|
|
2387
|
-
let i = 0;
|
|
2388
2523
|
for (const [key, _] of this.environment.entries(node.value)) {
|
|
2389
|
-
yield new JSONPathNode(key, node.location.concat(
|
|
2390
|
-
i++;
|
|
2524
|
+
yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
|
|
2391
2525
|
}
|
|
2392
2526
|
}
|
|
2393
2527
|
}
|
|
@@ -2396,6 +2530,56 @@ class KeysSelector extends JSONPathSelector {
|
|
|
2396
2530
|
return this.shorthand ? "[~]" : "~";
|
|
2397
2531
|
}
|
|
2398
2532
|
}
|
|
2533
|
+
class KeysFilterSelector extends JSONPathSelector {
|
|
2534
|
+
constructor(environment, token, expression) {
|
|
2535
|
+
super(environment, token);
|
|
2536
|
+
this.environment = environment;
|
|
2537
|
+
this.token = token;
|
|
2538
|
+
this.expression = expression;
|
|
2539
|
+
}
|
|
2540
|
+
resolve(nodes) {
|
|
2541
|
+
const rv = [];
|
|
2542
|
+
for (const node of nodes) {
|
|
2543
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2544
|
+
if (isObject(node.value)) {
|
|
2545
|
+
for (const [key, value] of this.environment.entries(node.value)) {
|
|
2546
|
+
const filterContext = {
|
|
2547
|
+
environment: this.environment,
|
|
2548
|
+
currentValue: value,
|
|
2549
|
+
rootValue: node.root,
|
|
2550
|
+
currentKey: key
|
|
2551
|
+
};
|
|
2552
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2553
|
+
rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
return rv;
|
|
2559
|
+
}
|
|
2560
|
+
*lazyResolve(nodes) {
|
|
2561
|
+
for (const node of nodes) {
|
|
2562
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2563
|
+
if (isObject(node.value)) {
|
|
2564
|
+
for (const [key, value] of this.environment.entries(node.value)) {
|
|
2565
|
+
const filterContext = {
|
|
2566
|
+
environment: this.environment,
|
|
2567
|
+
currentValue: value,
|
|
2568
|
+
rootValue: node.root,
|
|
2569
|
+
lazy: true,
|
|
2570
|
+
currentKey: key
|
|
2571
|
+
};
|
|
2572
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2573
|
+
yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
toString() {
|
|
2580
|
+
return `~?${this.expression.toString()}`;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2399
2583
|
|
|
2400
2584
|
const PRECEDENCE_LOWEST = 1;
|
|
2401
2585
|
const PRECEDENCE_LOGICAL_OR = 4;
|
|
@@ -2412,7 +2596,7 @@ const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
|
|
|
2412
2596
|
class Parser {
|
|
2413
2597
|
constructor(environment) {
|
|
2414
2598
|
this.environment = environment;
|
|
2415
|
-
this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction], [TokenKind.
|
|
2599
|
+
this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction], [TokenKind.CURRENT_KEY, this.parseCurrentKey]]);
|
|
2416
2600
|
}
|
|
2417
2601
|
parse(stream) {
|
|
2418
2602
|
if (stream.current.kind === TokenKind.ROOT) stream.next();
|
|
@@ -2444,6 +2628,8 @@ class Parser {
|
|
|
2444
2628
|
return new NameSelector(this.environment, stream.current, stream.current.value, true);
|
|
2445
2629
|
case TokenKind.WILD:
|
|
2446
2630
|
return new WildcardSelector(this.environment, stream.current, true);
|
|
2631
|
+
case TokenKind.KEY:
|
|
2632
|
+
return new KeySelector(this.environment, stream.current, stream.current.value, true);
|
|
2447
2633
|
case TokenKind.KEYS:
|
|
2448
2634
|
return new KeysSelector(this.environment, stream.current, true);
|
|
2449
2635
|
case TokenKind.DDOT:
|
|
@@ -2539,6 +2725,13 @@ class Parser {
|
|
|
2539
2725
|
case TokenKind.WILD:
|
|
2540
2726
|
items.push(new WildcardSelector(this.environment, stream.current));
|
|
2541
2727
|
break;
|
|
2728
|
+
case TokenKind.KEY_SINGLE_QUOTE_STRING:
|
|
2729
|
+
case TokenKind.KEY_DOUBLE_QUOTE_STRING:
|
|
2730
|
+
items.push(new KeySelector(this.environment, stream.current, this.decodeString(stream.current, true), false));
|
|
2731
|
+
break;
|
|
2732
|
+
case TokenKind.KEYS_FILTER:
|
|
2733
|
+
items.push(this.parseFilter(stream, true));
|
|
2734
|
+
break;
|
|
2542
2735
|
case TokenKind.KEYS:
|
|
2543
2736
|
items.push(new KeysSelector(this.environment, stream.current));
|
|
2544
2737
|
break;
|
|
@@ -2559,6 +2752,7 @@ class Parser {
|
|
|
2559
2752
|
return new BracketedSelection(this.environment, token, items);
|
|
2560
2753
|
}
|
|
2561
2754
|
parseFilter(stream) {
|
|
2755
|
+
let keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
2562
2756
|
const tok = stream.next();
|
|
2563
2757
|
const expr = this.parseFilterExpression(stream);
|
|
2564
2758
|
if (expr instanceof FunctionExtension) {
|
|
@@ -2567,7 +2761,7 @@ class Parser {
|
|
|
2567
2761
|
throw new JSONPathTypeError(`result of ${expr.name}() must be compared`, expr.token);
|
|
2568
2762
|
}
|
|
2569
2763
|
}
|
|
2570
|
-
return new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
|
|
2764
|
+
return keys ? new KeysFilterSelector(this.environment, tok, new LogicalExpression(tok, expr)) : new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
|
|
2571
2765
|
}
|
|
2572
2766
|
parseBoolean(stream) {
|
|
2573
2767
|
if (stream.current.kind === TokenKind.FALSE) return new BooleanLiteral(stream.current, false);
|
|
@@ -2602,13 +2796,20 @@ class Parser {
|
|
|
2602
2796
|
return new InfixExpression(tok, left, operator, right);
|
|
2603
2797
|
}
|
|
2604
2798
|
parseGroupedExpression(stream) {
|
|
2605
|
-
stream.
|
|
2799
|
+
if (stream.peek.kind === TokenKind.RPAREN) {
|
|
2800
|
+
throw new JSONPathSyntaxError(`empty paren expression`, stream.current);
|
|
2801
|
+
}
|
|
2802
|
+
stream.next(); // eat open paren
|
|
2803
|
+
|
|
2606
2804
|
let expr = this.parseFilterExpression(stream);
|
|
2607
2805
|
stream.next();
|
|
2608
2806
|
while (stream.current.kind !== TokenKind.RPAREN) {
|
|
2609
2807
|
if (stream.current.kind === TokenKind.EOF) {
|
|
2610
2808
|
throw new JSONPathSyntaxError("unbalanced parentheses", stream.current);
|
|
2611
2809
|
}
|
|
2810
|
+
if (!BINARY_OPERATORS.has(stream.current.kind)) {
|
|
2811
|
+
throw new JSONPathSyntaxError(`expected an expression, found '${stream.current.value}'`, stream.current);
|
|
2812
|
+
}
|
|
2612
2813
|
expr = this.parseInfixExpression(stream, expr);
|
|
2613
2814
|
}
|
|
2614
2815
|
stream.expect(TokenKind.RPAREN);
|
|
@@ -2664,7 +2865,7 @@ class Parser {
|
|
|
2664
2865
|
msg = "end of expression";
|
|
2665
2866
|
break;
|
|
2666
2867
|
default:
|
|
2667
|
-
msg = `'${stream.current.value}`;
|
|
2868
|
+
msg = `'${stream.current.value}'`;
|
|
2668
2869
|
}
|
|
2669
2870
|
throw new JSONPathSyntaxError(`unexpected ${msg}`, stream.current);
|
|
2670
2871
|
}
|
|
@@ -2988,6 +3189,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
2988
3189
|
JSONPathRecursionLimitError: JSONPathRecursionLimitError,
|
|
2989
3190
|
JSONPathSyntaxError: JSONPathSyntaxError,
|
|
2990
3191
|
JSONPathTypeError: JSONPathTypeError,
|
|
3192
|
+
KEY_MARK: KEY_MARK,
|
|
2991
3193
|
Nothing: Nothing,
|
|
2992
3194
|
Token: Token,
|
|
2993
3195
|
TokenKind: TokenKind,
|
|
@@ -3480,6 +3682,6 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3480
3682
|
apply: apply
|
|
3481
3683
|
});
|
|
3482
3684
|
|
|
3483
|
-
const version = "1.
|
|
3685
|
+
const version = "1.3.0";
|
|
3484
3686
|
|
|
3485
3687
|
export { DEFAULT_ENVIRONMENT, FunctionExpressionType, JSONPatch, JSONPatchError, JSONPatchTestFailure, JSONPath, JSONPathEnvironment, JSONPathError, JSONPathIndexError, JSONPathLexerError, JSONPathNode, JSONPathNodeList, JSONPathRecursionLimitError, JSONPathSyntaxError, JSONPathTypeError, JSONPointer, Nothing, RelativeJSONPointer, Token, TokenKind, UNDEFINED, apply, compile, index as jsonpatch, index$1 as jsonpath, index$3 as jsonpointer, lazyQuery, query, resolve, version };
|