@n1ru4l/json-patch-plus
Version:
This is a slimmed version of [jsondiffpatch](https://github.com/benjamine/jsondiffpatch). All the code is taken from the [jsondiffpatch](https://github.com/benjamine/jsondiffpatch) repository, slimmed down, slightly altered and converted to TypeScript.
222 lines (221 loc) • 6.69 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.patch = void 0;
function patch(params) {
const context = {
left: params.left,
delta: params.delta,
children: undefined,
name: undefined,
nested: false,
stopped: false,
};
function process(context) {
var _a;
const steps = [
nested_collectChildrenPatchFilter,
array_collectChildrenPatchFilter,
trivial_patchFilter,
nested_patchFilter,
array_patchFilter,
];
for (const step of steps) {
step(context);
if (context.stopped) {
context.stopped = false;
break;
}
}
if (context.children) {
for (const childrenContext of context.children) {
process(childrenContext);
context.result = (_a = context.result) !== null && _a !== void 0 ? _a : context.left;
if ("result" in childrenContext === false) {
delete context.result[childrenContext.name];
}
else {
context.result[childrenContext.name] =
childrenContext.result;
}
}
}
}
process(context);
return context.result;
}
exports.patch = patch;
function nested_collectChildrenPatchFilter(context) {
if (!context || !context.children) {
return;
}
if (context.delta._t) {
return;
}
for (let child of context.children) {
if (Object.prototype.hasOwnProperty.call(context.left, child.name) &&
child.result === undefined) {
delete context.left[child.name];
}
else if (context.left[child.name] !== child.result) {
context.left[child.name] = child.result;
}
}
context.result = context.left;
context.stopped = true;
}
function array_collectChildrenPatchFilter(context) {
if (!context || !context.children) {
return;
}
if (context.delta._t !== "a") {
return;
}
let length = context.children.length;
let child;
for (let index = 0; index < length; index++) {
child = context.children[index];
context.left[child.name] = child.result;
}
context.result = context.left;
context.stopped = true;
}
function trivial_patchFilter(context) {
if (typeof context.delta === "undefined") {
context.result = context.left;
return;
}
context.nested = !Array.isArray(context.delta);
if (context.nested) {
return;
}
if (context.delta.length === 1) {
context.result = context.delta[0];
context.stopped = true;
return;
}
if (context.delta.length === 2) {
context.result = context.delta[1];
context.stopped = true;
return;
}
if (context.delta.length === 3 && context.delta[2] === 0) {
context.stopped = true;
}
}
function nested_patchFilter(context) {
if (!context.nested) {
return;
}
if (context.delta._t) {
return;
}
for (let name in context.delta) {
if (context.children === undefined) {
context.children = [];
}
context.children.push({
left: context.left[name],
delta: context.delta[name],
name,
stopped: false,
});
}
context.stopped = true;
}
const ARRAY_MOVE = 3;
let compare = {
numerically(a, b) {
return a - b;
},
numericallyBy(name) {
return (a, b) => a[name] - b[name];
},
};
function array_patchFilter(context) {
if (!context.nested) {
return;
}
if (context.delta._t !== "a") {
return;
}
let index;
let index1;
let delta = context.delta;
let array = context.left;
// first, separate removals, insertions and modifications
let toRemove = [];
let toInsert = [];
let toModify = [];
for (index in delta) {
if (index !== "_t") {
if (index[0] === "_") {
// removed item from original array
if (delta[index][2] === 0 || delta[index][2] === ARRAY_MOVE) {
toRemove.push(parseInt(index.slice(1), 10));
}
else {
throw new Error(`only removal or move can be applied at original array indices,` +
` invalid diff type: ${delta[index][2]}`);
}
}
else {
if (delta[index].length === 1) {
// added item at new array
toInsert.push({
index: parseInt(index, 10),
value: delta[index][0],
});
}
else {
// modified item at new array
toModify.push({
index: parseInt(index, 10),
delta: delta[index],
});
}
}
}
}
// remove items, in reverse order to avoid sawing our own floor
toRemove = toRemove.sort(compare.numerically);
for (index = toRemove.length - 1; index >= 0; index--) {
index1 = toRemove[index];
let indexDiff = delta[`_${index1}`];
let removedValue = array.splice(index1, 1)[0];
if (indexDiff[2] === ARRAY_MOVE) {
// reinsert later
toInsert.push({
index: indexDiff[1],
value: removedValue,
});
}
}
// insert items, in reverse order to avoid moving our own floor
toInsert = toInsert.sort(compare.numericallyBy("index"));
let toInsertLength = toInsert.length;
for (index = 0; index < toInsertLength; index++) {
let insertion = toInsert[index];
array.splice(insertion.index, 0, insertion.value);
}
// apply modifications
let toModifyLength = toModify.length;
if (toModifyLength > 0) {
for (index = 0; index < toModifyLength; index++) {
let modification = toModify[index];
if (context.children === undefined) {
context.children = [];
}
context.children.push({
left: context.left[modification.index],
delta: modification.delta,
name: modification.index,
stopped: false,
});
}
}
if (!context.children) {
context.result = context.left;
context.stopped = true;
return;
}
}
;