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