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