UNPKG

@wizard9/json-patch-apply

Version:

A library for describing, calculating, and applying patches to Javascript Objects.

166 lines (163 loc) 5.66 kB
"use strict"; /** 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/>. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.PatchDiff = void 0; const common_1 = require("./common"); class PatchDiff { diff(source, target) { return this.diffAny(source, target, ""); } createOperation(op, path, optional) { let operation = { op: op, path: path }; Object.keys(optional).forEach(key => { if (common_1.hasKey(optional, key) && optional[key] != undefined) operation[key] = optional[key]; }); return operation; } diffAny(source, target, path) { let diff = []; if (source == undefined) { diff.push(...this.add(target, path)); } else if (target instanceof Object) { diff.push(...this.diffObject(source, target, path)); } else { diff.push(...this.diffPrimitive(source, target, path)); } return diff; } diffObject(source, target, path) { let diff = []; if (source instanceof Object) { let keys = this.getObjectKeys(source, target); // handles properties which are only in the source but not in the target keys.source.forEach(key => diff.push(...this.remove(source[key], `${path}/${String(key)}`))); // handle the keys which are in target only but not in source keys.target.forEach(key => diff.push(...this.add(common_1.getValue(target, key), `${path}/${String(key)}`))); // handle keys which are both in source and target keys.both.forEach(key => diff.push(...this.diffAny(source[key], common_1.getValue(target, key), `${path}/${String(key)}`))); } else { diff.push(...this.replace(target, path, source)); } return diff; } getObjectKeys(source, target) { let sk = {}; let keys = { source: [], target: [], both: [] }; Object.keys(source).forEach(key => sk[key] = 1); Object.keys(target).forEach(key => { if (sk[key]) { delete sk[key]; keys.both.push(key); } else { keys.target.push(key); } }); keys.source.push(...Object.keys(sk)); return keys; } diffPrimitive(source, target, path) { let diff = []; if (source !== target) { if (typeof source !== "undefined" && typeof target == "undefined") { diff.push(...this.remove(source, path)); } else { diff.push(...this.replace(target, path, source)); } } return diff; } /** * Recursively scans source creating micro patches to add each and every branch and leaf in the * tree of data under the given object. This is needed because we cannot merge adds of large * branches if they are an all or nothing operation. * * @param target * @param path * @param last */ add(target, path) { let diff = []; if (target instanceof Object) { diff.push(this.createOperation("add", path, { type: common_1.getValueType(target) })); Object.keys(target).forEach(key => diff.push(...this.add(target[key], `${path}/${key}`))); } else { diff.push({ op: "add", path: path || "", value: target }); } return diff; } /** * Creates a remove for all values removed for auditing purposes * @param source * @param path */ remove(source, path) { let diff = []; let optional = {}; if (source instanceof Object) { let keys = Object.keys(source); if (source instanceof Array) { keys = keys.sort((a, b) => b - a); } keys.forEach(key => diff.push(...this.remove(source[key], `${path}/${key}`))); if (source instanceof Array) { optional.type = common_1.ValueType.array; } else { optional.type = common_1.ValueType.object; } } else { optional.old = source; } diff.push(this.createOperation("remove", path, optional)); return diff; } replace(target, path, old) { let diff = []; diff.push(...this.remove(old, path)); let rem = common_1.firstElement(diff); let add = this.add(target, path); let first = common_1.firstElement(add); diff.push(this.createOperation("replace", path, { type: first.type, value: first.value, old: rem.old })); diff.push(...add); return diff; } } exports.PatchDiff = PatchDiff; //# sourceMappingURL=diff.js.map