UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

159 lines (158 loc) 6.38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.applyOperation = applyOperation; exports.applyPatch = applyPatch; const clone_1 = require("@jsonjoy.com/util/lib/json-clone/clone"); const hasOwnProperty_1 = require("@jsonjoy.com/util/lib/hasOwnProperty"); const json_pointer_1 = require("@jsonjoy.com/json-pointer"); const deepEqual_1 = require("@jsonjoy.com/util/lib/json-equal/deepEqual"); const { isArray } = Array; function applyOperation(doc, operation) { const path = operation.path; const isRoot = !path; if (isRoot) { switch (operation.op) { case 'add': case 'replace': doc = operation.value; return { doc: operation.value, old: doc }; case 'remove': return { doc: null, old: doc }; case 'move': { const { val } = (0, json_pointer_1.findByPointer)(operation.from, doc); return { doc: val, old: doc }; } case 'copy': { const { val } = (0, json_pointer_1.findByPointer)(operation.from, doc); return { doc: val, old: doc }; } case 'test': { if (!(0, deepEqual_1.deepEqual)(operation.value, doc)) throw new Error('TEST'); return { doc }; } } return { doc }; } let indexOfSlash = 0; let indexAfterSlash = 1; let obj = doc; let key = ''; while (indexOfSlash > -1) { indexOfSlash = path.indexOf('/', indexAfterSlash); key = indexOfSlash > -1 ? path.substring(indexAfterSlash, indexOfSlash) : path.substring(indexAfterSlash); indexAfterSlash = indexOfSlash + 1; if (isArray(obj)) { const length = obj.length; if (key === '-') key = length; else { const key2 = ~~key; if ('' + key2 !== key) throw new Error('INVALID_INDEX'); key = key2; if (key < 0 || key > length) throw new Error('INVALID_INDEX'); } if (indexOfSlash === -1) { switch (operation.op) { case 'add': { const old = obj[key]; if (key < obj.length) obj.splice(key, 0, operation.value); else obj.push(operation.value); return { doc, old }; } case 'replace': { const old = obj[key]; obj[key] = operation.value; return { doc, old }; } case 'remove': { const old = obj[key]; obj.splice(key, 1); return { doc, old }; } case 'move': { const removeResult = applyOperation(doc, { op: 'remove', path: operation.from }); return applyOperation(removeResult.doc, { op: 'add', path: operation.path, value: removeResult.old }); } case 'copy': { const old = obj[key]; const { val } = (0, json_pointer_1.findByPointer)(operation.from, doc); const value = (0, clone_1.clone)(val); if (key < obj.length) obj.splice(key, 0, value); else obj.push(value); return { doc, old }; } case 'test': { if (!(0, deepEqual_1.deepEqual)(operation.value, obj[key])) throw new Error('TEST'); return { doc }; } } break; } obj = obj[key]; } else if (typeof obj === 'object' && !!obj) { key = (0, json_pointer_1.unescapeComponent)(key); if (indexOfSlash === -1) { switch (operation.op) { case 'add': { const old = obj[key]; obj[key] = operation.value; return { doc, old }; } case 'replace': { const old = obj[key]; obj[key] = operation.value; return { doc, old }; } case 'remove': { const old = obj[key]; delete obj[key]; return { doc, old }; } case 'move': { const removeResult = applyOperation(doc, { op: 'remove', path: operation.from }); const addResult = applyOperation(doc, { op: 'add', path: operation.path, value: removeResult.old }); return addResult; } case 'copy': { const { val } = (0, json_pointer_1.findByPointer)(operation.from, doc); const value = (0, clone_1.clone)(val); const old = obj[key]; obj[key] = value; return { doc, old }; } case 'test': { if (!(0, deepEqual_1.deepEqual)(operation.value, obj[key])) throw new Error('TEST'); return { doc }; } } break; } obj = (0, hasOwnProperty_1.hasOwnProperty)(obj, key) ? obj[key] : undefined; } else throw new Error('NOT_FOUND'); } return { doc }; } function applyPatch(doc, patch, options) { if (!options.mutate) doc = (0, clone_1.clone)(doc); const res = []; for (let i = 0; i < patch.length; i++) { const operation = patch[i]; const opResult = applyOperation(doc, operation); res.push(opResult); doc = opResult.doc; } return { doc, res }; }