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

100% Statements 51/51
86.2% Branches 25/29
100% Functions 5/5
100% Lines 50/50

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 10513x 13x 13x   13x     13x 45507x               13x 7768x 7768x 7768x   7768x 32818x   2548x 3x   2545x   2548x     739x 739x     3877x 3877x     2x 2x     3239x 3239x     749x 749x     2422x 2422x     1469x 1469x     4423x   11134x   4423x     1x   1x   1x     5769x 5769x     2189x 2189x     1716x 1716x     3133x   11675x   3133x     542x 542x         7768x 7768x   7768x    
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.const(decodeId(op.value as types.JsonCodecTimestamp));
        } else {
          builder.const(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 '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;
};