json-p3 0.1.0 → 0.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 0.1.0
2
+ * json-p3 version 0.2.0
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
@@ -125,6 +125,20 @@ var json_p3 = (function (exports) {
125
125
  }
126
126
  }
127
127
 
128
+ /**
129
+ * Error thrown when the maximum recursion depth is reached.
130
+ */
131
+ class JSONPathRecursionLimitError extends JSONPathError {
132
+ constructor(message, token) {
133
+ super(message, token);
134
+ this.message = message;
135
+ this.token = token;
136
+ Object.setPrototypeOf(this, new.target.prototype);
137
+ this.name = "JSONPathRecursionLimitError";
138
+ this.message = withErrorContext(message, token);
139
+ }
140
+ }
141
+
128
142
  /**
129
143
  * Common types and type predicates.
130
144
  */
@@ -233,6 +247,10 @@ var json_p3 = (function (exports) {
233
247
  this.name = "JSONPointerError";
234
248
  }
235
249
  }
250
+
251
+ /**
252
+ * Base class for JSON Pointer resolution errors.
253
+ */
236
254
  class JSONPointerResolutionError extends JSONPointerError {
237
255
  constructor(message) {
238
256
  super(message);
@@ -241,6 +259,10 @@ var json_p3 = (function (exports) {
241
259
  this.name = "JSONPointerResolutionError";
242
260
  }
243
261
  }
262
+
263
+ /**
264
+ * Error thrown due to an out of range index when resolving a JSON Pointer.
265
+ */
244
266
  class JSONPointerIndexError extends JSONPointerResolutionError {
245
267
  constructor(message) {
246
268
  super(message);
@@ -249,6 +271,10 @@ var json_p3 = (function (exports) {
249
271
  this.name = "JSONPointerIndexError";
250
272
  }
251
273
  }
274
+
275
+ /**
276
+ * Error thrown due to a missing property when resolving a JSON Pointer.
277
+ */
252
278
  class JSONPointerKeyError extends JSONPointerResolutionError {
253
279
  constructor(message) {
254
280
  super(message);
@@ -257,6 +283,10 @@ var json_p3 = (function (exports) {
257
283
  this.name = "JSONPointerKeyError";
258
284
  }
259
285
  }
286
+
287
+ /**
288
+ * Error thrown due to invalid JSON Pointer syntax.
289
+ */
260
290
  class JSONPointerSyntaxError extends JSONPointerError {
261
291
  constructor(message) {
262
292
  super(message);
@@ -265,6 +295,10 @@ var json_p3 = (function (exports) {
265
295
  this.name = "JSONPointerSyntaxError";
266
296
  }
267
297
  }
298
+
299
+ /**
300
+ * Error thrown when trying to resolve a property or index against a primitive value.
301
+ */
268
302
  class JSONPointerTypeError extends JSONPointerResolutionError {
269
303
  constructor(message) {
270
304
  super(message);
@@ -324,8 +358,6 @@ var json_p3 = (function (exports) {
324
358
  }
325
359
  }
326
360
 
327
- // TODO: add `resolveWithFallback` to handle explicit `undefined`
328
-
329
361
  /**
330
362
  *
331
363
  * @param value -
@@ -364,6 +396,8 @@ var json_p3 = (function (exports) {
364
396
  }
365
397
  return pointer.split("/").map(token => token.replaceAll("~1", "/").replaceAll("~0", "~")).slice(1);
366
398
  }
399
+
400
+ // eslint-disable-next-line sonarjs/cognitive-complexity
367
401
  getItem(val, token, idx) {
368
402
  // NOTE:
369
403
  // - string primitives "have own" indices and `length`.
@@ -373,12 +407,23 @@ var json_p3 = (function (exports) {
373
407
  if (isArray(val)) {
374
408
  if (token !== "length" && Object.hasOwn(val, token)) {
375
409
  return val[Number(token)];
410
+ } else if (token.startsWith("#")) {
411
+ // handle non-standard '#' from relative json pointer
412
+ const maybeIndex = token.slice(1);
413
+ if (RE_INT.test(maybeIndex) && Object.hasOwn(val, maybeIndex)) {
414
+ return Number(maybeIndex);
415
+ } else {
416
+ throw new JSONPointerIndexError(`index out of range '${JSONPointer.encode(this.tokens.slice(0, idx + 1))}'`);
417
+ }
376
418
  } else {
377
419
  throw new JSONPointerIndexError(`index out of range '${JSONPointer.encode(this.tokens.slice(0, idx + 1))}'`);
378
420
  }
379
421
  } else if (isObject(val)) {
380
422
  if (Object.hasOwn(val, token)) {
381
423
  return val[token];
424
+ } else if (token.startsWith("#") && Object.hasOwn(val, token.slice(1))) {
425
+ // handle non-standard '#' from relative json pointer
426
+ return token.slice(1);
382
427
  } else {
383
428
  throw new JSONPointerKeyError(`no such property '${JSONPointer.encode(this.tokens.slice(0, idx + 1))}'`);
384
429
  }
@@ -398,10 +443,12 @@ var json_p3 = (function (exports) {
398
443
 
399
444
  /**
400
445
  * Join this pointer with _tokens_.
446
+ *
401
447
  * @param tokens - JSON Pointer strings, possibly without leading slashes.
402
448
  * If a token or "part" does have a leading slash, the previous pointer is
403
449
  * ignored and a new `JSONPointer` is created, then processing of the
404
450
  * remaining tokens continues.
451
+ *
405
452
  * @returns A new JSON Pointer that is the concatenation of all tokens or
406
453
  * "parts".
407
454
  */
@@ -451,8 +498,111 @@ var json_p3 = (function (exports) {
451
498
  }
452
499
  return new JSONPointer(JSONPointer.encode(this.tokens.slice(0, this.tokens.length - 1)));
453
500
  }
501
+ to(rel) {
502
+ const relativePointer = isString(rel) ? new RelativeJSONPointer(rel) : rel;
503
+ return relativePointer.to(this);
504
+ }
505
+ }
506
+ const RE_RELATIVE_POINTER = /(?<ORIGIN>\d+)(?<INDEX_G>(?<SIGN>[+-])(?<INDEX>\d))?(?<POINTER>.*)/s;
507
+ const RE_INT = /(0|[1-9][0-9]*)/;
508
+
509
+ /**
510
+ * A relative JSON Pointer.
511
+ *
512
+ * See https://www.ietf.org/id/draft-hha-relative-json-pointer-00.html
513
+ */
514
+ class RelativeJSONPointer {
515
+ /**
516
+ *
517
+ * @param rel -
518
+ */
519
+ constructor(rel) {
520
+ [this.origin, this.index, this.pointer] = this.parse(rel);
521
+ }
522
+
523
+ /**
524
+ *
525
+ * @returns
526
+ */
527
+ toString() {
528
+ const sign = this.index > 0 ? "+" : "";
529
+ const index = this.index === 0 ? "" : `${sign}${this.index}`;
530
+ return `${this.origin}${index}${this.pointer}`;
531
+ }
532
+
533
+ /**
534
+ *
535
+ * @param pointer -
536
+ */
537
+ to(pointer) {
538
+ const p = isString(pointer) ? new JSONPointer(pointer) : pointer;
539
+
540
+ // move to origin
541
+ if (this.origin > p.tokens.length) {
542
+ throw new JSONPointerIndexError(`origin (${this.origin}) exceeds root (${p.tokens.length})`);
543
+ }
544
+ const tokens = this.origin < 1 ? p.tokens.slice() : p.tokens.slice(0, -this.origin);
545
+
546
+ // array index offset
547
+ if (this.index && tokens.length && this.isIntLike(tokens.at(-1))) {
548
+ const newIndex = Number(tokens.at(-1)) + this.index;
549
+ if (newIndex < 0) {
550
+ throw new JSONPointerIndexError(`index offset out of range (${newIndex})`);
551
+ }
552
+ tokens[tokens.length - 1] = String(newIndex);
553
+ }
554
+
555
+ // pointer or index/property
556
+ if (this.pointer instanceof JSONPointer) {
557
+ tokens.push(...this.pointer.tokens);
558
+ } else {
559
+ tokens[tokens.length - 1] = `#${tokens[tokens.length - 1]}`;
560
+ }
561
+ return new JSONPointer(JSONPointer.encode(tokens));
562
+ }
563
+ parse(rel) {
564
+ const match = RE_RELATIVE_POINTER.exec(rel);
565
+ if (!match || !match.groups) {
566
+ throw new JSONPointerSyntaxError("failed to parse relative pointer");
567
+ }
454
568
 
455
- // TODO: to (relative pointer)
569
+ // steps to move
570
+ const origin = this.parseInt(match.groups.ORIGIN);
571
+
572
+ // optional index manipulation
573
+ let index = 0;
574
+ if (match.groups["INDEX_G"]) {
575
+ index = this.parseInt(match.groups.INDEX);
576
+ if (index === 0) {
577
+ throw new JSONPointerSyntaxError("index offset can't be zero");
578
+ }
579
+ if (match.groups.SIGN === "-") {
580
+ index = -index;
581
+ }
582
+ }
583
+
584
+ // pointer or '#'. an empty string is OK.
585
+ if (match.groups.POINTER === "#") {
586
+ return [origin, index, "#"];
587
+ }
588
+ return [origin, index, new JSONPointer(match.groups.POINTER)];
589
+ }
590
+ parseInt(s) {
591
+ if (s.startsWith("0") && s.length > 1) {
592
+ throw new JSONPointerSyntaxError("unexpected leading zero");
593
+ }
594
+ if (RE_INT.test(s)) {
595
+ return Number(s);
596
+ }
597
+ throw new JSONPointerSyntaxError(`expected an integer, found '${s}'`);
598
+ }
599
+ isIntLike(value) {
600
+ if (value === undefined || isNumber(value)) {
601
+ return true;
602
+ } else {
603
+ return RE_INT.test(value);
604
+ }
605
+ }
456
606
  }
457
607
 
458
608
  /**
@@ -487,6 +637,7 @@ var json_p3 = (function (exports) {
487
637
  JSONPointerResolutionError: JSONPointerResolutionError,
488
638
  JSONPointerSyntaxError: JSONPointerSyntaxError,
489
639
  JSONPointerTypeError: JSONPointerTypeError,
640
+ RelativeJSONPointer: RelativeJSONPointer,
490
641
  UNDEFINED: UNDEFINED,
491
642
  resolve: resolve
492
643
  });
@@ -784,7 +935,7 @@ var json_p3 = (function (exports) {
784
935
  this.args = args;
785
936
  }
786
937
  evaluate(context) {
787
- const func = context.environment.filterRegister.get(this.name);
938
+ const func = context.environment.functionRegister.get(this.name);
788
939
  if (!func) {
789
940
  throw new UndefinedFilterFunctionError(`filter function '${this.name}' is undefined`, this.token);
790
941
  }
@@ -804,13 +955,6 @@ var json_p3 = (function (exports) {
804
955
  if (value instanceof JSONPathNodeList && value.empty()) return false;
805
956
  return !(typeof value === "boolean" && value === false);
806
957
  }
807
-
808
- /**
809
- *
810
- * @param left -
811
- * @param operator -
812
- * @param right -
813
- */
814
958
  function compare(left, operator, right) {
815
959
  switch (operator) {
816
960
  case "==":
@@ -864,7 +1008,8 @@ var json_p3 = (function (exports) {
864
1008
  PrefixExpression: PrefixExpression,
865
1009
  RelativeQuery: RelativeQuery,
866
1010
  RootQuery: RootQuery,
867
- StringLiteral: StringLiteral
1011
+ StringLiteral: StringLiteral,
1012
+ compare: compare
868
1013
  });
869
1014
 
870
1015
  class Count {
@@ -1095,7 +1240,7 @@ var json_p3 = (function (exports) {
1095
1240
 
1096
1241
  // These regular expressions are to be used with Lexer.acceptMatchRun(),
1097
1242
  // which expects the sticky flag to be set.
1098
- const exponentPattern = /e[+-]\d+/y;
1243
+ const exponentPattern = /e[+-]?\d+/y;
1099
1244
  const functionNamePattern = /[a-z][a-z_0-9]*/y;
1100
1245
  const indexPattern = /-?\d+/y;
1101
1246
  const intPattern = /-?[0-9]+/y;
@@ -1388,6 +1533,10 @@ var json_p3 = (function (exports) {
1388
1533
  case "":
1389
1534
  case "]":
1390
1535
  l.filterLevel -= 1;
1536
+ if (l.parenStack.length === 1) {
1537
+ l.error("unbalanced parentheses");
1538
+ return null;
1539
+ }
1391
1540
  l.backup();
1392
1541
  return lexInsideBracketedSelection;
1393
1542
  case ",":
@@ -1617,7 +1766,7 @@ var json_p3 = (function (exports) {
1617
1766
  this.environment = environment;
1618
1767
  this.token = token;
1619
1768
  this.index = index;
1620
- if (index < this.environment.options.minIntIndex || index > this.environment.options.maxIntIndex) {
1769
+ if (index < this.environment.minIntIndex || index > this.environment.maxIntIndex) {
1621
1770
  throw new JSONPathIndexError("index out of range", this.token);
1622
1771
  }
1623
1772
  }
@@ -1672,15 +1821,11 @@ var json_p3 = (function (exports) {
1672
1821
  indices[_key] = arguments[_key];
1673
1822
  }
1674
1823
  for (const index of indices) {
1675
- if (index !== undefined && (index < this.environment.options.minIntIndex || index > this.environment.options.maxIntIndex)) {
1824
+ if (index !== undefined && (index < this.environment.minIntIndex || index > this.environment.maxIntIndex)) {
1676
1825
  throw new JSONPathIndexError("index out of range", this.token);
1677
1826
  }
1678
1827
  }
1679
1828
  }
1680
- normalizedIndex(length, index) {
1681
- if (index < 0 && length >= Math.abs(index)) return Math.min(length + index, length - 1);
1682
- return Math.min(index, length - 1);
1683
- }
1684
1829
 
1685
1830
  // eslint-disable-next-line sonarjs/cognitive-complexity
1686
1831
  slice(arr, start, stop, step) {
@@ -1764,17 +1909,21 @@ var json_p3 = (function (exports) {
1764
1909
  return "..";
1765
1910
  }
1766
1911
  visit(node) {
1912
+ let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
1913
+ if (depth >= this.environment.maxRecursionDepth) {
1914
+ throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
1915
+ }
1767
1916
  const rv = [];
1768
1917
  if (node.value instanceof String) return new JSONPathNodeList(rv);
1769
1918
  if (isArray(node.value)) {
1770
1919
  for (let i = 0; i < node.value.length; i++) {
1771
1920
  const _node = new JSONPathNode(node.value[i], node.location.concat(i), node.root);
1772
- rv.push(_node, ...this.visit(_node));
1921
+ rv.push(_node, ...this.visit(_node, depth + 1));
1773
1922
  }
1774
1923
  } else if (isObject(node.value)) {
1775
1924
  for (const [key, value] of Object.entries(node.value)) {
1776
1925
  const _node = new JSONPathNode(value, node.location.concat(key), node.root);
1777
- rv.push(_node, ...this.visit(_node));
1926
+ rv.push(_node, ...this.visit(_node, depth + 1));
1778
1927
  }
1779
1928
  }
1780
1929
  return new JSONPathNodeList(rv);
@@ -1884,6 +2033,18 @@ var json_p3 = (function (exports) {
1884
2033
  return nodes;
1885
2034
  }
1886
2035
 
2036
+ /**
2037
+ * Return a {@link JSONPathNode} instance for the first object found in
2038
+ * _value_ matching this query.
2039
+ *
2040
+ * @param value - JSON-like data to which this query will be applied.
2041
+ * @returns The first node in _value_ matching this query, or `undefined` if
2042
+ * there are no matches.
2043
+ */
2044
+ match(value) {
2045
+ return this.query(value).nodes.at(0);
2046
+ }
2047
+
1887
2048
  /**
1888
2049
  *
1889
2050
  */
@@ -2045,7 +2206,7 @@ var json_p3 = (function (exports) {
2045
2206
  const tok = stream.next();
2046
2207
  const expr = this.parseFilterExpression(stream);
2047
2208
  if (expr instanceof FunctionExtension) {
2048
- const func = this.environment.filterRegister.get(expr.name);
2209
+ const func = this.environment.functionRegister.get(expr.name);
2049
2210
  if (func && func.returnType === FunctionExpressionType.ValueType) {
2050
2211
  throw new JSONPathTypeError(`result of ${expr.name}() must be compared`, expr.token);
2051
2212
  }
@@ -2115,7 +2276,16 @@ var json_p3 = (function (exports) {
2115
2276
  if (!func) {
2116
2277
  throw new JSONPathSyntaxError(`unexpected '${stream.current.value}'`, stream.current);
2117
2278
  }
2118
- args.push(func.bind(this)(stream));
2279
+ let expr = func.bind(this)(stream);
2280
+
2281
+ // Could be a comparison/logical expression
2282
+ let peekKind = stream.peek.kind;
2283
+ while (BINARY_OPERATORS.has(peekKind)) {
2284
+ stream.next();
2285
+ expr = this.parseInfixExpression(stream, expr);
2286
+ peekKind = stream.peek.kind;
2287
+ }
2288
+ args.push(expr);
2119
2289
  if (stream.peek.kind !== TokenKind.RPAREN) {
2120
2290
  if (stream.peek.kind === TokenKind.RBRACKET) break;
2121
2291
  stream.expectPeek(TokenKind.COMMA);
@@ -2123,6 +2293,7 @@ var json_p3 = (function (exports) {
2123
2293
  }
2124
2294
  stream.next();
2125
2295
  }
2296
+ stream.expect(TokenKind.RPAREN);
2126
2297
  return new FunctionExtension(tok, tok.value, this.environment.checkWellTypedness(tok, args));
2127
2298
  }
2128
2299
  parseFilterExpression(stream) {
@@ -2167,7 +2338,7 @@ var json_p3 = (function (exports) {
2167
2338
  }
2168
2339
  throwForNonComparableFunction(expr) {
2169
2340
  if (!(expr instanceof FunctionExtension)) return;
2170
- const func = this.environment.filterRegister.get(expr.name);
2341
+ const func = this.environment.functionRegister.get(expr.name);
2171
2342
  if (func && func.returnType !== FunctionExpressionType.ValueType) {
2172
2343
  throw new JSONPathTypeError(`result of ${expr.name}() is not comparable`, expr.token);
2173
2344
  }
@@ -2175,30 +2346,53 @@ var json_p3 = (function (exports) {
2175
2346
  }
2176
2347
 
2177
2348
  /**
2178
- *
2349
+ * JSONPath environment options. The defaults are in compliance with JSONPath
2350
+ * standards.
2179
2351
  */
2180
2352
 
2181
- const defaultOptions = {
2182
- strict: true,
2183
- maxIntIndex: Math.pow(2, 53) - 1,
2184
- minIntIndex: -Math.pow(2, 53) - 1
2185
- };
2186
-
2187
2353
  /**
2188
2354
  *
2189
2355
  */
2190
2356
  class JSONPathEnvironment {
2191
2357
  /**
2358
+ * Indicates if the environment should to be strict about its compliance with
2359
+ * JSONPath standards.
2192
2360
  *
2361
+ * Defaults to `true`. Setting `strict` to `false` currently has no effect.
2362
+ * If/when we add non-standard features, the environment's strictness will
2363
+ * control their availability.
2364
+ */
2365
+
2366
+ /**
2367
+ * The maximum number allowed when indexing or slicing an array. Defaults to
2368
+ * 2**53 -1.
2369
+ */
2370
+
2371
+ /**
2372
+ * The minimum number allowed when indexing or slicing an array. Defaults to
2373
+ * -(2**53) -1.
2374
+ */
2375
+
2376
+ /**
2377
+ * The maximum number of objects and/or arrays the recursive descent selector
2378
+ * can visit before a `JSONPathRecursionLimitError` is thrown.
2379
+ */
2380
+
2381
+ /**
2382
+ * A map of function names to objects implementing the {@link FilterFunction}
2383
+ * interface. You are free to set or delete custom filter functions directly.
2193
2384
  */
2194
- filterRegister = new Map();
2385
+ functionRegister = new Map();
2195
2386
  /**
2196
2387
  *
2197
2388
  * @param options -
2198
2389
  */
2199
2390
  constructor() {
2200
- let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultOptions;
2201
- this.options = options;
2391
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2392
+ this.strict = options.strict ?? true;
2393
+ this.maxIntIndex = options.maxIntIndex ?? Math.pow(2, 53) - 1;
2394
+ this.minIntIndex = options.maxIntIndex ?? -Math.pow(2, 53) - 1;
2395
+ this.maxRecursionDepth = options.maxRecursionDepth ?? 50;
2202
2396
  this.parser = new Parser(this);
2203
2397
  this.setupFilterFunctions();
2204
2398
  }
@@ -2221,12 +2415,25 @@ var json_p3 = (function (exports) {
2221
2415
  query(path, value) {
2222
2416
  return this.compile(path).query(value);
2223
2417
  }
2418
+
2419
+ /**
2420
+ * Return a {@link JSONPathNode} instance for the first object found in
2421
+ * _value_ matching _path_.
2422
+ *
2423
+ * @param path - A JSONPath query.
2424
+ * @param value - JSON-like data to which the query _path_ will be applied.
2425
+ * @returns The first node in _value_ matching _path_, or `undefined` if
2426
+ * there are no matches.
2427
+ */
2428
+ match(path, value) {
2429
+ return this.compile(path).match(value);
2430
+ }
2224
2431
  setupFilterFunctions() {
2225
- this.filterRegister.set("count", new Count());
2226
- this.filterRegister.set("length", new Length());
2227
- this.filterRegister.set("search", new Search());
2228
- this.filterRegister.set("match", new Match());
2229
- this.filterRegister.set("value", new Value());
2432
+ this.functionRegister.set("count", new Count());
2433
+ this.functionRegister.set("length", new Length());
2434
+ this.functionRegister.set("search", new Search());
2435
+ this.functionRegister.set("match", new Match());
2436
+ this.functionRegister.set("value", new Value());
2230
2437
  }
2231
2438
 
2232
2439
  /**
@@ -2236,7 +2443,7 @@ var json_p3 = (function (exports) {
2236
2443
  */
2237
2444
  // eslint-disable-next-line sonarjs/cognitive-complexity
2238
2445
  checkWellTypedness(token, args) {
2239
- const func = this.filterRegister.get(token.value);
2446
+ const func = this.functionRegister.get(token.value);
2240
2447
  if (!func) {
2241
2448
  throw new UndefinedFilterFunctionError(`no such function '${token.value}'`, token);
2242
2449
  }
@@ -2250,17 +2457,17 @@ var json_p3 = (function (exports) {
2250
2457
  for (const [typ, arg, idx] of func.argTypes.map((t, i) => [t, args[i], i])) {
2251
2458
  switch (typ) {
2252
2459
  case FunctionExpressionType.ValueType:
2253
- if (!(arg instanceof FilterExpressionLiteral || arg instanceof JSONPathQuery && arg.path.singularQuery())) {
2460
+ if (!(arg instanceof FilterExpressionLiteral || arg instanceof JSONPathQuery && arg.path.singularQuery() || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.ValueType)) {
2254
2461
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of ValueType`, arg.token);
2255
2462
  }
2256
2463
  break;
2257
2464
  case FunctionExpressionType.LogicalType:
2258
- if (!(arg instanceof BooleanLiteral)) {
2465
+ if (!(arg instanceof JSONPathQuery || arg instanceof InfixExpression)) {
2259
2466
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of LogicalType`, arg.token);
2260
2467
  }
2261
2468
  break;
2262
2469
  case FunctionExpressionType.NodesType:
2263
- if (!(arg instanceof JSONPathQuery)) {
2470
+ if (!(arg instanceof JSONPathQuery || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.NodesType)) {
2264
2471
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of NodesType`, arg.token);
2265
2472
  }
2266
2473
  }
@@ -2315,6 +2522,19 @@ var json_p3 = (function (exports) {
2315
2522
  return DEFAULT_ENVIRONMENT.compile(path);
2316
2523
  }
2317
2524
 
2525
+ /**
2526
+ * Return a {@link JSONPathNode} instance for the first object found in
2527
+ * _value_ matching _path_.
2528
+ *
2529
+ * @param path - A JSONPath query.
2530
+ * @param value - JSON-like data to which the query _path_ will be applied.
2531
+ * @returns The first node in _value_ matching _path_, or `undefined` if
2532
+ * there are no matches.
2533
+ */
2534
+ function match(path, value) {
2535
+ return DEFAULT_ENVIRONMENT.match(path, value);
2536
+ }
2537
+
2318
2538
  var index$1 = /*#__PURE__*/Object.freeze({
2319
2539
  __proto__: null,
2320
2540
  DEFAULT_ENVIRONMENT: DEFAULT_ENVIRONMENT,
@@ -2326,6 +2546,7 @@ var json_p3 = (function (exports) {
2326
2546
  JSONPathLexerError: JSONPathLexerError,
2327
2547
  JSONPathNode: JSONPathNode,
2328
2548
  JSONPathNodeList: JSONPathNodeList,
2549
+ JSONPathRecursionLimitError: JSONPathRecursionLimitError,
2329
2550
  JSONPathSyntaxError: JSONPathSyntaxError,
2330
2551
  JSONPathTypeError: JSONPathTypeError,
2331
2552
  Nothing: Nothing,
@@ -2334,6 +2555,7 @@ var json_p3 = (function (exports) {
2334
2555
  compile: compile,
2335
2556
  expressions: expression,
2336
2557
  functions: index$2,
2558
+ match: match,
2337
2559
  query: query,
2338
2560
  selectors: selectors
2339
2561
  });
@@ -2634,6 +2856,15 @@ var json_p3 = (function (exports) {
2634
2856
  }
2635
2857
  }
2636
2858
 
2859
+ /**
2860
+ * @returns an iterator over ops in this patch.
2861
+ */
2862
+ *[Symbol.iterator]() {
2863
+ for (const op of this.ops) {
2864
+ yield op.toObject();
2865
+ }
2866
+ }
2867
+
2637
2868
  /**
2638
2869
  *
2639
2870
  * @param path -
@@ -2809,8 +3040,9 @@ var json_p3 = (function (exports) {
2809
3040
  apply: apply
2810
3041
  });
2811
3042
 
2812
- const version = "0.1.0";
3043
+ const version = "0.2.0";
2813
3044
 
3045
+ exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
2814
3046
  exports.FunctionExpressionType = FunctionExpressionType;
2815
3047
  exports.JSONPatch = JSONPatch;
2816
3048
  exports.JSONPatchError = JSONPatchError;
@@ -2822,10 +3054,12 @@ var json_p3 = (function (exports) {
2822
3054
  exports.JSONPathLexerError = JSONPathLexerError;
2823
3055
  exports.JSONPathNode = JSONPathNode;
2824
3056
  exports.JSONPathNodeList = JSONPathNodeList;
3057
+ exports.JSONPathRecursionLimitError = JSONPathRecursionLimitError;
2825
3058
  exports.JSONPathSyntaxError = JSONPathSyntaxError;
2826
3059
  exports.JSONPathTypeError = JSONPathTypeError;
2827
3060
  exports.JSONPointer = JSONPointer;
2828
3061
  exports.Nothing = Nothing;
3062
+ exports.RelativeJSONPointer = RelativeJSONPointer;
2829
3063
  exports.Token = Token;
2830
3064
  exports.TokenKind = TokenKind;
2831
3065
  exports.UNDEFINED = UNDEFINED;