json-p3 0.2.1 → 0.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/README.md +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/json-p3.cjs.js +312 -72
- package/dist/json-p3.esm.js +312 -73
- package/dist/json-p3.iife.js +312 -72
- package/dist/json-p3.iife.min.js +1 -1
- package/dist/json-p3.iife.min.js.map +1 -1
- package/dist/path/environment.d.ts +35 -10
- package/dist/path/expression.d.ts +1 -0
- package/dist/path/index.d.ts +18 -0
- package/dist/path/node.d.ts +1 -4
- package/dist/path/parse.d.ts +5 -2
- package/dist/path/path.d.ts +6 -0
- package/dist/path/selectors.d.ts +25 -9
- package/dist/path/types.d.ts +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -27
package/dist/json-p3.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* json-p3 version 0.
|
|
2
|
+
* json-p3 version 0.3.1
|
|
3
3
|
* https://github.com/jg-rp/json-p3
|
|
4
4
|
*
|
|
5
5
|
* MIT License
|
|
@@ -643,10 +643,6 @@ var index$3 = /*#__PURE__*/Object.freeze({
|
|
|
643
643
|
* The pair of a JSON value and its location found in the target JSON value.
|
|
644
644
|
*/
|
|
645
645
|
class JSONPathNode {
|
|
646
|
-
/**
|
|
647
|
-
* The normalized path to this node in the target JSON value.
|
|
648
|
-
*/
|
|
649
|
-
|
|
650
646
|
/**
|
|
651
647
|
* @param value - The JSON value found at _location_.
|
|
652
648
|
* @param location - The parts of a normalized path to _value_.
|
|
@@ -656,9 +652,12 @@ class JSONPathNode {
|
|
|
656
652
|
this.value = value;
|
|
657
653
|
this.location = location;
|
|
658
654
|
this.root = root;
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
655
|
+
}
|
|
656
|
+
get path() {
|
|
657
|
+
return (
|
|
658
|
+
// eslint-disable-next-line prefer-template
|
|
659
|
+
"$" + this.location.map(s => isString(s) ? `['${s}']` : `[${s}]`).join("")
|
|
660
|
+
);
|
|
662
661
|
}
|
|
663
662
|
|
|
664
663
|
/**
|
|
@@ -910,7 +909,7 @@ class JSONPathQuery extends FilterExpression {
|
|
|
910
909
|
}
|
|
911
910
|
class RelativeQuery extends JSONPathQuery {
|
|
912
911
|
evaluate(context) {
|
|
913
|
-
return this.path.query(context.currentValue);
|
|
912
|
+
return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.currentValue))) : this.path.query(context.currentValue);
|
|
914
913
|
}
|
|
915
914
|
toString() {
|
|
916
915
|
return `@${this.path.toString().slice(1)}`;
|
|
@@ -918,7 +917,7 @@ class RelativeQuery extends JSONPathQuery {
|
|
|
918
917
|
}
|
|
919
918
|
class RootQuery extends JSONPathQuery {
|
|
920
919
|
evaluate(context) {
|
|
921
|
-
return this.path.query(context.rootValue);
|
|
920
|
+
return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.rootValue))) : this.path.query(context.rootValue);
|
|
922
921
|
}
|
|
923
922
|
toString() {
|
|
924
923
|
return this.path.toString();
|
|
@@ -936,12 +935,26 @@ class FunctionExtension extends FilterExpression {
|
|
|
936
935
|
if (!func) {
|
|
937
936
|
throw new UndefinedFilterFunctionError(`filter function '${this.name}' is undefined`, this.token);
|
|
938
937
|
}
|
|
939
|
-
const args = this.args.map(arg => arg.evaluate(context)).map((arg, idx) => func.argTypes[idx] !== FunctionExpressionType.NodesType && arg instanceof JSONPathNodeList ?
|
|
938
|
+
const args = this.args.map(arg => arg.evaluate(context)).map((arg, idx) => func.argTypes[idx] !== FunctionExpressionType.NodesType && arg instanceof JSONPathNodeList ? this.unpack_node_list(arg) : arg);
|
|
940
939
|
return func.call(...args);
|
|
941
940
|
}
|
|
942
941
|
toString() {
|
|
943
942
|
return `${this.name}(${this.args.map(e => e.toString()).join(", ")})`;
|
|
944
943
|
}
|
|
944
|
+
unpack_node_list(arg) {
|
|
945
|
+
switch (arg.length) {
|
|
946
|
+
case 0:
|
|
947
|
+
// If the query results in an empty node list, the argument
|
|
948
|
+
// is the special result Nothing.
|
|
949
|
+
return Nothing;
|
|
950
|
+
case 1:
|
|
951
|
+
// If the query results in a node list consisting of a single
|
|
952
|
+
// node, the argument is the value of the node
|
|
953
|
+
return arg.nodes[0].value;
|
|
954
|
+
default:
|
|
955
|
+
return arg;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
945
958
|
}
|
|
946
959
|
|
|
947
960
|
/**
|
|
@@ -1720,6 +1733,10 @@ class JSONPathSelector {
|
|
|
1720
1733
|
this.token = token;
|
|
1721
1734
|
}
|
|
1722
1735
|
|
|
1736
|
+
/**
|
|
1737
|
+
* @param nodes - Nodes matched by preceding selectors.
|
|
1738
|
+
*/
|
|
1739
|
+
|
|
1723
1740
|
/**
|
|
1724
1741
|
* @param nodes - Nodes matched by preceding selectors.
|
|
1725
1742
|
*/
|
|
@@ -1747,7 +1764,14 @@ class NameSelector extends JSONPathSelector {
|
|
|
1747
1764
|
rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
|
|
1748
1765
|
}
|
|
1749
1766
|
}
|
|
1750
|
-
return
|
|
1767
|
+
return rv;
|
|
1768
|
+
}
|
|
1769
|
+
*lazyResolve(nodes) {
|
|
1770
|
+
for (const node of nodes) {
|
|
1771
|
+
if (hasStringKey(node.value, this.name)) {
|
|
1772
|
+
yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1751
1775
|
}
|
|
1752
1776
|
toString() {
|
|
1753
1777
|
return this.shorthand ? `['${this.name}']` : `'${this.name}'`;
|
|
@@ -1777,7 +1801,17 @@ class IndexSelector extends JSONPathSelector {
|
|
|
1777
1801
|
}
|
|
1778
1802
|
}
|
|
1779
1803
|
}
|
|
1780
|
-
return
|
|
1804
|
+
return rv;
|
|
1805
|
+
}
|
|
1806
|
+
*lazyResolve(nodes) {
|
|
1807
|
+
for (const node of nodes) {
|
|
1808
|
+
if (isArray(node.value)) {
|
|
1809
|
+
const normIndex = this.normalizedIndex(node.value.length);
|
|
1810
|
+
if (normIndex in node.value) {
|
|
1811
|
+
yield new JSONPathNode(node.value[normIndex], node.location.concat(normIndex), node.root);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1781
1815
|
}
|
|
1782
1816
|
toString() {
|
|
1783
1817
|
return String(this.index);
|
|
@@ -1805,7 +1839,15 @@ class SliceSelector extends JSONPathSelector {
|
|
|
1805
1839
|
rv.push(new JSONPathNode(value, node.location.concat(i), node.root));
|
|
1806
1840
|
}
|
|
1807
1841
|
}
|
|
1808
|
-
return
|
|
1842
|
+
return rv;
|
|
1843
|
+
}
|
|
1844
|
+
*lazyResolve(nodes) {
|
|
1845
|
+
for (const node of nodes) {
|
|
1846
|
+
if (!isArray(node.value)) continue;
|
|
1847
|
+
for (const [i, value] of this.slice(node.value, this.start, this.stop, this.step)) {
|
|
1848
|
+
yield new JSONPathNode(value, node.location.concat(i), node.root);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1809
1851
|
}
|
|
1810
1852
|
toString() {
|
|
1811
1853
|
const start = this.start ? this.start : "";
|
|
@@ -1888,22 +1930,92 @@ class WildcardSelector extends JSONPathSelector {
|
|
|
1888
1930
|
}
|
|
1889
1931
|
}
|
|
1890
1932
|
}
|
|
1891
|
-
return
|
|
1933
|
+
return rv;
|
|
1934
|
+
}
|
|
1935
|
+
*lazyResolve(nodes) {
|
|
1936
|
+
for (const node of nodes) {
|
|
1937
|
+
if (node.value instanceof String) continue;
|
|
1938
|
+
if (isArray(node.value)) {
|
|
1939
|
+
for (let i = 0; i < node.value.length; i++) {
|
|
1940
|
+
yield new JSONPathNode(node.value[i], node.location.concat(i), node.root);
|
|
1941
|
+
}
|
|
1942
|
+
} else if (isObject(node.value)) {
|
|
1943
|
+
for (const [key, value] of Object.entries(node.value)) {
|
|
1944
|
+
yield new JSONPathNode(value, node.location.concat(key), node.root);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1892
1948
|
}
|
|
1893
1949
|
toString() {
|
|
1894
1950
|
return this.shorthand ? "[*]" : "*";
|
|
1895
1951
|
}
|
|
1896
1952
|
}
|
|
1897
1953
|
class RecursiveDescentSegment extends JSONPathSelector {
|
|
1954
|
+
constructor(environment, token, selector) {
|
|
1955
|
+
super(environment, token);
|
|
1956
|
+
this.environment = environment;
|
|
1957
|
+
this.token = token;
|
|
1958
|
+
this.selector = selector;
|
|
1959
|
+
}
|
|
1898
1960
|
resolve(nodes) {
|
|
1899
1961
|
const rv = [];
|
|
1900
1962
|
for (const node of nodes) {
|
|
1901
|
-
rv.push(node
|
|
1963
|
+
rv.push(node);
|
|
1964
|
+
for (const _node of this.visit(node)) {
|
|
1965
|
+
rv.push(_node);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
return this.selector.resolve(rv);
|
|
1969
|
+
}
|
|
1970
|
+
*lazyResolve(nodes) {
|
|
1971
|
+
yield* this.selector.lazyResolve(this._lazyResolve(nodes));
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
1975
|
+
*_lazyResolve(nodes) {
|
|
1976
|
+
for (const _node of nodes) {
|
|
1977
|
+
const stack = [{
|
|
1978
|
+
node: _node,
|
|
1979
|
+
depth: 0
|
|
1980
|
+
}];
|
|
1981
|
+
yield _node;
|
|
1982
|
+
while (stack.length) {
|
|
1983
|
+
const {
|
|
1984
|
+
node: currentNode,
|
|
1985
|
+
depth
|
|
1986
|
+
} = stack.pop();
|
|
1987
|
+
if (depth >= this.environment.maxRecursionDepth) {
|
|
1988
|
+
throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
|
|
1989
|
+
}
|
|
1990
|
+
if (currentNode.value instanceof String) continue;
|
|
1991
|
+
if (isArray(currentNode.value)) {
|
|
1992
|
+
for (let i = 0; i < currentNode.value.length; i++) {
|
|
1993
|
+
const __node = new JSONPathNode(currentNode.value[i], currentNode.location.concat(i), currentNode.root);
|
|
1994
|
+
yield __node;
|
|
1995
|
+
if (isObject(__node.value)) {
|
|
1996
|
+
stack.push({
|
|
1997
|
+
node: __node,
|
|
1998
|
+
depth: depth + 1
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
} else if (isObject(currentNode.value)) {
|
|
2003
|
+
for (const [key, value] of Object.entries(currentNode.value)) {
|
|
2004
|
+
const __node = new JSONPathNode(value, currentNode.location.concat(key), currentNode.root);
|
|
2005
|
+
yield __node;
|
|
2006
|
+
if (isObject(__node.value)) {
|
|
2007
|
+
stack.push({
|
|
2008
|
+
node: __node,
|
|
2009
|
+
depth: depth + 1
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
1902
2015
|
}
|
|
1903
|
-
return new JSONPathNodeList(rv);
|
|
1904
2016
|
}
|
|
1905
2017
|
toString() {
|
|
1906
|
-
return
|
|
2018
|
+
return `..${this.selector.toString()}`;
|
|
1907
2019
|
}
|
|
1908
2020
|
visit(node) {
|
|
1909
2021
|
let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
|
@@ -1911,19 +2023,25 @@ class RecursiveDescentSegment extends JSONPathSelector {
|
|
|
1911
2023
|
throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
|
|
1912
2024
|
}
|
|
1913
2025
|
const rv = [];
|
|
1914
|
-
if (node.value instanceof String) return
|
|
2026
|
+
if (node.value instanceof String) return rv;
|
|
1915
2027
|
if (isArray(node.value)) {
|
|
1916
2028
|
for (let i = 0; i < node.value.length; i++) {
|
|
1917
2029
|
const _node = new JSONPathNode(node.value[i], node.location.concat(i), node.root);
|
|
1918
|
-
rv.push(_node
|
|
2030
|
+
rv.push(_node);
|
|
2031
|
+
for (const __node of this.visit(_node, depth + 1)) {
|
|
2032
|
+
rv.push(__node);
|
|
2033
|
+
}
|
|
1919
2034
|
}
|
|
1920
2035
|
} else if (isObject(node.value)) {
|
|
1921
2036
|
for (const [key, value] of Object.entries(node.value)) {
|
|
1922
2037
|
const _node = new JSONPathNode(value, node.location.concat(key), node.root);
|
|
1923
|
-
rv.push(_node
|
|
2038
|
+
rv.push(_node);
|
|
2039
|
+
for (const __node of this.visit(_node, depth + 1)) {
|
|
2040
|
+
rv.push(__node);
|
|
2041
|
+
}
|
|
1924
2042
|
}
|
|
1925
2043
|
}
|
|
1926
|
-
return
|
|
2044
|
+
return rv;
|
|
1927
2045
|
}
|
|
1928
2046
|
}
|
|
1929
2047
|
class FilterSelector extends JSONPathSelector {
|
|
@@ -1964,7 +2082,40 @@ class FilterSelector extends JSONPathSelector {
|
|
|
1964
2082
|
}
|
|
1965
2083
|
}
|
|
1966
2084
|
}
|
|
1967
|
-
return
|
|
2085
|
+
return rv;
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
2089
|
+
*lazyResolve(nodes) {
|
|
2090
|
+
for (const node of nodes) {
|
|
2091
|
+
if (node.value instanceof String) continue;
|
|
2092
|
+
if (isArray(node.value)) {
|
|
2093
|
+
for (let i = 0; i < node.value.length; i++) {
|
|
2094
|
+
const value = node.value[i];
|
|
2095
|
+
const filterContext = {
|
|
2096
|
+
environment: this.environment,
|
|
2097
|
+
currentValue: value,
|
|
2098
|
+
rootValue: node.root,
|
|
2099
|
+
lazy: true
|
|
2100
|
+
};
|
|
2101
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2102
|
+
yield new JSONPathNode(value, node.location.concat(i), node.root);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
} else if (isObject(node.value)) {
|
|
2106
|
+
for (const [key, value] of Object.entries(node.value)) {
|
|
2107
|
+
const filterContext = {
|
|
2108
|
+
environment: this.environment,
|
|
2109
|
+
currentValue: value,
|
|
2110
|
+
rootValue: node.root,
|
|
2111
|
+
lazy: true
|
|
2112
|
+
};
|
|
2113
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2114
|
+
yield new JSONPathNode(value, node.location.concat(key), node.root);
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
1968
2119
|
}
|
|
1969
2120
|
toString() {
|
|
1970
2121
|
return `?${this.expression.toString()}`;
|
|
@@ -1981,10 +2132,19 @@ class BracketedSelection extends JSONPathSelector {
|
|
|
1981
2132
|
const rv = [];
|
|
1982
2133
|
for (const node of nodes) {
|
|
1983
2134
|
for (const item of this.items) {
|
|
1984
|
-
|
|
2135
|
+
for (const _node of item.resolve([node])) {
|
|
2136
|
+
rv.push(_node);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
return rv;
|
|
2141
|
+
}
|
|
2142
|
+
*lazyResolve(nodes) {
|
|
2143
|
+
for (const node of nodes) {
|
|
2144
|
+
for (const item of this.items) {
|
|
2145
|
+
yield* item.lazyResolve([node]);
|
|
1985
2146
|
}
|
|
1986
2147
|
}
|
|
1987
|
-
return new JSONPathNodeList(rv);
|
|
1988
2148
|
}
|
|
1989
2149
|
toString() {
|
|
1990
2150
|
return `[${this.items.map(itm => itm.toString()).join(", ")}]`;
|
|
@@ -2023,10 +2183,23 @@ class JSONPath {
|
|
|
2023
2183
|
* @returns
|
|
2024
2184
|
*/
|
|
2025
2185
|
query(value) {
|
|
2026
|
-
let nodes =
|
|
2186
|
+
let nodes = [new JSONPathNode(value, [], value)];
|
|
2027
2187
|
for (const selector of this.selectors) {
|
|
2028
2188
|
nodes = selector.resolve(nodes);
|
|
2029
2189
|
}
|
|
2190
|
+
return new JSONPathNodeList(nodes);
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
/**
|
|
2194
|
+
*
|
|
2195
|
+
* @param value -
|
|
2196
|
+
* @returns
|
|
2197
|
+
*/
|
|
2198
|
+
lazyQuery(value) {
|
|
2199
|
+
let nodes = [new JSONPathNode(value, [], value)][Symbol.iterator]();
|
|
2200
|
+
for (const selector of this.selectors) {
|
|
2201
|
+
nodes = selector.lazyResolve(nodes);
|
|
2202
|
+
}
|
|
2030
2203
|
return nodes;
|
|
2031
2204
|
}
|
|
2032
2205
|
|
|
@@ -2039,7 +2212,10 @@ class JSONPath {
|
|
|
2039
2212
|
* there are no matches.
|
|
2040
2213
|
*/
|
|
2041
2214
|
match(value) {
|
|
2042
|
-
|
|
2215
|
+
const it = this.lazyQuery(value);
|
|
2216
|
+
const rv = it.next();
|
|
2217
|
+
if (rv.done) return undefined;
|
|
2218
|
+
return rv.value;
|
|
2043
2219
|
}
|
|
2044
2220
|
|
|
2045
2221
|
/**
|
|
@@ -2059,13 +2235,17 @@ class JSONPath {
|
|
|
2059
2235
|
}
|
|
2060
2236
|
|
|
2061
2237
|
const PRECEDENCE_LOWEST = 1;
|
|
2062
|
-
const PRECEDENCE_LOGICALRIGHT = 3;
|
|
2063
2238
|
const PRECEDENCE_LOGICAL_AND = 4;
|
|
2064
2239
|
const PRECEDENCE_LOGICAL_OR = 5;
|
|
2065
2240
|
const PRECEDENCE_COMPARISON = 6;
|
|
2066
|
-
const
|
|
2241
|
+
const PRECEDENCE_PREFIX = 7;
|
|
2242
|
+
const PRECEDENCES = new Map([[TokenKind.AND, PRECEDENCE_LOGICAL_AND], [TokenKind.EQ, PRECEDENCE_COMPARISON], [TokenKind.GE, PRECEDENCE_COMPARISON], [TokenKind.GT, PRECEDENCE_COMPARISON], [TokenKind.LE, PRECEDENCE_COMPARISON], [TokenKind.LT, PRECEDENCE_COMPARISON], [TokenKind.NE, PRECEDENCE_COMPARISON], [TokenKind.NOT, PRECEDENCE_PREFIX], [TokenKind.OR, PRECEDENCE_LOGICAL_OR], [TokenKind.RPAREN, PRECEDENCE_LOWEST]]);
|
|
2067
2243
|
const BINARY_OPERATORS = new Map([[TokenKind.AND, "&&"], [TokenKind.EQ, "=="], [TokenKind.GE, ">="], [TokenKind.GT, ">"], [TokenKind.LE, "<="], [TokenKind.LT, "<"], [TokenKind.NE, "!="], [TokenKind.OR, "||"]]);
|
|
2068
2244
|
const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
|
|
2245
|
+
|
|
2246
|
+
/**
|
|
2247
|
+
* JSONPath token stream parser.
|
|
2248
|
+
*/
|
|
2069
2249
|
class Parser {
|
|
2070
2250
|
constructor(environment) {
|
|
2071
2251
|
this.environment = environment;
|
|
@@ -2082,30 +2262,41 @@ class Parser {
|
|
|
2082
2262
|
parsePath(stream) {
|
|
2083
2263
|
let inFilter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
2084
2264
|
const selectors = [];
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
break;
|
|
2093
|
-
case TokenKind.DDOT:
|
|
2094
|
-
selectors.push(new RecursiveDescentSegment(this.environment, stream.current));
|
|
2095
|
-
break;
|
|
2096
|
-
case TokenKind.LBRACKET:
|
|
2097
|
-
selectors.push(this.parseBracketedSelection(stream));
|
|
2098
|
-
break;
|
|
2099
|
-
default:
|
|
2100
|
-
if (inFilter) {
|
|
2101
|
-
stream.backup();
|
|
2102
|
-
}
|
|
2103
|
-
break loop;
|
|
2265
|
+
for (;;) {
|
|
2266
|
+
const selector = this.parseSegment(stream);
|
|
2267
|
+
if (!selector) {
|
|
2268
|
+
if (inFilter) {
|
|
2269
|
+
stream.backup();
|
|
2270
|
+
}
|
|
2271
|
+
break;
|
|
2104
2272
|
}
|
|
2273
|
+
selectors.push(selector);
|
|
2105
2274
|
stream.next();
|
|
2106
2275
|
}
|
|
2107
2276
|
return selectors;
|
|
2108
2277
|
}
|
|
2278
|
+
parseSegment(stream) {
|
|
2279
|
+
switch (stream.current.kind) {
|
|
2280
|
+
case TokenKind.NAME:
|
|
2281
|
+
return new NameSelector(this.environment, stream.current, stream.current.value, true);
|
|
2282
|
+
case TokenKind.WILD:
|
|
2283
|
+
return new WildcardSelector(this.environment, stream.current, true);
|
|
2284
|
+
case TokenKind.DDOT:
|
|
2285
|
+
{
|
|
2286
|
+
const segmentToken = stream.current;
|
|
2287
|
+
stream.next();
|
|
2288
|
+
const selector = this.parseSegment(stream);
|
|
2289
|
+
if (!selector) {
|
|
2290
|
+
throw new JSONPathSyntaxError("bald descendant segment", stream.current);
|
|
2291
|
+
}
|
|
2292
|
+
return new RecursiveDescentSegment(this.environment, segmentToken, selector);
|
|
2293
|
+
}
|
|
2294
|
+
case TokenKind.LBRACKET:
|
|
2295
|
+
return this.parseBracketedSelection(stream);
|
|
2296
|
+
default:
|
|
2297
|
+
return null;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2109
2300
|
parseIndex(stream) {
|
|
2110
2301
|
if (stream.current.value.length > 1 && stream.current.value.startsWith("0") || stream.current.value.startsWith("-0")) {
|
|
2111
2302
|
throw new JSONPathSyntaxError("leading zero in index selector", stream.current);
|
|
@@ -2226,7 +2417,7 @@ class Parser {
|
|
|
2226
2417
|
parsePrefixExpression(stream) {
|
|
2227
2418
|
stream.expect(TokenKind.NOT);
|
|
2228
2419
|
stream.next();
|
|
2229
|
-
return new PrefixExpression(stream.current, "!", this.parseFilterExpression(stream,
|
|
2420
|
+
return new PrefixExpression(stream.current, "!", this.parseFilterExpression(stream, PRECEDENCE_PREFIX));
|
|
2230
2421
|
}
|
|
2231
2422
|
parseInfixExpression(stream, left) {
|
|
2232
2423
|
const tok = stream.next();
|
|
@@ -2236,11 +2427,9 @@ class Parser {
|
|
|
2236
2427
|
if (!operator) {
|
|
2237
2428
|
throw new JSONPathSyntaxError(`unknown operator '${tok.kind}'`, tok);
|
|
2238
2429
|
}
|
|
2239
|
-
this.throwForNonSingularQuery(left);
|
|
2240
|
-
this.throwForNonSingularQuery(right);
|
|
2241
2430
|
if (COMPARISON_OPERATORS.has(operator)) {
|
|
2242
|
-
this.
|
|
2243
|
-
this.
|
|
2431
|
+
this.throwForNonComparable(left);
|
|
2432
|
+
this.throwForNonComparable(right);
|
|
2244
2433
|
}
|
|
2245
2434
|
return new InfixExpression(tok, left, operator, right);
|
|
2246
2435
|
}
|
|
@@ -2328,16 +2517,15 @@ class Parser {
|
|
|
2328
2517
|
throw new JSONPathSyntaxError(`invalid ${isName ? "name selector" : "string literal"} '${token.value}'`, token);
|
|
2329
2518
|
}
|
|
2330
2519
|
}
|
|
2331
|
-
|
|
2520
|
+
throwForNonComparable(expr) {
|
|
2332
2521
|
if ((expr instanceof RootQuery || expr instanceof RelativeQuery) && !expr.path.singularQuery()) {
|
|
2333
|
-
throw new
|
|
2522
|
+
throw new JSONPathTypeError("non-singular query is not comparable", expr.token);
|
|
2334
2523
|
}
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
throw new JSONPathTypeError(`result of ${expr.name}() is not comparable`, expr.token);
|
|
2524
|
+
if (expr instanceof FunctionExtension) {
|
|
2525
|
+
const func = this.environment.functionRegister.get(expr.name);
|
|
2526
|
+
if (func && func.returnType !== FunctionExpressionType.ValueType) {
|
|
2527
|
+
throw new JSONPathTypeError(`result of ${expr.name}() is not comparable`, expr.token);
|
|
2528
|
+
}
|
|
2341
2529
|
}
|
|
2342
2530
|
}
|
|
2343
2531
|
}
|
|
@@ -2348,7 +2536,10 @@ class Parser {
|
|
|
2348
2536
|
*/
|
|
2349
2537
|
|
|
2350
2538
|
/**
|
|
2539
|
+
* A configuration object from which JSONPath queries can be evaluated.
|
|
2351
2540
|
*
|
|
2541
|
+
* An environment is where you'd register custom function extensions or set
|
|
2542
|
+
* the maximum recursion depth limit, for example.
|
|
2352
2543
|
*/
|
|
2353
2544
|
class JSONPathEnvironment {
|
|
2354
2545
|
/**
|
|
@@ -2381,8 +2572,7 @@ class JSONPathEnvironment {
|
|
|
2381
2572
|
*/
|
|
2382
2573
|
functionRegister = new Map();
|
|
2383
2574
|
/**
|
|
2384
|
-
*
|
|
2385
|
-
* @param options -
|
|
2575
|
+
* @param options - Environment configuration options.
|
|
2386
2576
|
*/
|
|
2387
2577
|
constructor() {
|
|
2388
2578
|
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -2395,9 +2585,8 @@ class JSONPathEnvironment {
|
|
|
2395
2585
|
}
|
|
2396
2586
|
|
|
2397
2587
|
/**
|
|
2398
|
-
*
|
|
2399
|
-
* @
|
|
2400
|
-
* @returns
|
|
2588
|
+
* @param path - A JSONPath query to parse.
|
|
2589
|
+
* @returns A new {@link JSONPath} object, bound to this environment.
|
|
2401
2590
|
*/
|
|
2402
2591
|
compile(path) {
|
|
2403
2592
|
return new JSONPath(this, this.parser.parse(new TokenStream(tokenize(path))));
|
|
@@ -2405,14 +2594,28 @@ class JSONPathEnvironment {
|
|
|
2405
2594
|
|
|
2406
2595
|
/**
|
|
2407
2596
|
*
|
|
2408
|
-
* @param path -
|
|
2409
|
-
* @param value -
|
|
2410
|
-
* @returns
|
|
2597
|
+
* @param path - A JSONPath query to parse and evaluate against _value_.
|
|
2598
|
+
* @param value - Data to which _path_ will be applied.
|
|
2599
|
+
* @returns The {@link JSONPathNodeList} resulting from applying _path_
|
|
2600
|
+
* to _value_.
|
|
2411
2601
|
*/
|
|
2412
2602
|
query(path, value) {
|
|
2413
2603
|
return this.compile(path).query(value);
|
|
2414
2604
|
}
|
|
2415
2605
|
|
|
2606
|
+
/**
|
|
2607
|
+
* A lazy version of {@link query} which is faster and more memory
|
|
2608
|
+
* efficient when querying some large datasets.
|
|
2609
|
+
*
|
|
2610
|
+
* @param path - A JSONPath query to parse and evaluate against _value_.
|
|
2611
|
+
* @param value - Data to which _path_ will be applied.
|
|
2612
|
+
* @returns A sequence of {@link JSONPathNode} objects resulting from
|
|
2613
|
+
* applying _path_ to _value_.
|
|
2614
|
+
*/
|
|
2615
|
+
lazyQuery(path, value) {
|
|
2616
|
+
return this.compile(path).lazyQuery(value);
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2416
2619
|
/**
|
|
2417
2620
|
* Return a {@link JSONPathNode} instance for the first object found in
|
|
2418
2621
|
* _value_ matching _path_.
|
|
@@ -2425,6 +2628,11 @@ class JSONPathEnvironment {
|
|
|
2425
2628
|
match(path, value) {
|
|
2426
2629
|
return this.compile(path).match(value);
|
|
2427
2630
|
}
|
|
2631
|
+
|
|
2632
|
+
/**
|
|
2633
|
+
* A hook for setting up the function register. You are encouraged to
|
|
2634
|
+
* override this method in classes extending `JSONPathEnvironment`.
|
|
2635
|
+
*/
|
|
2428
2636
|
setupFilterFunctions() {
|
|
2429
2637
|
this.functionRegister.set("count", new Count());
|
|
2430
2638
|
this.functionRegister.set("length", new Length());
|
|
@@ -2434,9 +2642,18 @@ class JSONPathEnvironment {
|
|
|
2434
2642
|
}
|
|
2435
2643
|
|
|
2436
2644
|
/**
|
|
2645
|
+
* Check the well-typedness of a function's arguments at compile-time.
|
|
2646
|
+
*
|
|
2647
|
+
* This method is called by the {@link Parser} when parsing function calls.
|
|
2648
|
+
* It is expected to throw a {@link JSONPathTypeError} if the function's
|
|
2649
|
+
* parameters are not well-typed.
|
|
2650
|
+
*
|
|
2651
|
+
* Override this if you want to deviate from the JSONPath Spec's function
|
|
2652
|
+
* extension type system.
|
|
2437
2653
|
*
|
|
2438
|
-
* @param token -
|
|
2439
|
-
*
|
|
2654
|
+
* @param token - The {@link Token} starting the function call. `Token.value`
|
|
2655
|
+
* will contain the name of the function.
|
|
2656
|
+
* @param args - One {@link FilterExpression} for each argument.
|
|
2440
2657
|
*/
|
|
2441
2658
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
2442
2659
|
checkWellTypedness(token, args) {
|
|
@@ -2503,6 +2720,27 @@ function query(path, value) {
|
|
|
2503
2720
|
return DEFAULT_ENVIRONMENT.query(path, value);
|
|
2504
2721
|
}
|
|
2505
2722
|
|
|
2723
|
+
/**
|
|
2724
|
+
* Lazily query JSON value _value_ with JSONPath expression _path_.
|
|
2725
|
+
* Lazy queries can be faster and more memory efficient when querying
|
|
2726
|
+
* large datasets, especially when using recursive decent selectors.
|
|
2727
|
+
*
|
|
2728
|
+
* @param path - A JSONPath expression/query.
|
|
2729
|
+
* @param value - The JSON-like value the JSONPath query is applied to.
|
|
2730
|
+
* @returns A sequence of {@link JSONPathNode} objects resulting from
|
|
2731
|
+
* applying _path_ to _value_.
|
|
2732
|
+
*
|
|
2733
|
+
* @throws {@link JSONPathSyntaxError}
|
|
2734
|
+
* If the path does not conform to standard syntax.
|
|
2735
|
+
*
|
|
2736
|
+
* @throws {@link JSONPathTypeError}
|
|
2737
|
+
* If filter function arguments are invalid, or filter expression are
|
|
2738
|
+
* used in an invalid way.
|
|
2739
|
+
*/
|
|
2740
|
+
function lazyQuery(path, value) {
|
|
2741
|
+
return DEFAULT_ENVIRONMENT.lazyQuery(path, value);
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2506
2744
|
/**
|
|
2507
2745
|
* Compile JSONPath _path_ for later use.
|
|
2508
2746
|
* @param path - A JSONPath expression/query.
|
|
@@ -2552,6 +2790,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
2552
2790
|
compile: compile,
|
|
2553
2791
|
expressions: expression,
|
|
2554
2792
|
functions: index$2,
|
|
2793
|
+
lazyQuery: lazyQuery,
|
|
2555
2794
|
match: match,
|
|
2556
2795
|
query: query,
|
|
2557
2796
|
selectors: selectors
|
|
@@ -3037,6 +3276,6 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3037
3276
|
apply: apply
|
|
3038
3277
|
});
|
|
3039
3278
|
|
|
3040
|
-
const version = "0.
|
|
3279
|
+
const version = "0.3.1";
|
|
3041
3280
|
|
|
3042
|
-
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, query, resolve, version };
|
|
3281
|
+
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 };
|