json-p3 1.0.0 → 1.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.
package/LICENCE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 James Prior
3
+ Copyright (c) 2024 James Prior
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -61,6 +61,15 @@ JSON P3 has zero runtime dependencies.
61
61
  | `json-p3-iife.js` | A bundle formatted as an Immediately Invoked Function Expression. |
62
62
  | `json-p3-iife.min.js` | A minified bundle formatted as an Immediately Invoked Function Expression. |
63
63
 
64
+ ## Compliance Environment Variables
65
+
66
+ These environment variables control the location of the compliance test suite under test and if nondeterministic object iteration is enabled for those tests.
67
+
68
+ | Environment Variable | Description |
69
+ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------- |
70
+ | `JSONP3_CTS_PATH` | The path to `cts.json` used by `compliance.test.ts`. Defaults to `tests/path/cts/cts.json`. |
71
+ | `JSONP3_CTS_NONDETERMINISTIC` | When set to `true`, enables nondeterministic iteration of JSON objects for `compliance.test.ts`. Defaults to `false`. |
72
+
64
73
  ## Contributing
65
74
 
66
75
  Please see [Contributing to JSON P3](https://github.com/jg-rp/json-p3/blob/main/CONTRIBUTING.md)
@@ -1,10 +1,10 @@
1
1
  /*
2
- * json-p3 version 1.0.0
2
+ * json-p3 version 1.1.1
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
6
6
  *
7
- * Copyright (c) 2023 James Prior
7
+ * Copyright (c) 2024 James Prior
8
8
  *
9
9
  * Permission is hereby granted, free of charge, to any person obtaining a copy
10
10
  * of this software and associated documentation files (the "Software"), to deal
@@ -862,12 +862,13 @@ class InfixExpression extends FilterExpression {
862
862
  this.left = left;
863
863
  this.operator = operator;
864
864
  this.right = right;
865
+ this.logical = operator === "&&" || operator === "||";
865
866
  }
866
867
  evaluate(context) {
867
868
  let left = this.left.evaluate(context);
868
- if (left instanceof JSONPathNodeList && left.nodes.length === 1) left = left.nodes[0].value;
869
+ if (!this.logical && left instanceof JSONPathNodeList && left.nodes.length === 1) left = left.nodes[0].value;
869
870
  let right = this.right.evaluate(context);
870
- if (right instanceof JSONPathNodeList && right.nodes.length === 1) right = right.nodes[0].value;
871
+ if (!this.logical && right instanceof JSONPathNodeList && right.nodes.length === 1) right = right.nodes[0].value;
871
872
  if (this.operator === "&&") {
872
873
  return isTruthy(left) && isTruthy(right);
873
874
  }
@@ -877,7 +878,7 @@ class InfixExpression extends FilterExpression {
877
878
  return compare(left, this.operator, right);
878
879
  }
879
880
  toString() {
880
- if (this.operator === "&&" || this.operator === "||") {
881
+ if (this.logical) {
881
882
  return `(${this.left.toString()} ${this.operator} ${this.right.toString()})`;
882
883
  }
883
884
  return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;
@@ -1927,7 +1928,7 @@ class WildcardSelector extends JSONPathSelector {
1927
1928
  rv.push(new JSONPathNode(node.value[i], node.location.concat(i), node.root));
1928
1929
  }
1929
1930
  } else if (isObject(node.value)) {
1930
- for (const [key, value] of Object.entries(node.value)) {
1931
+ for (const [key, value] of this.environment.entries(node.value)) {
1931
1932
  rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
1932
1933
  }
1933
1934
  }
@@ -1942,7 +1943,7 @@ class WildcardSelector extends JSONPathSelector {
1942
1943
  yield new JSONPathNode(node.value[i], node.location.concat(i), node.root);
1943
1944
  }
1944
1945
  } else if (isObject(node.value)) {
1945
- for (const [key, value] of Object.entries(node.value)) {
1946
+ for (const [key, value] of this.environment.entries(node.value)) {
1946
1947
  yield new JSONPathNode(value, node.location.concat(key), node.root);
1947
1948
  }
1948
1949
  }
@@ -2002,7 +2003,7 @@ class RecursiveDescentSegment extends JSONPathSelector {
2002
2003
  }
2003
2004
  }
2004
2005
  } else if (isObject(currentNode.value)) {
2005
- for (const [key, value] of Object.entries(currentNode.value)) {
2006
+ for (const [key, value] of this.environment.entries(currentNode.value)) {
2006
2007
  const __node = new JSONPathNode(value, currentNode.location.concat(key), currentNode.root);
2007
2008
  yield __node;
2008
2009
  if (isObject(__node.value)) {
@@ -2035,7 +2036,7 @@ class RecursiveDescentSegment extends JSONPathSelector {
2035
2036
  }
2036
2037
  }
2037
2038
  } else if (isObject(node.value)) {
2038
- for (const [key, value] of Object.entries(node.value)) {
2039
+ for (const [key, value] of this.environment.entries(node.value)) {
2039
2040
  const _node = new JSONPathNode(value, node.location.concat(key), node.root);
2040
2041
  rv.push(_node);
2041
2042
  for (const __node of this.visit(_node, depth + 1)) {
@@ -2072,7 +2073,7 @@ class FilterSelector extends JSONPathSelector {
2072
2073
  }
2073
2074
  }
2074
2075
  } else if (isObject(node.value)) {
2075
- for (const [key, value] of Object.entries(node.value)) {
2076
+ for (const [key, value] of this.environment.entries(node.value)) {
2076
2077
  const filterContext = {
2077
2078
  environment: this.environment,
2078
2079
  currentValue: value,
@@ -2105,7 +2106,7 @@ class FilterSelector extends JSONPathSelector {
2105
2106
  }
2106
2107
  }
2107
2108
  } else if (isObject(node.value)) {
2108
- for (const [key, value] of Object.entries(node.value)) {
2109
+ for (const [key, value] of this.environment.entries(node.value)) {
2109
2110
  const filterContext = {
2110
2111
  environment: this.environment,
2111
2112
  currentValue: value,
@@ -2237,8 +2238,8 @@ class JSONPath {
2237
2238
  }
2238
2239
 
2239
2240
  const PRECEDENCE_LOWEST = 1;
2240
- const PRECEDENCE_LOGICAL_AND = 4;
2241
- const PRECEDENCE_LOGICAL_OR = 5;
2241
+ const PRECEDENCE_LOGICAL_OR = 4;
2242
+ const PRECEDENCE_LOGICAL_AND = 5;
2242
2243
  const PRECEDENCE_COMPARISON = 6;
2243
2244
  const PRECEDENCE_PREFIX = 7;
2244
2245
  const PRECEDENCES = new Map([[TokenKind.AND, PRECEDENCE_LOGICAL_AND], [TokenKind.EQ, PRECEDENCE_COMPARISON], [TokenKind.GE, PRECEDENCE_COMPARISON], [TokenKind.GT, PRECEDENCE_COMPARISON], [TokenKind.LE, PRECEDENCE_COMPARISON], [TokenKind.LT, PRECEDENCE_COMPARISON], [TokenKind.NE, PRECEDENCE_COMPARISON], [TokenKind.NOT, PRECEDENCE_PREFIX], [TokenKind.OR, PRECEDENCE_LOGICAL_OR], [TokenKind.RPAREN, PRECEDENCE_LOWEST]]);
@@ -2568,6 +2569,10 @@ class JSONPathEnvironment {
2568
2569
  * can visit before a `JSONPathRecursionLimitError` is thrown.
2569
2570
  */
2570
2571
 
2572
+ /**
2573
+ * If `true`, enable nondeterministic ordering when iterating JSON object data.
2574
+ */
2575
+
2571
2576
  /**
2572
2577
  * A map of function names to objects implementing the {@link FilterFunction}
2573
2578
  * interface. You are free to set or delete custom filter functions directly.
@@ -2582,6 +2587,7 @@ class JSONPathEnvironment {
2582
2587
  this.maxIntIndex = options.maxIntIndex ?? Math.pow(2, 53) - 1;
2583
2588
  this.minIntIndex = options.maxIntIndex ?? -Math.pow(2, 53) - 1;
2584
2589
  this.maxRecursionDepth = options.maxRecursionDepth ?? 50;
2590
+ this.nondeterministic = options.nondeterministic ?? false;
2585
2591
  this.parser = new Parser(this);
2586
2592
  this.setupFilterFunctions();
2587
2593
  }
@@ -2690,6 +2696,29 @@ class JSONPathEnvironment {
2690
2696
  }
2691
2697
  return args;
2692
2698
  }
2699
+
2700
+ /**
2701
+ * Return an array of key/values of the enumerable properties in _obj_.
2702
+ *
2703
+ * If you want to introduce some nondeterminism to iterating JSON-like
2704
+ * objects, do it here. The wildcard selector, descendent segment and
2705
+ * filter selector all use `this.environment.entries`.
2706
+ *
2707
+ * @param obj - A JSON-like object.
2708
+ */
2709
+ entries(obj) {
2710
+ function shuffle(entries) {
2711
+ for (let i = entries.length - 1; i > 0; i--) {
2712
+ const j = Math.floor(Math.random() * (i + 1));
2713
+ [entries[i], entries[j]] = [entries[j], entries[i]];
2714
+ }
2715
+ return entries;
2716
+ }
2717
+ if (this.nondeterministic) {
2718
+ return shuffle(Object.entries(obj));
2719
+ }
2720
+ return Object.entries(obj);
2721
+ }
2693
2722
  }
2694
2723
 
2695
2724
  var index$2 = /*#__PURE__*/Object.freeze({
@@ -3278,7 +3307,7 @@ var index = /*#__PURE__*/Object.freeze({
3278
3307
  apply: apply
3279
3308
  });
3280
3309
 
3281
- const version = "1.0.0";
3310
+ const version = "1.1.1";
3282
3311
 
3283
3312
  exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
3284
3313
  exports.FunctionExpressionType = FunctionExpressionType;
@@ -1,10 +1,10 @@
1
1
  /*
2
- * json-p3 version 1.0.0
2
+ * json-p3 version 1.1.1
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
6
6
  *
7
- * Copyright (c) 2023 James Prior
7
+ * Copyright (c) 2024 James Prior
8
8
  *
9
9
  * Permission is hereby granted, free of charge, to any person obtaining a copy
10
10
  * of this software and associated documentation files (the "Software"), to deal
@@ -860,12 +860,13 @@ class InfixExpression extends FilterExpression {
860
860
  this.left = left;
861
861
  this.operator = operator;
862
862
  this.right = right;
863
+ this.logical = operator === "&&" || operator === "||";
863
864
  }
864
865
  evaluate(context) {
865
866
  let left = this.left.evaluate(context);
866
- if (left instanceof JSONPathNodeList && left.nodes.length === 1) left = left.nodes[0].value;
867
+ if (!this.logical && left instanceof JSONPathNodeList && left.nodes.length === 1) left = left.nodes[0].value;
867
868
  let right = this.right.evaluate(context);
868
- if (right instanceof JSONPathNodeList && right.nodes.length === 1) right = right.nodes[0].value;
869
+ if (!this.logical && right instanceof JSONPathNodeList && right.nodes.length === 1) right = right.nodes[0].value;
869
870
  if (this.operator === "&&") {
870
871
  return isTruthy(left) && isTruthy(right);
871
872
  }
@@ -875,7 +876,7 @@ class InfixExpression extends FilterExpression {
875
876
  return compare(left, this.operator, right);
876
877
  }
877
878
  toString() {
878
- if (this.operator === "&&" || this.operator === "||") {
879
+ if (this.logical) {
879
880
  return `(${this.left.toString()} ${this.operator} ${this.right.toString()})`;
880
881
  }
881
882
  return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;
@@ -1925,7 +1926,7 @@ class WildcardSelector extends JSONPathSelector {
1925
1926
  rv.push(new JSONPathNode(node.value[i], node.location.concat(i), node.root));
1926
1927
  }
1927
1928
  } else if (isObject(node.value)) {
1928
- for (const [key, value] of Object.entries(node.value)) {
1929
+ for (const [key, value] of this.environment.entries(node.value)) {
1929
1930
  rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
1930
1931
  }
1931
1932
  }
@@ -1940,7 +1941,7 @@ class WildcardSelector extends JSONPathSelector {
1940
1941
  yield new JSONPathNode(node.value[i], node.location.concat(i), node.root);
1941
1942
  }
1942
1943
  } else if (isObject(node.value)) {
1943
- for (const [key, value] of Object.entries(node.value)) {
1944
+ for (const [key, value] of this.environment.entries(node.value)) {
1944
1945
  yield new JSONPathNode(value, node.location.concat(key), node.root);
1945
1946
  }
1946
1947
  }
@@ -2000,7 +2001,7 @@ class RecursiveDescentSegment extends JSONPathSelector {
2000
2001
  }
2001
2002
  }
2002
2003
  } else if (isObject(currentNode.value)) {
2003
- for (const [key, value] of Object.entries(currentNode.value)) {
2004
+ for (const [key, value] of this.environment.entries(currentNode.value)) {
2004
2005
  const __node = new JSONPathNode(value, currentNode.location.concat(key), currentNode.root);
2005
2006
  yield __node;
2006
2007
  if (isObject(__node.value)) {
@@ -2033,7 +2034,7 @@ class RecursiveDescentSegment extends JSONPathSelector {
2033
2034
  }
2034
2035
  }
2035
2036
  } else if (isObject(node.value)) {
2036
- for (const [key, value] of Object.entries(node.value)) {
2037
+ for (const [key, value] of this.environment.entries(node.value)) {
2037
2038
  const _node = new JSONPathNode(value, node.location.concat(key), node.root);
2038
2039
  rv.push(_node);
2039
2040
  for (const __node of this.visit(_node, depth + 1)) {
@@ -2070,7 +2071,7 @@ class FilterSelector extends JSONPathSelector {
2070
2071
  }
2071
2072
  }
2072
2073
  } else if (isObject(node.value)) {
2073
- for (const [key, value] of Object.entries(node.value)) {
2074
+ for (const [key, value] of this.environment.entries(node.value)) {
2074
2075
  const filterContext = {
2075
2076
  environment: this.environment,
2076
2077
  currentValue: value,
@@ -2103,7 +2104,7 @@ class FilterSelector extends JSONPathSelector {
2103
2104
  }
2104
2105
  }
2105
2106
  } else if (isObject(node.value)) {
2106
- for (const [key, value] of Object.entries(node.value)) {
2107
+ for (const [key, value] of this.environment.entries(node.value)) {
2107
2108
  const filterContext = {
2108
2109
  environment: this.environment,
2109
2110
  currentValue: value,
@@ -2235,8 +2236,8 @@ class JSONPath {
2235
2236
  }
2236
2237
 
2237
2238
  const PRECEDENCE_LOWEST = 1;
2238
- const PRECEDENCE_LOGICAL_AND = 4;
2239
- const PRECEDENCE_LOGICAL_OR = 5;
2239
+ const PRECEDENCE_LOGICAL_OR = 4;
2240
+ const PRECEDENCE_LOGICAL_AND = 5;
2240
2241
  const PRECEDENCE_COMPARISON = 6;
2241
2242
  const PRECEDENCE_PREFIX = 7;
2242
2243
  const PRECEDENCES = new Map([[TokenKind.AND, PRECEDENCE_LOGICAL_AND], [TokenKind.EQ, PRECEDENCE_COMPARISON], [TokenKind.GE, PRECEDENCE_COMPARISON], [TokenKind.GT, PRECEDENCE_COMPARISON], [TokenKind.LE, PRECEDENCE_COMPARISON], [TokenKind.LT, PRECEDENCE_COMPARISON], [TokenKind.NE, PRECEDENCE_COMPARISON], [TokenKind.NOT, PRECEDENCE_PREFIX], [TokenKind.OR, PRECEDENCE_LOGICAL_OR], [TokenKind.RPAREN, PRECEDENCE_LOWEST]]);
@@ -2566,6 +2567,10 @@ class JSONPathEnvironment {
2566
2567
  * can visit before a `JSONPathRecursionLimitError` is thrown.
2567
2568
  */
2568
2569
 
2570
+ /**
2571
+ * If `true`, enable nondeterministic ordering when iterating JSON object data.
2572
+ */
2573
+
2569
2574
  /**
2570
2575
  * A map of function names to objects implementing the {@link FilterFunction}
2571
2576
  * interface. You are free to set or delete custom filter functions directly.
@@ -2580,6 +2585,7 @@ class JSONPathEnvironment {
2580
2585
  this.maxIntIndex = options.maxIntIndex ?? Math.pow(2, 53) - 1;
2581
2586
  this.minIntIndex = options.maxIntIndex ?? -Math.pow(2, 53) - 1;
2582
2587
  this.maxRecursionDepth = options.maxRecursionDepth ?? 50;
2588
+ this.nondeterministic = options.nondeterministic ?? false;
2583
2589
  this.parser = new Parser(this);
2584
2590
  this.setupFilterFunctions();
2585
2591
  }
@@ -2688,6 +2694,29 @@ class JSONPathEnvironment {
2688
2694
  }
2689
2695
  return args;
2690
2696
  }
2697
+
2698
+ /**
2699
+ * Return an array of key/values of the enumerable properties in _obj_.
2700
+ *
2701
+ * If you want to introduce some nondeterminism to iterating JSON-like
2702
+ * objects, do it here. The wildcard selector, descendent segment and
2703
+ * filter selector all use `this.environment.entries`.
2704
+ *
2705
+ * @param obj - A JSON-like object.
2706
+ */
2707
+ entries(obj) {
2708
+ function shuffle(entries) {
2709
+ for (let i = entries.length - 1; i > 0; i--) {
2710
+ const j = Math.floor(Math.random() * (i + 1));
2711
+ [entries[i], entries[j]] = [entries[j], entries[i]];
2712
+ }
2713
+ return entries;
2714
+ }
2715
+ if (this.nondeterministic) {
2716
+ return shuffle(Object.entries(obj));
2717
+ }
2718
+ return Object.entries(obj);
2719
+ }
2691
2720
  }
2692
2721
 
2693
2722
  var index$2 = /*#__PURE__*/Object.freeze({
@@ -3276,6 +3305,6 @@ var index = /*#__PURE__*/Object.freeze({
3276
3305
  apply: apply
3277
3306
  });
3278
3307
 
3279
- const version = "1.0.0";
3308
+ const version = "1.1.1";
3280
3309
 
3281
3310
  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 };
@@ -1,10 +1,10 @@
1
1
  /*
2
- * json-p3 version 1.0.0
2
+ * json-p3 version 1.1.1
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
6
6
  *
7
- * Copyright (c) 2023 James Prior
7
+ * Copyright (c) 2024 James Prior
8
8
  *
9
9
  * Permission is hereby granted, free of charge, to any person obtaining a copy
10
10
  * of this software and associated documentation files (the "Software"), to deal
@@ -863,12 +863,13 @@ var json_p3 = (function (exports) {
863
863
  this.left = left;
864
864
  this.operator = operator;
865
865
  this.right = right;
866
+ this.logical = operator === "&&" || operator === "||";
866
867
  }
867
868
  evaluate(context) {
868
869
  let left = this.left.evaluate(context);
869
- if (left instanceof JSONPathNodeList && left.nodes.length === 1) left = left.nodes[0].value;
870
+ if (!this.logical && left instanceof JSONPathNodeList && left.nodes.length === 1) left = left.nodes[0].value;
870
871
  let right = this.right.evaluate(context);
871
- if (right instanceof JSONPathNodeList && right.nodes.length === 1) right = right.nodes[0].value;
872
+ if (!this.logical && right instanceof JSONPathNodeList && right.nodes.length === 1) right = right.nodes[0].value;
872
873
  if (this.operator === "&&") {
873
874
  return isTruthy(left) && isTruthy(right);
874
875
  }
@@ -878,7 +879,7 @@ var json_p3 = (function (exports) {
878
879
  return compare(left, this.operator, right);
879
880
  }
880
881
  toString() {
881
- if (this.operator === "&&" || this.operator === "||") {
882
+ if (this.logical) {
882
883
  return `(${this.left.toString()} ${this.operator} ${this.right.toString()})`;
883
884
  }
884
885
  return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;
@@ -1928,7 +1929,7 @@ var json_p3 = (function (exports) {
1928
1929
  rv.push(new JSONPathNode(node.value[i], node.location.concat(i), node.root));
1929
1930
  }
1930
1931
  } else if (isObject(node.value)) {
1931
- for (const [key, value] of Object.entries(node.value)) {
1932
+ for (const [key, value] of this.environment.entries(node.value)) {
1932
1933
  rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
1933
1934
  }
1934
1935
  }
@@ -1943,7 +1944,7 @@ var json_p3 = (function (exports) {
1943
1944
  yield new JSONPathNode(node.value[i], node.location.concat(i), node.root);
1944
1945
  }
1945
1946
  } else if (isObject(node.value)) {
1946
- for (const [key, value] of Object.entries(node.value)) {
1947
+ for (const [key, value] of this.environment.entries(node.value)) {
1947
1948
  yield new JSONPathNode(value, node.location.concat(key), node.root);
1948
1949
  }
1949
1950
  }
@@ -2003,7 +2004,7 @@ var json_p3 = (function (exports) {
2003
2004
  }
2004
2005
  }
2005
2006
  } else if (isObject(currentNode.value)) {
2006
- for (const [key, value] of Object.entries(currentNode.value)) {
2007
+ for (const [key, value] of this.environment.entries(currentNode.value)) {
2007
2008
  const __node = new JSONPathNode(value, currentNode.location.concat(key), currentNode.root);
2008
2009
  yield __node;
2009
2010
  if (isObject(__node.value)) {
@@ -2036,7 +2037,7 @@ var json_p3 = (function (exports) {
2036
2037
  }
2037
2038
  }
2038
2039
  } else if (isObject(node.value)) {
2039
- for (const [key, value] of Object.entries(node.value)) {
2040
+ for (const [key, value] of this.environment.entries(node.value)) {
2040
2041
  const _node = new JSONPathNode(value, node.location.concat(key), node.root);
2041
2042
  rv.push(_node);
2042
2043
  for (const __node of this.visit(_node, depth + 1)) {
@@ -2073,7 +2074,7 @@ var json_p3 = (function (exports) {
2073
2074
  }
2074
2075
  }
2075
2076
  } else if (isObject(node.value)) {
2076
- for (const [key, value] of Object.entries(node.value)) {
2077
+ for (const [key, value] of this.environment.entries(node.value)) {
2077
2078
  const filterContext = {
2078
2079
  environment: this.environment,
2079
2080
  currentValue: value,
@@ -2106,7 +2107,7 @@ var json_p3 = (function (exports) {
2106
2107
  }
2107
2108
  }
2108
2109
  } else if (isObject(node.value)) {
2109
- for (const [key, value] of Object.entries(node.value)) {
2110
+ for (const [key, value] of this.environment.entries(node.value)) {
2110
2111
  const filterContext = {
2111
2112
  environment: this.environment,
2112
2113
  currentValue: value,
@@ -2238,8 +2239,8 @@ var json_p3 = (function (exports) {
2238
2239
  }
2239
2240
 
2240
2241
  const PRECEDENCE_LOWEST = 1;
2241
- const PRECEDENCE_LOGICAL_AND = 4;
2242
- const PRECEDENCE_LOGICAL_OR = 5;
2242
+ const PRECEDENCE_LOGICAL_OR = 4;
2243
+ const PRECEDENCE_LOGICAL_AND = 5;
2243
2244
  const PRECEDENCE_COMPARISON = 6;
2244
2245
  const PRECEDENCE_PREFIX = 7;
2245
2246
  const PRECEDENCES = new Map([[TokenKind.AND, PRECEDENCE_LOGICAL_AND], [TokenKind.EQ, PRECEDENCE_COMPARISON], [TokenKind.GE, PRECEDENCE_COMPARISON], [TokenKind.GT, PRECEDENCE_COMPARISON], [TokenKind.LE, PRECEDENCE_COMPARISON], [TokenKind.LT, PRECEDENCE_COMPARISON], [TokenKind.NE, PRECEDENCE_COMPARISON], [TokenKind.NOT, PRECEDENCE_PREFIX], [TokenKind.OR, PRECEDENCE_LOGICAL_OR], [TokenKind.RPAREN, PRECEDENCE_LOWEST]]);
@@ -2569,6 +2570,10 @@ var json_p3 = (function (exports) {
2569
2570
  * can visit before a `JSONPathRecursionLimitError` is thrown.
2570
2571
  */
2571
2572
 
2573
+ /**
2574
+ * If `true`, enable nondeterministic ordering when iterating JSON object data.
2575
+ */
2576
+
2572
2577
  /**
2573
2578
  * A map of function names to objects implementing the {@link FilterFunction}
2574
2579
  * interface. You are free to set or delete custom filter functions directly.
@@ -2583,6 +2588,7 @@ var json_p3 = (function (exports) {
2583
2588
  this.maxIntIndex = options.maxIntIndex ?? Math.pow(2, 53) - 1;
2584
2589
  this.minIntIndex = options.maxIntIndex ?? -Math.pow(2, 53) - 1;
2585
2590
  this.maxRecursionDepth = options.maxRecursionDepth ?? 50;
2591
+ this.nondeterministic = options.nondeterministic ?? false;
2586
2592
  this.parser = new Parser(this);
2587
2593
  this.setupFilterFunctions();
2588
2594
  }
@@ -2691,6 +2697,29 @@ var json_p3 = (function (exports) {
2691
2697
  }
2692
2698
  return args;
2693
2699
  }
2700
+
2701
+ /**
2702
+ * Return an array of key/values of the enumerable properties in _obj_.
2703
+ *
2704
+ * If you want to introduce some nondeterminism to iterating JSON-like
2705
+ * objects, do it here. The wildcard selector, descendent segment and
2706
+ * filter selector all use `this.environment.entries`.
2707
+ *
2708
+ * @param obj - A JSON-like object.
2709
+ */
2710
+ entries(obj) {
2711
+ function shuffle(entries) {
2712
+ for (let i = entries.length - 1; i > 0; i--) {
2713
+ const j = Math.floor(Math.random() * (i + 1));
2714
+ [entries[i], entries[j]] = [entries[j], entries[i]];
2715
+ }
2716
+ return entries;
2717
+ }
2718
+ if (this.nondeterministic) {
2719
+ return shuffle(Object.entries(obj));
2720
+ }
2721
+ return Object.entries(obj);
2722
+ }
2694
2723
  }
2695
2724
 
2696
2725
  var index$2 = /*#__PURE__*/Object.freeze({
@@ -3279,7 +3308,7 @@ var json_p3 = (function (exports) {
3279
3308
  apply: apply
3280
3309
  });
3281
3310
 
3282
- const version = "1.0.0";
3311
+ const version = "1.1.1";
3283
3312
 
3284
3313
  exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
3285
3314
  exports.FunctionExpressionType = FunctionExpressionType;