json-p3 1.2.1 → 1.3.1
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 +290 -50
- package/dist/json-p3.esm.js +290 -50
- package/dist/json-p3.iife.js +290 -50
- 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/functions/match.d.ts +1 -0
- package/dist/path/functions/search.d.ts +1 -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/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.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* json-p3 version 1.
|
|
2
|
+
* json-p3 version 1.3.1
|
|
3
3
|
* https://github.com/jg-rp/json-p3
|
|
4
4
|
*
|
|
5
5
|
* MIT License
|
|
@@ -641,6 +641,24 @@ var index$3 = /*#__PURE__*/Object.freeze({
|
|
|
641
641
|
resolve: resolve
|
|
642
642
|
});
|
|
643
643
|
|
|
644
|
+
const Nothing = Symbol.for("jsonpath.nothing");
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* ValueType for JSONPath function expression tye system.
|
|
648
|
+
*/
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Object passed to FilterExpression.evaluate().
|
|
652
|
+
*/
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* A type predicate for an object with a string property.
|
|
656
|
+
*/
|
|
657
|
+
function hasStringKey(value, key) {
|
|
658
|
+
return isObject(value) && Object.hasOwn(value, key);
|
|
659
|
+
}
|
|
660
|
+
const KEY_MARK = "\x02";
|
|
661
|
+
|
|
644
662
|
/**
|
|
645
663
|
* The pair of a JSON value and its location found in the target JSON value.
|
|
646
664
|
*/
|
|
@@ -658,7 +676,7 @@ class JSONPathNode {
|
|
|
658
676
|
get path() {
|
|
659
677
|
return (
|
|
660
678
|
// eslint-disable-next-line prefer-template
|
|
661
|
-
"$" + this.location.map(s => isString(s) ?
|
|
679
|
+
"$" + this.location.map(s => isString(s) ? this.decode_name_location(s) : `[${s}]`).join("")
|
|
662
680
|
);
|
|
663
681
|
}
|
|
664
682
|
|
|
@@ -671,6 +689,9 @@ class JSONPathNode {
|
|
|
671
689
|
}
|
|
672
690
|
return new JSONPointer(JSONPointer.encode(this.location.map(String)));
|
|
673
691
|
}
|
|
692
|
+
decode_name_location(name) {
|
|
693
|
+
return name.startsWith(KEY_MARK) ? `[~'${name.slice(1).replaceAll("'", "\\'")}']` : `['${name.replaceAll("'", "\\'")}']`;
|
|
694
|
+
}
|
|
674
695
|
}
|
|
675
696
|
|
|
676
697
|
/**
|
|
@@ -750,23 +771,6 @@ class JSONPathNodeList {
|
|
|
750
771
|
}
|
|
751
772
|
}
|
|
752
773
|
|
|
753
|
-
const Nothing = Symbol.for("jsonpath.nothing");
|
|
754
|
-
|
|
755
|
-
/**
|
|
756
|
-
* ValueType for JSONPath function expression tye system.
|
|
757
|
-
*/
|
|
758
|
-
|
|
759
|
-
/**
|
|
760
|
-
* Object passed to FilterExpression.evaluate().
|
|
761
|
-
*/
|
|
762
|
-
|
|
763
|
-
/**
|
|
764
|
-
* A type predicate for an object with a string property.
|
|
765
|
-
*/
|
|
766
|
-
function hasStringKey(value, key) {
|
|
767
|
-
return isObject(value) && Object.hasOwn(value, key);
|
|
768
|
-
}
|
|
769
|
-
|
|
770
774
|
/**
|
|
771
775
|
* Base class for all filter expressions.
|
|
772
776
|
*/
|
|
@@ -1112,9 +1116,53 @@ class Match {
|
|
|
1112
1116
|
}
|
|
1113
1117
|
fullMatch(pattern) {
|
|
1114
1118
|
const parts = [];
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1119
|
+
let nonCaptureGroup = false;
|
|
1120
|
+
if (!pattern.startsWith("^") && !pattern.startsWith("^(")) {
|
|
1121
|
+
nonCaptureGroup = true;
|
|
1122
|
+
parts.push("^(?:");
|
|
1123
|
+
}
|
|
1124
|
+
parts.push(this.mapRegexp(pattern));
|
|
1125
|
+
if (nonCaptureGroup && !pattern.endsWith("$") && !pattern.endsWith(")$")) {
|
|
1126
|
+
parts.push(")$");
|
|
1127
|
+
}
|
|
1128
|
+
return parts.join("");
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// See https://datatracker.ietf.org/doc/html/rfc9485#name-ecmascript-regexps
|
|
1132
|
+
mapRegexp(pattern) {
|
|
1133
|
+
let escaped = false;
|
|
1134
|
+
let charClass = false;
|
|
1135
|
+
const parts = [];
|
|
1136
|
+
for (const ch of pattern) {
|
|
1137
|
+
switch (ch) {
|
|
1138
|
+
case ".":
|
|
1139
|
+
if (!escaped && !charClass) {
|
|
1140
|
+
parts.push("(?:(?![\r\n])\\P{Cs}|\\p{Cs}\\p{Cs})");
|
|
1141
|
+
} else {
|
|
1142
|
+
parts.push(ch);
|
|
1143
|
+
escaped = false;
|
|
1144
|
+
}
|
|
1145
|
+
break;
|
|
1146
|
+
case "\\":
|
|
1147
|
+
escaped = true;
|
|
1148
|
+
parts.push(ch);
|
|
1149
|
+
break;
|
|
1150
|
+
case "[":
|
|
1151
|
+
charClass = true;
|
|
1152
|
+
escaped = false;
|
|
1153
|
+
parts.push(ch);
|
|
1154
|
+
break;
|
|
1155
|
+
case "]":
|
|
1156
|
+
charClass = false;
|
|
1157
|
+
escaped = false;
|
|
1158
|
+
parts.push(ch);
|
|
1159
|
+
break;
|
|
1160
|
+
default:
|
|
1161
|
+
escaped = false;
|
|
1162
|
+
parts.push(ch);
|
|
1163
|
+
break;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1118
1166
|
return parts.join("");
|
|
1119
1167
|
}
|
|
1120
1168
|
}
|
|
@@ -1143,7 +1191,7 @@ class Search {
|
|
|
1143
1191
|
}
|
|
1144
1192
|
}
|
|
1145
1193
|
try {
|
|
1146
|
-
const re = new RegExp(pattern, "u");
|
|
1194
|
+
const re = new RegExp(this.mapRegexp(pattern), "u");
|
|
1147
1195
|
if (this.cacheSize > 0) this.#cache.set(pattern, re);
|
|
1148
1196
|
return !!s.match(re);
|
|
1149
1197
|
} catch (error) {
|
|
@@ -1151,6 +1199,44 @@ class Search {
|
|
|
1151
1199
|
return false;
|
|
1152
1200
|
}
|
|
1153
1201
|
}
|
|
1202
|
+
|
|
1203
|
+
// See https://datatracker.ietf.org/doc/html/rfc9485#name-ecmascript-regexps
|
|
1204
|
+
mapRegexp(pattern) {
|
|
1205
|
+
let escaped = false;
|
|
1206
|
+
let charClass = false;
|
|
1207
|
+
const parts = [];
|
|
1208
|
+
for (const ch of pattern) {
|
|
1209
|
+
switch (ch) {
|
|
1210
|
+
case ".":
|
|
1211
|
+
if (!escaped && !charClass) {
|
|
1212
|
+
parts.push("(?:(?![\r\n])\\P{Cs}|\\p{Cs}\\p{Cs})");
|
|
1213
|
+
} else {
|
|
1214
|
+
parts.push(ch);
|
|
1215
|
+
escaped = false;
|
|
1216
|
+
}
|
|
1217
|
+
break;
|
|
1218
|
+
case "\\":
|
|
1219
|
+
escaped = true;
|
|
1220
|
+
parts.push(ch);
|
|
1221
|
+
break;
|
|
1222
|
+
case "[":
|
|
1223
|
+
charClass = true;
|
|
1224
|
+
escaped = false;
|
|
1225
|
+
parts.push(ch);
|
|
1226
|
+
break;
|
|
1227
|
+
case "]":
|
|
1228
|
+
charClass = false;
|
|
1229
|
+
escaped = false;
|
|
1230
|
+
parts.push(ch);
|
|
1231
|
+
break;
|
|
1232
|
+
default:
|
|
1233
|
+
escaped = false;
|
|
1234
|
+
parts.push(ch);
|
|
1235
|
+
break;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
return parts.join("");
|
|
1239
|
+
}
|
|
1154
1240
|
}
|
|
1155
1241
|
|
|
1156
1242
|
class Value {
|
|
@@ -1169,7 +1255,8 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
|
|
|
1169
1255
|
TokenKind["AND"] = "TOKEN_AND";
|
|
1170
1256
|
TokenKind["COLON"] = "TOKEN_COLON";
|
|
1171
1257
|
TokenKind["COMMA"] = "TOKEN_COMMA";
|
|
1172
|
-
TokenKind["CURRENT"] = "
|
|
1258
|
+
TokenKind["CURRENT"] = "TOKEN_CURRENT_VALUE";
|
|
1259
|
+
TokenKind["CURRENT_KEY"] = "TOKEN_CURRENT_KEY";
|
|
1173
1260
|
TokenKind["DDOT"] = "TOKEN_DDOT";
|
|
1174
1261
|
TokenKind["DOT"] = "TOKEN_DOT";
|
|
1175
1262
|
TokenKind["DOUBLE_QUOTE_STRING"] = "TOKEN_DOUBLE_QUOTE_STRING";
|
|
@@ -1183,7 +1270,10 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
|
|
|
1183
1270
|
TokenKind["GT"] = "TOKEN_GT";
|
|
1184
1271
|
TokenKind["INDEX"] = "TOKEN_INDEX";
|
|
1185
1272
|
TokenKind["KEY"] = "TOKEN_KEY";
|
|
1273
|
+
TokenKind["KEY_DOUBLE_QUOTE_STRING"] = "TOKEN_KEY_DOUBLE_QUOTE_STRING";
|
|
1274
|
+
TokenKind["KEY_SINGLE_QUOTE_STRING"] = "TOKEN_KEY_SINGLE_QUOTE_STRING";
|
|
1186
1275
|
TokenKind["KEYS"] = "TOKEN_KEYS";
|
|
1276
|
+
TokenKind["KEYS_FILTER"] = "TOKEN_KEYS_FILTER";
|
|
1187
1277
|
TokenKind["LBRACKET"] = "TOKEN_LBRACKET";
|
|
1188
1278
|
TokenKind["LE"] = "TOKEN_LE";
|
|
1189
1279
|
TokenKind["LG"] = "TOKEN_LG";
|
|
@@ -1264,6 +1354,7 @@ const indexPattern = /-?\d+/y;
|
|
|
1264
1354
|
const intPattern = /-?[0-9]+/y;
|
|
1265
1355
|
const namePattern = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/y;
|
|
1266
1356
|
const whitespace = new Set([" ", "\n", "\t", "\r"]);
|
|
1357
|
+
const nameFirstPattern = /[\u0080-\uFFFFa-zA-Z_]/; // don't set sticky bit
|
|
1267
1358
|
|
|
1268
1359
|
/**
|
|
1269
1360
|
* JSONPath lexical scanner.
|
|
@@ -1336,6 +1427,11 @@ class Lexer {
|
|
|
1336
1427
|
if (ch) this.backup();
|
|
1337
1428
|
return ch;
|
|
1338
1429
|
}
|
|
1430
|
+
peekMatch(pattern) {
|
|
1431
|
+
const ch = this.next();
|
|
1432
|
+
if (ch) this.backup();
|
|
1433
|
+
return pattern.test(ch);
|
|
1434
|
+
}
|
|
1339
1435
|
accept(valid) {
|
|
1340
1436
|
const ch = this.next();
|
|
1341
1437
|
if (valid.has(ch)) return true;
|
|
@@ -1465,10 +1561,28 @@ function lexDescendantSelection(l) {
|
|
|
1465
1561
|
l.emit(TokenKind.NAME);
|
|
1466
1562
|
return lexSegment;
|
|
1467
1563
|
}
|
|
1468
|
-
if (!l.environment.strict
|
|
1469
|
-
//
|
|
1470
|
-
|
|
1471
|
-
|
|
1564
|
+
if (!l.environment.strict) {
|
|
1565
|
+
// We're effectively disabling the _key selector_ and _keys filter selector_ if a
|
|
1566
|
+
// custom _keys selector_ is set.
|
|
1567
|
+
if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
|
|
1568
|
+
l.next();
|
|
1569
|
+
if (l.peekMatch(nameFirstPattern)) {
|
|
1570
|
+
// Non-standard key selector
|
|
1571
|
+
l.ignore(); // ignore ~
|
|
1572
|
+
l.acceptMatchRun(namePattern);
|
|
1573
|
+
l.emit(TokenKind.KEY);
|
|
1574
|
+
return lexSegment;
|
|
1575
|
+
} else {
|
|
1576
|
+
// Non-standard keys selector
|
|
1577
|
+
l.emit(TokenKind.KEYS);
|
|
1578
|
+
return lexSegment;
|
|
1579
|
+
}
|
|
1580
|
+
} else if (l.acceptMatchRun(l.environment.keysPattern)) {
|
|
1581
|
+
// NOTE: A custom keys pattern does not play well with other non-standard key selectors.
|
|
1582
|
+
// We leave this here for backwards compatibility.
|
|
1583
|
+
l.emit(TokenKind.KEYS);
|
|
1584
|
+
return lexSegment;
|
|
1585
|
+
}
|
|
1472
1586
|
}
|
|
1473
1587
|
const ch = l.next();
|
|
1474
1588
|
switch (ch) {
|
|
@@ -1493,9 +1607,28 @@ function lexDotSelector(l) {
|
|
|
1493
1607
|
l.error("unexpected whitespace after dot");
|
|
1494
1608
|
return null;
|
|
1495
1609
|
}
|
|
1496
|
-
if (!l.environment.strict
|
|
1497
|
-
|
|
1498
|
-
|
|
1610
|
+
if (!l.environment.strict) {
|
|
1611
|
+
// We're effectively disabling the _key selector_ and _keys filter selector_ if a
|
|
1612
|
+
// custom _keys selector_ is set.
|
|
1613
|
+
if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
|
|
1614
|
+
l.next();
|
|
1615
|
+
if (l.peekMatch(nameFirstPattern)) {
|
|
1616
|
+
// Non-standard key selector
|
|
1617
|
+
l.ignore(); // ignore ~
|
|
1618
|
+
l.acceptMatchRun(namePattern);
|
|
1619
|
+
l.emit(TokenKind.KEY);
|
|
1620
|
+
return lexSegment;
|
|
1621
|
+
} else {
|
|
1622
|
+
// Non-standard keys selector
|
|
1623
|
+
l.emit(TokenKind.KEYS);
|
|
1624
|
+
return lexSegment;
|
|
1625
|
+
}
|
|
1626
|
+
} else if (l.acceptMatchRun(l.environment.keysPattern)) {
|
|
1627
|
+
// NOTE: A custom keys pattern does not play well with other non-standard key selectors.
|
|
1628
|
+
// We leave this here for backwards compatibility.
|
|
1629
|
+
l.emit(TokenKind.KEYS);
|
|
1630
|
+
return lexSegment;
|
|
1631
|
+
}
|
|
1499
1632
|
}
|
|
1500
1633
|
if (l.acceptMatchRun(namePattern)) {
|
|
1501
1634
|
l.emit(TokenKind.NAME);
|
|
@@ -1518,8 +1651,25 @@ function lexInsideBracketedSelection(l) {
|
|
|
1518
1651
|
continue;
|
|
1519
1652
|
}
|
|
1520
1653
|
if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
|
|
1521
|
-
|
|
1522
|
-
|
|
1654
|
+
// FIXME: fall back to legacy behavior if keysPattern is not the default
|
|
1655
|
+
switch (l.peek()) {
|
|
1656
|
+
case "'":
|
|
1657
|
+
l.ignore(); // ~
|
|
1658
|
+
l.next();
|
|
1659
|
+
return lexSingleQuoteKeyString(l);
|
|
1660
|
+
case '"':
|
|
1661
|
+
l.ignore(); // ~
|
|
1662
|
+
l.next();
|
|
1663
|
+
return lexDoubleQuoteKeyString(l);
|
|
1664
|
+
case "?":
|
|
1665
|
+
l.next();
|
|
1666
|
+
l.emit(TokenKind.KEYS_FILTER);
|
|
1667
|
+
l.filterLevel += 1;
|
|
1668
|
+
return lexInsideFilter;
|
|
1669
|
+
default:
|
|
1670
|
+
l.emit(TokenKind.KEYS);
|
|
1671
|
+
continue;
|
|
1672
|
+
}
|
|
1523
1673
|
}
|
|
1524
1674
|
const ch = l.next();
|
|
1525
1675
|
switch (ch) {
|
|
@@ -1606,7 +1756,7 @@ function lexInsideFilter(l) {
|
|
|
1606
1756
|
l.emit(TokenKind.CURRENT);
|
|
1607
1757
|
return lexSegment;
|
|
1608
1758
|
case "#":
|
|
1609
|
-
l.emit(TokenKind.
|
|
1759
|
+
l.emit(TokenKind.CURRENT_KEY);
|
|
1610
1760
|
return lexSegment;
|
|
1611
1761
|
case ".":
|
|
1612
1762
|
l.backup();
|
|
@@ -1706,8 +1856,7 @@ function lexInsideFilter(l) {
|
|
|
1706
1856
|
* @param state - The state function to return control to.
|
|
1707
1857
|
* @returns String tokenizing state function.
|
|
1708
1858
|
*/
|
|
1709
|
-
function makeLexString(quote, state) {
|
|
1710
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
1859
|
+
function makeLexString(quote, state, token_kind) {
|
|
1711
1860
|
function _lexString(l) {
|
|
1712
1861
|
l.ignore();
|
|
1713
1862
|
if (l.peek() === quote) {
|
|
@@ -1733,7 +1882,7 @@ function makeLexString(quote, state) {
|
|
|
1733
1882
|
}
|
|
1734
1883
|
if (ch === quote) {
|
|
1735
1884
|
l.backup();
|
|
1736
|
-
l.emit(
|
|
1885
|
+
l.emit(token_kind);
|
|
1737
1886
|
l.next();
|
|
1738
1887
|
l.ignore();
|
|
1739
1888
|
return state;
|
|
@@ -1742,10 +1891,12 @@ function makeLexString(quote, state) {
|
|
|
1742
1891
|
}
|
|
1743
1892
|
return _lexString;
|
|
1744
1893
|
}
|
|
1745
|
-
const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection);
|
|
1746
|
-
const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection);
|
|
1747
|
-
const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter);
|
|
1748
|
-
const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter);
|
|
1894
|
+
const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection, TokenKind.SINGLE_QUOTE_STRING);
|
|
1895
|
+
const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection, TokenKind.DOUBLE_QUOTE_STRING);
|
|
1896
|
+
const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter, TokenKind.SINGLE_QUOTE_STRING);
|
|
1897
|
+
const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter, TokenKind.DOUBLE_QUOTE_STRING);
|
|
1898
|
+
const lexSingleQuoteKeyString = makeLexString("'", lexInsideBracketedSelection, TokenKind.KEY_SINGLE_QUOTE_STRING);
|
|
1899
|
+
const lexDoubleQuoteKeyString = makeLexString('"', lexInsideBracketedSelection, TokenKind.KEY_DOUBLE_QUOTE_STRING);
|
|
1749
1900
|
|
|
1750
1901
|
/**
|
|
1751
1902
|
* Base class for all JSONPath segments and selectors.
|
|
@@ -2394,6 +2545,38 @@ class CurrentKey extends FilterExpression {
|
|
|
2394
2545
|
}
|
|
2395
2546
|
}
|
|
2396
2547
|
|
|
2548
|
+
class KeySelector extends JSONPathSelector {
|
|
2549
|
+
constructor(environment, token, key) {
|
|
2550
|
+
let shorthand = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
2551
|
+
super(environment, token);
|
|
2552
|
+
this.environment = environment;
|
|
2553
|
+
this.token = token;
|
|
2554
|
+
this.key = key;
|
|
2555
|
+
this.shorthand = shorthand;
|
|
2556
|
+
}
|
|
2557
|
+
resolve(nodes) {
|
|
2558
|
+
const rv = [];
|
|
2559
|
+
for (const node of nodes) {
|
|
2560
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2561
|
+
if (isObject(node.value) && hasStringKey(node.value, this.key)) {
|
|
2562
|
+
rv.push(new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root));
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
return rv;
|
|
2566
|
+
}
|
|
2567
|
+
*lazyResolve(nodes) {
|
|
2568
|
+
for (const node of nodes) {
|
|
2569
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2570
|
+
if (isObject(node.value) && hasStringKey(node.value, this.key)) {
|
|
2571
|
+
yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
toString() {
|
|
2576
|
+
return this.shorthand ? `[~'${this.key.replaceAll("'", "\\'")}']` : `~'${this.key.replaceAll("'", "\\'")}'`;
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2397
2580
|
/**
|
|
2398
2581
|
* Object property name selector or array index selector.
|
|
2399
2582
|
*/
|
|
@@ -2410,10 +2593,8 @@ class KeysSelector extends JSONPathSelector {
|
|
|
2410
2593
|
for (const node of nodes) {
|
|
2411
2594
|
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2412
2595
|
if (isObject(node.value)) {
|
|
2413
|
-
let i = 0;
|
|
2414
2596
|
for (const [key, _] of this.environment.entries(node.value)) {
|
|
2415
|
-
rv.push(new JSONPathNode(key, node.location.concat(
|
|
2416
|
-
i++;
|
|
2597
|
+
rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
|
|
2417
2598
|
}
|
|
2418
2599
|
}
|
|
2419
2600
|
}
|
|
@@ -2423,10 +2604,8 @@ class KeysSelector extends JSONPathSelector {
|
|
|
2423
2604
|
for (const node of nodes) {
|
|
2424
2605
|
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2425
2606
|
if (isObject(node.value)) {
|
|
2426
|
-
let i = 0;
|
|
2427
2607
|
for (const [key, _] of this.environment.entries(node.value)) {
|
|
2428
|
-
yield new JSONPathNode(key, node.location.concat(
|
|
2429
|
-
i++;
|
|
2608
|
+
yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
|
|
2430
2609
|
}
|
|
2431
2610
|
}
|
|
2432
2611
|
}
|
|
@@ -2435,6 +2614,56 @@ class KeysSelector extends JSONPathSelector {
|
|
|
2435
2614
|
return this.shorthand ? "[~]" : "~";
|
|
2436
2615
|
}
|
|
2437
2616
|
}
|
|
2617
|
+
class KeysFilterSelector extends JSONPathSelector {
|
|
2618
|
+
constructor(environment, token, expression) {
|
|
2619
|
+
super(environment, token);
|
|
2620
|
+
this.environment = environment;
|
|
2621
|
+
this.token = token;
|
|
2622
|
+
this.expression = expression;
|
|
2623
|
+
}
|
|
2624
|
+
resolve(nodes) {
|
|
2625
|
+
const rv = [];
|
|
2626
|
+
for (const node of nodes) {
|
|
2627
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2628
|
+
if (isObject(node.value)) {
|
|
2629
|
+
for (const [key, value] of this.environment.entries(node.value)) {
|
|
2630
|
+
const filterContext = {
|
|
2631
|
+
environment: this.environment,
|
|
2632
|
+
currentValue: value,
|
|
2633
|
+
rootValue: node.root,
|
|
2634
|
+
currentKey: key
|
|
2635
|
+
};
|
|
2636
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2637
|
+
rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
return rv;
|
|
2643
|
+
}
|
|
2644
|
+
*lazyResolve(nodes) {
|
|
2645
|
+
for (const node of nodes) {
|
|
2646
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2647
|
+
if (isObject(node.value)) {
|
|
2648
|
+
for (const [key, value] of this.environment.entries(node.value)) {
|
|
2649
|
+
const filterContext = {
|
|
2650
|
+
environment: this.environment,
|
|
2651
|
+
currentValue: value,
|
|
2652
|
+
rootValue: node.root,
|
|
2653
|
+
lazy: true,
|
|
2654
|
+
currentKey: key
|
|
2655
|
+
};
|
|
2656
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2657
|
+
yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
toString() {
|
|
2664
|
+
return `~?${this.expression.toString()}`;
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2438
2667
|
|
|
2439
2668
|
const PRECEDENCE_LOWEST = 1;
|
|
2440
2669
|
const PRECEDENCE_LOGICAL_OR = 4;
|
|
@@ -2451,7 +2680,7 @@ const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
|
|
|
2451
2680
|
class Parser {
|
|
2452
2681
|
constructor(environment) {
|
|
2453
2682
|
this.environment = environment;
|
|
2454
|
-
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.
|
|
2683
|
+
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]]);
|
|
2455
2684
|
}
|
|
2456
2685
|
parse(stream) {
|
|
2457
2686
|
if (stream.current.kind === TokenKind.ROOT) stream.next();
|
|
@@ -2483,6 +2712,8 @@ class Parser {
|
|
|
2483
2712
|
return new NameSelector(this.environment, stream.current, stream.current.value, true);
|
|
2484
2713
|
case TokenKind.WILD:
|
|
2485
2714
|
return new WildcardSelector(this.environment, stream.current, true);
|
|
2715
|
+
case TokenKind.KEY:
|
|
2716
|
+
return new KeySelector(this.environment, stream.current, stream.current.value, true);
|
|
2486
2717
|
case TokenKind.KEYS:
|
|
2487
2718
|
return new KeysSelector(this.environment, stream.current, true);
|
|
2488
2719
|
case TokenKind.DDOT:
|
|
@@ -2578,6 +2809,13 @@ class Parser {
|
|
|
2578
2809
|
case TokenKind.WILD:
|
|
2579
2810
|
items.push(new WildcardSelector(this.environment, stream.current));
|
|
2580
2811
|
break;
|
|
2812
|
+
case TokenKind.KEY_SINGLE_QUOTE_STRING:
|
|
2813
|
+
case TokenKind.KEY_DOUBLE_QUOTE_STRING:
|
|
2814
|
+
items.push(new KeySelector(this.environment, stream.current, this.decodeString(stream.current, true), false));
|
|
2815
|
+
break;
|
|
2816
|
+
case TokenKind.KEYS_FILTER:
|
|
2817
|
+
items.push(this.parseFilter(stream, true));
|
|
2818
|
+
break;
|
|
2581
2819
|
case TokenKind.KEYS:
|
|
2582
2820
|
items.push(new KeysSelector(this.environment, stream.current));
|
|
2583
2821
|
break;
|
|
@@ -2598,6 +2836,7 @@ class Parser {
|
|
|
2598
2836
|
return new BracketedSelection(this.environment, token, items);
|
|
2599
2837
|
}
|
|
2600
2838
|
parseFilter(stream) {
|
|
2839
|
+
let keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
2601
2840
|
const tok = stream.next();
|
|
2602
2841
|
const expr = this.parseFilterExpression(stream);
|
|
2603
2842
|
if (expr instanceof FunctionExtension) {
|
|
@@ -2606,7 +2845,7 @@ class Parser {
|
|
|
2606
2845
|
throw new JSONPathTypeError(`result of ${expr.name}() must be compared`, expr.token);
|
|
2607
2846
|
}
|
|
2608
2847
|
}
|
|
2609
|
-
return new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
|
|
2848
|
+
return keys ? new KeysFilterSelector(this.environment, tok, new LogicalExpression(tok, expr)) : new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
|
|
2610
2849
|
}
|
|
2611
2850
|
parseBoolean(stream) {
|
|
2612
2851
|
if (stream.current.kind === TokenKind.FALSE) return new BooleanLiteral(stream.current, false);
|
|
@@ -3034,6 +3273,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
3034
3273
|
JSONPathRecursionLimitError: JSONPathRecursionLimitError,
|
|
3035
3274
|
JSONPathSyntaxError: JSONPathSyntaxError,
|
|
3036
3275
|
JSONPathTypeError: JSONPathTypeError,
|
|
3276
|
+
KEY_MARK: KEY_MARK,
|
|
3037
3277
|
Nothing: Nothing,
|
|
3038
3278
|
Token: Token,
|
|
3039
3279
|
TokenKind: TokenKind,
|
|
@@ -3526,7 +3766,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3526
3766
|
apply: apply
|
|
3527
3767
|
});
|
|
3528
3768
|
|
|
3529
|
-
const version = "1.
|
|
3769
|
+
const version = "1.3.1";
|
|
3530
3770
|
|
|
3531
3771
|
exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
|
|
3532
3772
|
exports.FunctionExpressionType = FunctionExpressionType;
|