Skip to content

Commit a0fcc72

Browse files
committed
feat(evaluate): provide rich diagnostic information on failure
Refs #21
1 parent 0edef45 commit a0fcc72

File tree

5 files changed

+71
-41
lines changed

5 files changed

+71
-41
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
- [Immutable.js](#immutablejs-evaluation-realm)
4949
- [Custom](#custom-evaluation-realms)
5050
- [Composing Realms](#composing-evaluation-realms)
51+
- [Diagnostics](#diagnostics)
5152
- [Compilation](#compilation)
5253
- [Representation](#representation)
5354
- [JSON String](#json-string)
@@ -508,6 +509,22 @@ const structure = [
508509
evaluate(structure, '/0/a/b/1', { realm : compositeRealm }); // => 'd'
509510
```
510511

512+
##### Diagnostics
513+
514+
`@swaggerexpert/json-pointer` provides rich diagnostic information to help identify and resolve issues during JSON Pointer evaluation.
515+
516+
When evaluation fails, the library throws errors from a well-defined hierarchy — all extending from `JSONPointerEvaluateError`.
517+
These errors carry detailed diagnostic metadata describing what failed, where it failed, and why.
518+
519+
Each error includes:
520+
521+
- `jsonPointer` – the full pointer being evaluated
522+
- `referenceToken` – the token that caused the failure
523+
- `referenceTokenPosition` – the index of that token within the pointer
524+
- `referenceTokens` – the full list of parsed reference tokens
525+
- `currentValue` – the value being evaluated at the point of failure
526+
- `realm` – the name of the evaluation realm (e.g., "json")
527+
511528
#### Compilation
512529

513530
Compilation is the process of transforming a list of reference tokens into a JSON Pointer.

src/errors/JSONPointerTypeError.js

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
import JSONPointerEvaluateError from './JSONPointerEvaluateError.js';
22

3-
class JSONPointerTypeError extends JSONPointerEvaluateError {
4-
constructor(message, options) {
5-
if (
6-
typeof message === 'undefined' &&
7-
(typeof options?.referenceToken === 'string' || typeof options?.referenceToken === 'number')
8-
) {
9-
message = `Reference token '${options.referenceToken}' cannot be applied to non object/array value)`;
10-
}
11-
12-
super(message, options);
13-
}
14-
}
3+
class JSONPointerTypeError extends JSONPointerEvaluateError {}
154

165
export default JSONPointerTypeError;

src/evaluate/index.js

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ const evaluate = (
2626
if (testArrayDash(referenceToken)) {
2727
if (strictArrays) {
2828
throw new JSONPointerIndexError(
29-
'Invalid array index: "-" always refers to a nonexistent element during evaluation',
29+
`Invalid array index "-" at position ${referenceTokenPosition} in ${jsonPointer}. The "-" token always refers to a nonexistent element during evaluation`,
3030
{
3131
jsonPointer,
3232
referenceTokens,
3333
referenceToken,
3434
referenceTokenPosition,
35+
currentValue: current,
36+
realm: realm.name,
3537
},
3638
);
3739
} else {
@@ -41,48 +43,64 @@ const evaluate = (
4143

4244
if (!testArrayIndex(referenceToken) && strictArrays) {
4345
throw new JSONPointerIndexError(
44-
`Invalid array index: '${referenceToken}' (MUST be "0", or digits without a leading "0")`,
46+
`Invalid array index "${referenceToken}" at position ${referenceTokenPosition} in ${jsonPointer}: index MUST be "0", or digits without a leading "0"`,
4547
{
4648
jsonPointer,
4749
referenceTokens,
4850
referenceToken,
4951
referenceTokenPosition,
52+
currentValue: current,
53+
realm: realm.name,
5054
},
5155
);
5256
}
5357

5458
const index = Number(referenceToken);
5559
if (index >= realm.sizeOf(current) && strictArrays) {
56-
throw new JSONPointerIndexError(`Invalid array index: '${index}' out of bounds`, {
57-
jsonPointer,
58-
referenceTokens,
59-
referenceToken: index,
60-
referenceTokenPosition,
61-
});
60+
throw new JSONPointerIndexError(
61+
`Invalid array index "${index}" at position ${referenceTokenPosition} in ${jsonPointer}: out of bounds`,
62+
{
63+
jsonPointer,
64+
referenceTokens,
65+
referenceToken: index,
66+
referenceTokenPosition,
67+
currentValue: current,
68+
realm: realm.name,
69+
},
70+
);
6271
}
6372
return realm.evaluate(current, referenceToken);
6473
}
6574

6675
if (realm.isObject(current)) {
6776
if (!realm.has(current, referenceToken) && strictObjects) {
68-
throw new JSONPointerKeyError(`Invalid object key: '${referenceToken}' not found`, {
69-
jsonPointer,
70-
referenceTokens,
71-
referenceToken,
72-
referenceTokenPosition,
73-
});
77+
throw new JSONPointerKeyError(
78+
`Invalid object key "${referenceToken}" at position ${referenceTokenPosition} in ${jsonPointer}: key not found in object`,
79+
{
80+
jsonPointer,
81+
referenceTokens,
82+
referenceToken,
83+
referenceTokenPosition,
84+
currentValue: current,
85+
realm: realm.name,
86+
},
87+
);
7488
}
7589

7690
return realm.evaluate(current, referenceToken);
7791
}
7892

79-
throw new JSONPointerTypeError(undefined, {
80-
jsonPointer,
81-
referenceTokens,
82-
referenceToken,
83-
referenceTokenPosition,
84-
currentValue: current,
85-
});
93+
throw new JSONPointerTypeError(
94+
`Invalid reference token "${referenceToken}" at position ${referenceTokenPosition} in ${jsonPointer}: cannot be applied to a non-object/non-array value`,
95+
{
96+
jsonPointer,
97+
referenceTokens,
98+
referenceToken,
99+
referenceTokenPosition,
100+
currentValue: current,
101+
realm: realm.name,
102+
},
103+
);
86104
}, value);
87105
} catch (error) {
88106
if (error instanceof JSONPointerEvaluateError) {

src/evaluate/realms/apidom.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@ class ApiDOMEvaluationRealm extends EvaluationRealm {
3030
const uniqueKeys = new Set(keys);
3131

3232
if (keys.length !== uniqueKeys.size) {
33-
throw new JSONPointerKeyError(`Object keys must be unique for '${referenceToken}'`, {
34-
currentValue: node,
35-
referenceToken,
36-
});
33+
throw new JSONPointerKeyError(
34+
`Object key "${referenceToken}" is not unique — JSON Pointer requires unique member names`,
35+
{
36+
currentValue: node,
37+
referenceToken,
38+
},
39+
);
3740
}
3841

3942
return node.hasKey(referenceToken);

src/evaluate/realms/minim.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@ class MinimEvaluationRealm extends EvaluationRealm {
3030
const uniqueKeys = new Set(keys);
3131

3232
if (keys.length !== uniqueKeys.size) {
33-
throw new JSONPointerKeyError(`Object keys must be unique for '${referenceToken}'`, {
34-
currentValue: node,
35-
referenceToken,
36-
});
33+
throw new JSONPointerKeyError(
34+
`Object key "${referenceToken}" is not unique — JSON Pointer requires unique member names`,
35+
{
36+
currentValue: node,
37+
referenceToken,
38+
},
39+
);
3740
}
3841

3942
return node.hasKey(referenceToken);

0 commit comments

Comments
 (0)