@wizard9/json-patch-apply
Version:
A library for describing, calculating, and applying patches to Javascript Objects.
177 lines (174 loc) • 6.67 kB
JavaScript
;
/**
This file is part of json-patch-apply.
json-patch-apply is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
json-patch-apply is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with json-patch-apply. If not, see <https://www.gnu.org/licenses/>.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PatchApply = void 0;
const common_1 = require("./common");
const pointer_1 = require("./pointer");
const _ = __importStar(require("lodash"));
/**
* This class is capable of patching any object.
*/
class PatchApply {
/**
* Applies a set of patch operations to a given object.
* @param source
* @param operations
*/
apply(source, ...operations) {
let result = source;
operations.forEach(operation => {
result = this.applyPatchOperation(result, operation);
});
return result;
}
/**
* Applies the given patch operation to the given object.
*
* @param source
* @param patch
*/
applyPatchOperation(source, patch) {
let find = pointer_1.JsonPointer.findParent(source, patch.path);
if (find == undefined) {
throw new Error(patch.op + " op should fail with missing parent key");
}
else if (patch.path == "") {
source = this.applyPatchToSelf(source, patch);
}
else {
this.applyPatchToChild(find, patch);
}
return source;
}
applyPatchToChild(find, patch) {
if (patch.op == "remove") {
if (find.parent instanceof Array) {
find.parent.splice(parseInt(String(find.key)), 1);
}
else {
delete find.parent[find.key];
}
}
else if ("replace" == patch.op) {
find.parent[find.key] = this.applyPatchToSelf(find.parent[find.key], patch);
}
else if ("move" == patch.op || "copy" == patch.op) {
if (!patch.from) {
throw new Error("Cannot move without a from path");
}
let from = pointer_1.JsonPointer.findParent(find.source, patch.from);
if (from == undefined || !from.parent.hasOwnProperty(from.key)) {
throw new Error("from path '" + patch.from + "' does not exist");
}
else if (patch.from != patch.path) {
if (find.parent instanceof Array && find.parent == from.parent) {
let from_idx = parseInt(String(from.key));
let to_idx = parseInt(String(find.key));
let value = from.parent[from_idx];
if ("move" == patch.op) {
find.parent.splice(from_idx, 1);
}
find.parent.splice(to_idx, 0, value);
}
else {
find.parent[find.key] = from.parent[from.key];
if ("move" == patch.op) {
delete from.parent[from.key];
}
}
}
}
else if ("add" == patch.op) {
if (find.parent instanceof Array) {
let idx = parseInt(String(find.key));
if (find.key == "-") {
idx = find.parent.length;
}
else {
if (isNaN(idx)) {
throw new Error("Object operation on array target");
}
else if (idx > find.parent.length || idx < 0) {
throw new Error("array index " + idx + " out of bounds");
}
}
if (idx >= find.parent.length) {
find.parent[idx] = this.applyPatchToSelf(find.parent[idx], patch);
}
else {
find.parent.splice(idx, 0, this.getValue(patch.type, patch.value));
}
}
else {
find.parent[find.key] = this.applyPatchToSelf(find.parent[find.key], patch);
}
}
else if ("test" == patch.op) {
if (!_.isEqual(find.parent[find.key], patch.value)) {
throw new Error(JSON.stringify(find.parent[find.key]) + "\n is not equal to \n" + JSON.stringify(patch.value));
}
}
else {
this.handleConflict(`Unsupported operation "${patch.op}": ${JSON.stringify(patch)}`);
}
}
applyPatchToSelf(source, patch) {
if ("remove" == patch.op) {
source = undefined;
}
else if ("add" == patch.op || "replace" == patch.op) {
source = this.getValue(patch.type, patch.value);
}
else {
this.handleConflict(`Unhandled condition: Patch ${JSON.stringify(patch)} cannot be applied to ${JSON.stringify(source)}`);
}
return source;
}
getValue(type, value) {
if (common_1.ValueType.array == type) {
return value != undefined ? value : [];
}
else if (common_1.ValueType.object == type) {
return value != undefined ? value : {};
}
else {
return value;
}
}
handleConflict(msg) {
throw new Error(msg);
}
}
exports.PatchApply = PatchApply;
//# sourceMappingURL=apply.js.map