json-p3 1.1.1 → 1.2.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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- * json-p3 version 1.1.1
2
+ * json-p3 version 1.2.1
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
@@ -1180,6 +1180,8 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1180
1180
  TokenKind["GE"] = "TOKEN_GE";
1181
1181
  TokenKind["GT"] = "TOKEN_GT";
1182
1182
  TokenKind["INDEX"] = "TOKEN_INDEX";
1183
+ TokenKind["KEY"] = "TOKEN_KEY";
1184
+ TokenKind["KEYS"] = "TOKEN_KEYS";
1183
1185
  TokenKind["LBRACKET"] = "TOKEN_LBRACKET";
1184
1186
  TokenKind["LE"] = "TOKEN_LE";
1185
1187
  TokenKind["LG"] = "TOKEN_LG";
@@ -1249,6 +1251,9 @@ class TokenStream {
1249
1251
  }
1250
1252
  }
1251
1253
 
1254
+ /** A lexer that accepts additional, non-standard tokens. */
1255
+
1256
+
1252
1257
  // These regular expressions are to be used with Lexer.acceptMatchRun(),
1253
1258
  // which expects the sticky flag to be set.
1254
1259
  const exponentPattern = /e[+-]?\d+/y;
@@ -1288,7 +1293,8 @@ class Lexer {
1288
1293
  /**
1289
1294
  * @param path - A JSONPath query.
1290
1295
  */
1291
- constructor(path) {
1296
+ constructor(environment, path) {
1297
+ this.environment = environment;
1292
1298
  this.path = path;
1293
1299
  }
1294
1300
  get pos() {
@@ -1388,8 +1394,8 @@ class Lexer {
1388
1394
  * @returns A two-tuple containing a lexer for _path_ and an array to populate
1389
1395
  * with tokens.
1390
1396
  */
1391
- function lex(path) {
1392
- const lexer = new Lexer(path);
1397
+ function lex(environment, path) {
1398
+ const lexer = new Lexer(environment, path);
1393
1399
  return [lexer, lexer.tokens];
1394
1400
  }
1395
1401
 
@@ -1398,8 +1404,8 @@ function lex(path) {
1398
1404
  * @param path - A JSONPath query.
1399
1405
  * @returns Tokens to be parsed by the parser.
1400
1406
  */
1401
- function tokenize(path) {
1402
- const [lexer, tokens] = lex(path);
1407
+ function tokenize(environment, path) {
1408
+ const [lexer, tokens] = lex(environment, path);
1403
1409
  lexer.run();
1404
1410
  if (tokens.length && tokens[tokens.length - 1].kind === TokenKind.ERROR) {
1405
1411
  throw new JSONPathSyntaxError(tokens[tokens.length - 1].value, tokens[tokens.length - 1]);
@@ -1452,6 +1458,16 @@ function lexSegment(l) {
1452
1458
  * @returns -
1453
1459
  */
1454
1460
  function lexDescendantSelection(l) {
1461
+ if (l.acceptMatchRun(namePattern)) {
1462
+ // Shorthand name
1463
+ l.emit(TokenKind.NAME);
1464
+ return lexSegment;
1465
+ }
1466
+ if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1467
+ // Non-standard keys selector
1468
+ l.emit(TokenKind.KEYS);
1469
+ return lexSegment;
1470
+ }
1455
1471
  const ch = l.next();
1456
1472
  switch (ch) {
1457
1473
  case "":
@@ -1465,13 +1481,8 @@ function lexDescendantSelection(l) {
1465
1481
  return lexInsideBracketedSelection;
1466
1482
  default:
1467
1483
  l.backup();
1468
- if (l.acceptMatchRun(namePattern)) {
1469
- l.emit(TokenKind.NAME);
1470
- return lexSegment;
1471
- } else {
1472
- l.error(`unexpected descendent selection token '${ch}'`);
1473
- return null;
1474
- }
1484
+ l.error(`unexpected descendent selection token '${ch}'`);
1485
+ return null;
1475
1486
  }
1476
1487
  }
1477
1488
  function lexDotSelector(l) {
@@ -1480,23 +1491,34 @@ function lexDotSelector(l) {
1480
1491
  l.error("unexpected whitespace after dot");
1481
1492
  return null;
1482
1493
  }
1483
- const ch = l.next();
1484
- if (ch === "*") {
1485
- l.emit(TokenKind.WILD);
1494
+ if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1495
+ l.emit(TokenKind.KEYS);
1486
1496
  return lexSegment;
1487
1497
  }
1488
- l.backup();
1489
1498
  if (l.acceptMatchRun(namePattern)) {
1490
1499
  l.emit(TokenKind.NAME);
1491
1500
  return lexSegment;
1492
- } else {
1493
- l.error(`unexpected shorthand selector '${ch}'`);
1494
- return null;
1495
1501
  }
1502
+ const ch = l.next();
1503
+ if (ch === "*") {
1504
+ l.emit(TokenKind.WILD);
1505
+ return lexSegment;
1506
+ }
1507
+ l.backup();
1508
+ l.error(`unexpected shorthand selector '${ch}'`);
1509
+ return null;
1496
1510
  }
1497
1511
  function lexInsideBracketedSelection(l) {
1498
1512
  for (;;) {
1499
1513
  l.ignoreWhitespace();
1514
+ if (l.acceptMatchRun(indexPattern)) {
1515
+ l.emit(TokenKind.INDEX);
1516
+ continue;
1517
+ }
1518
+ if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1519
+ l.emit(TokenKind.KEYS);
1520
+ continue;
1521
+ }
1500
1522
  const ch = l.next();
1501
1523
  switch (ch) {
1502
1524
  case "]":
@@ -1525,10 +1547,6 @@ function lexInsideBracketedSelection(l) {
1525
1547
  return lexDoubleQuoteStringInsideBracketSelection;
1526
1548
  default:
1527
1549
  l.backup();
1528
- if (l.acceptMatchRun(indexPattern)) {
1529
- l.emit(TokenKind.INDEX);
1530
- continue;
1531
- }
1532
1550
  l.error(`unexpected token '${ch}' in bracketed selection`);
1533
1551
  return null;
1534
1552
  }
@@ -1542,6 +1560,8 @@ function lexInsideFilter(l) {
1542
1560
  const ch = l.next();
1543
1561
  switch (ch) {
1544
1562
  case "":
1563
+ l.error("unclosed bracketed selection");
1564
+ return null;
1545
1565
  case "]":
1546
1566
  l.filterLevel -= 1;
1547
1567
  if (l.parenStack.length === 1) {
@@ -1583,6 +1603,9 @@ function lexInsideFilter(l) {
1583
1603
  case "@":
1584
1604
  l.emit(TokenKind.CURRENT);
1585
1605
  return lexSegment;
1606
+ case "#":
1607
+ l.emit(TokenKind.KEY);
1608
+ return lexSegment;
1586
1609
  case ".":
1587
1610
  l.backup();
1588
1611
  return lexSegment;
@@ -1761,7 +1784,7 @@ class NameSelector extends JSONPathSelector {
1761
1784
  resolve(nodes) {
1762
1785
  const rv = [];
1763
1786
  for (const node of nodes) {
1764
- if (hasStringKey(node.value, this.name)) {
1787
+ if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
1765
1788
  rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
1766
1789
  }
1767
1790
  }
@@ -1769,7 +1792,7 @@ class NameSelector extends JSONPathSelector {
1769
1792
  }
1770
1793
  *lazyResolve(nodes) {
1771
1794
  for (const node of nodes) {
1772
- if (hasStringKey(node.value, this.name)) {
1795
+ if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
1773
1796
  yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
1774
1797
  }
1775
1798
  }
@@ -1845,7 +1868,7 @@ class SliceSelector extends JSONPathSelector {
1845
1868
  *lazyResolve(nodes) {
1846
1869
  for (const node of nodes) {
1847
1870
  if (!isArray(node.value)) continue;
1848
- for (const [i, value] of this.slice(node.value, this.start, this.stop, this.step)) {
1871
+ for (const [i, value] of this.lazySlice(node.value, this.start, this.stop, this.step)) {
1849
1872
  yield new JSONPathNode(value, node.location.concat(i), node.root);
1850
1873
  }
1851
1874
  }
@@ -1908,6 +1931,45 @@ class SliceSelector extends JSONPathSelector {
1908
1931
  }
1909
1932
  return slicedArray;
1910
1933
  }
1934
+
1935
+ // eslint-disable-next-line sonarjs/cognitive-complexity
1936
+ *lazySlice(arr, start, stop, step) {
1937
+ if (!arr.length) return;
1938
+
1939
+ // Handle negative and undefined start values
1940
+ if (start === undefined || start === null) {
1941
+ start = step && step < 0 ? arr.length - 1 : 0;
1942
+ } else if (start < 0) {
1943
+ start = Math.max(arr.length + start, 0);
1944
+ } else {
1945
+ start = Math.min(start, arr.length - 1);
1946
+ }
1947
+
1948
+ // Handle negative and undefined stop values
1949
+ if (stop === undefined || stop === null) {
1950
+ stop = step && step < 0 ? -1 : arr.length;
1951
+ } else if (stop < 0) {
1952
+ stop = Math.max(arr.length + stop, -1);
1953
+ } else {
1954
+ stop = Math.min(stop, arr.length);
1955
+ }
1956
+
1957
+ // Perform the slice
1958
+ if (step === undefined) {
1959
+ // Default to a step of 1
1960
+ for (let i = start; i < stop; i += 1) {
1961
+ yield [i, arr[i]];
1962
+ }
1963
+ } else if (step > 0) {
1964
+ for (let i = start; i < stop; i += step) {
1965
+ yield [i, arr[i]];
1966
+ }
1967
+ } else if (step < 0) {
1968
+ for (let i = start; i > stop; i += step) {
1969
+ yield [i, arr[i]];
1970
+ }
1971
+ }
1972
+ }
1911
1973
  }
1912
1974
  class WildcardSelector extends JSONPathSelector {
1913
1975
  constructor(environment, token) {
@@ -1960,10 +2022,18 @@ class RecursiveDescentSegment extends JSONPathSelector {
1960
2022
  }
1961
2023
  resolve(nodes) {
1962
2024
  const rv = [];
1963
- for (const node of nodes) {
1964
- rv.push(node);
1965
- for (const _node of this.visit(node)) {
1966
- rv.push(_node);
2025
+ if (this.environment.nondeterministic) {
2026
+ for (const root of nodes) {
2027
+ for (const node of this.nondeterministicVisitor(root)) {
2028
+ rv.push(node);
2029
+ }
2030
+ }
2031
+ } else {
2032
+ for (const node of nodes) {
2033
+ rv.push(node);
2034
+ for (const _node of this.visitor(node)) {
2035
+ rv.push(_node);
2036
+ }
1967
2037
  }
1968
2038
  }
1969
2039
  return this.selector.resolve(rv);
@@ -2018,7 +2088,7 @@ class RecursiveDescentSegment extends JSONPathSelector {
2018
2088
  toString() {
2019
2089
  return `..${this.selector.toString()}`;
2020
2090
  }
2021
- visit(node) {
2091
+ visitor(node) {
2022
2092
  let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
2023
2093
  if (depth >= this.environment.maxRecursionDepth) {
2024
2094
  throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
@@ -2029,7 +2099,7 @@ class RecursiveDescentSegment extends JSONPathSelector {
2029
2099
  for (let i = 0; i < node.value.length; i++) {
2030
2100
  const _node = new JSONPathNode(node.value[i], node.location.concat(i), node.root);
2031
2101
  rv.push(_node);
2032
- for (const __node of this.visit(_node, depth + 1)) {
2102
+ for (const __node of this.visitor(_node, depth + 1)) {
2033
2103
  rv.push(__node);
2034
2104
  }
2035
2105
  }
@@ -2037,13 +2107,52 @@ class RecursiveDescentSegment extends JSONPathSelector {
2037
2107
  for (const [key, value] of this.environment.entries(node.value)) {
2038
2108
  const _node = new JSONPathNode(value, node.location.concat(key), node.root);
2039
2109
  rv.push(_node);
2040
- for (const __node of this.visit(_node, depth + 1)) {
2110
+ for (const __node of this.visitor(_node, depth + 1)) {
2041
2111
  rv.push(__node);
2042
2112
  }
2043
2113
  }
2044
2114
  }
2045
2115
  return rv;
2046
2116
  }
2117
+ nondeterministicVisitor(root) {
2118
+ let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
2119
+ const rv = [root];
2120
+ let queue = this.nondeterministicChildren(root).map(node => [node, depth]);
2121
+ while (queue.length) {
2122
+ const [node, _depth] = queue.shift();
2123
+ rv.push(node);
2124
+ if (_depth >= this.environment.maxRecursionDepth) {
2125
+ throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
2126
+ }
2127
+
2128
+ // Visit child nodes now or queue them for later?
2129
+ const visitChildren = Math.random() < 0.5;
2130
+ for (const child of this.nondeterministicChildren(node)) {
2131
+ if (visitChildren) {
2132
+ rv.push(child);
2133
+ const grandchildren = this.nondeterministicChildren(child).map(n => [n, _depth + 2]);
2134
+ queue = interleave(queue, grandchildren);
2135
+ } else {
2136
+ queue.push([child, _depth + 1]);
2137
+ }
2138
+ }
2139
+ }
2140
+ return rv;
2141
+ }
2142
+ nondeterministicChildren(node) {
2143
+ const _rv = [];
2144
+ if (node.value instanceof String) return _rv;
2145
+ if (isArray(node.value)) {
2146
+ for (let i = 0; i < node.value.length; i++) {
2147
+ _rv.push(new JSONPathNode(node.value[i], node.location.concat(i), node.root));
2148
+ }
2149
+ } else if (isObject(node.value)) {
2150
+ for (const [key, value] of this.environment.entries(node.value)) {
2151
+ _rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
2152
+ }
2153
+ }
2154
+ return _rv;
2155
+ }
2047
2156
  }
2048
2157
  class FilterSelector extends JSONPathSelector {
2049
2158
  constructor(environment, token, expression) {
@@ -2064,7 +2173,8 @@ class FilterSelector extends JSONPathSelector {
2064
2173
  const filterContext = {
2065
2174
  environment: this.environment,
2066
2175
  currentValue: value,
2067
- rootValue: node.root
2176
+ rootValue: node.root,
2177
+ currentKey: i
2068
2178
  };
2069
2179
  if (this.expression.evaluate(filterContext)) {
2070
2180
  rv.push(new JSONPathNode(value, node.location.concat(i), node.root));
@@ -2075,7 +2185,8 @@ class FilterSelector extends JSONPathSelector {
2075
2185
  const filterContext = {
2076
2186
  environment: this.environment,
2077
2187
  currentValue: value,
2078
- rootValue: node.root
2188
+ rootValue: node.root,
2189
+ currentKey: key
2079
2190
  };
2080
2191
  if (this.expression.evaluate(filterContext)) {
2081
2192
  rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
@@ -2097,7 +2208,8 @@ class FilterSelector extends JSONPathSelector {
2097
2208
  environment: this.environment,
2098
2209
  currentValue: value,
2099
2210
  rootValue: node.root,
2100
- lazy: true
2211
+ lazy: true,
2212
+ currentKey: i
2101
2213
  };
2102
2214
  if (this.expression.evaluate(filterContext)) {
2103
2215
  yield new JSONPathNode(value, node.location.concat(i), node.root);
@@ -2109,7 +2221,8 @@ class FilterSelector extends JSONPathSelector {
2109
2221
  environment: this.environment,
2110
2222
  currentValue: value,
2111
2223
  rootValue: node.root,
2112
- lazy: true
2224
+ lazy: true,
2225
+ currentKey: key
2113
2226
  };
2114
2227
  if (this.expression.evaluate(filterContext)) {
2115
2228
  yield new JSONPathNode(value, node.location.concat(key), node.root);
@@ -2152,6 +2265,41 @@ class BracketedSelection extends JSONPathSelector {
2152
2265
  }
2153
2266
  }
2154
2267
 
2268
+ /**
2269
+ * Randomly interleave elements from two arrays while maintaining relative
2270
+ * order of each input array.
2271
+ *
2272
+ * If _arrayA_ is empty, _arrayB_ is returned, and vice versa.
2273
+ */
2274
+ function interleave(arrayA, arrayB) {
2275
+ if (arrayA.length === 0) {
2276
+ return arrayB;
2277
+ }
2278
+ if (arrayB.length === 0) {
2279
+ return arrayA;
2280
+ }
2281
+
2282
+ // An array of iterators
2283
+ const iterators = [];
2284
+ const itA = arrayA[Symbol.iterator]();
2285
+ const itB = arrayB[Symbol.iterator]();
2286
+ for (let i = 0; i < arrayA.length; i++) {
2287
+ iterators.push(itA);
2288
+ }
2289
+ for (let i = 0; i < arrayB.length; i++) {
2290
+ iterators.push(itB);
2291
+ }
2292
+ shuffle(iterators);
2293
+ return iterators.map(it => it.next().value);
2294
+ }
2295
+ function shuffle(entries) {
2296
+ for (let i = entries.length - 1; i > 0; i--) {
2297
+ const j = Math.floor(Math.random() * (i + 1));
2298
+ [entries[i], entries[j]] = [entries[j], entries[i]];
2299
+ }
2300
+ return entries;
2301
+ }
2302
+
2155
2303
  var selectors = /*#__PURE__*/Object.freeze({
2156
2304
  __proto__: null,
2157
2305
  BracketedSelection: BracketedSelection,
@@ -2235,6 +2383,57 @@ class JSONPath {
2235
2383
  }
2236
2384
  }
2237
2385
 
2386
+ class CurrentKey extends FilterExpression {
2387
+ evaluate(context) {
2388
+ return context.currentKey ?? Nothing;
2389
+ }
2390
+ toString() {
2391
+ return "#";
2392
+ }
2393
+ }
2394
+
2395
+ /**
2396
+ * Object property name selector or array index selector.
2397
+ */
2398
+ class KeysSelector extends JSONPathSelector {
2399
+ constructor(environment, token) {
2400
+ let shorthand = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
2401
+ super(environment, token);
2402
+ this.environment = environment;
2403
+ this.token = token;
2404
+ this.shorthand = shorthand;
2405
+ }
2406
+ resolve(nodes) {
2407
+ const rv = [];
2408
+ for (const node of nodes) {
2409
+ if (node.value instanceof String || isArray(node.value)) continue;
2410
+ if (isObject(node.value)) {
2411
+ let i = 0;
2412
+ for (const [key, _] of this.environment.entries(node.value)) {
2413
+ rv.push(new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root));
2414
+ i++;
2415
+ }
2416
+ }
2417
+ }
2418
+ return rv;
2419
+ }
2420
+ *lazyResolve(nodes) {
2421
+ for (const node of nodes) {
2422
+ if (node.value instanceof String || isArray(node.value)) continue;
2423
+ if (isObject(node.value)) {
2424
+ let i = 0;
2425
+ for (const [key, _] of this.environment.entries(node.value)) {
2426
+ yield new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root);
2427
+ i++;
2428
+ }
2429
+ }
2430
+ }
2431
+ }
2432
+ toString() {
2433
+ return this.shorthand ? "[~]" : "~";
2434
+ }
2435
+ }
2436
+
2238
2437
  const PRECEDENCE_LOWEST = 1;
2239
2438
  const PRECEDENCE_LOGICAL_OR = 4;
2240
2439
  const PRECEDENCE_LOGICAL_AND = 5;
@@ -2250,7 +2449,7 @@ const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
2250
2449
  class Parser {
2251
2450
  constructor(environment) {
2252
2451
  this.environment = environment;
2253
- 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]]);
2452
+ 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.KEY, this.parseCurrentKey], [TokenKind.KEY, this.parseCurrentKey]]);
2254
2453
  }
2255
2454
  parse(stream) {
2256
2455
  if (stream.current.kind === TokenKind.ROOT) stream.next();
@@ -2282,6 +2481,8 @@ class Parser {
2282
2481
  return new NameSelector(this.environment, stream.current, stream.current.value, true);
2283
2482
  case TokenKind.WILD:
2284
2483
  return new WildcardSelector(this.environment, stream.current, true);
2484
+ case TokenKind.KEYS:
2485
+ return new KeysSelector(this.environment, stream.current, true);
2285
2486
  case TokenKind.DDOT:
2286
2487
  {
2287
2488
  const segmentToken = stream.current;
@@ -2375,6 +2576,9 @@ class Parser {
2375
2576
  case TokenKind.WILD:
2376
2577
  items.push(new WildcardSelector(this.environment, stream.current));
2377
2578
  break;
2579
+ case TokenKind.KEYS:
2580
+ items.push(new KeysSelector(this.environment, stream.current));
2581
+ break;
2378
2582
  case TokenKind.EOF:
2379
2583
  throw new JSONPathSyntaxError("unexpected end of query", stream.current);
2380
2584
  default:
@@ -2435,13 +2639,20 @@ class Parser {
2435
2639
  return new InfixExpression(tok, left, operator, right);
2436
2640
  }
2437
2641
  parseGroupedExpression(stream) {
2438
- stream.next();
2642
+ if (stream.peek.kind === TokenKind.RPAREN) {
2643
+ throw new JSONPathSyntaxError(`empty paren expression`, stream.current);
2644
+ }
2645
+ stream.next(); // eat open paren
2646
+
2439
2647
  let expr = this.parseFilterExpression(stream);
2440
2648
  stream.next();
2441
2649
  while (stream.current.kind !== TokenKind.RPAREN) {
2442
2650
  if (stream.current.kind === TokenKind.EOF) {
2443
2651
  throw new JSONPathSyntaxError("unbalanced parentheses", stream.current);
2444
2652
  }
2653
+ if (!BINARY_OPERATORS.has(stream.current.kind)) {
2654
+ throw new JSONPathSyntaxError(`expected an expression, found '${stream.current.value}'`, stream.current);
2655
+ }
2445
2656
  expr = this.parseInfixExpression(stream, expr);
2446
2657
  }
2447
2658
  stream.expect(TokenKind.RPAREN);
@@ -2455,6 +2666,9 @@ class Parser {
2455
2666
  const tok = stream.next();
2456
2667
  return new RelativeQuery(tok, new JSONPath(this.environment, this.parsePath(stream, true)));
2457
2668
  }
2669
+ parseCurrentKey(stream) {
2670
+ return new CurrentKey(stream.current);
2671
+ }
2458
2672
  parseFunction(stream) {
2459
2673
  const args = [];
2460
2674
  const tok = stream.next();
@@ -2494,7 +2708,7 @@ class Parser {
2494
2708
  msg = "end of expression";
2495
2709
  break;
2496
2710
  default:
2497
- msg = `'${stream.current.value}`;
2711
+ msg = `'${stream.current.value}'`;
2498
2712
  }
2499
2713
  throw new JSONPathSyntaxError(`unexpected ${msg}`, stream.current);
2500
2714
  }
@@ -2571,6 +2785,10 @@ class JSONPathEnvironment {
2571
2785
  * If `true`, enable nondeterministic ordering when iterating JSON object data.
2572
2786
  */
2573
2787
 
2788
+ /**
2789
+ * The pattern to use for the non-standard _keys selector_.
2790
+ */
2791
+
2574
2792
  /**
2575
2793
  * A map of function names to objects implementing the {@link FilterFunction}
2576
2794
  * interface. You are free to set or delete custom filter functions directly.
@@ -2586,6 +2804,7 @@ class JSONPathEnvironment {
2586
2804
  this.minIntIndex = options.maxIntIndex ?? -Math.pow(2, 53) - 1;
2587
2805
  this.maxRecursionDepth = options.maxRecursionDepth ?? 50;
2588
2806
  this.nondeterministic = options.nondeterministic ?? false;
2807
+ this.keysPattern = options.keysPattern ?? /~/y;
2589
2808
  this.parser = new Parser(this);
2590
2809
  this.setupFilterFunctions();
2591
2810
  }
@@ -2595,7 +2814,7 @@ class JSONPathEnvironment {
2595
2814
  * @returns A new {@link JSONPath} object, bound to this environment.
2596
2815
  */
2597
2816
  compile(path) {
2598
- return new JSONPath(this, this.parser.parse(new TokenStream(tokenize(path))));
2817
+ return new JSONPath(this, this.parser.parse(new TokenStream(tokenize(this, path))));
2599
2818
  }
2600
2819
 
2601
2820
  /**
@@ -2677,7 +2896,7 @@ class JSONPathEnvironment {
2677
2896
  for (const [typ, arg, idx] of func.argTypes.map((t, i) => [t, args[i], i])) {
2678
2897
  switch (typ) {
2679
2898
  case FunctionExpressionType.ValueType:
2680
- if (!(arg instanceof FilterExpressionLiteral || arg instanceof JSONPathQuery && arg.path.singularQuery() || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.ValueType)) {
2899
+ if (!(arg instanceof FilterExpressionLiteral || arg instanceof CurrentKey || arg instanceof JSONPathQuery && arg.path.singularQuery() || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.ValueType)) {
2681
2900
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of ValueType`, arg.token);
2682
2901
  }
2683
2902
  break;
@@ -3305,6 +3524,6 @@ var index = /*#__PURE__*/Object.freeze({
3305
3524
  apply: apply
3306
3525
  });
3307
3526
 
3308
- const version = "1.1.1";
3527
+ const version = "1.2.1";
3309
3528
 
3310
3529
  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 };