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

98.8% Statements 83/84
97.67% Branches 42/43
100% Functions 4/4
100% Lines 77/77

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 13214x 14x   14x       14x 82043x 82043x     14x 12321x 12321x 9265x 9265x   3056x                 14x 8576x 8576x   8576x 8576x 8576x 8576x 8576x 8576x   8576x 49288x 3798x 3798x 3x 3795x 288x   3507x   45490x 1402x 44088x 5927x 38161x 4x 38157x 4932x 33225x 1339x 31886x 3987x 27899x 2906x 24993x 6477x 17985x 6477x         6477x 18516x 2x 2x 2x         2x 18514x 7481x           7481x 11033x 2742x           2742x 8291x 2935x 8901x 2935x           2935x 5356x 14x           14x 5342x 4182x     12321x   4182x 1160x 1160x 1160x 1160x 1160x       8576x    
import * as operations from '../../operations';
import {type ITimespanStruct, type ITimestampStruct, Timestamp} from '../../clock';
import {JsonCrdtPatchOpcode, SESSION} from '../../constants';
import {toBase64} from '@jsonjoy.com/base64/lib/toBase64';
import type {Patch} from '../../Patch';
import type * as types from './types';
 
const timestamp = (sid: number, ts: ITimestampStruct): types.CompactCodecTimestamp => {
  const tsSessionId = ts.sid;
  return tsSessionId === sid ? ts.time : [tsSessionId, ts.time];
};
 
const timespan = (sid: number, span: ITimespanStruct): types.CompactCodecTimespan => {
  const ts = timestamp(sid, span);
  if (ts instanceof Array) {
    ts.push(span.span);
    return ts;
  }
  return [ts, span.span];
};
 
/**
 * Encodes a patch into a compact binary format into a JavaScript array.
 *
 * @param patch The patch to encode.
 * @returns The encoded patch as a JavaScript POJO.
 */
export const encode = (patch: Patch): types.CompactCodecPatch => {
  const id = patch.getId();
  Iif (!id) throw new Error('PATCH_EMPTY');
 
  const sid = id.sid;
  const time = id.time;
  const header: types.CompactCodecPatch[0] = sid === SESSION.SERVER ? [time] : [[sid, time]];
  const meta = patch.meta;
  if (meta !== undefined) header.push(meta);
  const res: types.CompactCodecPatch = [header];
 
  for (const op of patch.ops) {
    if (op instanceof operations.NewConOp) {
      const val = op.val;
      if (val instanceof Timestamp) {
        res.push([JsonCrdtPatchOpcode.new_con, timestamp(sid, val), true]);
      } else if (val === undefined) {
        res.push([JsonCrdtPatchOpcode.new_con]);
      } else {
        res.push([JsonCrdtPatchOpcode.new_con, val]);
      }
    } else if (op instanceof operations.NewValOp) {
      res.push([JsonCrdtPatchOpcode.new_val]);
    } else if (op instanceof operations.NewObjOp) {
      res.push([JsonCrdtPatchOpcode.new_obj]);
    } else if (op instanceof operations.NewVecOp) {
      res.push([JsonCrdtPatchOpcode.new_vec]);
    } else if (op instanceof operations.NewStrOp) {
      res.push([JsonCrdtPatchOpcode.new_str]);
    } else if (op instanceof operations.NewBinOp) {
      res.push([JsonCrdtPatchOpcode.new_bin]);
    } else if (op instanceof operations.NewArrOp) {
      res.push([JsonCrdtPatchOpcode.new_arr]);
    } else if (op instanceof operations.InsValOp) {
      res.push([JsonCrdtPatchOpcode.ins_val, timestamp(sid, op.obj), timestamp(sid, op.val)]);
    } else if (op instanceof operations.InsObjOp) {
      const tuples: types.CompactCodecInsObjOperation[2] = [];
      for (const [key, value] of op.data) tuples.push([key, timestamp(sid, value)]);
      const operation: types.CompactCodecInsObjOperation = [
        JsonCrdtPatchOpcode.ins_obj,
        timestamp(sid, op.obj),
        tuples,
      ];
      res.push(operation);
    } else if (op instanceof operations.InsVecOp) {
      const tuples: types.CompactCodecInsVecOperation[2] = [];
      for (const [key, value] of op.data) tuples.push([key, timestamp(sid, value)]);
      const operation: types.CompactCodecInsVecOperation = [
        JsonCrdtPatchOpcode.ins_vec,
        timestamp(sid, op.obj),
        tuples,
      ];
      res.push(operation);
    } else if (op instanceof operations.InsStrOp) {
      const operation: types.CompactCodecInsStrOperation = [
        JsonCrdtPatchOpcode.ins_str,
        timestamp(sid, op.obj),
        timestamp(sid, op.ref),
        op.data,
      ];
      res.push(operation);
    } else if (op instanceof operations.InsBinOp) {
      const operation: types.CompactCodecInsBinOperation = [
        JsonCrdtPatchOpcode.ins_bin,
        timestamp(sid, op.obj),
        timestamp(sid, op.ref),
        toBase64(op.data),
      ];
      res.push(operation);
    } else if (op instanceof operations.InsArrOp) {
      const elements: types.CompactCodecInsArrOperation[3] = [];
      for (const element of op.data) elements.push(timestamp(sid, element));
      const operation: types.CompactCodecInsArrOperation = [
        JsonCrdtPatchOpcode.ins_arr,
        timestamp(sid, op.obj),
        timestamp(sid, op.ref),
        elements,
      ];
      res.push(operation);
    } else if (op instanceof operations.UpdArrOp) {
      const operation: types.CompactCodecUpdArrOperation = [
        JsonCrdtPatchOpcode.upd_arr,
        timestamp(sid, op.obj),
        timestamp(sid, op.ref),
        timestamp(sid, op.val),
      ];
      res.push(operation);
    } else if (op instanceof operations.DelOp) {
      const operation: types.CompactCodecDelOperation = [
        JsonCrdtPatchOpcode.del,
        timestamp(sid, op.obj),
        op.what.map((span) => timespan(sid, span)),
      ];
      res.push(operation);
    } else if (op instanceof operations.NopOp) {
      const operation: types.CompactCodecNopOperation = [JsonCrdtPatchOpcode.nop];
      const len = op.len;
      if (len > 1) operation.push(len);
      res.push(operation);
    }
  }
 
  return res;
};