Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import escape from './escape.js';
import JSONPointerCompileError from './errors/JSONPointerCompileError.js';

const compile = (referenceTokens) => {
if (!Array.isArray(referenceTokens)) {
throw new TypeError('Reference tokens must be a list of strings or numbers');
}

try {
if (referenceTokens.length === 0) {
return '';
Expand All @@ -16,8 +20,9 @@ const compile = (referenceTokens) => {
})
.join('/')}`;
} catch (error) {
throw new JSONPointerCompileError('Unknown error during JSON Pointer compilation', {
throw new JSONPointerCompileError('Unexpected error during JSON Pointer compilation', {
cause: error,
referenceTokens,
});
}
};
Expand Down
11 changes: 9 additions & 2 deletions src/errors/JSONPointerKeyError.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import JSONPointerEvaluateError from './JSONPointerEvaluateError.js';

class JSONPointerKeyError extends JSONPointerEvaluateError {
constructor(referenceToken, options) {
super(`Invalid object key: '${referenceToken}' not found`, options);
constructor(message, options) {
if (
typeof message === 'undefined' &&
(typeof options?.referenceToken === 'string' || typeof options?.referenceToken === 'number')
) {
message = `Invalid object key: '${options.referenceToken}' not found`;
}

super(message, options);
}
}

Expand Down
14 changes: 9 additions & 5 deletions src/errors/JSONPointerTypeError.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import JSONPointerEvaluateError from './JSONPointerEvaluateError.js';

class JSONPointerTypeError extends JSONPointerEvaluateError {
constructor(referenceToken, options) {
super(
`Reference token '${referenceToken}' cannot be applied to non object/array value)`,
options,
);
constructor(message, options) {
if (
typeof message === 'undefined' &&
(typeof options?.referenceToken === 'string' || typeof options?.referenceToken === 'number')
) {
message = `Reference token '${options.referenceToken}' cannot be applied to non object/array value)`;
}

super(message, options);
}
}

Expand Down
50 changes: 40 additions & 10 deletions src/evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,23 @@ const evaluate = (
const { result, computed: referenceTokens } = parse(jsonPointer, parseOptions);

if (!result.success) {
throw new JSONPointerEvaluateError(`Invalid JSON Pointer: ${jsonPointer}`);
throw new JSONPointerEvaluateError(`Invalid JSON Pointer: ${jsonPointer}`, {
jsonPointer,
});
}

return referenceTokens.reduce((current, referenceToken) => {
if (typeof current !== 'object' || current === null) {
throw new JSONPointerTypeError(referenceToken);
}

return referenceTokens.reduce((current, referenceToken, referenceTokenPosition) => {
if (Array.isArray(current)) {
if (testArrayDash(referenceToken)) {
if (strictArrays) {
throw new JSONPointerIndexError(
'Invalid array index: "-" always refers to a nonexistent element during evaluation',
{
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
},
);
} else {
return current[current.length];
Expand All @@ -37,21 +41,47 @@ const evaluate = (
if (!testArrayIndex(referenceToken) && strictArrays) {
throw new JSONPointerIndexError(
`Invalid array index: '${referenceToken}' (MUST be "0", or digits without a leading "0")`,
{
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
},
);
}

const index = Number(referenceToken);
if (index >= current.length && strictArrays) {
throw new JSONPointerIndexError(`Invalid array index: '${index}' out of bounds`);
throw new JSONPointerIndexError(`Invalid array index: '${index}' out of bounds`, {
jsonPointer,
referenceTokens,
referenceToken: index,
referenceTokenPosition,
});
}
return current[index];
}

if (!Object.prototype.hasOwnProperty.call(current, referenceToken) && strictObjects) {
throw new JSONPointerKeyError(referenceToken);
if (typeof current === 'object' && current !== null) {
if (!Object.prototype.hasOwnProperty.call(current, referenceToken) && strictObjects) {
throw new JSONPointerKeyError(undefined, {
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
});
}

return current[referenceToken];
}

return current[referenceToken];
throw new JSONPointerTypeError(undefined, {
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
currentValue: current,
});
}, value);
};

Expand Down
7 changes: 5 additions & 2 deletions src/parse/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const grammar = new Grammar();

const parse = (jsonPointer, { evaluator = referenceTokenListEvaluator } = {}) => {
if (typeof jsonPointer !== 'string') {
throw new JSONPointerParseError('JSON Pointer must be a string');
throw new TypeError('JSON Pointer must be a string');
}

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

return { result, ast, computed };
} catch (error) {
throw new JSONPointerParseError('Unknown error during JSON Pointer parsing', { cause: error });
throw new JSONPointerParseError('Unexpected error during JSON Pointer parsing', {
cause: error,
jsonPointer,
});
}
};

Expand Down
4 changes: 4 additions & 0 deletions src/unescape.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
const unescape = (referenceToken) => {
if (typeof referenceToken !== 'string') {
throw new TypeError('Reference token must be a string');
}

return referenceToken.replace(/~1/g, '/').replace(/~0/g, '~');
};

Expand Down
2 changes: 1 addition & 1 deletion test/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ describe('compile', function () {
});

it('should throw error on invalid input', function () {
assert.throws(() => compile(null), JSONPointerCompileError);
assert.throws(() => compile(null), TypeError);
});
});
10 changes: 5 additions & 5 deletions test/parse.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert } from 'chai';

import { parse, JSONPointerParseError } from '../src/index.js';
import { parse } from '../src/index.js';

describe('parse', function () {
context('given valid source string', function () {
Expand Down Expand Up @@ -229,10 +229,10 @@ describe('parse', function () {

context('given non-string input', function () {
specify('should throw error', function () {
assert.throws(() => parse([]), JSONPointerParseError);
assert.throws(() => parse(1), JSONPointerParseError);
assert.throws(() => parse(null), JSONPointerParseError);
assert.throws(() => parse(undefined), JSONPointerParseError);
assert.throws(() => parse([]), TypeError);
assert.throws(() => parse(1), TypeError);
assert.throws(() => parse(null), TypeError);
assert.throws(() => parse(undefined), TypeError);
});
});
});