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 46684x               13x 7793x 7793x 7793x   7793x 33437x   2824x 3x   2821x   2824x     741x 741x     3992x 3992x     2x 2x     3247x 3247x     702x 702x     2425x 2425x     1567x 1567x     4624x   11647x   4624x     1x   1x   1x     5709x 5709x     2206x 2206x     1819x 1819x     2978x   11181x   2978x     600x 600x         7793x 7793x   7793x    
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;
};