json-p3 2.0.0 → 2.1.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 2.0.0
2
+ * json-p3 version 2.1.0
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) {
@@ -3166,8 +3261,17 @@ var json_p3 = (function (exports) {
3166
3261
  yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
3167
3262
  }
3168
3263
  }
3169
- toString() {
3170
- return `'${this.name}'`;
3264
+ toString(options) {
3265
+ const {
3266
+ form
3267
+ } = {
3268
+ ...defaultSerializationOptions,
3269
+ ...options
3270
+ };
3271
+ return form === "canonical" ? toCanonical(this.name) : toQuoted(this.name);
3272
+ }
3273
+ shorthand() {
3274
+ return toShorthand(this.name);
3171
3275
  }
3172
3276
  }
3173
3277
 
@@ -3436,8 +3540,8 @@ var json_p3 = (function (exports) {
3436
3540
  }
3437
3541
  }
3438
3542
  }
3439
- toString() {
3440
- return `?${this.expression.toString()}`;
3543
+ toString(options) {
3544
+ return `?${this.expression.toString(options)}`;
3441
3545
  }
3442
3546
  }
3443
3547
 
@@ -3468,7 +3572,7 @@ var json_p3 = (function (exports) {
3468
3572
  */
3469
3573
 
3470
3574
  /**
3471
- * Return a canonical string representation of this segment.
3575
+ * Return a string representation of this segment.
3472
3576
  */
3473
3577
  }
3474
3578
 
@@ -3490,8 +3594,18 @@ var json_p3 = (function (exports) {
3490
3594
  }
3491
3595
  }
3492
3596
  }
3493
- toString() {
3494
- return `[${this.selectors.map(s => s.toString()).join(", ")}]`;
3597
+ toString(options) {
3598
+ const {
3599
+ form
3600
+ } = {
3601
+ ...defaultSerializationOptions,
3602
+ ...options
3603
+ };
3604
+ if (form === "pretty" && this.selectors.length === 1 && this.selectors[0] instanceof NameSelector) {
3605
+ const shorthand = this.selectors[0].shorthand();
3606
+ if (shorthand != null) return `.${shorthand}`;
3607
+ }
3608
+ return `[${this.selectors.map(s => s.toString(options)).join(", ")}]`;
3495
3609
  }
3496
3610
  }
3497
3611
 
@@ -3518,8 +3632,8 @@ var json_p3 = (function (exports) {
3518
3632
  }
3519
3633
  }
3520
3634
  }
3521
- toString() {
3522
- return `..[${this.selectors.map(s => s.toString()).join(", ")}]`;
3635
+ toString(options) {
3636
+ return `..[${this.selectors.map(s => s.toString(options)).join(", ")}]`;
3523
3637
  }
3524
3638
  visit(node) {
3525
3639
  var _this = this;
@@ -3619,23 +3733,18 @@ var json_p3 = (function (exports) {
3619
3733
  }
3620
3734
 
3621
3735
  /**
3622
- *
3736
+ * A compiled JSONPath query ready to be applied to different data repeatedly.
3623
3737
  */
3624
3738
  class JSONPathQuery {
3625
- /**
3626
- *
3627
- * @param environment -
3628
- * @param segments -
3629
- */
3630
3739
  constructor(environment, segments) {
3631
3740
  this.environment = environment;
3632
3741
  this.segments = segments;
3633
3742
  }
3634
3743
 
3635
3744
  /**
3636
- *
3637
- * @param value -
3638
- * @returns
3745
+ * Apply this JSONPath query to _value_.
3746
+ * @param value - A JSON-like object to apply this query to.
3747
+ * @returns Nodes matched by applying this query to _value_.
3639
3748
  */
3640
3749
  query(value) {
3641
3750
  let nodes = [new JSONPathNode(value, [], value)];
@@ -3646,9 +3755,9 @@ var json_p3 = (function (exports) {
3646
3755
  }
3647
3756
 
3648
3757
  /**
3649
- *
3650
- * @param value -
3651
- * @returns
3758
+ * Apply this JSONPath query to _value_.
3759
+ * @param value - A JSON-like object to apply this query to.
3760
+ * @returns An iterator over nodes matched by applying this query to _value_.
3652
3761
  */
3653
3762
  lazyQuery(value) {
3654
3763
  let nodes = [new JSONPathNode(value, [], value)][Symbol.iterator]();
@@ -3674,11 +3783,15 @@ var json_p3 = (function (exports) {
3674
3783
  }
3675
3784
 
3676
3785
  /**
3677
- *
3786
+ * Return a string representation of this query.
3678
3787
  */
3679
- toString() {
3680
- return `$${this.segments.map(s => s.toString()).join("")}`;
3788
+ toString(options) {
3789
+ return `$${this.segments.map(s => s.toString(options)).join("")}`;
3681
3790
  }
3791
+
3792
+ /**
3793
+ * Return `true` if this query is a _singular query_, or `false` otherwise.
3794
+ */
3682
3795
  singularQuery() {
3683
3796
  for (const segment of this.segments) {
3684
3797
  if (segment instanceof DescendantSegment) return false;
@@ -3720,8 +3833,15 @@ var json_p3 = (function (exports) {
3720
3833
  yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
3721
3834
  }
3722
3835
  }
3723
- toString() {
3724
- return `~'${this.key.replaceAll("'", "\\'")}'`;
3836
+ toString(options) {
3837
+ const {
3838
+ form
3839
+ } = {
3840
+ ...defaultSerializationOptions,
3841
+ ...options
3842
+ };
3843
+ const serialize = form === "canonical" ? toCanonical : toQuoted;
3844
+ return `~${serialize(this.key)}`;
3725
3845
  }
3726
3846
  }
3727
3847
 
@@ -3797,8 +3917,8 @@ var json_p3 = (function (exports) {
3797
3917
  }
3798
3918
  }
3799
3919
  }
3800
- toString() {
3801
- return `~?${this.expression.toString()}`;
3920
+ toString(options) {
3921
+ return `~?${this.expression.toString(options)}`;
3802
3922
  }
3803
3923
  }
3804
3924
 
@@ -5088,7 +5208,7 @@ var json_p3 = (function (exports) {
5088
5208
  apply: apply
5089
5209
  });
5090
5210
 
5091
- const version = "2.0.0";
5211
+ const version = "2.1.0";
5092
5212
 
5093
5213
  exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
5094
5214
  exports.FunctionExpressionType = FunctionExpressionType;