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