Skip to content
Open
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
8 changes: 8 additions & 0 deletions lib/assign.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ describe("JsonPointer.assign", () => {
expect(subject[0]).to.equal("foo");
});
});

When("indexing into the array with an invalid index", () => {
const assign = JsonPointer.assign("/01");

Then("an error should be thrown", () => {
expect(() => assign(subject, "foo")).to.throw(TypeError, "Value at '' is an array and '01' is not a valid array index");
});
});
});

Given("a number", () => {
Expand Down
1 change: 1 addition & 0 deletions lib/get.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ describe("JsonPointer", () => {
["/bbb/-", { ccc: 13 }],
["/bar", undefined],
["/foo/2", undefined],
["/foo/01", undefined],
["/foo/-", undefined],
["/0", undefined]
];
Expand Down
48 changes: 31 additions & 17 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const _set = (segments, index, subject, value) => {

// currentSubject could also be an array, but this appeases the type system
const currentSubject = /** @type API.JsonObject */ (subject);
const computedSegment = computeSegment(subject, segment);
const computedSegment = computeSegment(subject, segment, cursorAt(segments, index));
currentSubject[computedSegment] = _set(segments, index + 1, currentSubject[computedSegment], value);
return currentSubject;
};
Expand Down Expand Up @@ -210,7 +210,7 @@ const _assign = (segments, subject, value, cursor = nil) => {
let lastSubject;

for (let segment of segments) {
segment = computeSegment(currentSubject, segment);
segment = computeSegment(currentSubject, segment, cursor);
lastSegment = segment;
lastSubject = currentSubject;
currentSubject = applySegment(currentSubject, segment, cursor);
Expand Down Expand Up @@ -274,13 +274,12 @@ const _unset = (segments, index, subject) => {

// currentSubject could also be an array, but this appeases the type system
const currentSubject = /** @type API.JsonObject */ (subject);
const computedSegment = computeSegment(currentSubject, segment);
const unsetSubject = _unset(segments, index + 1, currentSubject[computedSegment]);
if (computedSegment in currentSubject) {
const unsetSubject = _unset(segments, index + 1, getSegment(currentSubject, segment));
if (hasSegment(currentSubject, segment)) {
if (unsetSubject === undefined) {
delete currentSubject[computedSegment];
delete currentSubject[segment];
} else {
currentSubject[computedSegment] = unsetSubject;
currentSubject[segment] = unsetSubject;
}
}
return currentSubject;
Expand Down Expand Up @@ -324,8 +323,7 @@ const _remove = (segments, subject, cursor = nil) => {
/** @type API.Json | undefined */
let lastSubject;

for (let segment of segments) {
segment = computeSegment(currentSubject, segment);
for (const segment of segments) {
lastSegment = segment;
lastSubject = currentSubject;
currentSubject = applySegment(currentSubject, segment, cursor);
Expand All @@ -337,7 +335,10 @@ const _remove = (segments, subject, cursor = nil) => {
}

// lastSubject could also be an array, but this appeases the type system
delete /** @type API.JsonObject */ (lastSubject)[lastSegment];
const container = /** @type API.JsonObject */ (lastSubject);
if (hasSegment(container, lastSegment)) {
delete container[lastSegment];
}
};

/** @type API.append */
Expand All @@ -364,23 +365,34 @@ const unescape = (segment) => {
* @overload
* @param {API.Json[]} value
* @param {string} segment
* @param {string} cursor
* @returns {number}
*
* @overload
* @param {API.Json | undefined} value
* @param {string} segment
* @param {string} cursor
* @returns {string}
*
* @param {API.Json | undefined} value
* @param {string} segment
* @param {string} cursor
* @returns {string | number}
*/
const computeSegment = (value, segment) => {
if (Array.isArray(value)) {
return segment === "-" ? value.length : parseInt(segment, 10);
} else {
const computeSegment = (value, segment, cursor) => {
if (!Array.isArray(value)) {
return segment;
}

if (segment === "-") {
return value.length;
}

if (!/^(?:0|[1-9][0-9]*)$/.test(segment)) {
throw TypeError(`Value at '${cursor}' is an array and '${segment}' is not a valid array index`);
}

return Number(segment);
};

/** @type (value: API.Json | undefined, segment: string, cursor?: string) => API.Json | undefined */
Expand All @@ -394,13 +406,15 @@ const applySegment = (value, segment, cursor = "") => {

/** @type (value: API.JsonObject | API.Json[], segment: string) => API.Json | undefined */
const getSegment = (value, segment) => {
const computedSegment = computeSegment(value, segment);
if (Object.hasOwn(value, computedSegment)) {
if (hasSegment(value, segment)) {
// value could also be an array, but this appeases the type system
return /** @type API.JsonObject */ (value)[computedSegment];
return /** @type API.JsonObject */ (value)[segment];
}
};

/** @type (value: API.JsonObject | API.Json[], segment: string) => boolean */
const hasSegment = (value, segment) => Object.prototype.propertyIsEnumerable.call(value, segment);

/** @type (value: API.Json | undefined) => value is API.JsonObject | API.Json[] */
const isContainer = (value) => typeof value === "object" && value !== null;

Expand Down
8 changes: 8 additions & 0 deletions lib/set.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ describe("JsonPointer.set", () => {
expect(subject.length).to.equal(0);
});
});

When("indexing into the array with an invalid index", () => {
const set = JsonPointer.set("/01");

Then("an error should be thrown", () => {
expect(() => set(subject, "foo")).to.throw(TypeError, "Value at '' is an array and '01' is not a valid array index");
});
});
});

Given("a number", () => {
Expand Down