Skip to content

Commit 9186c38

Browse files
authored
feat(errors): add support for attaching data to errors (#12)
Closes #5
1 parent 610ae6a commit 9186c38

File tree

8 files changed

+79
-26
lines changed

8 files changed

+79
-26
lines changed

src/compile.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import escape from './escape.js';
22
import JSONPointerCompileError from './errors/JSONPointerCompileError.js';
33

44
const compile = (referenceTokens) => {
5+
if (!Array.isArray(referenceTokens)) {
6+
throw new TypeError('Reference tokens must be a list of strings or numbers');
7+
}
8+
59
try {
610
if (referenceTokens.length === 0) {
711
return '';
@@ -16,8 +20,9 @@ const compile = (referenceTokens) => {
1620
})
1721
.join('/')}`;
1822
} catch (error) {
19-
throw new JSONPointerCompileError('Unknown error during JSON Pointer compilation', {
23+
throw new JSONPointerCompileError('Unexpected error during JSON Pointer compilation', {
2024
cause: error,
25+
referenceTokens,
2126
});
2227
}
2328
};

src/errors/JSONPointerKeyError.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import JSONPointerEvaluateError from './JSONPointerEvaluateError.js';
22

33
class JSONPointerKeyError extends JSONPointerEvaluateError {
4-
constructor(referenceToken, options) {
5-
super(`Invalid object key: '${referenceToken}' not found`, options);
4+
constructor(message, options) {
5+
if (
6+
typeof message === 'undefined' &&
7+
(typeof options?.referenceToken === 'string' || typeof options?.referenceToken === 'number')
8+
) {
9+
message = `Invalid object key: '${options.referenceToken}' not found`;
10+
}
11+
12+
super(message, options);
613
}
714
}
815

src/errors/JSONPointerTypeError.js

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

33
class JSONPointerTypeError extends JSONPointerEvaluateError {
4-
constructor(referenceToken, options) {
5-
super(
6-
`Reference token '${referenceToken}' cannot be applied to non object/array value)`,
7-
options,
8-
);
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);
913
}
1014
}
1115

src/evaluate.js

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,23 @@ const evaluate = (
1515
const { result, computed: referenceTokens } = parse(jsonPointer, parseOptions);
1616

1717
if (!result.success) {
18-
throw new JSONPointerEvaluateError(`Invalid JSON Pointer: ${jsonPointer}`);
18+
throw new JSONPointerEvaluateError(`Invalid JSON Pointer: ${jsonPointer}`, {
19+
jsonPointer,
20+
});
1921
}
2022

21-
return referenceTokens.reduce((current, referenceToken) => {
22-
if (typeof current !== 'object' || current === null) {
23-
throw new JSONPointerTypeError(referenceToken);
24-
}
25-
23+
return referenceTokens.reduce((current, referenceToken, referenceTokenPosition) => {
2624
if (Array.isArray(current)) {
2725
if (testArrayDash(referenceToken)) {
2826
if (strictArrays) {
2927
throw new JSONPointerIndexError(
3028
'Invalid array index: "-" always refers to a nonexistent element during evaluation',
29+
{
30+
jsonPointer,
31+
referenceTokens,
32+
referenceToken,
33+
referenceTokenPosition,
34+
},
3135
);
3236
} else {
3337
return current[current.length];
@@ -37,21 +41,47 @@ const evaluate = (
3741
if (!testArrayIndex(referenceToken) && strictArrays) {
3842
throw new JSONPointerIndexError(
3943
`Invalid array index: '${referenceToken}' (MUST be "0", or digits without a leading "0")`,
44+
{
45+
jsonPointer,
46+
referenceTokens,
47+
referenceToken,
48+
referenceTokenPosition,
49+
},
4050
);
4151
}
4252

4353
const index = Number(referenceToken);
4454
if (index >= current.length && strictArrays) {
45-
throw new JSONPointerIndexError(`Invalid array index: '${index}' out of bounds`);
55+
throw new JSONPointerIndexError(`Invalid array index: '${index}' out of bounds`, {
56+
jsonPointer,
57+
referenceTokens,
58+
referenceToken: index,
59+
referenceTokenPosition,
60+
});
4661
}
4762
return current[index];
4863
}
4964

50-
if (!Object.prototype.hasOwnProperty.call(current, referenceToken) && strictObjects) {
51-
throw new JSONPointerKeyError(referenceToken);
65+
if (typeof current === 'object' && current !== null) {
66+
if (!Object.prototype.hasOwnProperty.call(current, referenceToken) && strictObjects) {
67+
throw new JSONPointerKeyError(undefined, {
68+
jsonPointer,
69+
referenceTokens,
70+
referenceToken,
71+
referenceTokenPosition,
72+
});
73+
}
74+
75+
return current[referenceToken];
5276
}
5377

54-
return current[referenceToken];
78+
throw new JSONPointerTypeError(undefined, {
79+
jsonPointer,
80+
referenceTokens,
81+
referenceToken,
82+
referenceTokenPosition,
83+
currentValue: current,
84+
});
5585
}, value);
5686
};
5787

src/parse/index.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const grammar = new Grammar();
1010

1111
const parse = (jsonPointer, { evaluator = referenceTokenListEvaluator } = {}) => {
1212
if (typeof jsonPointer !== 'string') {
13-
throw new JSONPointerParseError('JSON Pointer must be a string');
13+
throw new TypeError('JSON Pointer must be a string');
1414
}
1515

1616
try {
@@ -31,7 +31,10 @@ const parse = (jsonPointer, { evaluator = referenceTokenListEvaluator } = {}) =>
3131

3232
return { result, ast, computed };
3333
} catch (error) {
34-
throw new JSONPointerParseError('Unknown error during JSON Pointer parsing', { cause: error });
34+
throw new JSONPointerParseError('Unexpected error during JSON Pointer parsing', {
35+
cause: error,
36+
jsonPointer,
37+
});
3538
}
3639
};
3740

src/unescape.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
const unescape = (referenceToken) => {
2+
if (typeof referenceToken !== 'string') {
3+
throw new TypeError('Reference token must be a string');
4+
}
5+
26
return referenceToken.replace(/~1/g, '/').replace(/~0/g, '~');
37
};
48

test/compile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ describe('compile', function () {
4040
});
4141

4242
it('should throw error on invalid input', function () {
43-
assert.throws(() => compile(null), JSONPointerCompileError);
43+
assert.throws(() => compile(null), TypeError);
4444
});
4545
});

test/parse.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { assert } from 'chai';
22

3-
import { parse, JSONPointerParseError } from '../src/index.js';
3+
import { parse } from '../src/index.js';
44

55
describe('parse', function () {
66
context('given valid source string', function () {
@@ -229,10 +229,10 @@ describe('parse', function () {
229229

230230
context('given non-string input', function () {
231231
specify('should throw error', function () {
232-
assert.throws(() => parse([]), JSONPointerParseError);
233-
assert.throws(() => parse(1), JSONPointerParseError);
234-
assert.throws(() => parse(null), JSONPointerParseError);
235-
assert.throws(() => parse(undefined), JSONPointerParseError);
232+
assert.throws(() => parse([]), TypeError);
233+
assert.throws(() => parse(1), TypeError);
234+
assert.throws(() => parse(null), TypeError);
235+
assert.throws(() => parse(undefined), TypeError);
236236
});
237237
});
238238
});

0 commit comments

Comments
 (0)