json-p3 0.2.1 → 0.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/README.md CHANGED
@@ -39,6 +39,7 @@ console.log(nodes.values()); // [ 'John', 'Sally', 'Jane' ]
39
39
 
40
40
  - Docs: https://jg-rp.github.io/json-p3/
41
41
  - Install: https://jg-rp.github.io/json-p3/#install
42
+ - JSONPath playground: https://jg-rp.github.io/json-p3/playground
42
43
  - JSONPath syntax: https://jg-rp.github.io/json-p3/guides/jsonpath-syntax
43
44
  - API reference: https://jg-rp.github.io/json-p3/api
44
45
  - Change log: https://github.com/jg-rp/json-p3/blob/main/CHANGELOG.md
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export declare const version = "__VERSION__";
2
2
  export * as jsonpath from "./path";
3
- export { DEFAULT_ENVIRONMENT, FunctionExpressionType, JSONPath, JSONPathEnvironment, JSONPathError, JSONPathIndexError, JSONPathLexerError, JSONPathNode, JSONPathNodeList, JSONPathSyntaxError, JSONPathTypeError, JSONPathRecursionLimitError, Token, TokenKind, Nothing, query, compile, } from "./path";
3
+ export { DEFAULT_ENVIRONMENT, FunctionExpressionType, JSONPath, JSONPathEnvironment, JSONPathError, JSONPathIndexError, JSONPathLexerError, JSONPathNode, JSONPathNodeList, JSONPathSyntaxError, JSONPathTypeError, JSONPathRecursionLimitError, Token, TokenKind, Nothing, lazyQuery, query, compile, } from "./path";
4
4
  export type { JSONPathEnvironmentOptions, FilterFunction } from "./path";
5
5
  export * as jsonpointer from "./pointer";
6
6
  export { JSONPointer, RelativeJSONPointer, resolve, UNDEFINED, } from "./pointer";
@@ -1,5 +1,5 @@
1
1
  /*
2
- * json-p3 version 0.2.0
2
+ * json-p3 version 0.3.0
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
@@ -645,10 +645,6 @@ var index$3 = /*#__PURE__*/Object.freeze({
645
645
  * The pair of a JSON value and its location found in the target JSON value.
646
646
  */
647
647
  class JSONPathNode {
648
- /**
649
- * The normalized path to this node in the target JSON value.
650
- */
651
-
652
648
  /**
653
649
  * @param value - The JSON value found at _location_.
654
650
  * @param location - The parts of a normalized path to _value_.
@@ -658,9 +654,12 @@ class JSONPathNode {
658
654
  this.value = value;
659
655
  this.location = location;
660
656
  this.root = root;
661
- this.path =
662
- // eslint-disable-next-line prefer-template
663
- "$" + location.map(s => isString(s) ? `['${s}']` : `[${s}]`).join("");
657
+ }
658
+ get path() {
659
+ return (
660
+ // eslint-disable-next-line prefer-template
661
+ "$" + this.location.map(s => isString(s) ? `['${s}']` : `[${s}]`).join("")
662
+ );
664
663
  }
665
664
 
666
665
  /**
@@ -912,7 +911,7 @@ class JSONPathQuery extends FilterExpression {
912
911
  }
913
912
  class RelativeQuery extends JSONPathQuery {
914
913
  evaluate(context) {
915
- return this.path.query(context.currentValue);
914
+ return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.currentValue))) : this.path.query(context.currentValue);
916
915
  }
917
916
  toString() {
918
917
  return `@${this.path.toString().slice(1)}`;
@@ -920,7 +919,7 @@ class RelativeQuery extends JSONPathQuery {
920
919
  }
921
920
  class RootQuery extends JSONPathQuery {
922
921
  evaluate(context) {
923
- return this.path.query(context.rootValue);
922
+ return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.rootValue))) : this.path.query(context.rootValue);
924
923
  }
925
924
  toString() {
926
925
  return this.path.toString();
@@ -1722,6 +1721,10 @@ class JSONPathSelector {
1722
1721
  this.token = token;
1723
1722
  }
1724
1723
 
1724
+ /**
1725
+ * @param nodes - Nodes matched by preceding selectors.
1726
+ */
1727
+
1725
1728
  /**
1726
1729
  * @param nodes - Nodes matched by preceding selectors.
1727
1730
  */
@@ -1749,7 +1752,14 @@ class NameSelector extends JSONPathSelector {
1749
1752
  rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
1750
1753
  }
1751
1754
  }
1752
- return new JSONPathNodeList(rv);
1755
+ return rv;
1756
+ }
1757
+ *lazyResolve(nodes) {
1758
+ for (const node of nodes) {
1759
+ if (hasStringKey(node.value, this.name)) {
1760
+ yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
1761
+ }
1762
+ }
1753
1763
  }
1754
1764
  toString() {
1755
1765
  return this.shorthand ? `['${this.name}']` : `'${this.name}'`;
@@ -1779,7 +1789,17 @@ class IndexSelector extends JSONPathSelector {
1779
1789
  }
1780
1790
  }
1781
1791
  }
1782
- return new JSONPathNodeList(rv);
1792
+ return rv;
1793
+ }
1794
+ *lazyResolve(nodes) {
1795
+ for (const node of nodes) {
1796
+ if (isArray(node.value)) {
1797
+ const normIndex = this.normalizedIndex(node.value.length);
1798
+ if (normIndex in node.value) {
1799
+ yield new JSONPathNode(node.value[normIndex], node.location.concat(normIndex), node.root);
1800
+ }
1801
+ }
1802
+ }
1783
1803
  }
1784
1804
  toString() {
1785
1805
  return String(this.index);
@@ -1807,7 +1827,15 @@ class SliceSelector extends JSONPathSelector {
1807
1827
  rv.push(new JSONPathNode(value, node.location.concat(i), node.root));
1808
1828
  }
1809
1829
  }
1810
- return new JSONPathNodeList(rv);
1830
+ return rv;
1831
+ }
1832
+ *lazyResolve(nodes) {
1833
+ for (const node of nodes) {
1834
+ if (!isArray(node.value)) continue;
1835
+ for (const [i, value] of this.slice(node.value, this.start, this.stop, this.step)) {
1836
+ yield new JSONPathNode(value, node.location.concat(i), node.root);
1837
+ }
1838
+ }
1811
1839
  }
1812
1840
  toString() {
1813
1841
  const start = this.start ? this.start : "";
@@ -1890,22 +1918,92 @@ class WildcardSelector extends JSONPathSelector {
1890
1918
  }
1891
1919
  }
1892
1920
  }
1893
- return new JSONPathNodeList(rv);
1921
+ return rv;
1922
+ }
1923
+ *lazyResolve(nodes) {
1924
+ for (const node of nodes) {
1925
+ if (node.value instanceof String) continue;
1926
+ if (isArray(node.value)) {
1927
+ for (let i = 0; i < node.value.length; i++) {
1928
+ yield new JSONPathNode(node.value[i], node.location.concat(i), node.root);
1929
+ }
1930
+ } else if (isObject(node.value)) {
1931
+ for (const [key, value] of Object.entries(node.value)) {
1932
+ yield new JSONPathNode(value, node.location.concat(key), node.root);
1933
+ }
1934
+ }
1935
+ }
1894
1936
  }
1895
1937
  toString() {
1896
1938
  return this.shorthand ? "[*]" : "*";
1897
1939
  }
1898
1940
  }
1899
1941
  class RecursiveDescentSegment extends JSONPathSelector {
1942
+ constructor(environment, token, selector) {
1943
+ super(environment, token);
1944
+ this.environment = environment;
1945
+ this.token = token;
1946
+ this.selector = selector;
1947
+ }
1900
1948
  resolve(nodes) {
1901
1949
  const rv = [];
1902
1950
  for (const node of nodes) {
1903
- rv.push(node, ...this.visit(node));
1951
+ rv.push(node);
1952
+ for (const _node of this.visit(node)) {
1953
+ rv.push(_node);
1954
+ }
1955
+ }
1956
+ return this.selector.resolve(rv);
1957
+ }
1958
+ *lazyResolve(nodes) {
1959
+ yield* this.selector.lazyResolve(this._lazyResolve(nodes));
1960
+ }
1961
+
1962
+ // eslint-disable-next-line sonarjs/cognitive-complexity
1963
+ *_lazyResolve(nodes) {
1964
+ for (const _node of nodes) {
1965
+ const stack = [{
1966
+ node: _node,
1967
+ depth: 0
1968
+ }];
1969
+ yield _node;
1970
+ while (stack.length) {
1971
+ const {
1972
+ node: currentNode,
1973
+ depth
1974
+ } = stack.pop();
1975
+ if (depth >= this.environment.maxRecursionDepth) {
1976
+ throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
1977
+ }
1978
+ if (currentNode.value instanceof String) continue;
1979
+ if (isArray(currentNode.value)) {
1980
+ for (let i = 0; i < currentNode.value.length; i++) {
1981
+ const __node = new JSONPathNode(currentNode.value[i], currentNode.location.concat(i), currentNode.root);
1982
+ yield __node;
1983
+ if (isObject(__node.value)) {
1984
+ stack.push({
1985
+ node: __node,
1986
+ depth: depth + 1
1987
+ });
1988
+ }
1989
+ }
1990
+ } else if (isObject(currentNode.value)) {
1991
+ for (const [key, value] of Object.entries(currentNode.value)) {
1992
+ const __node = new JSONPathNode(value, currentNode.location.concat(key), currentNode.root);
1993
+ yield __node;
1994
+ if (isObject(__node.value)) {
1995
+ stack.push({
1996
+ node: __node,
1997
+ depth: depth + 1
1998
+ });
1999
+ }
2000
+ }
2001
+ }
2002
+ }
1904
2003
  }
1905
- return new JSONPathNodeList(rv);
1906
2004
  }
1907
2005
  toString() {
1908
- return "..";
2006
+ return `..${this.selector.toString()}`;
1909
2007
  }
1910
2008
  visit(node) {
1911
2009
  let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
@@ -1913,19 +2011,25 @@ class RecursiveDescentSegment extends JSONPathSelector {
1913
2011
  throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
1914
2012
  }
1915
2013
  const rv = [];
1916
- if (node.value instanceof String) return new JSONPathNodeList(rv);
2014
+ if (node.value instanceof String) return rv;
1917
2015
  if (isArray(node.value)) {
1918
2016
  for (let i = 0; i < node.value.length; i++) {
1919
2017
  const _node = new JSONPathNode(node.value[i], node.location.concat(i), node.root);
1920
- rv.push(_node, ...this.visit(_node, depth + 1));
2018
+ rv.push(_node);
2019
+ for (const __node of this.visit(_node, depth + 1)) {
2020
+ rv.push(__node);
2021
+ }
1921
2022
  }
1922
2023
  } else if (isObject(node.value)) {
1923
2024
  for (const [key, value] of Object.entries(node.value)) {
1924
2025
  const _node = new JSONPathNode(value, node.location.concat(key), node.root);
1925
- rv.push(_node, ...this.visit(_node, depth + 1));
2026
+ rv.push(_node);
2027
+ for (const __node of this.visit(_node, depth + 1)) {
2028
+ rv.push(__node);
2029
+ }
1926
2030
  }
1927
2031
  }
1928
- return new JSONPathNodeList(rv);
2032
+ return rv;
1929
2033
  }
1930
2034
  }
1931
2035
  class FilterSelector extends JSONPathSelector {
@@ -1966,7 +2070,40 @@ class FilterSelector extends JSONPathSelector {
1966
2070
  }
1967
2071
  }
1968
2072
  }
1969
- return new JSONPathNodeList(rv);
2073
+ return rv;
2074
+ }
2075
+
2076
+ // eslint-disable-next-line sonarjs/cognitive-complexity
2077
+ *lazyResolve(nodes) {
2078
+ for (const node of nodes) {
2079
+ if (node.value instanceof String) continue;
2080
+ if (isArray(node.value)) {
2081
+ for (let i = 0; i < node.value.length; i++) {
2082
+ const value = node.value[i];
2083
+ const filterContext = {
2084
+ environment: this.environment,
2085
+ currentValue: value,
2086
+ rootValue: node.root,
2087
+ lazy: true
2088
+ };
2089
+ if (this.expression.evaluate(filterContext)) {
2090
+ yield new JSONPathNode(value, node.location.concat(i), node.root);
2091
+ }
2092
+ }
2093
+ } else if (isObject(node.value)) {
2094
+ for (const [key, value] of Object.entries(node.value)) {
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(key), node.root);
2103
+ }
2104
+ }
2105
+ }
2106
+ }
1970
2107
  }
1971
2108
  toString() {
1972
2109
  return `?${this.expression.toString()}`;
@@ -1983,10 +2120,19 @@ class BracketedSelection extends JSONPathSelector {
1983
2120
  const rv = [];
1984
2121
  for (const node of nodes) {
1985
2122
  for (const item of this.items) {
1986
- rv.push(...item.resolve(new JSONPathNodeList([node])));
2123
+ for (const _node of item.resolve([node])) {
2124
+ rv.push(_node);
2125
+ }
2126
+ }
2127
+ }
2128
+ return rv;
2129
+ }
2130
+ *lazyResolve(nodes) {
2131
+ for (const node of nodes) {
2132
+ for (const item of this.items) {
2133
+ yield* item.lazyResolve([node]);
1987
2134
  }
1988
2135
  }
1989
- return new JSONPathNodeList(rv);
1990
2136
  }
1991
2137
  toString() {
1992
2138
  return `[${this.items.map(itm => itm.toString()).join(", ")}]`;
@@ -2025,10 +2171,23 @@ class JSONPath {
2025
2171
  * @returns
2026
2172
  */
2027
2173
  query(value) {
2028
- let nodes = new JSONPathNodeList([new JSONPathNode(value, [], value)]);
2174
+ let nodes = [new JSONPathNode(value, [], value)];
2029
2175
  for (const selector of this.selectors) {
2030
2176
  nodes = selector.resolve(nodes);
2031
2177
  }
2178
+ return new JSONPathNodeList(nodes);
2179
+ }
2180
+
2181
+ /**
2182
+ *
2183
+ * @param value -
2184
+ * @returns
2185
+ */
2186
+ lazyQuery(value) {
2187
+ let nodes = [new JSONPathNode(value, [], value)][Symbol.iterator]();
2188
+ for (const selector of this.selectors) {
2189
+ nodes = selector.lazyResolve(nodes);
2190
+ }
2032
2191
  return nodes;
2033
2192
  }
2034
2193
 
@@ -2041,7 +2200,10 @@ class JSONPath {
2041
2200
  * there are no matches.
2042
2201
  */
2043
2202
  match(value) {
2044
- return this.query(value).nodes.at(0);
2203
+ const it = this.lazyQuery(value);
2204
+ const rv = it.next();
2205
+ if (rv.done) return undefined;
2206
+ return rv.value;
2045
2207
  }
2046
2208
 
2047
2209
  /**
@@ -2061,13 +2223,17 @@ class JSONPath {
2061
2223
  }
2062
2224
 
2063
2225
  const PRECEDENCE_LOWEST = 1;
2064
- const PRECEDENCE_LOGICALRIGHT = 3;
2065
2226
  const PRECEDENCE_LOGICAL_AND = 4;
2066
2227
  const PRECEDENCE_LOGICAL_OR = 5;
2067
2228
  const PRECEDENCE_COMPARISON = 6;
2068
- 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_LOGICALRIGHT], [TokenKind.OR, PRECEDENCE_LOGICAL_OR], [TokenKind.RPAREN, PRECEDENCE_LOWEST]]);
2229
+ const PRECEDENCE_PREFIX = 7;
2230
+ 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]]);
2069
2231
  const BINARY_OPERATORS = new Map([[TokenKind.AND, "&&"], [TokenKind.EQ, "=="], [TokenKind.GE, ">="], [TokenKind.GT, ">"], [TokenKind.LE, "<="], [TokenKind.LT, "<"], [TokenKind.NE, "!="], [TokenKind.OR, "||"]]);
2070
2232
  const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
2233
+
2234
+ /**
2235
+ * JSONPath token stream parser.
2236
+ */
2071
2237
  class Parser {
2072
2238
  constructor(environment) {
2073
2239
  this.environment = environment;
@@ -2084,30 +2250,41 @@ class Parser {
2084
2250
  parsePath(stream) {
2085
2251
  let inFilter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2086
2252
  const selectors = [];
2087
- loop: for (;;) {
2088
- switch (stream.current.kind) {
2089
- case TokenKind.NAME:
2090
- selectors.push(new NameSelector(this.environment, stream.current, stream.current.value, true));
2091
- break;
2092
- case TokenKind.WILD:
2093
- selectors.push(new WildcardSelector(this.environment, stream.current, true));
2094
- break;
2095
- case TokenKind.DDOT:
2096
- selectors.push(new RecursiveDescentSegment(this.environment, stream.current));
2097
- break;
2098
- case TokenKind.LBRACKET:
2099
- selectors.push(this.parseBracketedSelection(stream));
2100
- break;
2101
- default:
2102
- if (inFilter) {
2103
- stream.backup();
2104
- }
2105
- break loop;
2253
+ for (;;) {
2254
+ const selector = this.parseSegment(stream);
2255
+ if (!selector) {
2256
+ if (inFilter) {
2257
+ stream.backup();
2258
+ }
2259
+ break;
2106
2260
  }
2261
+ selectors.push(selector);
2107
2262
  stream.next();
2108
2263
  }
2109
2264
  return selectors;
2110
2265
  }
2266
+ parseSegment(stream) {
2267
+ switch (stream.current.kind) {
2268
+ case TokenKind.NAME:
2269
+ return new NameSelector(this.environment, stream.current, stream.current.value, true);
2270
+ case TokenKind.WILD:
2271
+ return new WildcardSelector(this.environment, stream.current, true);
2272
+ case TokenKind.DDOT:
2273
+ {
2274
+ const segmentToken = stream.current;
2275
+ stream.next();
2276
+ const selector = this.parseSegment(stream);
2277
+ if (!selector) {
2278
+ throw new JSONPathSyntaxError("bald descendant segment", stream.current);
2279
+ }
2280
+ return new RecursiveDescentSegment(this.environment, segmentToken, selector);
2281
+ }
2282
+ case TokenKind.LBRACKET:
2283
+ return this.parseBracketedSelection(stream);
2284
+ default:
2285
+ return null;
2286
+ }
2287
+ }
2111
2288
  parseIndex(stream) {
2112
2289
  if (stream.current.value.length > 1 && stream.current.value.startsWith("0") || stream.current.value.startsWith("-0")) {
2113
2290
  throw new JSONPathSyntaxError("leading zero in index selector", stream.current);
@@ -2228,7 +2405,7 @@ class Parser {
2228
2405
  parsePrefixExpression(stream) {
2229
2406
  stream.expect(TokenKind.NOT);
2230
2407
  stream.next();
2231
- return new PrefixExpression(stream.current, "!", this.parseFilterExpression(stream, PRECEDENCE_LOGICALRIGHT));
2408
+ return new PrefixExpression(stream.current, "!", this.parseFilterExpression(stream, PRECEDENCE_PREFIX));
2232
2409
  }
2233
2410
  parseInfixExpression(stream, left) {
2234
2411
  const tok = stream.next();
@@ -2350,7 +2527,10 @@ class Parser {
2350
2527
  */
2351
2528
 
2352
2529
  /**
2530
+ * A configuration object from which JSONPath queries can be evaluated.
2353
2531
  *
2532
+ * An environment is where you'd register custom function extensions or set
2533
+ * the maximum recursion depth limit, for example.
2354
2534
  */
2355
2535
  class JSONPathEnvironment {
2356
2536
  /**
@@ -2383,8 +2563,7 @@ class JSONPathEnvironment {
2383
2563
  */
2384
2564
  functionRegister = new Map();
2385
2565
  /**
2386
- *
2387
- * @param options -
2566
+ * @param options - Environment configuration options.
2388
2567
  */
2389
2568
  constructor() {
2390
2569
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -2397,9 +2576,8 @@ class JSONPathEnvironment {
2397
2576
  }
2398
2577
 
2399
2578
  /**
2400
- *
2401
- * @param path -
2402
- * @returns
2579
+ * @param path - A JSONPath query to parse.
2580
+ * @returns A new {@link JSONPath} object, bound to this environment.
2403
2581
  */
2404
2582
  compile(path) {
2405
2583
  return new JSONPath(this, this.parser.parse(new TokenStream(tokenize(path))));
@@ -2407,14 +2585,28 @@ class JSONPathEnvironment {
2407
2585
 
2408
2586
  /**
2409
2587
  *
2410
- * @param path -
2411
- * @param value -
2412
- * @returns
2588
+ * @param path - A JSONPath query to parse and evaluate against _value_.
2589
+ * @param value - Data to which _path_ will be applied.
2590
+ * @returns The {@link JSONPathNodeList} resulting from applying _path_
2591
+ * to _value_.
2413
2592
  */
2414
2593
  query(path, value) {
2415
2594
  return this.compile(path).query(value);
2416
2595
  }
2417
2596
 
2597
+ /**
2598
+ * A lazy version of {@link query} which is faster and more memory
2599
+ * efficient when querying some large datasets.
2600
+ *
2601
+ * @param path - A JSONPath query to parse and evaluate against _value_.
2602
+ * @param value - Data to which _path_ will be applied.
2603
+ * @returns A sequence of {@link JSONPathNode} objects resulting from
2604
+ * applying _path_ to _value_.
2605
+ */
2606
+ lazyQuery(path, value) {
2607
+ return this.compile(path).lazyQuery(value);
2608
+ }
2609
+
2418
2610
  /**
2419
2611
  * Return a {@link JSONPathNode} instance for the first object found in
2420
2612
  * _value_ matching _path_.
@@ -2427,6 +2619,11 @@ class JSONPathEnvironment {
2427
2619
  match(path, value) {
2428
2620
  return this.compile(path).match(value);
2429
2621
  }
2622
+
2623
+ /**
2624
+ * A hook for setting up the function register. You are encouraged to
2625
+ * override this method in classes extending `JSONPathEnvironment`.
2626
+ */
2430
2627
  setupFilterFunctions() {
2431
2628
  this.functionRegister.set("count", new Count());
2432
2629
  this.functionRegister.set("length", new Length());
@@ -2436,9 +2633,18 @@ class JSONPathEnvironment {
2436
2633
  }
2437
2634
 
2438
2635
  /**
2636
+ * Check the well-typedness of a function's arguments at compile-time.
2637
+ *
2638
+ * This method is called by the {@link Parser} when parsing function calls.
2639
+ * It is expected to throw a {@link JSONPathTypeError} if the function's
2640
+ * parameters are not well-typed.
2641
+ *
2642
+ * Override this if you want to deviate from the JSONPath Spec's function
2643
+ * extension type system.
2439
2644
  *
2440
- * @param token -
2441
- * @param args -
2645
+ * @param token - The {@link Token} starting the function call. `Token.value`
2646
+ * will contain the name of the function.
2647
+ * @param args - One {@link FilterExpression} for each argument.
2442
2648
  */
2443
2649
  // eslint-disable-next-line sonarjs/cognitive-complexity
2444
2650
  checkWellTypedness(token, args) {
@@ -2505,6 +2711,27 @@ function query(path, value) {
2505
2711
  return DEFAULT_ENVIRONMENT.query(path, value);
2506
2712
  }
2507
2713
 
2714
+ /**
2715
+ * Lazily query JSON value _value_ with JSONPath expression _path_.
2716
+ * Lazy queries can be faster and more memory efficient when querying
2717
+ * large datasets, especially when using recursive decent selectors.
2718
+ *
2719
+ * @param path - A JSONPath expression/query.
2720
+ * @param value - The JSON-like value the JSONPath query is applied to.
2721
+ * @returns A sequence of {@link JSONPathNode} objects resulting from
2722
+ * applying _path_ to _value_.
2723
+ *
2724
+ * @throws {@link JSONPathSyntaxError}
2725
+ * If the path does not conform to standard syntax.
2726
+ *
2727
+ * @throws {@link JSONPathTypeError}
2728
+ * If filter function arguments are invalid, or filter expression are
2729
+ * used in an invalid way.
2730
+ */
2731
+ function lazyQuery(path, value) {
2732
+ return DEFAULT_ENVIRONMENT.lazyQuery(path, value);
2733
+ }
2734
+
2508
2735
  /**
2509
2736
  * Compile JSONPath _path_ for later use.
2510
2737
  * @param path - A JSONPath expression/query.
@@ -2554,6 +2781,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
2554
2781
  compile: compile,
2555
2782
  expressions: expression,
2556
2783
  functions: index$2,
2784
+ lazyQuery: lazyQuery,
2557
2785
  match: match,
2558
2786
  query: query,
2559
2787
  selectors: selectors
@@ -3039,7 +3267,7 @@ var index = /*#__PURE__*/Object.freeze({
3039
3267
  apply: apply
3040
3268
  });
3041
3269
 
3042
- const version = "0.2.0";
3270
+ const version = "0.3.0";
3043
3271
 
3044
3272
  exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
3045
3273
  exports.FunctionExpressionType = FunctionExpressionType;
@@ -3067,6 +3295,7 @@ exports.compile = compile;
3067
3295
  exports.jsonpatch = index;
3068
3296
  exports.jsonpath = index$1;
3069
3297
  exports.jsonpointer = index$3;
3298
+ exports.lazyQuery = lazyQuery;
3070
3299
  exports.query = query;
3071
3300
  exports.resolve = resolve;
3072
3301
  exports.version = version;