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

100% Statements 52/52
86.66% Branches 26/30
100% Functions 5/5
100% Lines 51/51

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 10913x 13x     13x     13x 46787x               13x 7871x 7871x 7871x   7871x 33554x   2712x 3x   2709x   2712x     704x 704x     4034x 4034x     2x 2x     3247x 3247x     751x 751x     2449x 2449x     1512x 1512x     4662x   11623x   4662x     1x   1x   1x     5728x 5728x     2174x 2174x     1809x 1809x     9x 9x     3186x   11676x   3186x     574x 574x         7871x 7871x   7871x    
import {fromBase64} from '@jsonjoy.com/base64/lib/fromBase64';
import {ts, ClockVector, ServerClockVector, tss, type ITimestampStruct} from '../../clock';
import {SESSION} from '../../constants';
import type {Patch} from '../../Patch';
import {PatchBuilder} from '../../PatchBuilder';
import type * as types from './types';
 
const decodeId = (time: types.JsonCodecTimestamp): ITimestampStruct =>
  typeof time === 'number' ? ts(SESSION.SERVER, time) : ts(time[0], time[1]);
 
/**
 * Decodes a JSON CRDT patch from a JavaScript POJO into a {@link Patch} instance.
 *
 * @param data A JavaScript POJO representing a JSON CRDT patch in "verbose" format.
 * @returns A decoded {@link Patch} instance.
 */
export const decode = (data: types.JsonCodecPatch): Patch => {
  const {id, ops} = data;
  const clock = typeof id === 'number' ? new ServerClockVector(SESSION.SERVER, id) : new ClockVector(id[0], id[1]);
  const builder = new PatchBuilder(clock);
 
  for (const op of ops) {
    switch (op.op) {
      case 'new_con': {
        if (op.timestamp) {
          builder.con(decodeId(op.value as types.JsonCodecTimestamp));
        } else {
          builder.con(op.value);
        }
        break;
      }
      case 'new_val': {
        builder.val();
        break;
      }
      case 'new_obj': {
        builder.obj();
        break;
      }
      case 'new_vec': {
        builder.vec();
        break;
      }
      case 'new_str': {
        builder.str();
        break;
      }
      case 'new_bin': {
        builder.bin();
        break;
      }
      case 'new_arr': {
        builder.arr();
        break;
      }
      case 'ins_val': {
        builder.setVal(decodeId(op.obj), decodeId(op.value));
        break;
      }
      case 'ins_obj': {
        builder.insObj(
          decodeId(op.obj),
          (op as types.JsonCodecInsObjOperation).value.map(([key, id]) => [key, decodeId(id)]),
        );
        break;
      }
      case 'ins_vec': {
        builder.insVec(
          decodeId(op.obj),
          (op as types.JsonCodecInsVecOperation).value.map(([key, id]) => [key, decodeId(id)]),
        );
        break;
      }
      case 'ins_str': {
        builder.insStr(decodeId(op.obj), decodeId(op.after || op.obj), op.value);
        break;
      }
      case 'ins_bin': {
        builder.insBin(decodeId(op.obj), decodeId(op.after || op.obj), fromBase64(op.value));
        break;
      }
      case 'ins_arr': {
        builder.insArr(decodeId(op.obj), decodeId(op.after || op.obj), op.values.map(decodeId));
        break;
      }
      case 'upd_arr': {
        builder.updArr(decodeId(op.obj), decodeId(op.ref), decodeId(op.value));
        break;
      }
      case 'del': {
        builder.del(
          decodeId(op.obj),
          op.what.map((spans) => tss(...spans)),
        );
        break;
      }
      case 'nop': {
        builder.nop(op.len || 1);
        break;
      }
    }
  }
 
  const patch = builder.patch;
  patch.meta = data.meta;
 
  return patch;
};