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

100% Statements 90/90
100% Branches 26/26
100% Functions 3/3
100% Lines 82/82

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 138 139 140 141 142 143 144 145  13x 13x                 13x       13x 69716x     13x 12321x                 13x 8575x 8575x 8575x 8575x 8575x 8575x 8575x   8575x 49283x 49283x   3798x 3798x 3798x     1402x 1402x     5926x 5926x     4x 4x     4931x 4931x     1339x 1339x     3987x 3987x     2905x 2905x     6476x 6476x 6476x 6476x 6476x 17984x 17984x   6476x 6476x     2x 2x 2x 2x 2x 2x 2x   2x 2x     7480x 7480x     2742x 2742x     2935x 2935x 2935x 2935x 2935x 8901x 2935x 2935x     14x 14x 14x 14x 14x     4182x 4182x 4182x 4182x 12321x 4182x 4182x     1160x 1160x         8575x 8575x   8575x    
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.con(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.upd_arr: {
        const obj = timestamp(sid, op[1]);
        const ref = timestamp(sid, op[2]);
        const val = timestamp(sid, op[3]);
        builder.updArr(obj, ref, val);
        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;
};