Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | 8x 8x 8x 8x 8x 8x 8x 8x 632x 632x 632x 187x 187x 193x 193x 193x 622x 622x 622x 272x 272x 102x 102x 102x 170x 130x 130x 130x 40x 40x 622x 622x 622x 615x 615x 40x 40x 575x 575x 575x 107x 107x 107x 107x 107x 105x 81x 468x 466x 466x 2x 618x 7x 7x 4x 4x 4x 3x 3x 3x 7x 7x 618x | import {find, isArrayReference, isObjectReference} from '@jsonjoy.com/json-pointer'; import type {JsonOp} from './types'; import {evaluate as evalExpression} from '@jsonjoy.com/json-expression/lib/evaluate'; import {comparePath} from './util'; import {EDIT_TYPE} from './constants'; import {apply as applyStr} from '../ot-string-irreversible/apply'; import {apply as applyBin} from '../ot-binary-irreversible/apply'; import {Vars} from '@jsonjoy.com/json-expression/lib/Vars'; export const apply = (doc: unknown, op: JsonOp): unknown => { const [test, pick = [], data = [], drop = [], edit = []] = op; const testLength = test.length; if (testLength) { const expressionContext = {vars: new Vars(doc)}; for (let i = 0; i < testLength; i++) { const testExpr = test[i]; const testValue = evalExpression(testExpr, expressionContext); if (!testValue) throw new Error('TEST'); } } const registers = new Map<number, unknown>(); const picksSorted = pick.sort((a, b) => comparePath(a[1], b[1])); for (const [regId, path] of picksSorted) { const ref = find(doc, path); if (isArrayReference(ref)) { const {obj, key, val} = ref; obj.splice(key, 1); registers.set(regId, val); } else if (isObjectReference(ref)) { const {obj, key, val} = ref; delete obj[key]; registers.set(regId, val); } else { doc = undefined; registers.set(regId, ref.val); } } for (const [regId, value] of data) registers.set(regId, value); const dropsSorted = drop.sort((a, b) => comparePath(b[1], a[1])); for (const [regId, where] of dropsSorted) { const value = registers.get(regId); if (!where.length) { doc = value; continue; } const path = where.slice(0, -1); const {val} = find(doc, path); if (val instanceof Array) { let index = where[where.length - 1]; if (index === '-') index = val.length; const index2 = ~~index; if (typeof index === 'string') Iif ('' + index2 !== index) throw new Error('INVALID_INDEX'); if (index2 > val.length || index2 < -1) throw new Error('INVALID_INDEX'); if (index2 === -1 || index2 === val.length) val.push(value); else val.splice(index2, 0, value); } else if (val && typeof val === 'object') { const key = where[where.length - 1]; (val as any)[key] = value; } else { throw new Error('NOT_FOUND'); } } for (const [type, path, operation] of edit) { const {val, obj, key} = find(doc, path); let newVal: unknown; switch (type) { case EDIT_TYPE.OT_STRING: { Iif (typeof val !== 'string') throw new Error('NOT_STR'); newVal = applyStr(val, operation); break; } case EDIT_TYPE.OT_BINARY: { Iif (!(val instanceof Uint8Array)) throw new Error('NOT_BIN'); newVal = applyBin(val, operation); break; } } Iif (!obj) doc = newVal; else (obj as any)[key as any] = newVal; } return doc; }; |