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

98.78% Statements 81/82
97.56% Branches 40/41
100% Functions 4/4
100% Lines 75/75

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 12414x 14x   14x 14x     14x 83311x 83311x     14x 13324x 13324x 10116x 10116x   3208x                 14x 8823x 8823x   8823x 8823x 8823x 8823x 8823x 8823x   8823x 49637x 3866x 3866x 3x 3863x 264x   3599x   45771x 1427x 44344x 5973x 38371x 4x 38367x 4982x 33385x 1352x 32033x 3975x 28058x 3139x 24919x 6369x 17883x 6369x         6369x 18550x 2x 2x 2x         2x 18548x 7445x           7445x 11103x 2827x           2827x 8276x 2958x 8824x 2958x           2958x 5318x 4166x     13324x   4166x 1152x 1152x 1152x 1152x 1152x       8823x    
import * as operations from '../../operations';
import {type ITimespanStruct, type ITimestampStruct, Timestamp} from '../../clock';
import type {Patch} from '../../Patch';
import {JsonCrdtPatchOpcode, SESSION} from '../../constants';
import {toBase64} from '@jsonjoy.com/base64/lib/toBase64';
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.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;
};