json-p3 0.1.1 → 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.1
2
+ * json-p3 version 0.2.0
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
@@ -122,6 +122,20 @@ class JSONPathSyntaxError extends JSONPathError {
122
122
  }
123
123
  }
124
124
 
125
+ /**
126
+ * Error thrown when the maximum recursion depth is reached.
127
+ */
128
+ class JSONPathRecursionLimitError extends JSONPathError {
129
+ constructor(message, token) {
130
+ super(message, token);
131
+ this.message = message;
132
+ this.token = token;
133
+ Object.setPrototypeOf(this, new.target.prototype);
134
+ this.name = "JSONPathRecursionLimitError";
135
+ this.message = withErrorContext(message, token);
136
+ }
137
+ }
138
+
125
139
  /**
126
140
  * Common types and type predicates.
127
141
  */
@@ -230,6 +244,10 @@ class JSONPointerError extends Error {
230
244
  this.name = "JSONPointerError";
231
245
  }
232
246
  }
247
+
248
+ /**
249
+ * Base class for JSON Pointer resolution errors.
250
+ */
233
251
  class JSONPointerResolutionError extends JSONPointerError {
234
252
  constructor(message) {
235
253
  super(message);
@@ -238,6 +256,10 @@ class JSONPointerResolutionError extends JSONPointerError {
238
256
  this.name = "JSONPointerResolutionError";
239
257
  }
240
258
  }
259
+
260
+ /**
261
+ * Error thrown due to an out of range index when resolving a JSON Pointer.
262
+ */
241
263
  class JSONPointerIndexError extends JSONPointerResolutionError {
242
264
  constructor(message) {
243
265
  super(message);
@@ -246,6 +268,10 @@ class JSONPointerIndexError extends JSONPointerResolutionError {
246
268
  this.name = "JSONPointerIndexError";
247
269
  }
248
270
  }
271
+
272
+ /**
273
+ * Error thrown due to a missing property when resolving a JSON Pointer.
274
+ */
249
275
  class JSONPointerKeyError extends JSONPointerResolutionError {
250
276
  constructor(message) {
251
277
  super(message);
@@ -254,6 +280,10 @@ class JSONPointerKeyError extends JSONPointerResolutionError {
254
280
  this.name = "JSONPointerKeyError";
255
281
  }
256
282
  }
283
+
284
+ /**
285
+ * Error thrown due to invalid JSON Pointer syntax.
286
+ */
257
287
  class JSONPointerSyntaxError extends JSONPointerError {
258
288
  constructor(message) {
259
289
  super(message);
@@ -262,6 +292,10 @@ class JSONPointerSyntaxError extends JSONPointerError {
262
292
  this.name = "JSONPointerSyntaxError";
263
293
  }
264
294
  }
295
+
296
+ /**
297
+ * Error thrown when trying to resolve a property or index against a primitive value.
298
+ */
265
299
  class JSONPointerTypeError extends JSONPointerResolutionError {
266
300
  constructor(message) {
267
301
  super(message);
@@ -321,8 +355,6 @@ class JSONPointer {
321
355
  }
322
356
  }
323
357
 
324
- // TODO: add `resolveWithFallback` to handle explicit `undefined`
325
-
326
358
  /**
327
359
  *
328
360
  * @param value -
@@ -361,6 +393,8 @@ class JSONPointer {
361
393
  }
362
394
  return pointer.split("/").map(token => token.replaceAll("~1", "/").replaceAll("~0", "~")).slice(1);
363
395
  }
396
+
397
+ // eslint-disable-next-line sonarjs/cognitive-complexity
364
398
  getItem(val, token, idx) {
365
399
  // NOTE:
366
400
  // - string primitives "have own" indices and `length`.
@@ -370,12 +404,23 @@ class JSONPointer {
370
404
  if (isArray(val)) {
371
405
  if (token !== "length" && Object.hasOwn(val, token)) {
372
406
  return val[Number(token)];
407
+ } else if (token.startsWith("#")) {
408
+ // handle non-standard '#' from relative json pointer
409
+ const maybeIndex = token.slice(1);
410
+ if (RE_INT.test(maybeIndex) && Object.hasOwn(val, maybeIndex)) {
411
+ return Number(maybeIndex);
412
+ } else {
413
+ throw new JSONPointerIndexError(`index out of range '${JSONPointer.encode(this.tokens.slice(0, idx + 1))}'`);
414
+ }
373
415
  } else {
374
416
  throw new JSONPointerIndexError(`index out of range '${JSONPointer.encode(this.tokens.slice(0, idx + 1))}'`);
375
417
  }
376
418
  } else if (isObject(val)) {
377
419
  if (Object.hasOwn(val, token)) {
378
420
  return val[token];
421
+ } else if (token.startsWith("#") && Object.hasOwn(val, token.slice(1))) {
422
+ // handle non-standard '#' from relative json pointer
423
+ return token.slice(1);
379
424
  } else {
380
425
  throw new JSONPointerKeyError(`no such property '${JSONPointer.encode(this.tokens.slice(0, idx + 1))}'`);
381
426
  }
@@ -450,8 +495,111 @@ class JSONPointer {
450
495
  }
451
496
  return new JSONPointer(JSONPointer.encode(this.tokens.slice(0, this.tokens.length - 1)));
452
497
  }
498
+ to(rel) {
499
+ const relativePointer = isString(rel) ? new RelativeJSONPointer(rel) : rel;
500
+ return relativePointer.to(this);
501
+ }
502
+ }
503
+ const RE_RELATIVE_POINTER = /(?<ORIGIN>\d+)(?<INDEX_G>(?<SIGN>[+-])(?<INDEX>\d))?(?<POINTER>.*)/s;
504
+ const RE_INT = /(0|[1-9][0-9]*)/;
505
+
506
+ /**
507
+ * A relative JSON Pointer.
508
+ *
509
+ * See https://www.ietf.org/id/draft-hha-relative-json-pointer-00.html
510
+ */
511
+ class RelativeJSONPointer {
512
+ /**
513
+ *
514
+ * @param rel -
515
+ */
516
+ constructor(rel) {
517
+ [this.origin, this.index, this.pointer] = this.parse(rel);
518
+ }
519
+
520
+ /**
521
+ *
522
+ * @returns
523
+ */
524
+ toString() {
525
+ const sign = this.index > 0 ? "+" : "";
526
+ const index = this.index === 0 ? "" : `${sign}${this.index}`;
527
+ return `${this.origin}${index}${this.pointer}`;
528
+ }
529
+
530
+ /**
531
+ *
532
+ * @param pointer -
533
+ */
534
+ to(pointer) {
535
+ const p = isString(pointer) ? new JSONPointer(pointer) : pointer;
536
+
537
+ // move to origin
538
+ if (this.origin > p.tokens.length) {
539
+ throw new JSONPointerIndexError(`origin (${this.origin}) exceeds root (${p.tokens.length})`);
540
+ }
541
+ const tokens = this.origin < 1 ? p.tokens.slice() : p.tokens.slice(0, -this.origin);
542
+
543
+ // array index offset
544
+ if (this.index && tokens.length && this.isIntLike(tokens.at(-1))) {
545
+ const newIndex = Number(tokens.at(-1)) + this.index;
546
+ if (newIndex < 0) {
547
+ throw new JSONPointerIndexError(`index offset out of range (${newIndex})`);
548
+ }
549
+ tokens[tokens.length - 1] = String(newIndex);
550
+ }
551
+
552
+ // pointer or index/property
553
+ if (this.pointer instanceof JSONPointer) {
554
+ tokens.push(...this.pointer.tokens);
555
+ } else {
556
+ tokens[tokens.length - 1] = `#${tokens[tokens.length - 1]}`;
557
+ }
558
+ return new JSONPointer(JSONPointer.encode(tokens));
559
+ }
560
+ parse(rel) {
561
+ const match = RE_RELATIVE_POINTER.exec(rel);
562
+ if (!match || !match.groups) {
563
+ throw new JSONPointerSyntaxError("failed to parse relative pointer");
564
+ }
565
+
566
+ // steps to move
567
+ const origin = this.parseInt(match.groups.ORIGIN);
453
568
 
454
- // TODO: to (relative pointer)
569
+ // optional index manipulation
570
+ let index = 0;
571
+ if (match.groups["INDEX_G"]) {
572
+ index = this.parseInt(match.groups.INDEX);
573
+ if (index === 0) {
574
+ throw new JSONPointerSyntaxError("index offset can't be zero");
575
+ }
576
+ if (match.groups.SIGN === "-") {
577
+ index = -index;
578
+ }
579
+ }
580
+
581
+ // pointer or '#'. an empty string is OK.
582
+ if (match.groups.POINTER === "#") {
583
+ return [origin, index, "#"];
584
+ }
585
+ return [origin, index, new JSONPointer(match.groups.POINTER)];
586
+ }
587
+ parseInt(s) {
588
+ if (s.startsWith("0") && s.length > 1) {
589
+ throw new JSONPointerSyntaxError("unexpected leading zero");
590
+ }
591
+ if (RE_INT.test(s)) {
592
+ return Number(s);
593
+ }
594
+ throw new JSONPointerSyntaxError(`expected an integer, found '${s}'`);
595
+ }
596
+ isIntLike(value) {
597
+ if (value === undefined || isNumber(value)) {
598
+ return true;
599
+ } else {
600
+ return RE_INT.test(value);
601
+ }
602
+ }
455
603
  }
456
604
 
457
605
  /**
@@ -486,6 +634,7 @@ var index$3 = /*#__PURE__*/Object.freeze({
486
634
  JSONPointerResolutionError: JSONPointerResolutionError,
487
635
  JSONPointerSyntaxError: JSONPointerSyntaxError,
488
636
  JSONPointerTypeError: JSONPointerTypeError,
637
+ RelativeJSONPointer: RelativeJSONPointer,
489
638
  UNDEFINED: UNDEFINED,
490
639
  resolve: resolve
491
640
  });
@@ -783,7 +932,7 @@ class FunctionExtension extends FilterExpression {
783
932
  this.args = args;
784
933
  }
785
934
  evaluate(context) {
786
- const func = context.environment.filterRegister.get(this.name);
935
+ const func = context.environment.functionRegister.get(this.name);
787
936
  if (!func) {
788
937
  throw new UndefinedFilterFunctionError(`filter function '${this.name}' is undefined`, this.token);
789
938
  }
@@ -803,13 +952,6 @@ function isTruthy(value) {
803
952
  if (value instanceof JSONPathNodeList && value.empty()) return false;
804
953
  return !(typeof value === "boolean" && value === false);
805
954
  }
806
-
807
- /**
808
- *
809
- * @param left -
810
- * @param operator -
811
- * @param right -
812
- */
813
955
  function compare(left, operator, right) {
814
956
  switch (operator) {
815
957
  case "==":
@@ -863,7 +1005,8 @@ var expression = /*#__PURE__*/Object.freeze({
863
1005
  PrefixExpression: PrefixExpression,
864
1006
  RelativeQuery: RelativeQuery,
865
1007
  RootQuery: RootQuery,
866
- StringLiteral: StringLiteral
1008
+ StringLiteral: StringLiteral,
1009
+ compare: compare
867
1010
  });
868
1011
 
869
1012
  class Count {
@@ -1387,6 +1530,10 @@ function lexInsideFilter(l) {
1387
1530
  case "":
1388
1531
  case "]":
1389
1532
  l.filterLevel -= 1;
1533
+ if (l.parenStack.length === 1) {
1534
+ l.error("unbalanced parentheses");
1535
+ return null;
1536
+ }
1390
1537
  l.backup();
1391
1538
  return lexInsideBracketedSelection;
1392
1539
  case ",":
@@ -1616,7 +1763,7 @@ class IndexSelector extends JSONPathSelector {
1616
1763
  this.environment = environment;
1617
1764
  this.token = token;
1618
1765
  this.index = index;
1619
- if (index < this.environment.options.minIntIndex || index > this.environment.options.maxIntIndex) {
1766
+ if (index < this.environment.minIntIndex || index > this.environment.maxIntIndex) {
1620
1767
  throw new JSONPathIndexError("index out of range", this.token);
1621
1768
  }
1622
1769
  }
@@ -1671,15 +1818,11 @@ class SliceSelector extends JSONPathSelector {
1671
1818
  indices[_key] = arguments[_key];
1672
1819
  }
1673
1820
  for (const index of indices) {
1674
- if (index !== undefined && (index < this.environment.options.minIntIndex || index > this.environment.options.maxIntIndex)) {
1821
+ if (index !== undefined && (index < this.environment.minIntIndex || index > this.environment.maxIntIndex)) {
1675
1822
  throw new JSONPathIndexError("index out of range", this.token);
1676
1823
  }
1677
1824
  }
1678
1825
  }
1679
- normalizedIndex(length, index) {
1680
- if (index < 0 && length >= Math.abs(index)) return Math.min(length + index, length - 1);
1681
- return Math.min(index, length - 1);
1682
- }
1683
1826
 
1684
1827
  // eslint-disable-next-line sonarjs/cognitive-complexity
1685
1828
  slice(arr, start, stop, step) {
@@ -1763,17 +1906,21 @@ class RecursiveDescentSegment extends JSONPathSelector {
1763
1906
  return "..";
1764
1907
  }
1765
1908
  visit(node) {
1909
+ let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
1910
+ if (depth >= this.environment.maxRecursionDepth) {
1911
+ throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
1912
+ }
1766
1913
  const rv = [];
1767
1914
  if (node.value instanceof String) return new JSONPathNodeList(rv);
1768
1915
  if (isArray(node.value)) {
1769
1916
  for (let i = 0; i < node.value.length; i++) {
1770
1917
  const _node = new JSONPathNode(node.value[i], node.location.concat(i), node.root);
1771
- rv.push(_node, ...this.visit(_node));
1918
+ rv.push(_node, ...this.visit(_node, depth + 1));
1772
1919
  }
1773
1920
  } else if (isObject(node.value)) {
1774
1921
  for (const [key, value] of Object.entries(node.value)) {
1775
1922
  const _node = new JSONPathNode(value, node.location.concat(key), node.root);
1776
- rv.push(_node, ...this.visit(_node));
1923
+ rv.push(_node, ...this.visit(_node, depth + 1));
1777
1924
  }
1778
1925
  }
1779
1926
  return new JSONPathNodeList(rv);
@@ -1883,6 +2030,18 @@ class JSONPath {
1883
2030
  return nodes;
1884
2031
  }
1885
2032
 
2033
+ /**
2034
+ * Return a {@link JSONPathNode} instance for the first object found in
2035
+ * _value_ matching this query.
2036
+ *
2037
+ * @param value - JSON-like data to which this query will be applied.
2038
+ * @returns The first node in _value_ matching this query, or `undefined` if
2039
+ * there are no matches.
2040
+ */
2041
+ match(value) {
2042
+ return this.query(value).nodes.at(0);
2043
+ }
2044
+
1886
2045
  /**
1887
2046
  *
1888
2047
  */
@@ -2044,7 +2203,7 @@ class Parser {
2044
2203
  const tok = stream.next();
2045
2204
  const expr = this.parseFilterExpression(stream);
2046
2205
  if (expr instanceof FunctionExtension) {
2047
- const func = this.environment.filterRegister.get(expr.name);
2206
+ const func = this.environment.functionRegister.get(expr.name);
2048
2207
  if (func && func.returnType === FunctionExpressionType.ValueType) {
2049
2208
  throw new JSONPathTypeError(`result of ${expr.name}() must be compared`, expr.token);
2050
2209
  }
@@ -2114,7 +2273,16 @@ class Parser {
2114
2273
  if (!func) {
2115
2274
  throw new JSONPathSyntaxError(`unexpected '${stream.current.value}'`, stream.current);
2116
2275
  }
2117
- args.push(func.bind(this)(stream));
2276
+ let expr = func.bind(this)(stream);
2277
+
2278
+ // Could be a comparison/logical expression
2279
+ let peekKind = stream.peek.kind;
2280
+ while (BINARY_OPERATORS.has(peekKind)) {
2281
+ stream.next();
2282
+ expr = this.parseInfixExpression(stream, expr);
2283
+ peekKind = stream.peek.kind;
2284
+ }
2285
+ args.push(expr);
2118
2286
  if (stream.peek.kind !== TokenKind.RPAREN) {
2119
2287
  if (stream.peek.kind === TokenKind.RBRACKET) break;
2120
2288
  stream.expectPeek(TokenKind.COMMA);
@@ -2122,6 +2290,7 @@ class Parser {
2122
2290
  }
2123
2291
  stream.next();
2124
2292
  }
2293
+ stream.expect(TokenKind.RPAREN);
2125
2294
  return new FunctionExtension(tok, tok.value, this.environment.checkWellTypedness(tok, args));
2126
2295
  }
2127
2296
  parseFilterExpression(stream) {
@@ -2166,7 +2335,7 @@ class Parser {
2166
2335
  }
2167
2336
  throwForNonComparableFunction(expr) {
2168
2337
  if (!(expr instanceof FunctionExtension)) return;
2169
- const func = this.environment.filterRegister.get(expr.name);
2338
+ const func = this.environment.functionRegister.get(expr.name);
2170
2339
  if (func && func.returnType !== FunctionExpressionType.ValueType) {
2171
2340
  throw new JSONPathTypeError(`result of ${expr.name}() is not comparable`, expr.token);
2172
2341
  }
@@ -2174,30 +2343,53 @@ class Parser {
2174
2343
  }
2175
2344
 
2176
2345
  /**
2177
- *
2346
+ * JSONPath environment options. The defaults are in compliance with JSONPath
2347
+ * standards.
2178
2348
  */
2179
2349
 
2180
- const defaultOptions = {
2181
- strict: true,
2182
- maxIntIndex: Math.pow(2, 53) - 1,
2183
- minIntIndex: -Math.pow(2, 53) - 1
2184
- };
2185
-
2186
2350
  /**
2187
2351
  *
2188
2352
  */
2189
2353
  class JSONPathEnvironment {
2190
2354
  /**
2355
+ * Indicates if the environment should to be strict about its compliance with
2356
+ * JSONPath standards.
2191
2357
  *
2358
+ * Defaults to `true`. Setting `strict` to `false` currently has no effect.
2359
+ * If/when we add non-standard features, the environment's strictness will
2360
+ * control their availability.
2192
2361
  */
2193
- filterRegister = new Map();
2362
+
2363
+ /**
2364
+ * The maximum number allowed when indexing or slicing an array. Defaults to
2365
+ * 2**53 -1.
2366
+ */
2367
+
2368
+ /**
2369
+ * The minimum number allowed when indexing or slicing an array. Defaults to
2370
+ * -(2**53) -1.
2371
+ */
2372
+
2373
+ /**
2374
+ * The maximum number of objects and/or arrays the recursive descent selector
2375
+ * can visit before a `JSONPathRecursionLimitError` is thrown.
2376
+ */
2377
+
2378
+ /**
2379
+ * A map of function names to objects implementing the {@link FilterFunction}
2380
+ * interface. You are free to set or delete custom filter functions directly.
2381
+ */
2382
+ functionRegister = new Map();
2194
2383
  /**
2195
2384
  *
2196
2385
  * @param options -
2197
2386
  */
2198
2387
  constructor() {
2199
- let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultOptions;
2200
- this.options = options;
2388
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2389
+ this.strict = options.strict ?? true;
2390
+ this.maxIntIndex = options.maxIntIndex ?? Math.pow(2, 53) - 1;
2391
+ this.minIntIndex = options.maxIntIndex ?? -Math.pow(2, 53) - 1;
2392
+ this.maxRecursionDepth = options.maxRecursionDepth ?? 50;
2201
2393
  this.parser = new Parser(this);
2202
2394
  this.setupFilterFunctions();
2203
2395
  }
@@ -2220,12 +2412,25 @@ class JSONPathEnvironment {
2220
2412
  query(path, value) {
2221
2413
  return this.compile(path).query(value);
2222
2414
  }
2415
+
2416
+ /**
2417
+ * Return a {@link JSONPathNode} instance for the first object found in
2418
+ * _value_ matching _path_.
2419
+ *
2420
+ * @param path - A JSONPath query.
2421
+ * @param value - JSON-like data to which the query _path_ will be applied.
2422
+ * @returns The first node in _value_ matching _path_, or `undefined` if
2423
+ * there are no matches.
2424
+ */
2425
+ match(path, value) {
2426
+ return this.compile(path).match(value);
2427
+ }
2223
2428
  setupFilterFunctions() {
2224
- this.filterRegister.set("count", new Count());
2225
- this.filterRegister.set("length", new Length());
2226
- this.filterRegister.set("search", new Search());
2227
- this.filterRegister.set("match", new Match());
2228
- this.filterRegister.set("value", new Value());
2429
+ this.functionRegister.set("count", new Count());
2430
+ this.functionRegister.set("length", new Length());
2431
+ this.functionRegister.set("search", new Search());
2432
+ this.functionRegister.set("match", new Match());
2433
+ this.functionRegister.set("value", new Value());
2229
2434
  }
2230
2435
 
2231
2436
  /**
@@ -2235,7 +2440,7 @@ class JSONPathEnvironment {
2235
2440
  */
2236
2441
  // eslint-disable-next-line sonarjs/cognitive-complexity
2237
2442
  checkWellTypedness(token, args) {
2238
- const func = this.filterRegister.get(token.value);
2443
+ const func = this.functionRegister.get(token.value);
2239
2444
  if (!func) {
2240
2445
  throw new UndefinedFilterFunctionError(`no such function '${token.value}'`, token);
2241
2446
  }
@@ -2249,17 +2454,17 @@ class JSONPathEnvironment {
2249
2454
  for (const [typ, arg, idx] of func.argTypes.map((t, i) => [t, args[i], i])) {
2250
2455
  switch (typ) {
2251
2456
  case FunctionExpressionType.ValueType:
2252
- if (!(arg instanceof FilterExpressionLiteral || arg instanceof JSONPathQuery && arg.path.singularQuery())) {
2457
+ if (!(arg instanceof FilterExpressionLiteral || arg instanceof JSONPathQuery && arg.path.singularQuery() || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.ValueType)) {
2253
2458
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of ValueType`, arg.token);
2254
2459
  }
2255
2460
  break;
2256
2461
  case FunctionExpressionType.LogicalType:
2257
- if (!(arg instanceof BooleanLiteral)) {
2462
+ if (!(arg instanceof JSONPathQuery || arg instanceof InfixExpression)) {
2258
2463
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of LogicalType`, arg.token);
2259
2464
  }
2260
2465
  break;
2261
2466
  case FunctionExpressionType.NodesType:
2262
- if (!(arg instanceof JSONPathQuery)) {
2467
+ if (!(arg instanceof JSONPathQuery || arg instanceof FunctionExtension && this.functionRegister.get(arg.name)?.returnType === FunctionExpressionType.NodesType)) {
2263
2468
  throw new JSONPathTypeError(`${token.value}() argument ${idx} must be of NodesType`, arg.token);
2264
2469
  }
2265
2470
  }
@@ -2314,6 +2519,19 @@ function compile(path) {
2314
2519
  return DEFAULT_ENVIRONMENT.compile(path);
2315
2520
  }
2316
2521
 
2522
+ /**
2523
+ * Return a {@link JSONPathNode} instance for the first object found in
2524
+ * _value_ matching _path_.
2525
+ *
2526
+ * @param path - A JSONPath query.
2527
+ * @param value - JSON-like data to which the query _path_ will be applied.
2528
+ * @returns The first node in _value_ matching _path_, or `undefined` if
2529
+ * there are no matches.
2530
+ */
2531
+ function match(path, value) {
2532
+ return DEFAULT_ENVIRONMENT.match(path, value);
2533
+ }
2534
+
2317
2535
  var index$1 = /*#__PURE__*/Object.freeze({
2318
2536
  __proto__: null,
2319
2537
  DEFAULT_ENVIRONMENT: DEFAULT_ENVIRONMENT,
@@ -2325,6 +2543,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
2325
2543
  JSONPathLexerError: JSONPathLexerError,
2326
2544
  JSONPathNode: JSONPathNode,
2327
2545
  JSONPathNodeList: JSONPathNodeList,
2546
+ JSONPathRecursionLimitError: JSONPathRecursionLimitError,
2328
2547
  JSONPathSyntaxError: JSONPathSyntaxError,
2329
2548
  JSONPathTypeError: JSONPathTypeError,
2330
2549
  Nothing: Nothing,
@@ -2333,6 +2552,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
2333
2552
  compile: compile,
2334
2553
  expressions: expression,
2335
2554
  functions: index$2,
2555
+ match: match,
2336
2556
  query: query,
2337
2557
  selectors: selectors
2338
2558
  });
@@ -2633,6 +2853,15 @@ class JSONPatch {
2633
2853
  }
2634
2854
  }
2635
2855
 
2856
+ /**
2857
+ * @returns an iterator over ops in this patch.
2858
+ */
2859
+ *[Symbol.iterator]() {
2860
+ for (const op of this.ops) {
2861
+ yield op.toObject();
2862
+ }
2863
+ }
2864
+
2636
2865
  /**
2637
2866
  *
2638
2867
  * @param path -
@@ -2808,6 +3037,6 @@ var index = /*#__PURE__*/Object.freeze({
2808
3037
  apply: apply
2809
3038
  });
2810
3039
 
2811
- const version = "0.1.1";
3040
+ const version = "0.2.0";
2812
3041
 
2813
- export { FunctionExpressionType, JSONPatch, JSONPatchError, JSONPatchTestFailure, JSONPath, JSONPathEnvironment, JSONPathError, JSONPathIndexError, JSONPathLexerError, JSONPathNode, JSONPathNodeList, JSONPathSyntaxError, JSONPathTypeError, JSONPointer, Nothing, Token, TokenKind, UNDEFINED, apply, compile, index as jsonpatch, index$1 as jsonpath, index$3 as jsonpointer, query, resolve, version };
3042
+ 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, query, resolve, version };