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
@@ -654,6 +654,30 @@ var json_p3 = (function (exports) {
654
654
  resolve: resolve
655
655
  });
656
656
 
657
+ /**
658
+ * An identifier that is allowed in both JS and JSONPath.
659
+ * JSONPath identifiers are generally much more permissive than JS ones, but
660
+ * they don't allow the character "$", so we take the intersection of the two
661
+ * when deciding whether to use dot shorthand for canonical serialization of
662
+ * simple names.
663
+ */
664
+ const SHORTHAND_COMPATIBLE_IDENTIFIER = /^[\p{ID_Start}_]\p{ID_Continue}*$/u;
665
+
666
+ /** Usable in a quoted path. */
667
+ function toQuoted(name) {
668
+ return name.includes("'") && !name.includes('"') ? JSON.stringify(name) : toCanonical(name);
669
+ }
670
+
671
+ /** Usable in a normalized path. */
672
+ function toCanonical(name) {
673
+ return `'${JSON.stringify(name).slice(1, -1).replaceAll('\\"', '"').replaceAll("'", "\\'")}'`;
674
+ }
675
+
676
+ /** Usable in a shorthand path. */
677
+ function toShorthand(name) {
678
+ return SHORTHAND_COMPATIBLE_IDENTIFIER.test(name) ? name : null;
679
+ }
680
+
657
681
  const Nothing = Symbol.for("jsonpath.nothing");
658
682
 
659
683
  /**
@@ -661,7 +685,7 @@ var json_p3 = (function (exports) {
661
685
  */
662
686
 
663
687
  /**
664
- * Object passed to FilterExpression.evaluate().
688
+ * Object passed to `FilterExpression.evaluate()`.
665
689
  */
666
690
 
667
691
  /**
@@ -672,6 +696,14 @@ var json_p3 = (function (exports) {
672
696
  }
673
697
  const KEY_MARK = "\x02";
674
698
 
699
+ /**
700
+ * Options for serializing paths.
701
+ */
702
+
703
+ const defaultSerializationOptions = {
704
+ form: "pretty"
705
+ };
706
+
675
707
  /**
676
708
  * The pair of a JSON value and its location found in the target JSON value.
677
709
  */
@@ -686,10 +718,31 @@ var json_p3 = (function (exports) {
686
718
  this.location = location;
687
719
  this.root = root;
688
720
  }
721
+
722
+ /**
723
+ * @deprecated Use {@link getPath} with `options.form` set to `canonical` instead.
724
+ */
689
725
  get path() {
726
+ return this.getPath({
727
+ form: "canonical"
728
+ });
729
+ }
730
+
731
+ /**
732
+ * Get the path to this node in the target JSON value.
733
+ *
734
+ * Given that the path refers to the singular current node, the returned path
735
+ * will always be a normalized path if `options.form` is set to `canonical`,
736
+ * following section 2.7 of RFC 9535.
737
+ */
738
+ getPath(options) {
739
+ const opts = {
740
+ ...defaultSerializationOptions,
741
+ ...options
742
+ };
690
743
  return (
691
744
  // eslint-disable-next-line prefer-template
692
- "$" + this.location.map(s => isString(s) ? this.decode_name_location(s) : `[${s}]`).join("")
745
+ "$" + this.location.map(s => isString(s) ? this.decodeNameLocation(s, opts) : `[${s}]`).join("")
693
746
  );
694
747
  }
695
748
 
@@ -702,8 +755,16 @@ var json_p3 = (function (exports) {
702
755
  }
703
756
  return new JSONPointer(JSONPointer.encode(this.location.map(String)));
704
757
  }
705
- decode_name_location(name) {
706
- return name.startsWith(KEY_MARK) ? `[~'${name.slice(1).replaceAll("'", "\\'")}']` : `['${name.replaceAll("'", "\\'")}']`;
758
+ decodeNameLocation(name, options) {
759
+ const normalized = options.form === "canonical";
760
+ const serialize = normalized ? toCanonical : toQuoted;
761
+ const hasKeyMark = name.startsWith(KEY_MARK);
762
+ if (hasKeyMark) name = name.slice(1);
763
+ const shorthand = toShorthand(name);
764
+ if (hasKeyMark) {
765
+ return normalized || shorthand == null ? `[~${serialize(name)}]` : `.~${shorthand}`;
766
+ }
767
+ return normalized || shorthand == null ? `[${serialize(name)}]` : `.${shorthand}`;
707
768
  }
708
769
  }
709
770
 
@@ -764,8 +825,8 @@ var json_p3 = (function (exports) {
764
825
  * A normalized path contains only property name and index selectors, and
765
826
  * always uses bracketed segments, never shorthand selectors.
766
827
  */
767
- paths() {
768
- return this.nodes.map(node => node.path);
828
+ paths(options) {
829
+ return this.nodes.map(node => node.getPath(options));
769
830
  }
770
831
 
771
832
  /**
@@ -837,7 +898,7 @@ var json_p3 = (function (exports) {
837
898
  return this.value;
838
899
  }
839
900
  toString() {
840
- return JSON.stringify(this.value);
901
+ return toCanonical(this.value);
841
902
  }
842
903
  }
843
904
  class NumberLiteral extends FilterExpressionLiteral {
@@ -868,10 +929,13 @@ var json_p3 = (function (exports) {
868
929
  }
869
930
  throw new JSONPathTypeError(`unknown operator '${this.operator}'`, this.token);
870
931
  }
871
- toString() {
872
- return `${this.operator}${this.right.toString()}`;
932
+ toString(options) {
933
+ return `${this.operator}${this.right.toString(options)}`;
873
934
  }
874
935
  }
936
+ const PRECEDENCE_LOGICAL_OR$1 = 4;
937
+ const PRECEDENCE_LOGICAL_AND$1 = 5;
938
+ const PRECEDENCE_PREFIX$1 = 7;
875
939
  class InfixExpression extends FilterExpression {
876
940
  constructor(token, left, operator, right) {
877
941
  super(token);
@@ -894,11 +958,12 @@ var json_p3 = (function (exports) {
894
958
  }
895
959
  return compare(left, this.operator, right);
896
960
  }
897
- toString() {
961
+ toString(options) {
962
+ // Note that `LogicalExpression.toString()` does not call this.
898
963
  if (this.logical) {
899
- return `(${this.left.toString()} ${this.operator} ${this.right.toString()})`;
964
+ return `(${this.left.toString(options)} ${this.operator} ${this.right.toString(options)})`;
900
965
  }
901
- return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;
966
+ return `${this.left.toString(options)} ${this.operator} ${this.right.toString(options)}`;
902
967
  }
903
968
  }
904
969
  class LogicalExpression extends FilterExpression {
@@ -912,8 +977,38 @@ var json_p3 = (function (exports) {
912
977
  if (value instanceof JSONPathNodeList) return value.nodes.length > 0; // existence
913
978
  return isTruthy(value);
914
979
  }
915
- toString() {
916
- return this.expression.toString();
980
+ toString(options) {
981
+ // Minimize parentheses in logical expressions.
982
+ function _toString(expression, parentPrecedence) {
983
+ if (expression instanceof InfixExpression) {
984
+ let precedence;
985
+ let op;
986
+ let left;
987
+ let right;
988
+ if (expression.operator === "&&") {
989
+ precedence = PRECEDENCE_LOGICAL_AND$1;
990
+ op = "&&";
991
+ left = _toString(expression.left, precedence);
992
+ right = _toString(expression.right, precedence);
993
+ } else if (expression.operator === "||") {
994
+ precedence = PRECEDENCE_LOGICAL_OR$1;
995
+ op = "||";
996
+ left = _toString(expression.left, precedence);
997
+ right = _toString(expression.right, precedence);
998
+ } else {
999
+ return expression.toString(options);
1000
+ }
1001
+ const expr = `${left} ${op} ${right}`;
1002
+ return precedence < parentPrecedence ? `(${expr})` : expr;
1003
+ }
1004
+ if (expression instanceof PrefixExpression) {
1005
+ const operand = _toString(expression.right, PRECEDENCE_PREFIX$1);
1006
+ const expr = `!${operand}`;
1007
+ return parentPrecedence > PRECEDENCE_PREFIX$1 ? `(${expr})` : expr;
1008
+ }
1009
+ return expression.toString(options);
1010
+ }
1011
+ return _toString(this.expression, 0);
917
1012
  }
918
1013
  }
919
1014
 
@@ -931,16 +1026,16 @@ var json_p3 = (function (exports) {
931
1026
  evaluate(context) {
932
1027
  return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.currentValue))) : this.path.query(context.currentValue);
933
1028
  }
934
- toString() {
935
- return `@${this.path.toString().slice(1)}`;
1029
+ toString(options) {
1030
+ return `@${this.path.toString(options).slice(1)}`;
936
1031
  }
937
1032
  }
938
1033
  class RootQuery extends FilterQuery {
939
1034
  evaluate(context) {
940
1035
  return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.rootValue))) : this.path.query(context.rootValue);
941
1036
  }
942
- toString() {
943
- return this.path.toString();
1037
+ toString(options) {
1038
+ return this.path.toString(options);
944
1039
  }
945
1040
  }
946
1041
  class FunctionExtension extends FilterExpression {
@@ -958,8 +1053,8 @@ var json_p3 = (function (exports) {
958
1053
  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);
959
1054
  return func.call(...args);
960
1055
  }
961
- toString() {
962
- return `${this.name}(${this.args.map(e => e.toString()).join(", ")})`;
1056
+ toString(options) {
1057
+ return `${this.name}(${this.args.map(e => e.toString(options)).join(", ")})`;
963
1058
  }
964
1059
  unpack_node_list(arg) {
965
1060
  switch (arg.length) {
@@ -2896,7 +2991,6 @@ var json_p3 = (function (exports) {
2896
2991
  switch (ch) {
2897
2992
  case "]":
2898
2993
  l.emit(TokenKind.RBRACKET);
2899
- if (l.filterLevel) return lexInsideFilter;
2900
2994
  return lexSegment;
2901
2995
  case "":
2902
2996
  l.error("unclosed bracketed selection");
@@ -3166,8 +3260,17 @@ var json_p3 = (function (exports) {
3166
3260
  yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
3167
3261
  }
3168
3262
  }
3169
- toString() {
3170
- return `'${this.name}'`;
3263
+ toString(options) {
3264
+ const {
3265
+ form
3266
+ } = {
3267
+ ...defaultSerializationOptions,
3268
+ ...options
3269
+ };
3270
+ return form === "canonical" ? toCanonical(this.name) : toQuoted(this.name);
3271
+ }
3272
+ shorthand() {
3273
+ return toShorthand(this.name);
3171
3274
  }
3172
3275
  }
3173
3276
 
@@ -3436,8 +3539,8 @@ var json_p3 = (function (exports) {
3436
3539
  }
3437
3540
  }
3438
3541
  }
3439
- toString() {
3440
- return `?${this.expression.toString()}`;
3542
+ toString(options) {
3543
+ return `?${this.expression.toString(options)}`;
3441
3544
  }
3442
3545
  }
3443
3546
 
@@ -3468,7 +3571,7 @@ var json_p3 = (function (exports) {
3468
3571
  */
3469
3572
 
3470
3573
  /**
3471
- * Return a canonical string representation of this segment.
3574
+ * Return a string representation of this segment.
3472
3575
  */
3473
3576
  }
3474
3577
 
@@ -3490,8 +3593,18 @@ var json_p3 = (function (exports) {
3490
3593
  }
3491
3594
  }
3492
3595
  }
3493
- toString() {
3494
- return `[${this.selectors.map(s => s.toString()).join(", ")}]`;
3596
+ toString(options) {
3597
+ const {
3598
+ form
3599
+ } = {
3600
+ ...defaultSerializationOptions,
3601
+ ...options
3602
+ };
3603
+ if (form === "pretty" && this.selectors.length === 1 && this.selectors[0] instanceof NameSelector) {
3604
+ const shorthand = this.selectors[0].shorthand();
3605
+ if (shorthand != null) return `.${shorthand}`;
3606
+ }
3607
+ return `[${this.selectors.map(s => s.toString(options)).join(", ")}]`;
3495
3608
  }
3496
3609
  }
3497
3610
 
@@ -3518,8 +3631,8 @@ var json_p3 = (function (exports) {
3518
3631
  }
3519
3632
  }
3520
3633
  }
3521
- toString() {
3522
- return `..[${this.selectors.map(s => s.toString()).join(", ")}]`;
3634
+ toString(options) {
3635
+ return `..[${this.selectors.map(s => s.toString(options)).join(", ")}]`;
3523
3636
  }
3524
3637
  visit(node) {
3525
3638
  var _this = this;
@@ -3619,23 +3732,18 @@ var json_p3 = (function (exports) {
3619
3732
  }
3620
3733
 
3621
3734
  /**
3622
- *
3735
+ * A compiled JSONPath query ready to be applied to different data repeatedly.
3623
3736
  */
3624
3737
  class JSONPathQuery {
3625
- /**
3626
- *
3627
- * @param environment -
3628
- * @param segments -
3629
- */
3630
3738
  constructor(environment, segments) {
3631
3739
  this.environment = environment;
3632
3740
  this.segments = segments;
3633
3741
  }
3634
3742
 
3635
3743
  /**
3636
- *
3637
- * @param value -
3638
- * @returns
3744
+ * Apply this JSONPath query to _value_.
3745
+ * @param value - A JSON-like object to apply this query to.
3746
+ * @returns Nodes matched by applying this query to _value_.
3639
3747
  */
3640
3748
  query(value) {
3641
3749
  let nodes = [new JSONPathNode(value, [], value)];
@@ -3646,9 +3754,9 @@ var json_p3 = (function (exports) {
3646
3754
  }
3647
3755
 
3648
3756
  /**
3649
- *
3650
- * @param value -
3651
- * @returns
3757
+ * Apply this JSONPath query to _value_.
3758
+ * @param value - A JSON-like object to apply this query to.
3759
+ * @returns An iterator over nodes matched by applying this query to _value_.
3652
3760
  */
3653
3761
  lazyQuery(value) {
3654
3762
  let nodes = [new JSONPathNode(value, [], value)][Symbol.iterator]();
@@ -3674,11 +3782,15 @@ var json_p3 = (function (exports) {
3674
3782
  }
3675
3783
 
3676
3784
  /**
3677
- *
3785
+ * Return a string representation of this query.
3678
3786
  */
3679
- toString() {
3680
- return `$${this.segments.map(s => s.toString()).join("")}`;
3787
+ toString(options) {
3788
+ return `$${this.segments.map(s => s.toString(options)).join("")}`;
3681
3789
  }
3790
+
3791
+ /**
3792
+ * Return `true` if this query is a _singular query_, or `false` otherwise.
3793
+ */
3682
3794
  singularQuery() {
3683
3795
  for (const segment of this.segments) {
3684
3796
  if (segment instanceof DescendantSegment) return false;
@@ -3720,8 +3832,15 @@ var json_p3 = (function (exports) {
3720
3832
  yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
3721
3833
  }
3722
3834
  }
3723
- toString() {
3724
- return `~'${this.key.replaceAll("'", "\\'")}'`;
3835
+ toString(options) {
3836
+ const {
3837
+ form
3838
+ } = {
3839
+ ...defaultSerializationOptions,
3840
+ ...options
3841
+ };
3842
+ const serialize = form === "canonical" ? toCanonical : toQuoted;
3843
+ return `~${serialize(this.key)}`;
3725
3844
  }
3726
3845
  }
3727
3846
 
@@ -3797,8 +3916,8 @@ var json_p3 = (function (exports) {
3797
3916
  }
3798
3917
  }
3799
3918
  }
3800
- toString() {
3801
- return `~?${this.expression.toString()}`;
3919
+ toString(options) {
3920
+ return `~?${this.expression.toString(options)}`;
3802
3921
  }
3803
3922
  }
3804
3923
 
@@ -5088,7 +5207,7 @@ var json_p3 = (function (exports) {
5088
5207
  apply: apply
5089
5208
  });
5090
5209
 
5091
- const version = "2.0.0";
5210
+ const version = "2.1.1";
5092
5211
 
5093
5212
  exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
5094
5213
  exports.FunctionExpressionType = FunctionExpressionType;