import json1 from 'ot-json1' const integerRegEx = /^\d+$/ function isInteger (v) { return Boolean(v.match(integerRegEx)) } export function patchAtomToOpAtom (patchOp) { const { path, op, value } = patchOp const pathArray = path.split('/').slice(1).map(pathItem => { // Need to convert array indices into integers. Assumes no objects have integer string keys. return isInteger(pathItem) ? parseInt(pathItem, 10) : pathItem }) if (op === 'add') { return json1.insertOp(pathArray, value) } if (op === 'remove') { return json1.removeOp(pathArray) } if (op === 'replace') { return json1.replaceOp(pathArray, true, value) } throw new Error(`No handler for op '${op}'.`) } export function opAtomToPatchAtom (op) { const pathArray = op.slice(0, -1) const path = ['', ...pathArray].join('/') const opOp = op[op.length - 1] const { i, r } = opOp if (r !== undefined) { if (i !== undefined) { return { op: 'replace', value: i, path } } return { op: 'remove', path } } else if (i !== undefined) { return { op: 'add', path, value: i } } throw new Error(`No handler for op '${op}'.`) } export function patchToOp (patch = []) { const op = patch.map(patchAtomToOpAtom).reduce(json1.type.compose, null) return op } export function opToPatch (op = []) { if (!op || !op.length) { return [] } const atoms = [] const queue = [op] while (queue.length) { const curOp = queue.shift() if (_opIsAtom(curOp)) { atoms.push(curOp) continue } const path = [] for (const opPart of curOp) { if (_isStrOrNum(opPart)) { path.push(opPart) continue } if (Array.isArray(opPart)) { queue.push([...path, ...opPart]) continue } queue.push([...path, opPart]) if (opPart.r && opPart.i === undefined) { break } } } const patch = atoms.map(atom => opAtomToPatchAtom(atom)) return patch } function _opIsAtom (op = []) { const last = op[op.length - 1] const lastIsObj = (typeof last === 'object') && (!Array.isArray(last)) const hasIntermediateObjs = op.slice(0, -1).some(item => typeof item === 'object') const isAtom = lastIsObj && !hasIntermediateObjs return isAtom } function _isStrOrNum (v) { return (typeof v === 'number') || (typeof v === 'string') }