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.
- package/README.md +21 -288
- package/dist/index.d.ts +2 -2
- package/dist/json-p3.cjs.js +276 -44
- package/dist/json-p3.esm.js +274 -45
- package/dist/json-p3.iife.js +276 -44
- package/dist/json-p3.iife.min.js +1 -1
- package/dist/json-p3.iife.min.js.map +1 -1
- package/dist/patch/patch.d.ts +4 -0
- package/dist/path/environment.d.ts +56 -10
- package/dist/path/errors.d.ts +8 -0
- package/dist/path/expression.d.ts +1 -0
- package/dist/path/index.d.ts +12 -2
- package/dist/path/path.d.ts +10 -1
- package/dist/path/selectors.d.ts +0 -1
- package/dist/pointer/errors.d.ts +15 -0
- package/dist/pointer/index.d.ts +1 -1
- package/dist/pointer/pointer.d.ts +29 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
package/dist/json-p3.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* json-p3 version 0.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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] :
|
|
2200
|
-
this.
|
|
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.
|
|
2225
|
-
this.
|
|
2226
|
-
this.
|
|
2227
|
-
this.
|
|
2228
|
-
this.
|
|
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.
|
|
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
|
|
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.
|
|
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 };
|