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 46969x               13x 7888x 7888x 7888x   7888x 33587x   2696x 3x   2693x   2696x     802x 802x     3948x 3948x     2x 2x     3243x 3243x     783x 783x     2450x 2450x     1573x 1573x     4510x   11375x   4510x     1x   1x   1x     5643x 5643x     2261x 2261x     1871x 1871x     3242x   12365x   3242x     562x 562x         7888x 7888x   7888x    
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;
};