json-p3 2.0.0 → 2.1.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 2.0.0
2
+ * json-p3 version 2.1.1
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
@@ -651,6 +651,30 @@ var index$3 = /*#__PURE__*/Object.freeze({
651
651
  resolve: resolve
652
652
  });
653
653
 
654
+ /**
655
+ * An identifier that is allowed in both JS and JSONPath.
656
+ * JSONPath identifiers are generally much more permissive than JS ones, but
657
+ * they don't allow the character "$", so we take the intersection of the two
658
+ * when deciding whether to use dot shorthand for canonical serialization of
659
+ * simple names.
660
+ */
661
+ const SHORTHAND_COMPATIBLE_IDENTIFIER = /^[\p{ID_Start}_]\p{ID_Continue}*$/u;
662
+
663
+ /** Usable in a quoted path. */
664
+ function toQuoted(name) {
665
+ return name.includes("'") && !name.includes('"') ? JSON.stringify(name) : toCanonical(name);
666
+ }
667
+
668
+ /** Usable in a normalized path. */
669
+ function toCanonical(name) {
670
+ return `'${JSON.stringify(name).slice(1, -1).replaceAll('\\"', '"').replaceAll("'", "\\'")}'`;
671
+ }
672
+
673
+ /** Usable in a shorthand path. */
674
+ function toShorthand(name) {
675
+ return SHORTHAND_COMPATIBLE_IDENTIFIER.test(name) ? name : null;
676
+ }
677
+
654
678
  const Nothing = Symbol.for("jsonpath.nothing");
655
679
 
656
680
  /**
@@ -658,7 +682,7 @@ const Nothing = Symbol.for("jsonpath.nothing");
658
682
  */
659
683
 
660
684
  /**
661
- * Object passed to FilterExpression.evaluate().
685
+ * Object passed to `FilterExpression.evaluate()`.
662
686
  */
663
687
 
664
688
  /**
@@ -669,6 +693,14 @@ function hasStringKey(value, key) {
669
693
  }
670
694
  const KEY_MARK = "\x02";
671
695
 
696
+ /**
697
+ * Options for serializing paths.
698
+ */
699
+
700
+ const defaultSerializationOptions = {
701
+ form: "pretty"
702
+ };
703
+
672
704
  /**
673
705
  * The pair of a JSON value and its location found in the target JSON value.
674
706
  */
@@ -683,10 +715,31 @@ class JSONPathNode {
683
715
  this.location = location;
684
716
  this.root = root;
685
717
  }
718
+
719
+ /**
720
+ * @deprecated Use {@link getPath} with `options.form` set to `canonical` instead.
721
+ */
686
722
  get path() {
723
+ return this.getPath({
724
+ form: "canonical"
725
+ });
726
+ }
727
+
728
+ /**
729
+ * Get the path to this node in the target JSON value.
730
+ *
731
+ * Given that the path refers to the singular current node, the returned path
732
+ * will always be a normalized path if `options.form` is set to `canonical`,
733
+ * following section 2.7 of RFC 9535.
734
+ */
735
+ getPath(options) {
736
+ const opts = {
737
+ ...defaultSerializationOptions,
738
+ ...options
739
+ };
687
740
  return (
688
741
  // eslint-disable-next-line prefer-template
689
- "$" + this.location.map(s => isString(s) ? this.decode_name_location(s) : `[${s}]`).join("")
742
+ "$" + this.location.map(s => isString(s) ? this.decodeNameLocation(s, opts) : `[${s}]`).join("")
690
743
  );
691
744
  }
692
745
 
@@ -699,8 +752,16 @@ class JSONPathNode {
699
752
  }
700
753
  return new JSONPointer(JSONPointer.encode(this.location.map(String)));
701
754
  }
702
- decode_name_location(name) {
703
- return name.startsWith(KEY_MARK) ? `[~'${name.slice(1).replaceAll("'", "\\'")}']` : `['${name.replaceAll("'", "\\'")}']`;
755
+ decodeNameLocation(name, options) {
756
+ const normalized = options.form === "canonical";
757
+ const serialize = normalized ? toCanonical : toQuoted;
758
+ const hasKeyMark = name.startsWith(KEY_MARK);
759
+ if (hasKeyMark) name = name.slice(1);
760
+ const shorthand = toShorthand(name);
761
+ if (hasKeyMark) {
762
+ return normalized || shorthand == null ? `[~${serialize(name)}]` : `.~${shorthand}`;
763
+ }
764
+ return normalized || shorthand == null ? `[${serialize(name)}]` : `.${shorthand}`;
704
765
  }
705
766
  }
706
767
 
@@ -761,8 +822,8 @@ class JSONPathNodeList {
761
822
  * A normalized path contains only property name and index selectors, and
762
823
  * always uses bracketed segments, never shorthand selectors.
763
824
  */
764
- paths() {
765
- return this.nodes.map(node => node.path);
825
+ paths(options) {
826
+ return this.nodes.map(node => node.getPath(options));
766
827
  }
767
828
 
768
829
  /**
@@ -834,7 +895,7 @@ class StringLiteral extends FilterExpressionLiteral {
834
895
  return this.value;
835
896
  }
836
897
  toString() {
837
- return JSON.stringify(this.value);
898
+ return toCanonical(this.value);
838
899
  }
839
900
  }
840
901
  class NumberLiteral extends FilterExpressionLiteral {
@@ -865,10 +926,13 @@ class PrefixExpression extends FilterExpression {
865
926
  }
866
927
  throw new JSONPathTypeError(`unknown operator '${this.operator}'`, this.token);
867
928
  }
868
- toString() {
869
- return `${this.operator}${this.right.toString()}`;
929
+ toString(options) {
930
+ return `${this.operator}${this.right.toString(options)}`;
870
931
  }
871
932
  }
933
+ const PRECEDENCE_LOGICAL_OR$1 = 4;
934
+ const PRECEDENCE_LOGICAL_AND$1 = 5;
935
+ const PRECEDENCE_PREFIX$1 = 7;
872
936
  class InfixExpression extends FilterExpression {
873
937
  constructor(token, left, operator, right) {
874
938
  super(token);
@@ -891,11 +955,12 @@ class InfixExpression extends FilterExpression {
891
955
  }
892
956
  return compare(left, this.operator, right);
893
957
  }
894
- toString() {
958
+ toString(options) {
959
+ // Note that `LogicalExpression.toString()` does not call this.
895
960
  if (this.logical) {
896
- return `(${this.left.toString()} ${this.operator} ${this.right.toString()})`;
961
+ return `(${this.left.toString(options)} ${this.operator} ${this.right.toString(options)})`;
897
962
  }
898
- return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;
963
+ return `${this.left.toString(options)} ${this.operator} ${this.right.toString(options)}`;
899
964
  }
900
965
  }
901
966
  class LogicalExpression extends FilterExpression {
@@ -909,8 +974,38 @@ class LogicalExpression extends FilterExpression {
909
974
  if (value instanceof JSONPathNodeList) return value.nodes.length > 0; // existence
910
975
  return isTruthy(value);
911
976
  }
912
- toString() {
913
- return this.expression.toString();
977
+ toString(options) {
978
+ // Minimize parentheses in logical expressions.
979
+ function _toString(expression, parentPrecedence) {
980
+ if (expression instanceof InfixExpression) {
981
+ let precedence;
982
+ let op;
983
+ let left;
984
+ let right;
985
+ if (expression.operator === "&&") {
986
+ precedence = PRECEDENCE_LOGICAL_AND$1;
987
+ op = "&&";
988
+ left = _toString(expression.left, precedence);
989
+ right = _toString(expression.right, precedence);
990
+ } else if (expression.operator === "||") {
991
+ precedence = PRECEDENCE_LOGICAL_OR$1;
992
+ op = "||";
993
+ left = _toString(expression.left, precedence);
994
+ right = _toString(expression.right, precedence);
995
+ } else {
996
+ return expression.toString(options);
997
+ }
998
+ const expr = `${left} ${op} ${right}`;
999
+ return precedence < parentPrecedence ? `(${expr})` : expr;
1000
+ }
1001
+ if (expression instanceof PrefixExpression) {
1002
+ const operand = _toString(expression.right, PRECEDENCE_PREFIX$1);
1003
+ const expr = `!${operand}`;
1004
+ return parentPrecedence > PRECEDENCE_PREFIX$1 ? `(${expr})` : expr;
1005
+ }
1006
+ return expression.toString(options);
1007
+ }
1008
+ return _toString(this.expression, 0);
914
1009
  }
915
1010
  }
916
1011
 
@@ -928,16 +1023,16 @@ class RelativeQuery extends FilterQuery {
928
1023
  evaluate(context) {
929
1024
  return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.currentValue))) : this.path.query(context.currentValue);
930
1025
  }
931
- toString() {
932
- return `@${this.path.toString().slice(1)}`;
1026
+ toString(options) {
1027
+ return `@${this.path.toString(options).slice(1)}`;
933
1028
  }
934
1029
  }
935
1030
  class RootQuery extends FilterQuery {
936
1031
  evaluate(context) {
937
1032
  return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.rootValue))) : this.path.query(context.rootValue);
938
1033
  }
939
- toString() {
940
- return this.path.toString();
1034
+ toString(options) {
1035
+ return this.path.toString(options);
941
1036
  }
942
1037
  }
943
1038
  class FunctionExtension extends FilterExpression {
@@ -955,8 +1050,8 @@ class FunctionExtension extends FilterExpression {
955
1050
  const args = this.args.map(arg => arg.evaluate(context)).map((arg, idx) => func.argTypes[idx] !== FunctionExpressionType.NodesType && arg instanceof JSONPathNodeList ? this.unpack_node_list(arg) : arg);
956
1051
  return func.call(...args);
957
1052
  }
958
- toString() {
959
- return `${this.name}(${this.args.map(e => e.toString()).join(", ")})`;
1053
+ toString(options) {
1054
+ return `${this.name}(${this.args.map(e => e.toString(options)).join(", ")})`;
960
1055
  }
961
1056
  unpack_node_list(arg) {
962
1057
  switch (arg.length) {
@@ -2893,7 +2988,6 @@ function lexInsideBracketedSelection(l) {
2893
2988
  switch (ch) {
2894
2989
  case "]":
2895
2990
  l.emit(TokenKind.RBRACKET);
2896
- if (l.filterLevel) return lexInsideFilter;
2897
2991
  return lexSegment;
2898
2992
  case "":
2899
2993
  l.error("unclosed bracketed selection");
@@ -3163,8 +3257,17 @@ class NameSelector extends JSONPathSelector {
3163
3257
  yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
3164
3258
  }
3165
3259
  }
3166
- toString() {
3167
- return `'${this.name}'`;
3260
+ toString(options) {
3261
+ const {
3262
+ form
3263
+ } = {
3264
+ ...defaultSerializationOptions,
3265
+ ...options
3266
+ };
3267
+ return form === "canonical" ? toCanonical(this.name) : toQuoted(this.name);
3268
+ }
3269
+ shorthand() {
3270
+ return toShorthand(this.name);
3168
3271
  }
3169
3272
  }
3170
3273
 
@@ -3433,8 +3536,8 @@ class FilterSelector extends JSONPathSelector {
3433
3536
  }
3434
3537
  }
3435
3538
  }
3436
- toString() {
3437
- return `?${this.expression.toString()}`;
3539
+ toString(options) {
3540
+ return `?${this.expression.toString(options)}`;
3438
3541
  }
3439
3542
  }
3440
3543
 
@@ -3465,7 +3568,7 @@ class JSONPathSegment {
3465
3568
  */
3466
3569
 
3467
3570
  /**
3468
- * Return a canonical string representation of this segment.
3571
+ * Return a string representation of this segment.
3469
3572
  */
3470
3573
  }
3471
3574
 
@@ -3487,8 +3590,18 @@ class ChildSegment extends JSONPathSegment {
3487
3590
  }
3488
3591
  }
3489
3592
  }
3490
- toString() {
3491
- return `[${this.selectors.map(s => s.toString()).join(", ")}]`;
3593
+ toString(options) {
3594
+ const {
3595
+ form
3596
+ } = {
3597
+ ...defaultSerializationOptions,
3598
+ ...options
3599
+ };
3600
+ if (form === "pretty" && this.selectors.length === 1 && this.selectors[0] instanceof NameSelector) {
3601
+ const shorthand = this.selectors[0].shorthand();
3602
+ if (shorthand != null) return `.${shorthand}`;
3603
+ }
3604
+ return `[${this.selectors.map(s => s.toString(options)).join(", ")}]`;
3492
3605
  }
3493
3606
  }
3494
3607
 
@@ -3515,8 +3628,8 @@ class DescendantSegment extends JSONPathSegment {
3515
3628
  }
3516
3629
  }
3517
3630
  }
3518
- toString() {
3519
- return `..[${this.selectors.map(s => s.toString()).join(", ")}]`;
3631
+ toString(options) {
3632
+ return `..[${this.selectors.map(s => s.toString(options)).join(", ")}]`;
3520
3633
  }
3521
3634
  visit(node) {
3522
3635
  var _this = this;
@@ -3616,23 +3729,18 @@ function shuffle(entries) {
3616
3729
  }
3617
3730
 
3618
3731
  /**
3619
- *
3732
+ * A compiled JSONPath query ready to be applied to different data repeatedly.
3620
3733
  */
3621
3734
  class JSONPathQuery {
3622
- /**
3623
- *
3624
- * @param environment -
3625
- * @param segments -
3626
- */
3627
3735
  constructor(environment, segments) {
3628
3736
  this.environment = environment;
3629
3737
  this.segments = segments;
3630
3738
  }
3631
3739
 
3632
3740
  /**
3633
- *
3634
- * @param value -
3635
- * @returns
3741
+ * Apply this JSONPath query to _value_.
3742
+ * @param value - A JSON-like object to apply this query to.
3743
+ * @returns Nodes matched by applying this query to _value_.
3636
3744
  */
3637
3745
  query(value) {
3638
3746
  let nodes = [new JSONPathNode(value, [], value)];
@@ -3643,9 +3751,9 @@ class JSONPathQuery {
3643
3751
  }
3644
3752
 
3645
3753
  /**
3646
- *
3647
- * @param value -
3648
- * @returns
3754
+ * Apply this JSONPath query to _value_.
3755
+ * @param value - A JSON-like object to apply this query to.
3756
+ * @returns An iterator over nodes matched by applying this query to _value_.
3649
3757
  */
3650
3758
  lazyQuery(value) {
3651
3759
  let nodes = [new JSONPathNode(value, [], value)][Symbol.iterator]();
@@ -3671,11 +3779,15 @@ class JSONPathQuery {
3671
3779
  }
3672
3780
 
3673
3781
  /**
3674
- *
3782
+ * Return a string representation of this query.
3675
3783
  */
3676
- toString() {
3677
- return `$${this.segments.map(s => s.toString()).join("")}`;
3784
+ toString(options) {
3785
+ return `$${this.segments.map(s => s.toString(options)).join("")}`;
3678
3786
  }
3787
+
3788
+ /**
3789
+ * Return `true` if this query is a _singular query_, or `false` otherwise.
3790
+ */
3679
3791
  singularQuery() {
3680
3792
  for (const segment of this.segments) {
3681
3793
  if (segment instanceof DescendantSegment) return false;
@@ -3717,8 +3829,15 @@ class KeySelector extends JSONPathSelector {
3717
3829
  yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
3718
3830
  }
3719
3831
  }
3720
- toString() {
3721
- return `~'${this.key.replaceAll("'", "\\'")}'`;
3832
+ toString(options) {
3833
+ const {
3834
+ form
3835
+ } = {
3836
+ ...defaultSerializationOptions,
3837
+ ...options
3838
+ };
3839
+ const serialize = form === "canonical" ? toCanonical : toQuoted;
3840
+ return `~${serialize(this.key)}`;
3722
3841
  }
3723
3842
  }
3724
3843
 
@@ -3794,8 +3913,8 @@ class KeysFilterSelector extends JSONPathSelector {
3794
3913
  }
3795
3914
  }
3796
3915
  }
3797
- toString() {
3798
- return `~?${this.expression.toString()}`;
3916
+ toString(options) {
3917
+ return `~?${this.expression.toString(options)}`;
3799
3918
  }
3800
3919
  }
3801
3920
 
@@ -5085,6 +5204,6 @@ var index = /*#__PURE__*/Object.freeze({
5085
5204
  apply: apply
5086
5205
  });
5087
5206
 
5088
- const version = "2.0.0";
5207
+ const version = "2.1.1";
5089
5208
 
5090
5209
  export { DEFAULT_ENVIRONMENT, FunctionExpressionType, JSONPatch, JSONPatchError, JSONPatchTestFailure, JSONPathEnvironment, JSONPathError, JSONPathIndexError, JSONPathLexerError, JSONPathNode, JSONPathNodeList, JSONPathQuery, 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 };