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