json-p3 1.1.1 → 1.2.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.1.1
2
+ * json-p3 version 1.2.0
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
@@ -1180,6 +1180,8 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1180
1180
  TokenKind["GE"] = "TOKEN_GE";
1181
1181
  TokenKind["GT"] = "TOKEN_GT";
1182
1182
  TokenKind["INDEX"] = "TOKEN_INDEX";
1183
+ TokenKind["KEY"] = "TOKEN_KEY";
1184
+ TokenKind["KEYS"] = "TOKEN_KEYS";
1183
1185
  TokenKind["LBRACKET"] = "TOKEN_LBRACKET";
1184
1186
  TokenKind["LE"] = "TOKEN_LE";
1185
1187
  TokenKind["LG"] = "TOKEN_LG";
@@ -1249,6 +1251,9 @@ class TokenStream {
1249
1251
  }
1250
1252
  }
1251
1253
 
1254
+ /** A lexer that accepts additional, non-standard tokens. */
1255
+
1256
+
1252
1257
  // These regular expressions are to be used with Lexer.acceptMatchRun(),
1253
1258
  // which expects the sticky flag to be set.
1254
1259
  const exponentPattern = /e[+-]?\d+/y;
@@ -1288,7 +1293,8 @@ class Lexer {
1288
1293
  /**
1289
1294
  * @param path - A JSONPath query.
1290
1295
  */
1291
- constructor(path) {
1296
+ constructor(environment, path) {
1297
+ this.environment = environment;
1292
1298
  this.path = path;
1293
1299
  }
1294
1300
  get pos() {
@@ -1388,8 +1394,8 @@ class Lexer {
1388
1394
  * @returns A two-tuple containing a lexer for _path_ and an array to populate
1389
1395
  * with tokens.
1390
1396
  */
1391
- function lex(path) {
1392
- const lexer = new Lexer(path);
1397
+ function lex(environment, path) {
1398
+ const lexer = new Lexer(environment, path);
1393
1399
  return [lexer, lexer.tokens];
1394
1400
  }
1395
1401
 
@@ -1398,8 +1404,8 @@ function lex(path) {
1398
1404
  * @param path - A JSONPath query.
1399
1405
  * @returns Tokens to be parsed by the parser.
1400
1406
  */
1401
- function tokenize(path) {
1402
- const [lexer, tokens] = lex(path);
1407
+ function tokenize(environment, path) {
1408
+ const [lexer, tokens] = lex(environment, path);
1403
1409
  lexer.run();
1404
1410
  if (tokens.length && tokens[tokens.length - 1].kind === TokenKind.ERROR) {
1405
1411
  throw new JSONPathSyntaxError(tokens[tokens.length - 1].value, tokens[tokens.length - 1]);
@@ -1452,6 +1458,16 @@ function lexSegment(l) {
1452
1458
  * @returns -
1453
1459
  */
1454
1460
  function lexDescendantSelection(l) {
1461
+ if (l.acceptMatchRun(namePattern)) {
1462
+ // Shorthand name
1463
+ l.emit(TokenKind.NAME);
1464
+ return lexSegment;
1465
+ }
1466
+ if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1467
+ // Non-standard keys selector
1468
+ l.emit(TokenKind.KEYS);
1469
+ return lexSegment;
1470
+ }
1455
1471
  const ch = l.next();
1456
1472
  switch (ch) {
1457
1473
  case "":
@@ -1465,13 +1481,8 @@ function lexDescendantSelection(l) {
1465
1481
  return lexInsideBracketedSelection;
1466
1482
  default:
1467
1483
  l.backup();
1468
- if (l.acceptMatchRun(namePattern)) {
1469
- l.emit(TokenKind.NAME);
1470
- return lexSegment;
1471
- } else {
1472
- l.error(`unexpected descendent selection token '${ch}'`);
1473
- return null;
1474
- }
1484
+ l.error(`unexpected descendent selection token '${ch}'`);
1485
+ return null;
1475
1486
  }
1476
1487
  }
1477
1488
  function lexDotSelector(l) {
@@ -1480,23 +1491,34 @@ function lexDotSelector(l) {
1480
1491
  l.error("unexpected whitespace after dot");
1481
1492
  return null;
1482
1493
  }
1483
- const ch = l.next();
1484
- if (ch === "*") {
1485
- l.emit(TokenKind.WILD);
1494
+ if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1495
+ l.emit(TokenKind.KEYS);
1486
1496
  return lexSegment;
1487
1497
  }
1488
- l.backup();
1489
1498
  if (l.acceptMatchRun(namePattern)) {
1490
1499
  l.emit(TokenKind.NAME);
1491
1500
  return lexSegment;
1492
- } else {
1493
- l.error(`unexpected shorthand selector '${ch}'`);
1494
- return null;
1495
1501
  }
1502
+ const ch = l.next();
1503
+ if (ch === "*") {
1504
+ l.emit(TokenKind.WILD);
1505
+ return lexSegment;
1506
+ }
1507
+ l.backup();
1508
+ l.error(`unexpected shorthand selector '${ch}'`);
1509
+ return null;
1496
1510
  }
1497
1511
  function lexInsideBracketedSelection(l) {
1498
1512
  for (;;) {
1499
1513
  l.ignoreWhitespace();
1514
+ if (l.acceptMatchRun(indexPattern)) {
1515
+ l.emit(TokenKind.INDEX);
1516
+ continue;
1517
+ }
1518
+ if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1519
+ l.emit(TokenKind.KEYS);
1520
+ continue;
1521
+ }
1500
1522
  const ch = l.next();
1501
1523
  switch (ch) {
1502
1524
  case "]":
@@ -1525,10 +1547,6 @@ function lexInsideBracketedSelection(l) {
1525
1547
  return lexDoubleQuoteStringInsideBracketSelection;
1526
1548
  default:
1527
1549
  l.backup();
1528
- if (l.acceptMatchRun(indexPattern)) {
1529
- l.emit(TokenKind.INDEX);
1530
- continue;
1531
- }
1532
1550
  l.error(`unexpected token '${ch}' in bracketed selection`);
1533
1551
  return null;
1534
1552
  }
@@ -1542,6 +1560,8 @@ function lexInsideFilter(l) {
1542
1560
  const ch = l.next();
1543
1561
  switch (ch) {
1544
1562
  case "":
1563
+ l.error("unclosed bracketed selection");
1564
+ return null;
1545
1565
  case "]":
1546
1566
  l.filterLevel -= 1;
1547
1567
  if (l.parenStack.length === 1) {
@@ -1583,6 +1603,9 @@ function lexInsideFilter(l) {
1583
1603
  case "@":
1584
1604
  l.emit(TokenKind.CURRENT);
1585
1605
  return lexSegment;
1606
+ case "#":
1607
+ l.emit(TokenKind.KEY);
1608
+ return lexSegment;
1586
1609
  case ".":
1587
1610
  l.backup();
1588
1611
  return lexSegment;
@@ -1960,12 +1983,22 @@ class RecursiveDescentSegment extends JSONPathSelector {
1960
1983
  }
1961
1984
  resolve(nodes) {
1962
1985
  const rv = [];
1963
- for (const node of nodes) {
1964
- rv.push(node);
1965
- for (const _node of this.visit(node)) {
1966
- rv.push(_node);
1986
+ if (this.environment.nondeterministic) {
1987
+ for (const root of nodes) {
1988
+ for (const node of this.nondeterministicVisitor(root)) {
1989
+ rv.push(node);
1990
+ }
1991
+ }
1992
+ } else {
1993
+ for (const node of nodes) {
1994
+ rv.push(node);
1995
+ for (const _node of this.visitor(node)) {
1996
+ rv.push(_node);
1997
+ }
1967
1998
  }
1968
1999
  }
2000
+
2001
+ // console.log(JSON.stringify(rv.map((n: any) => n.value)));
1969
2002
  return this.selector.resolve(rv);
1970
2003
  }
1971
2004
  *lazyResolve(nodes) {
@@ -2018,7 +2051,7 @@ class RecursiveDescentSegment extends JSONPathSelector {
2018
2051
  toString() {
2019
2052
  return `..${this.selector.toString()}`;
2020
2053
  }
2021
- visit(node) {
2054
+ visitor(node) {
2022
2055
  let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
2023
2056
  if (depth >= this.environment.maxRecursionDepth) {
2024
2057
  throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
@@ -2029,7 +2062,7 @@ class RecursiveDescentSegment extends JSONPathSelector {
2029
2062
  for (let i = 0; i < node.value.length; i++) {
2030
2063
  const _node = new JSONPathNode(node.value[i], node.location.concat(i), node.root);
2031
2064
  rv.push(_node);
2032
- for (const __node of this.visit(_node, depth + 1)) {
2065
+ for (const __node of this.visitor(_node, depth + 1)) {
2033
2066
  rv.push(__node);
2034
2067
  }
2035
2068
  }
@@ -2037,13 +2070,52 @@ class RecursiveDescentSegment extends JSONPathSelector {
2037
2070
  for (const [key, value] of this.environment.entries(node.value)) {
2038
2071
  const _node = new JSONPathNode(value, node.location.concat(key), node.root);
2039
2072
  rv.push(_node);
2040
- for (const __node of this.visit(_node, depth + 1)) {
2073
+ for (const __node of this.visitor(_node, depth + 1)) {
2041
2074
  rv.push(__node);
2042
2075
  }
2043
2076
  }
2044
2077
  }
2045
2078
  return rv;
2046
2079
  }
2080
+ nondeterministicVisitor(root) {
2081
+ let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
2082
+ const rv = [root];
2083
+ let queue = this.nondeterministicChildren(root).map(node => [node, depth]);
2084
+ while (queue.length) {
2085
+ const [node, _depth] = queue.shift();
2086
+ rv.push(node);
2087
+ if (_depth >= this.environment.maxRecursionDepth) {
2088
+ throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
2089
+ }
2090
+
2091
+ // Visit child nodes now or queue them for later?
2092
+ const visitChildren = Math.random() < 0.5;
2093
+ for (const child of this.nondeterministicChildren(node)) {
2094
+ if (visitChildren) {
2095
+ rv.push(child);
2096
+ const grandchildren = this.nondeterministicChildren(child).map(n => [n, _depth + 2]);
2097
+ queue = interleave(queue, grandchildren);
2098
+ } else {
2099
+ queue.push([child, _depth + 1]);
2100
+ }
2101
+ }
2102
+ }
2103
+ return rv;
2104
+ }
2105
+ nondeterministicChildren(node) {
2106
+ const _rv = [];
2107
+ if (node.value instanceof String) return _rv;
2108
+ if (isArray(node.value)) {
2109
+ for (let i = 0; i < node.value.length; i++) {
2110
+ _rv.push(new JSONPathNode(node.value[i], node.location.concat(i), node.root));
2111
+ }
2112
+ } else if (isObject(node.value)) {
2113
+ for (const [key, value] of this.environment.entries(node.value)) {
2114
+ _rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
2115
+ }
2116
+ }
2117
+ return _rv;
2118
+ }
2047
2119
  }
2048
2120
  class FilterSelector extends JSONPathSelector {
2049
2121
  constructor(environment, token, expression) {
@@ -2064,7 +2136,8 @@ class FilterSelector extends JSONPathSelector {
2064
2136
  const filterContext = {
2065
2137
  environment: this.environment,
2066
2138
  currentValue: value,
2067
- rootValue: node.root
2139
+ rootValue: node.root,
2140
+ currentKey: i
2068
2141
  };
2069
2142
  if (this.expression.evaluate(filterContext)) {
2070
2143
  rv.push(new JSONPathNode(value, node.location.concat(i), node.root));
@@ -2075,7 +2148,8 @@ class FilterSelector extends JSONPathSelector {
2075
2148
  const filterContext = {
2076
2149
  environment: this.environment,
2077
2150
  currentValue: value,
2078
- rootValue: node.root
2151
+ rootValue: node.root,
2152
+ currentKey: key
2079
2153
  };
2080
2154
  if (this.expression.evaluate(filterContext)) {
2081
2155
  rv.push(new JSONPathNode(value, node.location.concat(key), node.root));
@@ -2097,7 +2171,8 @@ class FilterSelector extends JSONPathSelector {
2097
2171
  environment: this.environment,
2098
2172
  currentValue: value,
2099
2173
  rootValue: node.root,
2100
- lazy: true
2174
+ lazy: true,
2175
+ currentKey: i
2101
2176
  };
2102
2177
  if (this.expression.evaluate(filterContext)) {
2103
2178
  yield new JSONPathNode(value, node.location.concat(i), node.root);
@@ -2109,7 +2184,8 @@ class FilterSelector extends JSONPathSelector {
2109
2184
  environment: this.environment,
2110
2185
  currentValue: value,
2111
2186
  rootValue: node.root,
2112
- lazy: true
2187
+ lazy: true,
2188
+ currentKey: key
2113
2189
  };
2114
2190
  if (this.expression.evaluate(filterContext)) {
2115
2191
  yield new JSONPathNode(value, node.location.concat(key), node.root);
@@ -2152,6 +2228,41 @@ class BracketedSelection extends JSONPathSelector {
2152
2228
  }
2153
2229
  }
2154
2230
 
2231
+ /**
2232
+ * Randomly interleave elements from two arrays while maintaining relative
2233
+ * order of each input array.
2234
+ *
2235
+ * If _arrayA_ is empty, _arrayB_ is returned, and vice versa.
2236
+ */
2237
+ function interleave(arrayA, arrayB) {
2238
+ if (arrayA.length === 0) {
2239
+ return arrayB;
2240
+ }
2241
+ if (arrayB.length === 0) {
2242
+ return arrayA;
2243
+ }
2244
+
2245
+ // An array of iterators
2246
+ const iterators = [];
2247
+ const itA = arrayA[Symbol.iterator]();
2248
+ const itB = arrayB[Symbol.iterator]();
2249
+ for (let i = 0; i < arrayA.length; i++) {
2250
+ iterators.push(itA);
2251
+ }
2252
+ for (let i = 0; i < arrayB.length; i++) {
2253
+ iterators.push(itB);
2254
+ }
2255
+ shuffle(iterators);
2256
+ return iterators.map(it => it.next().value);
2257
+ }
2258
+ function shuffle(entries) {
2259
+ for (let i = entries.length - 1; i > 0; i--) {
2260
+ const j = Math.floor(Math.random() * (i + 1));
2261
+ [entries[i], entries[j]] = [entries[j], entries[i]];
2262
+ }
2263
+ return entries;
2264
+ }
2265
+
2155
2266
  var selectors = /*#__PURE__*/Object.freeze({
2156
2267
  __proto__: null,
2157
2268
  BracketedSelection: BracketedSelection,
@@ -2235,6 +2346,57 @@ class JSONPath {
2235
2346
  }
2236
2347
  }
2237
2348
 
2349
+ class CurrentKey extends FilterExpression {
2350
+ evaluate(context) {
2351
+ return context.currentKey ?? Nothing;
2352
+ }
2353
+ toString() {
2354
+ return "#";
2355
+ }
2356
+ }
2357
+
2358
+ /**
2359
+ * Object property name selector or array index selector.
2360
+ */
2361
+ class KeysSelector extends JSONPathSelector {
2362
+ constructor(environment, token) {
2363
+ let shorthand = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
2364
+ super(environment, token);
2365
+ this.environment = environment;
2366
+ this.token = token;
2367
+ this.shorthand = shorthand;
2368
+ }
2369
+ resolve(nodes) {
2370
+ const rv = [];
2371
+ for (const node of nodes) {
2372
+ if (node.value instanceof String || isArray(node.value)) continue;
2373
+ if (isObject(node.value)) {
2374
+ let i = 0;
2375
+ for (const [key, _] of this.environment.entries(node.value)) {
2376
+ rv.push(new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root));
2377
+ i++;
2378
+ }
2379
+ }
2380
+ }
2381
+ return rv;
2382
+ }
2383
+ *lazyResolve(nodes) {
2384
+ for (const node of nodes) {
2385
+ if (node.value instanceof String || isArray(node.value)) continue;
2386
+ if (isObject(node.value)) {
2387
+ let i = 0;
2388
+ for (const [key, _] of this.environment.entries(node.value)) {
2389
+ yield new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root);
2390
+ i++;
2391
+ }
2392
+ }
2393
+ }
2394
+ }
2395
+ toString() {
2396
+ return this.shorthand ? "[~]" : "~";
2397
+ }
2398
+ }
2399
+
2238
2400
  const PRECEDENCE_LOWEST = 1;
2239
2401
  const PRECEDENCE_LOGICAL_OR = 4;
2240
2402
  const PRECEDENCE_LOGICAL_AND = 5;
@@ -2250,7 +2412,7 @@ const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
2250
2412
  class Parser {
2251
2413
  constructor(environment) {
2252
2414
  this.environment = environment;
2253
- this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction]]);
2415
+ this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction], [TokenKind.KEY, this.parseCurrentKey], [TokenKind.KEY, this.parseCurrentKey]]);
2254
2416
  }
2255
2417
  parse(stream) {
2256
2418
  if (stream.current.kind === TokenKind.ROOT) stream.next();
@@ -2282,6 +2444,8 @@ class Parser {
2282
2444
  return new NameSelector(this.environment, stream.current, stream.current.value, true);
2283
2445
  case TokenKind.WILD:
2284
2446
  return new WildcardSelector(this.environment, stream.current, true);
2447
+ case TokenKind.KEYS:
2448
+ return new KeysSelector(this.environment, stream.current, true);
2285
2449
  case TokenKind.DDOT:
2286
2450
  {
2287
2451
  const segmentToken = stream.current;
@@ -2375,6 +2539,9 @@ class Parser {
2375
2539
  case TokenKind.WILD:
2376
2540
  items.push(new WildcardSelector(this.environment, stream.current));
2377
2541
  break;
2542
+ case TokenKind.KEYS:
2543
+ items.push(new KeysSelector(this.environment, stream.current));
2544
+ break;
2378
2545
  case TokenKind.EOF:
2379
2546
  throw new JSONPathSyntaxError("unexpected end of query", stream.current);
2380
2547
  default:
@@ -2455,6 +2622,9 @@ class Parser {
2455
2622
  const tok = stream.next();
2456
2623
  return new RelativeQuery(tok, new JSONPath(this.environment, this.parsePath(stream, true)));
2457
2624
  }
2625
+ parseCurrentKey(stream) {
2626
+ return new CurrentKey(stream.current);
2627
+ }
2458
2628
  parseFunction(stream) {
2459
2629
  const args = [];
2460
2630
  const tok = stream.next();
@@ -2571,6 +2741,10 @@ class JSONPathEnvironment {
2571
2741
  * If `true`, enable nondeterministic ordering when iterating JSON object data.
2572
2742
  */
2573
2743
 
2744
+ /**
2745
+ * The pattern to use for the non-standard _keys selector_.
2746
+ */
2747
+
2574
2748
  /**
2575
2749
  * A map of function names to objects implementing the {@link FilterFunction}
2576
2750
  * interface. You are free to set or delete custom filter functions directly.
@@ -2586,6 +2760,7 @@ class JSONPathEnvironment {
2586
2760
  this.minIntIndex = options.maxIntIndex ?? -Math.pow(2, 53) - 1;
2587
2761
  this.maxRecursionDepth = options.maxRecursionDepth ?? 50;
2588
2762
  this.nondeterministic = options.nondeterministic ?? false;
2763
+ this.keysPattern = options.keysPattern ?? /~/y;
2589
2764
  this.parser = new Parser(this);
2590
2765
  this.setupFilterFunctions();
2591
2766
  }
@@ -2595,7 +2770,7 @@ class JSONPathEnvironment {
2595
2770
  * @returns A new {@link JSONPath} object, bound to this environment.
2596
2771
  */
2597
2772
  compile(path) {
2598
- return new JSONPath(this, this.parser.parse(new TokenStream(tokenize(path))));
2773
+ return new JSONPath(this, this.parser.parse(new TokenStream(tokenize(this, path))));
2599
2774
  }
2600
2775
 
2601
2776
  /**
@@ -2677,7 +2852,7 @@ class JSONPathEnvironment {
2677
2852
  for (const [typ, arg, idx] of func.argTypes.map((t, i) => [t, args[i], i])) {
2678
2853
  switch (typ) {
2679
2854
  case FunctionExpressionType.ValueType:
2680
- if (!(arg instanceof FilterExpressionLiteral || arg instanceof JSONPathQuery && arg.path.singularQuery() || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.ValueType)) {
2855
+ if (!(arg instanceof FilterExpressionLiteral || arg instanceof CurrentKey || arg instanceof JSONPathQuery && arg.path.singularQuery() || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.ValueType)) {
2681
2856
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of ValueType`, arg.token);
2682
2857
  }
2683
2858
  break;
@@ -3305,6 +3480,6 @@ var index = /*#__PURE__*/Object.freeze({
3305
3480
  apply: apply
3306
3481
  });
3307
3482
 
3308
- const version = "1.1.1";
3483
+ const version = "1.2.0";
3309
3484
 
3310
3485
  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 };