json-p3 1.2.1 → 1.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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- * json-p3 version 1.2.1
2
+ * json-p3 version 1.3.0
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
@@ -641,6 +641,24 @@ var index$3 = /*#__PURE__*/Object.freeze({
641
641
  resolve: resolve
642
642
  });
643
643
 
644
+ const Nothing = Symbol.for("jsonpath.nothing");
645
+
646
+ /**
647
+ * ValueType for JSONPath function expression tye system.
648
+ */
649
+
650
+ /**
651
+ * Object passed to FilterExpression.evaluate().
652
+ */
653
+
654
+ /**
655
+ * A type predicate for an object with a string property.
656
+ */
657
+ function hasStringKey(value, key) {
658
+ return isObject(value) && Object.hasOwn(value, key);
659
+ }
660
+ const KEY_MARK = "\x02";
661
+
644
662
  /**
645
663
  * The pair of a JSON value and its location found in the target JSON value.
646
664
  */
@@ -658,7 +676,7 @@ class JSONPathNode {
658
676
  get path() {
659
677
  return (
660
678
  // eslint-disable-next-line prefer-template
661
- "$" + this.location.map(s => isString(s) ? `['${s}']` : `[${s}]`).join("")
679
+ "$" + this.location.map(s => isString(s) ? this.decode_name_location(s) : `[${s}]`).join("")
662
680
  );
663
681
  }
664
682
 
@@ -671,6 +689,9 @@ class JSONPathNode {
671
689
  }
672
690
  return new JSONPointer(JSONPointer.encode(this.location.map(String)));
673
691
  }
692
+ decode_name_location(name) {
693
+ return name.startsWith(KEY_MARK) ? `[~'${name.slice(1).replaceAll("'", "\\'")}']` : `['${name.replaceAll("'", "\\'")}']`;
694
+ }
674
695
  }
675
696
 
676
697
  /**
@@ -750,23 +771,6 @@ class JSONPathNodeList {
750
771
  }
751
772
  }
752
773
 
753
- const Nothing = Symbol.for("jsonpath.nothing");
754
-
755
- /**
756
- * ValueType for JSONPath function expression tye system.
757
- */
758
-
759
- /**
760
- * Object passed to FilterExpression.evaluate().
761
- */
762
-
763
- /**
764
- * A type predicate for an object with a string property.
765
- */
766
- function hasStringKey(value, key) {
767
- return isObject(value) && Object.hasOwn(value, key);
768
- }
769
-
770
774
  /**
771
775
  * Base class for all filter expressions.
772
776
  */
@@ -1169,7 +1173,8 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1169
1173
  TokenKind["AND"] = "TOKEN_AND";
1170
1174
  TokenKind["COLON"] = "TOKEN_COLON";
1171
1175
  TokenKind["COMMA"] = "TOKEN_COMMA";
1172
- TokenKind["CURRENT"] = "TOKEN_CURRENT_NODE";
1176
+ TokenKind["CURRENT"] = "TOKEN_CURRENT_VALUE";
1177
+ TokenKind["CURRENT_KEY"] = "TOKEN_CURRENT_KEY";
1173
1178
  TokenKind["DDOT"] = "TOKEN_DDOT";
1174
1179
  TokenKind["DOT"] = "TOKEN_DOT";
1175
1180
  TokenKind["DOUBLE_QUOTE_STRING"] = "TOKEN_DOUBLE_QUOTE_STRING";
@@ -1183,7 +1188,10 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1183
1188
  TokenKind["GT"] = "TOKEN_GT";
1184
1189
  TokenKind["INDEX"] = "TOKEN_INDEX";
1185
1190
  TokenKind["KEY"] = "TOKEN_KEY";
1191
+ TokenKind["KEY_DOUBLE_QUOTE_STRING"] = "TOKEN_KEY_DOUBLE_QUOTE_STRING";
1192
+ TokenKind["KEY_SINGLE_QUOTE_STRING"] = "TOKEN_KEY_SINGLE_QUOTE_STRING";
1186
1193
  TokenKind["KEYS"] = "TOKEN_KEYS";
1194
+ TokenKind["KEYS_FILTER"] = "TOKEN_KEYS_FILTER";
1187
1195
  TokenKind["LBRACKET"] = "TOKEN_LBRACKET";
1188
1196
  TokenKind["LE"] = "TOKEN_LE";
1189
1197
  TokenKind["LG"] = "TOKEN_LG";
@@ -1264,6 +1272,7 @@ const indexPattern = /-?\d+/y;
1264
1272
  const intPattern = /-?[0-9]+/y;
1265
1273
  const namePattern = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/y;
1266
1274
  const whitespace = new Set([" ", "\n", "\t", "\r"]);
1275
+ const nameFirstPattern = /[\u0080-\uFFFFa-zA-Z_]/; // don't set sticky bit
1267
1276
 
1268
1277
  /**
1269
1278
  * JSONPath lexical scanner.
@@ -1336,6 +1345,11 @@ class Lexer {
1336
1345
  if (ch) this.backup();
1337
1346
  return ch;
1338
1347
  }
1348
+ peekMatch(pattern) {
1349
+ const ch = this.next();
1350
+ if (ch) this.backup();
1351
+ return pattern.test(ch);
1352
+ }
1339
1353
  accept(valid) {
1340
1354
  const ch = this.next();
1341
1355
  if (valid.has(ch)) return true;
@@ -1465,10 +1479,28 @@ function lexDescendantSelection(l) {
1465
1479
  l.emit(TokenKind.NAME);
1466
1480
  return lexSegment;
1467
1481
  }
1468
- if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1469
- // Non-standard keys selector
1470
- l.emit(TokenKind.KEYS);
1471
- return lexSegment;
1482
+ if (!l.environment.strict) {
1483
+ // We're effectively disabling the _key selector_ and _keys filter selector_ if a
1484
+ // custom _keys selector_ is set.
1485
+ if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
1486
+ l.next();
1487
+ if (l.peekMatch(nameFirstPattern)) {
1488
+ // Non-standard key selector
1489
+ l.ignore(); // ignore ~
1490
+ l.acceptMatchRun(namePattern);
1491
+ l.emit(TokenKind.KEY);
1492
+ return lexSegment;
1493
+ } else {
1494
+ // Non-standard keys selector
1495
+ l.emit(TokenKind.KEYS);
1496
+ return lexSegment;
1497
+ }
1498
+ } else if (l.acceptMatchRun(l.environment.keysPattern)) {
1499
+ // NOTE: A custom keys pattern does not play well with other non-standard key selectors.
1500
+ // We leave this here for backwards compatibility.
1501
+ l.emit(TokenKind.KEYS);
1502
+ return lexSegment;
1503
+ }
1472
1504
  }
1473
1505
  const ch = l.next();
1474
1506
  switch (ch) {
@@ -1493,9 +1525,28 @@ function lexDotSelector(l) {
1493
1525
  l.error("unexpected whitespace after dot");
1494
1526
  return null;
1495
1527
  }
1496
- if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1497
- l.emit(TokenKind.KEYS);
1498
- return lexSegment;
1528
+ if (!l.environment.strict) {
1529
+ // We're effectively disabling the _key selector_ and _keys filter selector_ if a
1530
+ // custom _keys selector_ is set.
1531
+ if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
1532
+ l.next();
1533
+ if (l.peekMatch(nameFirstPattern)) {
1534
+ // Non-standard key selector
1535
+ l.ignore(); // ignore ~
1536
+ l.acceptMatchRun(namePattern);
1537
+ l.emit(TokenKind.KEY);
1538
+ return lexSegment;
1539
+ } else {
1540
+ // Non-standard keys selector
1541
+ l.emit(TokenKind.KEYS);
1542
+ return lexSegment;
1543
+ }
1544
+ } else if (l.acceptMatchRun(l.environment.keysPattern)) {
1545
+ // NOTE: A custom keys pattern does not play well with other non-standard key selectors.
1546
+ // We leave this here for backwards compatibility.
1547
+ l.emit(TokenKind.KEYS);
1548
+ return lexSegment;
1549
+ }
1499
1550
  }
1500
1551
  if (l.acceptMatchRun(namePattern)) {
1501
1552
  l.emit(TokenKind.NAME);
@@ -1518,8 +1569,25 @@ function lexInsideBracketedSelection(l) {
1518
1569
  continue;
1519
1570
  }
1520
1571
  if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1521
- l.emit(TokenKind.KEYS);
1522
- continue;
1572
+ switch (l.peek()) {
1573
+ case "'":
1574
+ l.ignore(); // ~
1575
+ l.next();
1576
+ return lexSingleQuoteKeyString(l);
1577
+ case '"':
1578
+ l.ignore(); // ~
1579
+ l.next();
1580
+ return lexDoubleQuoteKeyString(l);
1581
+ case "?":
1582
+ l.ignore(); // ~
1583
+ l.next();
1584
+ l.emit(TokenKind.KEYS_FILTER);
1585
+ l.filterLevel += 1;
1586
+ return lexInsideFilter;
1587
+ default:
1588
+ l.emit(TokenKind.KEYS);
1589
+ continue;
1590
+ }
1523
1591
  }
1524
1592
  const ch = l.next();
1525
1593
  switch (ch) {
@@ -1606,7 +1674,7 @@ function lexInsideFilter(l) {
1606
1674
  l.emit(TokenKind.CURRENT);
1607
1675
  return lexSegment;
1608
1676
  case "#":
1609
- l.emit(TokenKind.KEY);
1677
+ l.emit(TokenKind.CURRENT_KEY);
1610
1678
  return lexSegment;
1611
1679
  case ".":
1612
1680
  l.backup();
@@ -1706,8 +1774,7 @@ function lexInsideFilter(l) {
1706
1774
  * @param state - The state function to return control to.
1707
1775
  * @returns String tokenizing state function.
1708
1776
  */
1709
- function makeLexString(quote, state) {
1710
- // eslint-disable-next-line sonarjs/cognitive-complexity
1777
+ function makeLexString(quote, state, token_kind) {
1711
1778
  function _lexString(l) {
1712
1779
  l.ignore();
1713
1780
  if (l.peek() === quote) {
@@ -1733,7 +1800,7 @@ function makeLexString(quote, state) {
1733
1800
  }
1734
1801
  if (ch === quote) {
1735
1802
  l.backup();
1736
- l.emit(quote === "'" ? TokenKind.SINGLE_QUOTE_STRING : TokenKind.DOUBLE_QUOTE_STRING);
1803
+ l.emit(token_kind);
1737
1804
  l.next();
1738
1805
  l.ignore();
1739
1806
  return state;
@@ -1742,10 +1809,12 @@ function makeLexString(quote, state) {
1742
1809
  }
1743
1810
  return _lexString;
1744
1811
  }
1745
- const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection);
1746
- const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection);
1747
- const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter);
1748
- const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter);
1812
+ const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection, TokenKind.SINGLE_QUOTE_STRING);
1813
+ const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection, TokenKind.DOUBLE_QUOTE_STRING);
1814
+ const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter, TokenKind.SINGLE_QUOTE_STRING);
1815
+ const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter, TokenKind.DOUBLE_QUOTE_STRING);
1816
+ const lexSingleQuoteKeyString = makeLexString("'", lexInsideBracketedSelection, TokenKind.KEY_SINGLE_QUOTE_STRING);
1817
+ const lexDoubleQuoteKeyString = makeLexString('"', lexInsideBracketedSelection, TokenKind.KEY_DOUBLE_QUOTE_STRING);
1749
1818
 
1750
1819
  /**
1751
1820
  * Base class for all JSONPath segments and selectors.
@@ -2394,6 +2463,38 @@ class CurrentKey extends FilterExpression {
2394
2463
  }
2395
2464
  }
2396
2465
 
2466
+ class KeySelector extends JSONPathSelector {
2467
+ constructor(environment, token, key) {
2468
+ let shorthand = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
2469
+ super(environment, token);
2470
+ this.environment = environment;
2471
+ this.token = token;
2472
+ this.key = key;
2473
+ this.shorthand = shorthand;
2474
+ }
2475
+ resolve(nodes) {
2476
+ const rv = [];
2477
+ for (const node of nodes) {
2478
+ if (node.value instanceof String || isArray(node.value)) continue;
2479
+ if (isObject(node.value) && hasStringKey(node.value, this.key)) {
2480
+ rv.push(new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root));
2481
+ }
2482
+ }
2483
+ return rv;
2484
+ }
2485
+ *lazyResolve(nodes) {
2486
+ for (const node of nodes) {
2487
+ if (node.value instanceof String || isArray(node.value)) continue;
2488
+ if (isObject(node.value) && hasStringKey(node.value, this.key)) {
2489
+ yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
2490
+ }
2491
+ }
2492
+ }
2493
+ toString() {
2494
+ return this.shorthand ? `[~'${this.key.replaceAll("'", "\\'")}']` : `~'${this.key.replaceAll("'", "\\'")}'`;
2495
+ }
2496
+ }
2497
+
2397
2498
  /**
2398
2499
  * Object property name selector or array index selector.
2399
2500
  */
@@ -2410,10 +2511,8 @@ class KeysSelector extends JSONPathSelector {
2410
2511
  for (const node of nodes) {
2411
2512
  if (node.value instanceof String || isArray(node.value)) continue;
2412
2513
  if (isObject(node.value)) {
2413
- let i = 0;
2414
2514
  for (const [key, _] of this.environment.entries(node.value)) {
2415
- rv.push(new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root));
2416
- i++;
2515
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
2417
2516
  }
2418
2517
  }
2419
2518
  }
@@ -2423,10 +2522,8 @@ class KeysSelector extends JSONPathSelector {
2423
2522
  for (const node of nodes) {
2424
2523
  if (node.value instanceof String || isArray(node.value)) continue;
2425
2524
  if (isObject(node.value)) {
2426
- let i = 0;
2427
2525
  for (const [key, _] of this.environment.entries(node.value)) {
2428
- yield new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root);
2429
- i++;
2526
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
2430
2527
  }
2431
2528
  }
2432
2529
  }
@@ -2435,6 +2532,56 @@ class KeysSelector extends JSONPathSelector {
2435
2532
  return this.shorthand ? "[~]" : "~";
2436
2533
  }
2437
2534
  }
2535
+ class KeysFilterSelector extends JSONPathSelector {
2536
+ constructor(environment, token, expression) {
2537
+ super(environment, token);
2538
+ this.environment = environment;
2539
+ this.token = token;
2540
+ this.expression = expression;
2541
+ }
2542
+ resolve(nodes) {
2543
+ const rv = [];
2544
+ for (const node of nodes) {
2545
+ if (node.value instanceof String || isArray(node.value)) continue;
2546
+ if (isObject(node.value)) {
2547
+ for (const [key, value] of this.environment.entries(node.value)) {
2548
+ const filterContext = {
2549
+ environment: this.environment,
2550
+ currentValue: value,
2551
+ rootValue: node.root,
2552
+ currentKey: key
2553
+ };
2554
+ if (this.expression.evaluate(filterContext)) {
2555
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
2556
+ }
2557
+ }
2558
+ }
2559
+ }
2560
+ return rv;
2561
+ }
2562
+ *lazyResolve(nodes) {
2563
+ for (const node of nodes) {
2564
+ if (node.value instanceof String || isArray(node.value)) continue;
2565
+ if (isObject(node.value)) {
2566
+ for (const [key, value] of this.environment.entries(node.value)) {
2567
+ const filterContext = {
2568
+ environment: this.environment,
2569
+ currentValue: value,
2570
+ rootValue: node.root,
2571
+ lazy: true,
2572
+ currentKey: key
2573
+ };
2574
+ if (this.expression.evaluate(filterContext)) {
2575
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
2576
+ }
2577
+ }
2578
+ }
2579
+ }
2580
+ }
2581
+ toString() {
2582
+ return `~?${this.expression.toString()}`;
2583
+ }
2584
+ }
2438
2585
 
2439
2586
  const PRECEDENCE_LOWEST = 1;
2440
2587
  const PRECEDENCE_LOGICAL_OR = 4;
@@ -2451,7 +2598,7 @@ const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
2451
2598
  class Parser {
2452
2599
  constructor(environment) {
2453
2600
  this.environment = environment;
2454
- this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction], [TokenKind.KEY, this.parseCurrentKey], [TokenKind.KEY, this.parseCurrentKey]]);
2601
+ this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction], [TokenKind.CURRENT_KEY, this.parseCurrentKey]]);
2455
2602
  }
2456
2603
  parse(stream) {
2457
2604
  if (stream.current.kind === TokenKind.ROOT) stream.next();
@@ -2483,6 +2630,8 @@ class Parser {
2483
2630
  return new NameSelector(this.environment, stream.current, stream.current.value, true);
2484
2631
  case TokenKind.WILD:
2485
2632
  return new WildcardSelector(this.environment, stream.current, true);
2633
+ case TokenKind.KEY:
2634
+ return new KeySelector(this.environment, stream.current, stream.current.value, true);
2486
2635
  case TokenKind.KEYS:
2487
2636
  return new KeysSelector(this.environment, stream.current, true);
2488
2637
  case TokenKind.DDOT:
@@ -2578,6 +2727,13 @@ class Parser {
2578
2727
  case TokenKind.WILD:
2579
2728
  items.push(new WildcardSelector(this.environment, stream.current));
2580
2729
  break;
2730
+ case TokenKind.KEY_SINGLE_QUOTE_STRING:
2731
+ case TokenKind.KEY_DOUBLE_QUOTE_STRING:
2732
+ items.push(new KeySelector(this.environment, stream.current, this.decodeString(stream.current, true), false));
2733
+ break;
2734
+ case TokenKind.KEYS_FILTER:
2735
+ items.push(this.parseFilter(stream, true));
2736
+ break;
2581
2737
  case TokenKind.KEYS:
2582
2738
  items.push(new KeysSelector(this.environment, stream.current));
2583
2739
  break;
@@ -2598,6 +2754,7 @@ class Parser {
2598
2754
  return new BracketedSelection(this.environment, token, items);
2599
2755
  }
2600
2756
  parseFilter(stream) {
2757
+ let keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2601
2758
  const tok = stream.next();
2602
2759
  const expr = this.parseFilterExpression(stream);
2603
2760
  if (expr instanceof FunctionExtension) {
@@ -2606,7 +2763,7 @@ class Parser {
2606
2763
  throw new JSONPathTypeError(`result of ${expr.name}() must be compared`, expr.token);
2607
2764
  }
2608
2765
  }
2609
- return new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
2766
+ return keys ? new KeysFilterSelector(this.environment, tok, new LogicalExpression(tok, expr)) : new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
2610
2767
  }
2611
2768
  parseBoolean(stream) {
2612
2769
  if (stream.current.kind === TokenKind.FALSE) return new BooleanLiteral(stream.current, false);
@@ -3034,6 +3191,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
3034
3191
  JSONPathRecursionLimitError: JSONPathRecursionLimitError,
3035
3192
  JSONPathSyntaxError: JSONPathSyntaxError,
3036
3193
  JSONPathTypeError: JSONPathTypeError,
3194
+ KEY_MARK: KEY_MARK,
3037
3195
  Nothing: Nothing,
3038
3196
  Token: Token,
3039
3197
  TokenKind: TokenKind,
@@ -3526,7 +3684,7 @@ var index = /*#__PURE__*/Object.freeze({
3526
3684
  apply: apply
3527
3685
  });
3528
3686
 
3529
- const version = "1.2.1";
3687
+ const version = "1.3.0";
3530
3688
 
3531
3689
  exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
3532
3690
  exports.FunctionExpressionType = FunctionExpressionType;