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
@@ -639,6 +639,24 @@ var index$3 = /*#__PURE__*/Object.freeze({
639
639
  resolve: resolve
640
640
  });
641
641
 
642
+ const Nothing = Symbol.for("jsonpath.nothing");
643
+
644
+ /**
645
+ * ValueType for JSONPath function expression tye system.
646
+ */
647
+
648
+ /**
649
+ * Object passed to FilterExpression.evaluate().
650
+ */
651
+
652
+ /**
653
+ * A type predicate for an object with a string property.
654
+ */
655
+ function hasStringKey(value, key) {
656
+ return isObject(value) && Object.hasOwn(value, key);
657
+ }
658
+ const KEY_MARK = "\x02";
659
+
642
660
  /**
643
661
  * The pair of a JSON value and its location found in the target JSON value.
644
662
  */
@@ -656,7 +674,7 @@ class JSONPathNode {
656
674
  get path() {
657
675
  return (
658
676
  // eslint-disable-next-line prefer-template
659
- "$" + this.location.map(s => isString(s) ? `['${s}']` : `[${s}]`).join("")
677
+ "$" + this.location.map(s => isString(s) ? this.decode_name_location(s) : `[${s}]`).join("")
660
678
  );
661
679
  }
662
680
 
@@ -669,6 +687,9 @@ class JSONPathNode {
669
687
  }
670
688
  return new JSONPointer(JSONPointer.encode(this.location.map(String)));
671
689
  }
690
+ decode_name_location(name) {
691
+ return name.startsWith(KEY_MARK) ? `[~'${name.slice(1).replaceAll("'", "\\'")}']` : `['${name.replaceAll("'", "\\'")}']`;
692
+ }
672
693
  }
673
694
 
674
695
  /**
@@ -748,23 +769,6 @@ class JSONPathNodeList {
748
769
  }
749
770
  }
750
771
 
751
- const Nothing = Symbol.for("jsonpath.nothing");
752
-
753
- /**
754
- * ValueType for JSONPath function expression tye system.
755
- */
756
-
757
- /**
758
- * Object passed to FilterExpression.evaluate().
759
- */
760
-
761
- /**
762
- * A type predicate for an object with a string property.
763
- */
764
- function hasStringKey(value, key) {
765
- return isObject(value) && Object.hasOwn(value, key);
766
- }
767
-
768
772
  /**
769
773
  * Base class for all filter expressions.
770
774
  */
@@ -1167,7 +1171,8 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1167
1171
  TokenKind["AND"] = "TOKEN_AND";
1168
1172
  TokenKind["COLON"] = "TOKEN_COLON";
1169
1173
  TokenKind["COMMA"] = "TOKEN_COMMA";
1170
- TokenKind["CURRENT"] = "TOKEN_CURRENT_NODE";
1174
+ TokenKind["CURRENT"] = "TOKEN_CURRENT_VALUE";
1175
+ TokenKind["CURRENT_KEY"] = "TOKEN_CURRENT_KEY";
1171
1176
  TokenKind["DDOT"] = "TOKEN_DDOT";
1172
1177
  TokenKind["DOT"] = "TOKEN_DOT";
1173
1178
  TokenKind["DOUBLE_QUOTE_STRING"] = "TOKEN_DOUBLE_QUOTE_STRING";
@@ -1181,7 +1186,10 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1181
1186
  TokenKind["GT"] = "TOKEN_GT";
1182
1187
  TokenKind["INDEX"] = "TOKEN_INDEX";
1183
1188
  TokenKind["KEY"] = "TOKEN_KEY";
1189
+ TokenKind["KEY_DOUBLE_QUOTE_STRING"] = "TOKEN_KEY_DOUBLE_QUOTE_STRING";
1190
+ TokenKind["KEY_SINGLE_QUOTE_STRING"] = "TOKEN_KEY_SINGLE_QUOTE_STRING";
1184
1191
  TokenKind["KEYS"] = "TOKEN_KEYS";
1192
+ TokenKind["KEYS_FILTER"] = "TOKEN_KEYS_FILTER";
1185
1193
  TokenKind["LBRACKET"] = "TOKEN_LBRACKET";
1186
1194
  TokenKind["LE"] = "TOKEN_LE";
1187
1195
  TokenKind["LG"] = "TOKEN_LG";
@@ -1262,6 +1270,7 @@ const indexPattern = /-?\d+/y;
1262
1270
  const intPattern = /-?[0-9]+/y;
1263
1271
  const namePattern = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/y;
1264
1272
  const whitespace = new Set([" ", "\n", "\t", "\r"]);
1273
+ const nameFirstPattern = /[\u0080-\uFFFFa-zA-Z_]/; // don't set sticky bit
1265
1274
 
1266
1275
  /**
1267
1276
  * JSONPath lexical scanner.
@@ -1334,6 +1343,11 @@ class Lexer {
1334
1343
  if (ch) this.backup();
1335
1344
  return ch;
1336
1345
  }
1346
+ peekMatch(pattern) {
1347
+ const ch = this.next();
1348
+ if (ch) this.backup();
1349
+ return pattern.test(ch);
1350
+ }
1337
1351
  accept(valid) {
1338
1352
  const ch = this.next();
1339
1353
  if (valid.has(ch)) return true;
@@ -1463,10 +1477,28 @@ function lexDescendantSelection(l) {
1463
1477
  l.emit(TokenKind.NAME);
1464
1478
  return lexSegment;
1465
1479
  }
1466
- if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1467
- // Non-standard keys selector
1468
- l.emit(TokenKind.KEYS);
1469
- return lexSegment;
1480
+ if (!l.environment.strict) {
1481
+ // We're effectively disabling the _key selector_ and _keys filter selector_ if a
1482
+ // custom _keys selector_ is set.
1483
+ if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
1484
+ l.next();
1485
+ if (l.peekMatch(nameFirstPattern)) {
1486
+ // Non-standard key selector
1487
+ l.ignore(); // ignore ~
1488
+ l.acceptMatchRun(namePattern);
1489
+ l.emit(TokenKind.KEY);
1490
+ return lexSegment;
1491
+ } else {
1492
+ // Non-standard keys selector
1493
+ l.emit(TokenKind.KEYS);
1494
+ return lexSegment;
1495
+ }
1496
+ } else if (l.acceptMatchRun(l.environment.keysPattern)) {
1497
+ // NOTE: A custom keys pattern does not play well with other non-standard key selectors.
1498
+ // We leave this here for backwards compatibility.
1499
+ l.emit(TokenKind.KEYS);
1500
+ return lexSegment;
1501
+ }
1470
1502
  }
1471
1503
  const ch = l.next();
1472
1504
  switch (ch) {
@@ -1491,9 +1523,28 @@ function lexDotSelector(l) {
1491
1523
  l.error("unexpected whitespace after dot");
1492
1524
  return null;
1493
1525
  }
1494
- if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1495
- l.emit(TokenKind.KEYS);
1496
- return lexSegment;
1526
+ if (!l.environment.strict) {
1527
+ // We're effectively disabling the _key selector_ and _keys filter selector_ if a
1528
+ // custom _keys selector_ is set.
1529
+ if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
1530
+ l.next();
1531
+ if (l.peekMatch(nameFirstPattern)) {
1532
+ // Non-standard key selector
1533
+ l.ignore(); // ignore ~
1534
+ l.acceptMatchRun(namePattern);
1535
+ l.emit(TokenKind.KEY);
1536
+ return lexSegment;
1537
+ } else {
1538
+ // Non-standard keys selector
1539
+ l.emit(TokenKind.KEYS);
1540
+ return lexSegment;
1541
+ }
1542
+ } else if (l.acceptMatchRun(l.environment.keysPattern)) {
1543
+ // NOTE: A custom keys pattern does not play well with other non-standard key selectors.
1544
+ // We leave this here for backwards compatibility.
1545
+ l.emit(TokenKind.KEYS);
1546
+ return lexSegment;
1547
+ }
1497
1548
  }
1498
1549
  if (l.acceptMatchRun(namePattern)) {
1499
1550
  l.emit(TokenKind.NAME);
@@ -1516,8 +1567,25 @@ function lexInsideBracketedSelection(l) {
1516
1567
  continue;
1517
1568
  }
1518
1569
  if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1519
- l.emit(TokenKind.KEYS);
1520
- continue;
1570
+ switch (l.peek()) {
1571
+ case "'":
1572
+ l.ignore(); // ~
1573
+ l.next();
1574
+ return lexSingleQuoteKeyString(l);
1575
+ case '"':
1576
+ l.ignore(); // ~
1577
+ l.next();
1578
+ return lexDoubleQuoteKeyString(l);
1579
+ case "?":
1580
+ l.ignore(); // ~
1581
+ l.next();
1582
+ l.emit(TokenKind.KEYS_FILTER);
1583
+ l.filterLevel += 1;
1584
+ return lexInsideFilter;
1585
+ default:
1586
+ l.emit(TokenKind.KEYS);
1587
+ continue;
1588
+ }
1521
1589
  }
1522
1590
  const ch = l.next();
1523
1591
  switch (ch) {
@@ -1604,7 +1672,7 @@ function lexInsideFilter(l) {
1604
1672
  l.emit(TokenKind.CURRENT);
1605
1673
  return lexSegment;
1606
1674
  case "#":
1607
- l.emit(TokenKind.KEY);
1675
+ l.emit(TokenKind.CURRENT_KEY);
1608
1676
  return lexSegment;
1609
1677
  case ".":
1610
1678
  l.backup();
@@ -1704,8 +1772,7 @@ function lexInsideFilter(l) {
1704
1772
  * @param state - The state function to return control to.
1705
1773
  * @returns String tokenizing state function.
1706
1774
  */
1707
- function makeLexString(quote, state) {
1708
- // eslint-disable-next-line sonarjs/cognitive-complexity
1775
+ function makeLexString(quote, state, token_kind) {
1709
1776
  function _lexString(l) {
1710
1777
  l.ignore();
1711
1778
  if (l.peek() === quote) {
@@ -1731,7 +1798,7 @@ function makeLexString(quote, state) {
1731
1798
  }
1732
1799
  if (ch === quote) {
1733
1800
  l.backup();
1734
- l.emit(quote === "'" ? TokenKind.SINGLE_QUOTE_STRING : TokenKind.DOUBLE_QUOTE_STRING);
1801
+ l.emit(token_kind);
1735
1802
  l.next();
1736
1803
  l.ignore();
1737
1804
  return state;
@@ -1740,10 +1807,12 @@ function makeLexString(quote, state) {
1740
1807
  }
1741
1808
  return _lexString;
1742
1809
  }
1743
- const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection);
1744
- const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection);
1745
- const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter);
1746
- const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter);
1810
+ const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection, TokenKind.SINGLE_QUOTE_STRING);
1811
+ const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection, TokenKind.DOUBLE_QUOTE_STRING);
1812
+ const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter, TokenKind.SINGLE_QUOTE_STRING);
1813
+ const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter, TokenKind.DOUBLE_QUOTE_STRING);
1814
+ const lexSingleQuoteKeyString = makeLexString("'", lexInsideBracketedSelection, TokenKind.KEY_SINGLE_QUOTE_STRING);
1815
+ const lexDoubleQuoteKeyString = makeLexString('"', lexInsideBracketedSelection, TokenKind.KEY_DOUBLE_QUOTE_STRING);
1747
1816
 
1748
1817
  /**
1749
1818
  * Base class for all JSONPath segments and selectors.
@@ -2392,6 +2461,38 @@ class CurrentKey extends FilterExpression {
2392
2461
  }
2393
2462
  }
2394
2463
 
2464
+ class KeySelector extends JSONPathSelector {
2465
+ constructor(environment, token, key) {
2466
+ let shorthand = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
2467
+ super(environment, token);
2468
+ this.environment = environment;
2469
+ this.token = token;
2470
+ this.key = key;
2471
+ this.shorthand = shorthand;
2472
+ }
2473
+ resolve(nodes) {
2474
+ const rv = [];
2475
+ for (const node of nodes) {
2476
+ if (node.value instanceof String || isArray(node.value)) continue;
2477
+ if (isObject(node.value) && hasStringKey(node.value, this.key)) {
2478
+ rv.push(new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root));
2479
+ }
2480
+ }
2481
+ return rv;
2482
+ }
2483
+ *lazyResolve(nodes) {
2484
+ for (const node of nodes) {
2485
+ if (node.value instanceof String || isArray(node.value)) continue;
2486
+ if (isObject(node.value) && hasStringKey(node.value, this.key)) {
2487
+ yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
2488
+ }
2489
+ }
2490
+ }
2491
+ toString() {
2492
+ return this.shorthand ? `[~'${this.key.replaceAll("'", "\\'")}']` : `~'${this.key.replaceAll("'", "\\'")}'`;
2493
+ }
2494
+ }
2495
+
2395
2496
  /**
2396
2497
  * Object property name selector or array index selector.
2397
2498
  */
@@ -2408,10 +2509,8 @@ class KeysSelector extends JSONPathSelector {
2408
2509
  for (const node of nodes) {
2409
2510
  if (node.value instanceof String || isArray(node.value)) continue;
2410
2511
  if (isObject(node.value)) {
2411
- let i = 0;
2412
2512
  for (const [key, _] of this.environment.entries(node.value)) {
2413
- rv.push(new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root));
2414
- i++;
2513
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
2415
2514
  }
2416
2515
  }
2417
2516
  }
@@ -2421,10 +2520,8 @@ class KeysSelector extends JSONPathSelector {
2421
2520
  for (const node of nodes) {
2422
2521
  if (node.value instanceof String || isArray(node.value)) continue;
2423
2522
  if (isObject(node.value)) {
2424
- let i = 0;
2425
2523
  for (const [key, _] of this.environment.entries(node.value)) {
2426
- yield new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root);
2427
- i++;
2524
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
2428
2525
  }
2429
2526
  }
2430
2527
  }
@@ -2433,6 +2530,56 @@ class KeysSelector extends JSONPathSelector {
2433
2530
  return this.shorthand ? "[~]" : "~";
2434
2531
  }
2435
2532
  }
2533
+ class KeysFilterSelector extends JSONPathSelector {
2534
+ constructor(environment, token, expression) {
2535
+ super(environment, token);
2536
+ this.environment = environment;
2537
+ this.token = token;
2538
+ this.expression = expression;
2539
+ }
2540
+ resolve(nodes) {
2541
+ const rv = [];
2542
+ for (const node of nodes) {
2543
+ if (node.value instanceof String || isArray(node.value)) continue;
2544
+ if (isObject(node.value)) {
2545
+ for (const [key, value] of this.environment.entries(node.value)) {
2546
+ const filterContext = {
2547
+ environment: this.environment,
2548
+ currentValue: value,
2549
+ rootValue: node.root,
2550
+ currentKey: key
2551
+ };
2552
+ if (this.expression.evaluate(filterContext)) {
2553
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
2554
+ }
2555
+ }
2556
+ }
2557
+ }
2558
+ return rv;
2559
+ }
2560
+ *lazyResolve(nodes) {
2561
+ for (const node of nodes) {
2562
+ if (node.value instanceof String || isArray(node.value)) continue;
2563
+ if (isObject(node.value)) {
2564
+ for (const [key, value] of this.environment.entries(node.value)) {
2565
+ const filterContext = {
2566
+ environment: this.environment,
2567
+ currentValue: value,
2568
+ rootValue: node.root,
2569
+ lazy: true,
2570
+ currentKey: key
2571
+ };
2572
+ if (this.expression.evaluate(filterContext)) {
2573
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
2574
+ }
2575
+ }
2576
+ }
2577
+ }
2578
+ }
2579
+ toString() {
2580
+ return `~?${this.expression.toString()}`;
2581
+ }
2582
+ }
2436
2583
 
2437
2584
  const PRECEDENCE_LOWEST = 1;
2438
2585
  const PRECEDENCE_LOGICAL_OR = 4;
@@ -2449,7 +2596,7 @@ const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
2449
2596
  class Parser {
2450
2597
  constructor(environment) {
2451
2598
  this.environment = environment;
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]]);
2599
+ 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]]);
2453
2600
  }
2454
2601
  parse(stream) {
2455
2602
  if (stream.current.kind === TokenKind.ROOT) stream.next();
@@ -2481,6 +2628,8 @@ class Parser {
2481
2628
  return new NameSelector(this.environment, stream.current, stream.current.value, true);
2482
2629
  case TokenKind.WILD:
2483
2630
  return new WildcardSelector(this.environment, stream.current, true);
2631
+ case TokenKind.KEY:
2632
+ return new KeySelector(this.environment, stream.current, stream.current.value, true);
2484
2633
  case TokenKind.KEYS:
2485
2634
  return new KeysSelector(this.environment, stream.current, true);
2486
2635
  case TokenKind.DDOT:
@@ -2576,6 +2725,13 @@ class Parser {
2576
2725
  case TokenKind.WILD:
2577
2726
  items.push(new WildcardSelector(this.environment, stream.current));
2578
2727
  break;
2728
+ case TokenKind.KEY_SINGLE_QUOTE_STRING:
2729
+ case TokenKind.KEY_DOUBLE_QUOTE_STRING:
2730
+ items.push(new KeySelector(this.environment, stream.current, this.decodeString(stream.current, true), false));
2731
+ break;
2732
+ case TokenKind.KEYS_FILTER:
2733
+ items.push(this.parseFilter(stream, true));
2734
+ break;
2579
2735
  case TokenKind.KEYS:
2580
2736
  items.push(new KeysSelector(this.environment, stream.current));
2581
2737
  break;
@@ -2596,6 +2752,7 @@ class Parser {
2596
2752
  return new BracketedSelection(this.environment, token, items);
2597
2753
  }
2598
2754
  parseFilter(stream) {
2755
+ let keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2599
2756
  const tok = stream.next();
2600
2757
  const expr = this.parseFilterExpression(stream);
2601
2758
  if (expr instanceof FunctionExtension) {
@@ -2604,7 +2761,7 @@ class Parser {
2604
2761
  throw new JSONPathTypeError(`result of ${expr.name}() must be compared`, expr.token);
2605
2762
  }
2606
2763
  }
2607
- return new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
2764
+ return keys ? new KeysFilterSelector(this.environment, tok, new LogicalExpression(tok, expr)) : new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
2608
2765
  }
2609
2766
  parseBoolean(stream) {
2610
2767
  if (stream.current.kind === TokenKind.FALSE) return new BooleanLiteral(stream.current, false);
@@ -3032,6 +3189,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
3032
3189
  JSONPathRecursionLimitError: JSONPathRecursionLimitError,
3033
3190
  JSONPathSyntaxError: JSONPathSyntaxError,
3034
3191
  JSONPathTypeError: JSONPathTypeError,
3192
+ KEY_MARK: KEY_MARK,
3035
3193
  Nothing: Nothing,
3036
3194
  Token: Token,
3037
3195
  TokenKind: TokenKind,
@@ -3524,6 +3682,6 @@ var index = /*#__PURE__*/Object.freeze({
3524
3682
  apply: apply
3525
3683
  });
3526
3684
 
3527
- const version = "1.2.1";
3685
+ const version = "1.3.0";
3528
3686
 
3529
3687
  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 };