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