All files / json-crdt-patch/codec/compact decode.ts

100% Statements 87/87
100% Branches 25/25
100% Functions 3/3
100% Lines 79/79

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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 13813x 13x 13x                 13x 13x     13x 68943x     13x 13375x                 13x 8583x 8583x 8583x 8583x 8583x 8583x 8583x   8583x 48848x 48848x   3677x 3677x 3677x     1370x 1370x     5819x 5819x     4x 4x     4887x 4887x     1394x 1394x     3868x 3868x     2864x 2864x     6483x 6483x 6483x 6483x 6483x 17833x 17833x   6483x 6483x     2x 2x 2x 2x 2x 2x 2x   2x 2x     7135x 7135x     2926x 2926x     2914x 2914x 2914x 2914x 2914x 8553x 2914x 2914x     4389x 4389x 4389x 4389x 13375x 4389x 4389x     1116x 1116x         8583x 8583x   8583x    
import {JsonCrdtPatchOpcode} from '../../constants';
import {fromBase64} from '@jsonjoy.com/base64/lib/fromBase64';
import {
  type ITimespanStruct,
  type ITimestampStruct,
  ClockVector,
  ServerClockVector,
  Timespan,
  Timestamp,
} from '../../clock';
import type {Patch} from '../../Patch';
import {PatchBuilder} from '../../PatchBuilder';
import {SESSION} from '../../constants';
import type * as types from './types';
 
const timestamp = (sid: number, x: types.CompactCodecTimestamp): ITimestampStruct => {
  return Array.isArray(x) ? new Timestamp(x[0], x[1]) : new Timestamp(sid, x);
};
 
const timespan = (sid: number, span: types.CompactCodecTimespan): ITimespanStruct => {
  return span.length === 3 ? new Timespan(span[0], span[1], span[2]) : new Timespan(sid, span[0], span[1]);
};
 
/**
 * Decodes a JSON CRDT Patch from a "compact" POJO into a {@link Patch} instance.
 *
 * @param data A JavaScript POJO array in the compact codec format.
 * @returns A decoded patch.
 */
export const decode = (data: types.CompactCodecPatch): Patch => {
  const header = data[0];
  const x = header[0];
  const clock = Array.isArray(x) ? new ClockVector(x[0], x[1]) : new ServerClockVector(SESSION.SERVER, x as number);
  const sid = clock.sid;
  const time = clock.time;
  const builder = new PatchBuilder(clock);
  const length = data.length;
 
  for (let i = 1; i < length; i++) {
    const op = data[i] as types.CompactCodecOperation;
    switch (op[0]) {
      case JsonCrdtPatchOpcode.new_con: {
        const [, value, isTimestamp] = op;
        builder.const(isTimestamp ? timestamp(sid, value as types.CompactCodecTimestamp) : value);
        break;
      }
      case JsonCrdtPatchOpcode.new_val: {
        builder.val();
        break;
      }
      case JsonCrdtPatchOpcode.new_obj: {
        builder.obj();
        break;
      }
      case JsonCrdtPatchOpcode.new_vec: {
        builder.vec();
        break;
      }
      case JsonCrdtPatchOpcode.new_str: {
        builder.str();
        break;
      }
      case JsonCrdtPatchOpcode.new_bin: {
        builder.bin();
        break;
      }
      case JsonCrdtPatchOpcode.new_arr: {
        builder.arr();
        break;
      }
      case JsonCrdtPatchOpcode.ins_val: {
        builder.setVal(timestamp(sid, op[1]), timestamp(sid, op[2]));
        break;
      }
      case JsonCrdtPatchOpcode.ins_obj: {
        const obj = timestamp(sid, op[1]);
        const tuples: [key: string, value: ITimestampStruct][] = [];
        const value = op[2];
        const length = value.length;
        for (let j = 0; j < length; j++) {
          const [key, x] = value[j];
          tuples.push([key, timestamp(sid, x)]);
        }
        builder.insObj(obj, tuples);
        break;
      }
      case JsonCrdtPatchOpcode.ins_vec: {
        const obj = timestamp(sid, op[1]);
        const tuples: [key: number, value: ITimestampStruct][] = [];
        const value = op[2];
        const length = value.length;
        for (let j = 0; j < length; j++) {
          const [key, x] = value[j];
          tuples.push([key, timestamp(sid, x)]);
        }
        builder.insVec(obj, tuples);
        break;
      }
      case JsonCrdtPatchOpcode.ins_str: {
        builder.insStr(timestamp(sid, op[1]), timestamp(sid, op[2]), op[3]);
        break;
      }
      case JsonCrdtPatchOpcode.ins_bin: {
        builder.insBin(timestamp(sid, op[1]), timestamp(sid, op[2]), fromBase64(op[3]));
        break;
      }
      case JsonCrdtPatchOpcode.ins_arr: {
        const obj = timestamp(sid, op[1]);
        const ref = timestamp(sid, op[2]);
        const value = op[3];
        const elements: ITimestampStruct[] = [];
        const length = value.length;
        for (let j = 0; j < length; j++) elements.push(timestamp(sid, value[j]));
        builder.insArr(obj, ref, elements);
        break;
      }
      case JsonCrdtPatchOpcode.del: {
        const obj = timestamp(sid, op[1]);
        const spans = op[2];
        const what: ITimespanStruct[] = [];
        const length = spans.length;
        for (let i = 0; i < length; i++) what.push(timespan(sid, spans[i]));
        builder.del(obj, what);
        break;
      }
      case JsonCrdtPatchOpcode.nop: {
        builder.nop(op[1] || 1);
        break;
      }
    }
  }
 
  const patch = builder.patch;
  patch.meta = header[1];
 
  return patch;
};