json-p3 1.3.5 → 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 1.3.5
2
+ * json-p3 version 2.1.0
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,35 +974,65 @@ 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
 
917
1012
  /**
918
1013
  * Base class for relative and absolute JSONPath query expressions.
919
1014
  */
920
- class JSONPathQuery extends FilterExpression {
1015
+ class FilterQuery extends FilterExpression {
921
1016
  constructor(token, path) {
922
1017
  super(token);
923
1018
  this.token = token;
924
1019
  this.path = path;
925
1020
  }
926
1021
  }
927
- class RelativeQuery extends JSONPathQuery {
1022
+ 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
- class RootQuery extends JSONPathQuery {
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) {
@@ -1026,9 +1121,9 @@ var expression = /*#__PURE__*/Object.freeze({
1026
1121
  BooleanLiteral: BooleanLiteral,
1027
1122
  FilterExpression: FilterExpression,
1028
1123
  FilterExpressionLiteral: FilterExpressionLiteral,
1124
+ FilterQuery: FilterQuery,
1029
1125
  FunctionExtension: FunctionExtension,
1030
1126
  InfixExpression: InfixExpression,
1031
- JSONPathQuery: JSONPathQuery,
1032
1127
  LogicalExpression: LogicalExpression,
1033
1128
  NullLiteral: NullLiteral,
1034
1129
  NumberLiteral: NumberLiteral,
@@ -3129,11 +3224,11 @@ class JSONPathSelector {
3129
3224
  }
3130
3225
 
3131
3226
  /**
3132
- * @param nodes - Nodes matched by preceding selectors.
3227
+ * @param node - Nodes matched by preceding selectors.
3133
3228
  */
3134
3229
 
3135
3230
  /**
3136
- * @param nodes - Nodes matched by preceding selectors.
3231
+ * @param node - Nodes matched by preceding selectors.
3137
3232
  */
3138
3233
 
3139
3234
  /**
@@ -3145,31 +3240,35 @@ class JSONPathSelector {
3145
3240
  * Shorthand and quoted name selector.
3146
3241
  */
3147
3242
  class NameSelector extends JSONPathSelector {
3148
- constructor(environment, token, name, shorthand) {
3243
+ constructor(environment, token, name) {
3149
3244
  super(environment, token);
3150
3245
  this.environment = environment;
3151
3246
  this.token = token;
3152
3247
  this.name = name;
3153
- this.shorthand = shorthand;
3154
3248
  }
3155
- resolve(nodes) {
3249
+ resolve(node) {
3156
3250
  const rv = [];
3157
- for (const node of nodes) {
3158
- if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
3159
- rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
3160
- }
3251
+ if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
3252
+ rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
3161
3253
  }
3162
3254
  return rv;
3163
3255
  }
3164
- *lazyResolve(nodes) {
3165
- for (const node of nodes) {
3166
- if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
3167
- yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
3168
- }
3256
+ *lazyResolve(node) {
3257
+ if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
3258
+ yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
3169
3259
  }
3170
3260
  }
3171
- toString() {
3172
- return this.shorthand ? `['${this.name}']` : `'${this.name}'`;
3261
+ toString(options) {
3262
+ const {
3263
+ form
3264
+ } = {
3265
+ ...defaultSerializationOptions,
3266
+ ...options
3267
+ };
3268
+ return form === "canonical" ? toCanonical(this.name) : toQuoted(this.name);
3269
+ }
3270
+ shorthand() {
3271
+ return toShorthand(this.name);
3173
3272
  }
3174
3273
  }
3175
3274
 
@@ -3186,25 +3285,21 @@ class IndexSelector extends JSONPathSelector {
3186
3285
  throw new JSONPathIndexError("index out of range", this.token);
3187
3286
  }
3188
3287
  }
3189
- resolve(nodes) {
3288
+ resolve(node) {
3190
3289
  const rv = [];
3191
- for (const node of nodes) {
3192
- if (isArray(node.value)) {
3193
- const normIndex = this.normalizedIndex(node.value.length);
3194
- if (normIndex in node.value) {
3195
- rv.push(new JSONPathNode(node.value[normIndex], node.location.concat(normIndex), node.root));
3196
- }
3290
+ if (isArray(node.value)) {
3291
+ const normIndex = this.normalizedIndex(node.value.length);
3292
+ if (normIndex in node.value) {
3293
+ rv.push(new JSONPathNode(node.value[normIndex], node.location.concat(normIndex), node.root));
3197
3294
  }
3198
3295
  }
3199
3296
  return rv;
3200
3297
  }
3201
- *lazyResolve(nodes) {
3202
- for (const node of nodes) {
3203
- if (isArray(node.value)) {
3204
- const normIndex = this.normalizedIndex(node.value.length);
3205
- if (normIndex in node.value) {
3206
- yield new JSONPathNode(node.value[normIndex], node.location.concat(normIndex), node.root);
3207
- }
3298
+ *lazyResolve(node) {
3299
+ if (isArray(node.value)) {
3300
+ const normIndex = this.normalizedIndex(node.value.length);
3301
+ if (normIndex in node.value) {
3302
+ yield new JSONPathNode(node.value[normIndex], node.location.concat(normIndex), node.root);
3208
3303
  }
3209
3304
  }
3210
3305
  }
@@ -3226,19 +3321,16 @@ class SliceSelector extends JSONPathSelector {
3226
3321
  this.step = step;
3227
3322
  this.checkRange(start, stop, step);
3228
3323
  }
3229
- resolve(nodes) {
3324
+ resolve(node) {
3230
3325
  const rv = [];
3231
- for (const node of nodes) {
3232
- if (!isArray(node.value)) continue;
3233
- for (const [i, value] of this.slice(node.value, this.start, this.stop, this.step)) {
3234
- rv.push(new JSONPathNode(value, node.location.concat(i), node.root));
3235
- }
3326
+ if (!isArray(node.value)) return rv;
3327
+ for (const [i, value] of this.slice(node.value, this.start, this.stop, this.step)) {
3328
+ rv.push(new JSONPathNode(value, node.location.concat(i), node.root));
3236
3329
  }
3237
3330
  return rv;
3238
3331
  }
3239
- *lazyResolve(nodes) {
3240
- for (const node of nodes) {
3241
- if (!isArray(node.value)) continue;
3332
+ *lazyResolve(node) {
3333
+ if (isArray(node.value)) {
3242
3334
  for (const [i, value] of this.lazySlice(node.value, this.start, this.stop, this.step)) {
3243
3335
  yield new JSONPathNode(value, node.location.concat(i), node.root);
3244
3336
  }
@@ -3344,281 +3436,185 @@ class SliceSelector extends JSONPathSelector {
3344
3436
  }
3345
3437
  class WildcardSelector extends JSONPathSelector {
3346
3438
  constructor(environment, token) {
3347
- let shorthand = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
3348
3439
  super(environment, token);
3349
3440
  this.environment = environment;
3350
3441
  this.token = token;
3351
- this.shorthand = shorthand;
3352
3442
  }
3353
- resolve(nodes) {
3443
+ resolve(node) {
3354
3444
  const rv = [];
3355
- for (const node of nodes) {
3356
- if (node.value instanceof String) continue;
3357
- if (isArray(node.value)) {
3358
- for (let i = 0; i < node.value.length; i++) {
3359
- rv.push(new JSONPathNode(node.value[i], node.location.concat(i), node.root));
3360
- }
3361
- } else if (isObject(node.value)) {
3362
- for (const [key, value] of this.environment.entries(node.value)) {
3363
- rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
3364
- }
3445
+ if (node.value instanceof String) return rv;
3446
+ if (isArray(node.value)) {
3447
+ for (let i = 0; i < node.value.length; i++) {
3448
+ rv.push(new JSONPathNode(node.value[i], node.location.concat(i), node.root));
3449
+ }
3450
+ } else if (isObject(node.value)) {
3451
+ for (const [key, value] of this.environment.entries(node.value)) {
3452
+ rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
3365
3453
  }
3366
3454
  }
3367
3455
  return rv;
3368
3456
  }
3369
- *lazyResolve(nodes) {
3370
- for (const node of nodes) {
3371
- if (node.value instanceof String) continue;
3372
- if (isArray(node.value)) {
3373
- for (let i = 0; i < node.value.length; i++) {
3374
- yield new JSONPathNode(node.value[i], node.location.concat(i), node.root);
3375
- }
3376
- } else if (isObject(node.value)) {
3377
- for (const [key, value] of this.environment.entries(node.value)) {
3378
- yield new JSONPathNode(value, node.location.concat(key), node.root);
3379
- }
3457
+ *lazyResolve(node) {
3458
+ if (isArray(node.value)) {
3459
+ for (let i = 0; i < node.value.length; i++) {
3460
+ yield new JSONPathNode(node.value[i], node.location.concat(i), node.root);
3461
+ }
3462
+ } else if (isObject(node.value) && !isString(node.value)) {
3463
+ for (const [key, value] of this.environment.entries(node.value)) {
3464
+ yield new JSONPathNode(value, node.location.concat(key), node.root);
3380
3465
  }
3381
3466
  }
3382
3467
  }
3383
3468
  toString() {
3384
- return this.shorthand ? "[*]" : "*";
3469
+ return "*";
3385
3470
  }
3386
3471
  }
3387
- class RecursiveDescentSegment extends JSONPathSelector {
3388
- constructor(environment, token, selector) {
3472
+ class FilterSelector extends JSONPathSelector {
3473
+ constructor(environment, token, expression) {
3389
3474
  super(environment, token);
3390
3475
  this.environment = environment;
3391
3476
  this.token = token;
3392
- this.selector = selector;
3393
- }
3394
- resolve(nodes) {
3395
- const rv = [];
3396
- if (this.environment.nondeterministic) {
3397
- for (const root of nodes) {
3398
- for (const node of this.nondeterministicVisitor(root)) {
3399
- rv.push(node);
3400
- }
3401
- }
3402
- } else {
3403
- for (const node of nodes) {
3404
- rv.push(node);
3405
- for (const _node of this.visitor(node)) {
3406
- rv.push(_node);
3407
- }
3408
- }
3409
- }
3410
- return this.selector.resolve(rv);
3411
- }
3412
- *lazyResolve(nodes) {
3413
- yield* this.selector.lazyResolve(this._lazyResolve(nodes));
3414
- }
3415
-
3416
- // eslint-disable-next-line sonarjs/cognitive-complexity
3417
- *_lazyResolve(nodes) {
3418
- for (const _node of nodes) {
3419
- const stack = [{
3420
- node: _node,
3421
- depth: 0
3422
- }];
3423
- yield _node;
3424
- while (stack.length) {
3425
- const {
3426
- node: currentNode,
3427
- depth
3428
- } = stack.pop();
3429
- if (depth >= this.environment.maxRecursionDepth) {
3430
- throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
3431
- }
3432
- if (currentNode.value instanceof String) continue;
3433
- if (isArray(currentNode.value)) {
3434
- for (let i = 0; i < currentNode.value.length; i++) {
3435
- const __node = new JSONPathNode(currentNode.value[i], currentNode.location.concat(i), currentNode.root);
3436
- yield __node;
3437
- if (isObject(__node.value)) {
3438
- stack.push({
3439
- node: __node,
3440
- depth: depth + 1
3441
- });
3442
- }
3443
- }
3444
- } else if (isObject(currentNode.value)) {
3445
- for (const [key, value] of this.environment.entries(currentNode.value)) {
3446
- const __node = new JSONPathNode(value, currentNode.location.concat(key), currentNode.root);
3447
- yield __node;
3448
- if (isObject(__node.value)) {
3449
- stack.push({
3450
- node: __node,
3451
- depth: depth + 1
3452
- });
3453
- }
3454
- }
3455
- }
3456
- }
3457
- }
3458
- }
3459
- toString() {
3460
- return `..${this.selector.toString()}`;
3477
+ this.expression = expression;
3461
3478
  }
3462
- visitor(node) {
3463
- let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
3464
- if (depth >= this.environment.maxRecursionDepth) {
3465
- throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
3466
- }
3479
+ resolve(node) {
3467
3480
  const rv = [];
3468
3481
  if (node.value instanceof String) return rv;
3469
3482
  if (isArray(node.value)) {
3470
3483
  for (let i = 0; i < node.value.length; i++) {
3471
- const _node = new JSONPathNode(node.value[i], node.location.concat(i), node.root);
3472
- rv.push(_node);
3473
- for (const __node of this.visitor(_node, depth + 1)) {
3474
- rv.push(__node);
3484
+ const value = node.value[i];
3485
+ const filterContext = {
3486
+ environment: this.environment,
3487
+ currentValue: value,
3488
+ rootValue: node.root,
3489
+ currentKey: i
3490
+ };
3491
+ if (this.expression.evaluate(filterContext)) {
3492
+ rv.push(new JSONPathNode(value, node.location.concat(i), node.root));
3475
3493
  }
3476
3494
  }
3477
3495
  } else if (isObject(node.value)) {
3478
3496
  for (const [key, value] of this.environment.entries(node.value)) {
3479
- const _node = new JSONPathNode(value, node.location.concat(key), node.root);
3480
- rv.push(_node);
3481
- for (const __node of this.visitor(_node, depth + 1)) {
3482
- rv.push(__node);
3483
- }
3484
- }
3485
- }
3486
- return rv;
3487
- }
3488
- nondeterministicVisitor(root) {
3489
- let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
3490
- const rv = [root];
3491
- let queue = this.nondeterministicChildren(root).map(node => [node, depth]);
3492
- while (queue.length) {
3493
- const [node, _depth] = queue.shift();
3494
- rv.push(node);
3495
- if (_depth >= this.environment.maxRecursionDepth) {
3496
- throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
3497
- }
3498
-
3499
- // Visit child nodes now or queue them for later?
3500
- const visitChildren = Math.random() < 0.5;
3501
- for (const child of this.nondeterministicChildren(node)) {
3502
- if (visitChildren) {
3503
- rv.push(child);
3504
- const grandchildren = this.nondeterministicChildren(child).map(n => [n, _depth + 2]);
3505
- queue = interleave(queue, grandchildren);
3506
- } else {
3507
- queue.push([child, _depth + 1]);
3497
+ const filterContext = {
3498
+ environment: this.environment,
3499
+ currentValue: value,
3500
+ rootValue: node.root,
3501
+ currentKey: key
3502
+ };
3503
+ if (this.expression.evaluate(filterContext)) {
3504
+ rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
3508
3505
  }
3509
3506
  }
3510
3507
  }
3511
3508
  return rv;
3512
3509
  }
3513
- nondeterministicChildren(node) {
3514
- const _rv = [];
3515
- if (node.value instanceof String) return _rv;
3510
+ *lazyResolve(node) {
3516
3511
  if (isArray(node.value)) {
3517
3512
  for (let i = 0; i < node.value.length; i++) {
3518
- _rv.push(new JSONPathNode(node.value[i], node.location.concat(i), node.root));
3513
+ const value = node.value[i];
3514
+ const filterContext = {
3515
+ environment: this.environment,
3516
+ currentValue: value,
3517
+ rootValue: node.root,
3518
+ lazy: true,
3519
+ currentKey: i
3520
+ };
3521
+ if (this.expression.evaluate(filterContext)) {
3522
+ yield new JSONPathNode(value, node.location.concat(i), node.root);
3523
+ }
3519
3524
  }
3520
- } else if (isObject(node.value)) {
3525
+ } else if (isObject(node.value) && !isString(node.value)) {
3521
3526
  for (const [key, value] of this.environment.entries(node.value)) {
3522
- _rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
3527
+ const filterContext = {
3528
+ environment: this.environment,
3529
+ currentValue: value,
3530
+ rootValue: node.root,
3531
+ lazy: true,
3532
+ currentKey: key
3533
+ };
3534
+ if (this.expression.evaluate(filterContext)) {
3535
+ yield new JSONPathNode(value, node.location.concat(key), node.root);
3536
+ }
3523
3537
  }
3524
3538
  }
3525
- return _rv;
3539
+ }
3540
+ toString(options) {
3541
+ return `?${this.expression.toString(options)}`;
3526
3542
  }
3527
3543
  }
3528
- class FilterSelector extends JSONPathSelector {
3529
- constructor(environment, token, expression) {
3530
- super(environment, token);
3544
+
3545
+ var selectors = /*#__PURE__*/Object.freeze({
3546
+ __proto__: null,
3547
+ FilterSelector: FilterSelector,
3548
+ IndexSelector: IndexSelector,
3549
+ JSONPathSelector: JSONPathSelector,
3550
+ NameSelector: NameSelector,
3551
+ SliceSelector: SliceSelector,
3552
+ WildcardSelector: WildcardSelector
3553
+ });
3554
+
3555
+ /** Base class for all JSONPath segments. Both shorthand and bracketed. */
3556
+ class JSONPathSegment {
3557
+ constructor(environment, token, selectors) {
3531
3558
  this.environment = environment;
3532
3559
  this.token = token;
3533
- this.expression = expression;
3560
+ this.selectors = selectors;
3534
3561
  }
3535
3562
 
3536
- // eslint-disable-next-line sonarjs/cognitive-complexity
3563
+ /**
3564
+ * @param nodes - Nodes matched by preceding segments.
3565
+ */
3566
+
3567
+ /**
3568
+ * @param nodes - Nodes matched by preceding segments.
3569
+ */
3570
+
3571
+ /**
3572
+ * Return a string representation of this segment.
3573
+ */
3574
+ }
3575
+
3576
+ /** The child selection segment. */
3577
+ class ChildSegment extends JSONPathSegment {
3537
3578
  resolve(nodes) {
3538
3579
  const rv = [];
3539
3580
  for (const node of nodes) {
3540
- if (node.value instanceof String) continue;
3541
- if (isArray(node.value)) {
3542
- for (let i = 0; i < node.value.length; i++) {
3543
- const value = node.value[i];
3544
- const filterContext = {
3545
- environment: this.environment,
3546
- currentValue: value,
3547
- rootValue: node.root,
3548
- currentKey: i
3549
- };
3550
- if (this.expression.evaluate(filterContext)) {
3551
- rv.push(new JSONPathNode(value, node.location.concat(i), node.root));
3552
- }
3553
- }
3554
- } else if (isObject(node.value)) {
3555
- for (const [key, value] of this.environment.entries(node.value)) {
3556
- const filterContext = {
3557
- environment: this.environment,
3558
- currentValue: value,
3559
- rootValue: node.root,
3560
- currentKey: key
3561
- };
3562
- if (this.expression.evaluate(filterContext)) {
3563
- rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
3564
- }
3565
- }
3581
+ for (const selector of this.selectors) {
3582
+ rv.push(...selector.resolve(node));
3566
3583
  }
3567
3584
  }
3568
3585
  return rv;
3569
3586
  }
3570
-
3571
- // eslint-disable-next-line sonarjs/cognitive-complexity
3572
3587
  *lazyResolve(nodes) {
3573
3588
  for (const node of nodes) {
3574
- if (node.value instanceof String) continue;
3575
- if (isArray(node.value)) {
3576
- for (let i = 0; i < node.value.length; i++) {
3577
- const value = node.value[i];
3578
- const filterContext = {
3579
- environment: this.environment,
3580
- currentValue: value,
3581
- rootValue: node.root,
3582
- lazy: true,
3583
- currentKey: i
3584
- };
3585
- if (this.expression.evaluate(filterContext)) {
3586
- yield new JSONPathNode(value, node.location.concat(i), node.root);
3587
- }
3588
- }
3589
- } else if (isObject(node.value)) {
3590
- for (const [key, value] of this.environment.entries(node.value)) {
3591
- const filterContext = {
3592
- environment: this.environment,
3593
- currentValue: value,
3594
- rootValue: node.root,
3595
- lazy: true,
3596
- currentKey: key
3597
- };
3598
- if (this.expression.evaluate(filterContext)) {
3599
- yield new JSONPathNode(value, node.location.concat(key), node.root);
3600
- }
3601
- }
3589
+ for (const selector of this.selectors) {
3590
+ yield* selector.resolve(node);
3602
3591
  }
3603
3592
  }
3604
3593
  }
3605
- toString() {
3606
- return `?${this.expression.toString()}`;
3594
+ toString(options) {
3595
+ const {
3596
+ form
3597
+ } = {
3598
+ ...defaultSerializationOptions,
3599
+ ...options
3600
+ };
3601
+ if (form === "pretty" && this.selectors.length === 1 && this.selectors[0] instanceof NameSelector) {
3602
+ const shorthand = this.selectors[0].shorthand();
3603
+ if (shorthand != null) return `.${shorthand}`;
3604
+ }
3605
+ return `[${this.selectors.map(s => s.toString(options)).join(", ")}]`;
3607
3606
  }
3608
3607
  }
3609
- class BracketedSelection extends JSONPathSelector {
3610
- constructor(environment, token, items) {
3611
- super(environment, token);
3612
- this.environment = environment;
3613
- this.token = token;
3614
- this.items = items;
3615
- }
3608
+
3609
+ /** The recursive descent segment. */
3610
+ class DescendantSegment extends JSONPathSegment {
3616
3611
  resolve(nodes) {
3617
3612
  const rv = [];
3613
+ const visitor = (this.environment.nondeterministic ? this.nondeterministicVisit : this.visit).bind(this);
3618
3614
  for (const node of nodes) {
3619
- for (const item of this.items) {
3620
- for (const _node of item.resolve([node])) {
3621
- rv.push(_node);
3615
+ for (const _node of visitor(node)) {
3616
+ for (const selector of this.selectors) {
3617
+ rv.push(...selector.resolve(_node));
3622
3618
  }
3623
3619
  }
3624
3620
  }
@@ -3626,13 +3622,75 @@ class BracketedSelection extends JSONPathSelector {
3626
3622
  }
3627
3623
  *lazyResolve(nodes) {
3628
3624
  for (const node of nodes) {
3629
- for (const item of this.items) {
3630
- yield* item.lazyResolve([node]);
3625
+ for (const _node of this.visit(node)) {
3626
+ for (const selector of this.selectors) {
3627
+ yield* selector.resolve(_node);
3628
+ }
3631
3629
  }
3632
3630
  }
3633
3631
  }
3634
- toString() {
3635
- return `[${this.items.map(itm => itm.toString()).join(", ")}]`;
3632
+ toString(options) {
3633
+ return `..[${this.selectors.map(s => s.toString(options)).join(", ")}]`;
3634
+ }
3635
+ visit(node) {
3636
+ var _this = this;
3637
+ let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
3638
+ return function* () {
3639
+ if (depth >= _this.environment.maxRecursionDepth) {
3640
+ throw new JSONPathRecursionLimitError("recursion limit reached", _this.token);
3641
+ }
3642
+ yield node;
3643
+ if (isArray(node.value)) {
3644
+ for (let i = 0; i < node.value.length; i++) {
3645
+ const _node = new JSONPathNode(node.value[i], node.location.concat(i), node.root);
3646
+ yield* _this.visit(_node, depth + 1);
3647
+ }
3648
+ } else if (isObject(node.value)) {
3649
+ for (const [key, value] of _this.environment.entries(node.value)) {
3650
+ const _node = new JSONPathNode(value, node.location.concat(key), node.root);
3651
+ yield* _this.visit(_node, depth + 1);
3652
+ }
3653
+ }
3654
+ }();
3655
+ }
3656
+ nondeterministicVisit(root) {
3657
+ var _this2 = this;
3658
+ let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
3659
+ return function* () {
3660
+ let queue = Array.from(_this2.nondeterministicChildren(root)).map(node => [node, depth]);
3661
+ yield root;
3662
+ while (queue.length) {
3663
+ const [node, _depth] = queue.shift();
3664
+ yield node;
3665
+ if (_depth >= _this2.environment.maxRecursionDepth) {
3666
+ throw new JSONPathRecursionLimitError("recursion limit reached", _this2.token);
3667
+ }
3668
+
3669
+ // Visit child nodes now or queue them for later?
3670
+ const visitChildren = Math.random() < 0.5;
3671
+ for (const child of _this2.nondeterministicChildren(node)) {
3672
+ if (visitChildren) {
3673
+ yield child;
3674
+ const grandchildren = Array.from(_this2.nondeterministicChildren(child)).map(n => [n, _depth + 2]);
3675
+ queue = interleave(queue, grandchildren);
3676
+ } else {
3677
+ queue.push([child, _depth + 1]);
3678
+ }
3679
+ }
3680
+ }
3681
+ }();
3682
+ }
3683
+ *nondeterministicChildren(node) {
3684
+ if (isString(node.value)) return;
3685
+ if (isArray(node.value)) {
3686
+ for (let i = 0; i < node.value.length; i++) {
3687
+ yield new JSONPathNode(node.value[i], node.location.concat(i), node.root);
3688
+ }
3689
+ } else if (isObject(node.value)) {
3690
+ for (const [key, value] of this.environment.entries(node.value)) {
3691
+ yield new JSONPathNode(value, node.location.concat(key), node.root);
3692
+ }
3693
+ }
3636
3694
  }
3637
3695
  }
3638
3696
 
@@ -3671,54 +3729,37 @@ function shuffle(entries) {
3671
3729
  return entries;
3672
3730
  }
3673
3731
 
3674
- var selectors = /*#__PURE__*/Object.freeze({
3675
- __proto__: null,
3676
- BracketedSelection: BracketedSelection,
3677
- FilterSelector: FilterSelector,
3678
- IndexSelector: IndexSelector,
3679
- JSONPathSelector: JSONPathSelector,
3680
- NameSelector: NameSelector,
3681
- RecursiveDescentSegment: RecursiveDescentSegment,
3682
- SliceSelector: SliceSelector,
3683
- WildcardSelector: WildcardSelector
3684
- });
3685
-
3686
3732
  /**
3687
- *
3733
+ * A compiled JSONPath query ready to be applied to different data repeatedly.
3688
3734
  */
3689
- class JSONPath {
3690
- /**
3691
- *
3692
- * @param environment -
3693
- * @param selectors -
3694
- */
3695
- constructor(environment, selectors) {
3735
+ class JSONPathQuery {
3736
+ constructor(environment, segments) {
3696
3737
  this.environment = environment;
3697
- this.selectors = selectors;
3738
+ this.segments = segments;
3698
3739
  }
3699
3740
 
3700
3741
  /**
3701
- *
3702
- * @param value -
3703
- * @returns
3742
+ * Apply this JSONPath query to _value_.
3743
+ * @param value - A JSON-like object to apply this query to.
3744
+ * @returns Nodes matched by applying this query to _value_.
3704
3745
  */
3705
3746
  query(value) {
3706
3747
  let nodes = [new JSONPathNode(value, [], value)];
3707
- for (const selector of this.selectors) {
3708
- nodes = selector.resolve(nodes);
3748
+ for (const segment of this.segments) {
3749
+ nodes = segment.resolve(nodes);
3709
3750
  }
3710
3751
  return new JSONPathNodeList(nodes);
3711
3752
  }
3712
3753
 
3713
3754
  /**
3714
- *
3715
- * @param value -
3716
- * @returns
3755
+ * Apply this JSONPath query to _value_.
3756
+ * @param value - A JSON-like object to apply this query to.
3757
+ * @returns An iterator over nodes matched by applying this query to _value_.
3717
3758
  */
3718
3759
  lazyQuery(value) {
3719
3760
  let nodes = [new JSONPathNode(value, [], value)][Symbol.iterator]();
3720
- for (const selector of this.selectors) {
3721
- nodes = selector.lazyResolve(nodes);
3761
+ for (const segment of this.segments) {
3762
+ nodes = segment.lazyResolve(nodes);
3722
3763
  }
3723
3764
  return nodes;
3724
3765
  }
@@ -3739,15 +3780,21 @@ class JSONPath {
3739
3780
  }
3740
3781
 
3741
3782
  /**
3742
- *
3783
+ * Return a string representation of this query.
3743
3784
  */
3744
- toString() {
3745
- return `$${this.selectors.map(s => s.toString()).join("")}`;
3785
+ toString(options) {
3786
+ return `$${this.segments.map(s => s.toString(options)).join("")}`;
3746
3787
  }
3788
+
3789
+ /**
3790
+ * Return `true` if this query is a _singular query_, or `false` otherwise.
3791
+ */
3747
3792
  singularQuery() {
3748
- for (const selector of this.selectors) {
3749
- if (selector instanceof NameSelector) continue;
3750
- if (selector instanceof BracketedSelection && selector.items.length === 1 && (selector.items[0] instanceof NameSelector || selector.items[0] instanceof IndexSelector)) continue;
3793
+ for (const segment of this.segments) {
3794
+ if (segment instanceof DescendantSegment) return false;
3795
+ if (segment.selectors.length === 1 && (segment.selectors[0] instanceof NameSelector || segment.selectors[0] instanceof IndexSelector)) {
3796
+ continue;
3797
+ }
3751
3798
  return false;
3752
3799
  }
3753
3800
  return true;
@@ -3765,33 +3812,33 @@ class CurrentKey extends FilterExpression {
3765
3812
 
3766
3813
  class KeySelector extends JSONPathSelector {
3767
3814
  constructor(environment, token, key) {
3768
- let shorthand = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
3769
3815
  super(environment, token);
3770
3816
  this.environment = environment;
3771
3817
  this.token = token;
3772
3818
  this.key = key;
3773
- this.shorthand = shorthand;
3774
3819
  }
3775
- resolve(nodes) {
3820
+ resolve(node) {
3776
3821
  const rv = [];
3777
- for (const node of nodes) {
3778
- if (node.value instanceof String || isArray(node.value)) continue;
3779
- if (isObject(node.value) && hasStringKey(node.value, this.key)) {
3780
- rv.push(new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root));
3781
- }
3822
+ if (node.value instanceof String || isArray(node.value)) return rv;
3823
+ if (isObject(node.value) && hasStringKey(node.value, this.key)) {
3824
+ rv.push(new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root));
3782
3825
  }
3783
3826
  return rv;
3784
3827
  }
3785
- *lazyResolve(nodes) {
3786
- for (const node of nodes) {
3787
- if (node.value instanceof String || isArray(node.value)) continue;
3788
- if (isObject(node.value) && hasStringKey(node.value, this.key)) {
3789
- yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
3790
- }
3828
+ *lazyResolve(node) {
3829
+ if (!isString(node.value) && isObject(node.value) && hasStringKey(node.value, this.key)) {
3830
+ yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
3791
3831
  }
3792
3832
  }
3793
- toString() {
3794
- return this.shorthand ? `[~'${this.key.replaceAll("'", "\\'")}']` : `~'${this.key.replaceAll("'", "\\'")}'`;
3833
+ toString(options) {
3834
+ const {
3835
+ form
3836
+ } = {
3837
+ ...defaultSerializationOptions,
3838
+ ...options
3839
+ };
3840
+ const serialize = form === "canonical" ? toCanonical : toQuoted;
3841
+ return `~${serialize(this.key)}`;
3795
3842
  }
3796
3843
  }
3797
3844
 
@@ -3800,36 +3847,29 @@ class KeySelector extends JSONPathSelector {
3800
3847
  */
3801
3848
  class KeysSelector extends JSONPathSelector {
3802
3849
  constructor(environment, token) {
3803
- let shorthand = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
3804
3850
  super(environment, token);
3805
3851
  this.environment = environment;
3806
3852
  this.token = token;
3807
- this.shorthand = shorthand;
3808
3853
  }
3809
- resolve(nodes) {
3854
+ resolve(node) {
3810
3855
  const rv = [];
3811
- for (const node of nodes) {
3812
- if (node.value instanceof String || isArray(node.value)) continue;
3813
- if (isObject(node.value)) {
3814
- for (const [key, _] of this.environment.entries(node.value)) {
3815
- rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
3816
- }
3856
+ if (node.value instanceof String || isArray(node.value)) return rv;
3857
+ if (isObject(node.value)) {
3858
+ for (const [key, _] of this.environment.entries(node.value)) {
3859
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
3817
3860
  }
3818
3861
  }
3819
3862
  return rv;
3820
3863
  }
3821
- *lazyResolve(nodes) {
3822
- for (const node of nodes) {
3823
- if (node.value instanceof String || isArray(node.value)) continue;
3824
- if (isObject(node.value)) {
3825
- for (const [key, _] of this.environment.entries(node.value)) {
3826
- yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
3827
- }
3864
+ *lazyResolve(node) {
3865
+ if (isObject(node.value) && !isString(node.value) && !isArray(node.value)) {
3866
+ for (const [key, _] of this.environment.entries(node.value)) {
3867
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
3828
3868
  }
3829
3869
  }
3830
3870
  }
3831
3871
  toString() {
3832
- return this.shorthand ? "[~]" : "~";
3872
+ return "~";
3833
3873
  }
3834
3874
  }
3835
3875
  class KeysFilterSelector extends JSONPathSelector {
@@ -3839,47 +3879,43 @@ class KeysFilterSelector extends JSONPathSelector {
3839
3879
  this.token = token;
3840
3880
  this.expression = expression;
3841
3881
  }
3842
- resolve(nodes) {
3882
+ resolve(node) {
3843
3883
  const rv = [];
3844
- for (const node of nodes) {
3845
- if (node.value instanceof String || isArray(node.value)) continue;
3846
- if (isObject(node.value)) {
3847
- for (const [key, value] of this.environment.entries(node.value)) {
3848
- const filterContext = {
3849
- environment: this.environment,
3850
- currentValue: value,
3851
- rootValue: node.root,
3852
- currentKey: key
3853
- };
3854
- if (this.expression.evaluate(filterContext)) {
3855
- rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
3856
- }
3884
+ if (node.value instanceof String || isArray(node.value)) return rv;
3885
+ if (isObject(node.value)) {
3886
+ for (const [key, value] of this.environment.entries(node.value)) {
3887
+ const filterContext = {
3888
+ environment: this.environment,
3889
+ currentValue: value,
3890
+ rootValue: node.root,
3891
+ currentKey: key
3892
+ };
3893
+ if (this.expression.evaluate(filterContext)) {
3894
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
3857
3895
  }
3858
3896
  }
3859
3897
  }
3860
3898
  return rv;
3861
3899
  }
3862
- *lazyResolve(nodes) {
3863
- for (const node of nodes) {
3864
- if (node.value instanceof String || isArray(node.value)) continue;
3865
- if (isObject(node.value)) {
3866
- for (const [key, value] of this.environment.entries(node.value)) {
3867
- const filterContext = {
3868
- environment: this.environment,
3869
- currentValue: value,
3870
- rootValue: node.root,
3871
- lazy: true,
3872
- currentKey: key
3873
- };
3874
- if (this.expression.evaluate(filterContext)) {
3875
- yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
3876
- }
3900
+ *lazyResolve(node) {
3901
+ if (node.value instanceof String || isArray(node.value)) return;
3902
+ if (isObject(node.value)) {
3903
+ for (const [key, value] of this.environment.entries(node.value)) {
3904
+ const filterContext = {
3905
+ environment: this.environment,
3906
+ currentValue: value,
3907
+ rootValue: node.root,
3908
+ lazy: true,
3909
+ currentKey: key
3910
+ };
3911
+ if (this.expression.evaluate(filterContext)) {
3912
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
3877
3913
  }
3878
3914
  }
3879
3915
  }
3880
3916
  }
3881
- toString() {
3882
- return `~?${this.expression.toString()}`;
3917
+ toString(options) {
3918
+ return `~?${this.expression.toString(options)}`;
3883
3919
  }
3884
3920
  }
3885
3921
 
@@ -3902,52 +3938,59 @@ class Parser {
3902
3938
  }
3903
3939
  parse(stream) {
3904
3940
  if (stream.current.kind === TokenKind.ROOT) stream.next();
3905
- const selectors = this.parsePath(stream);
3941
+ const segments = this.parseQuery(stream);
3906
3942
  if (stream.current.kind !== TokenKind.EOF) {
3907
3943
  throw new JSONPathSyntaxError(`unexpected token '${stream.current.kind}'`, stream.current);
3908
3944
  }
3909
- return selectors;
3945
+ return segments;
3910
3946
  }
3911
- parsePath(stream) {
3947
+ parseQuery(stream) {
3912
3948
  let inFilter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
3913
- const selectors = [];
3914
- for (;;) {
3915
- const selector = this.parseSegment(stream);
3916
- if (!selector) {
3917
- if (inFilter) {
3918
- stream.backup();
3919
- }
3920
- break;
3949
+ const segments = [];
3950
+ loop: for (;;) {
3951
+ switch (stream.current.kind) {
3952
+ case TokenKind.DDOT:
3953
+ {
3954
+ const token = stream.next();
3955
+ const selectors = this.parseSelectors(stream);
3956
+ segments.push(new DescendantSegment(this.environment, token, selectors));
3957
+ break;
3958
+ }
3959
+ case TokenKind.LBRACKET:
3960
+ case TokenKind.KEY:
3961
+ case TokenKind.KEYS:
3962
+ case TokenKind.NAME:
3963
+ case TokenKind.WILD:
3964
+ {
3965
+ const token = stream.current;
3966
+ const selectors = this.parseSelectors(stream);
3967
+ segments.push(new ChildSegment(this.environment, token, selectors));
3968
+ break;
3969
+ }
3970
+ default:
3971
+ {
3972
+ if (inFilter) stream.backup();
3973
+ break loop;
3974
+ }
3921
3975
  }
3922
- selectors.push(selector);
3923
3976
  stream.next();
3924
3977
  }
3925
- return selectors;
3978
+ return segments;
3926
3979
  }
3927
- parseSegment(stream) {
3980
+ parseSelectors(stream) {
3928
3981
  switch (stream.current.kind) {
3929
3982
  case TokenKind.NAME:
3930
- return new NameSelector(this.environment, stream.current, stream.current.value, true);
3983
+ return [new NameSelector(this.environment, stream.current, stream.current.value)];
3931
3984
  case TokenKind.WILD:
3932
- return new WildcardSelector(this.environment, stream.current, true);
3985
+ return [new WildcardSelector(this.environment, stream.current)];
3933
3986
  case TokenKind.KEY:
3934
- return new KeySelector(this.environment, stream.current, stream.current.value, true);
3987
+ return [new KeySelector(this.environment, stream.current, stream.current.value)];
3935
3988
  case TokenKind.KEYS:
3936
- return new KeysSelector(this.environment, stream.current, true);
3937
- case TokenKind.DDOT:
3938
- {
3939
- const segmentToken = stream.current;
3940
- stream.next();
3941
- const selector = this.parseSegment(stream);
3942
- if (!selector) {
3943
- throw new JSONPathSyntaxError("bald descendant segment", stream.current);
3944
- }
3945
- return new RecursiveDescentSegment(this.environment, segmentToken, selector);
3946
- }
3989
+ return [new KeysSelector(this.environment, stream.current)];
3947
3990
  case TokenKind.LBRACKET:
3948
3991
  return this.parseBracketedSelection(stream);
3949
3992
  default:
3950
- return null;
3993
+ return [];
3951
3994
  }
3952
3995
  }
3953
3996
  parseIndex(stream) {
@@ -4004,38 +4047,38 @@ class Parser {
4004
4047
  }
4005
4048
  parseBracketedSelection(stream) {
4006
4049
  const token = stream.next();
4007
- const items = [];
4050
+ const selectors = [];
4008
4051
  while (stream.current.kind !== TokenKind.RBRACKET) {
4009
4052
  switch (stream.current.kind) {
4010
4053
  case TokenKind.SINGLE_QUOTE_STRING:
4011
4054
  case TokenKind.DOUBLE_QUOTE_STRING:
4012
- items.push(new NameSelector(this.environment, stream.current, this.decodeString(stream.current), false));
4055
+ selectors.push(new NameSelector(this.environment, stream.current, this.decodeString(stream.current)));
4013
4056
  break;
4014
4057
  case TokenKind.FILTER:
4015
- items.push(this.parseFilter(stream));
4058
+ selectors.push(this.parseFilter(stream));
4016
4059
  break;
4017
4060
  case TokenKind.INDEX:
4018
4061
  if (stream.peek.kind === TokenKind.COLON) {
4019
- items.push(this.parseSlice(stream));
4062
+ selectors.push(this.parseSlice(stream));
4020
4063
  } else {
4021
- items.push(this.parseIndex(stream));
4064
+ selectors.push(this.parseIndex(stream));
4022
4065
  }
4023
4066
  break;
4024
4067
  case TokenKind.COLON:
4025
- items.push(this.parseSlice(stream));
4068
+ selectors.push(this.parseSlice(stream));
4026
4069
  break;
4027
4070
  case TokenKind.WILD:
4028
- items.push(new WildcardSelector(this.environment, stream.current));
4071
+ selectors.push(new WildcardSelector(this.environment, stream.current));
4029
4072
  break;
4030
4073
  case TokenKind.KEY_SINGLE_QUOTE_STRING:
4031
4074
  case TokenKind.KEY_DOUBLE_QUOTE_STRING:
4032
- items.push(new KeySelector(this.environment, stream.current, this.decodeString(stream.current), false));
4075
+ selectors.push(new KeySelector(this.environment, stream.current, this.decodeString(stream.current)));
4033
4076
  break;
4034
4077
  case TokenKind.KEYS_FILTER:
4035
- items.push(this.parseFilter(stream, true));
4078
+ selectors.push(this.parseFilter(stream, true));
4036
4079
  break;
4037
4080
  case TokenKind.KEYS:
4038
- items.push(new KeysSelector(this.environment, stream.current));
4081
+ selectors.push(new KeysSelector(this.environment, stream.current));
4039
4082
  break;
4040
4083
  case TokenKind.EOF:
4041
4084
  throw new JSONPathSyntaxError("unexpected end of query", stream.current);
@@ -4049,10 +4092,10 @@ class Parser {
4049
4092
  }
4050
4093
  stream.next();
4051
4094
  }
4052
- if (!items.length) {
4095
+ if (!selectors.length) {
4053
4096
  throw new JSONPathSyntaxError("empty bracketed segment", token);
4054
4097
  }
4055
- return new BracketedSelection(this.environment, token, items);
4098
+ return selectors;
4056
4099
  }
4057
4100
  parseFilter(stream) {
4058
4101
  let keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
@@ -4132,11 +4175,11 @@ class Parser {
4132
4175
  }
4133
4176
  parseRootQuery(stream) {
4134
4177
  const tok = stream.next();
4135
- return new RootQuery(tok, new JSONPath(this.environment, this.parsePath(stream, true)));
4178
+ return new RootQuery(tok, new JSONPathQuery(this.environment, this.parseQuery(stream, true)));
4136
4179
  }
4137
4180
  parseRelativeQuery(stream) {
4138
4181
  const tok = stream.next();
4139
- return new RelativeQuery(tok, new JSONPath(this.environment, this.parsePath(stream, true)));
4182
+ return new RelativeQuery(tok, new JSONPathQuery(this.environment, this.parseQuery(stream, true)));
4140
4183
  }
4141
4184
  parseCurrentKey(stream) {
4142
4185
  return new CurrentKey(stream.current);
@@ -4438,10 +4481,10 @@ class JSONPathEnvironment {
4438
4481
 
4439
4482
  /**
4440
4483
  * @param path - A JSONPath query to parse.
4441
- * @returns A new {@link JSONPath} object, bound to this environment.
4484
+ * @returns A new {@link JSONPathQuery} object, bound to this environment.
4442
4485
  */
4443
4486
  compile(path) {
4444
- return new JSONPath(this, this.parser.parse(new TokenStream(tokenize(this, path))));
4487
+ return new JSONPathQuery(this, this.parser.parse(new TokenStream(tokenize(this, path))));
4445
4488
  }
4446
4489
 
4447
4490
  /**
@@ -4496,7 +4539,7 @@ class JSONPathEnvironment {
4496
4539
  /**
4497
4540
  * Check the well-typedness of a function's arguments at compile-time.
4498
4541
  *
4499
- * This method is called by the {@link Parser} when parsing function calls.
4542
+ * This method is called by the parser when parsing function calls.
4500
4543
  * It is expected to throw a {@link JSONPathTypeError} if the function's
4501
4544
  * parameters are not well-typed.
4502
4545
  *
@@ -4523,17 +4566,17 @@ class JSONPathEnvironment {
4523
4566
  for (const [typ, arg, idx] of func.argTypes.map((t, i) => [t, args[i], i])) {
4524
4567
  switch (typ) {
4525
4568
  case FunctionExpressionType.ValueType:
4526
- if (!(arg instanceof FilterExpressionLiteral || arg instanceof CurrentKey || arg instanceof JSONPathQuery && arg.path.singularQuery() || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.ValueType)) {
4569
+ if (!(arg instanceof FilterExpressionLiteral || arg instanceof CurrentKey || arg instanceof FilterQuery && arg.path.singularQuery() || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.ValueType)) {
4527
4570
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of ValueType`, arg.token);
4528
4571
  }
4529
4572
  break;
4530
4573
  case FunctionExpressionType.LogicalType:
4531
- if (!(arg instanceof JSONPathQuery || arg instanceof InfixExpression)) {
4574
+ if (!(arg instanceof FilterQuery || arg instanceof InfixExpression)) {
4532
4575
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of LogicalType`, arg.token);
4533
4576
  }
4534
4577
  break;
4535
4578
  case FunctionExpressionType.NodesType:
4536
- if (!(arg instanceof JSONPathQuery || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.NodesType)) {
4579
+ if (!(arg instanceof FilterQuery || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.NodesType)) {
4537
4580
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of NodesType`, arg.token);
4538
4581
  }
4539
4582
  }
@@ -4649,14 +4692,16 @@ var index$1 = /*#__PURE__*/Object.freeze({
4649
4692
  __proto__: null,
4650
4693
  DEFAULT_ENVIRONMENT: DEFAULT_ENVIRONMENT,
4651
4694
  FunctionExpressionType: FunctionExpressionType,
4652
- JSONPath: JSONPath,
4653
4695
  JSONPathEnvironment: JSONPathEnvironment,
4654
4696
  JSONPathError: JSONPathError,
4655
4697
  JSONPathIndexError: JSONPathIndexError,
4656
4698
  JSONPathLexerError: JSONPathLexerError,
4657
4699
  JSONPathNode: JSONPathNode,
4658
4700
  JSONPathNodeList: JSONPathNodeList,
4701
+ JSONPathQuery: JSONPathQuery,
4659
4702
  JSONPathRecursionLimitError: JSONPathRecursionLimitError,
4703
+ JSONPathSegment: JSONPathSegment,
4704
+ JSONPathSelector: JSONPathSelector,
4660
4705
  JSONPathSyntaxError: JSONPathSyntaxError,
4661
4706
  JSONPathTypeError: JSONPathTypeError,
4662
4707
  KEY_MARK: KEY_MARK,
@@ -5160,6 +5205,6 @@ var index = /*#__PURE__*/Object.freeze({
5160
5205
  apply: apply
5161
5206
  });
5162
5207
 
5163
- const version = "1.3.5";
5208
+ const version = "2.1.0";
5164
5209
 
5165
- 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 };
5210
+ 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 };