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

98.27% Statements 57/58
97.22% Branches 35/36
100% Functions 5/5
100% Lines 54/54

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 10715x   15x 15x 15x     15x 46686x               15x 7795x 7795x   7795x 7795x         7795x   7795x 33438x 2824x 2824x 3x   2821x   30614x 3992x 26622x 2x 26620x 2425x 24195x 3247x 20948x 702x 20246x 741x 19505x 4624x     11647x   14881x 1x     1x   14880x 1567x         13313x 5709x           7604x 2206x           5398x 1819x           3579x 2980x     11183x   2980x 599x 599x     599x 599x 599x       7795x    
import * as operations from '../../operations';
import type {Patch} from '../../Patch';
import {SESSION} from '../../constants';
import {toBase64} from '@jsonjoy.com/base64/lib/toBase64';
import {type ITimestampStruct, Timestamp} from '../../clock';
import type * as types from './types';
 
const encodeTimestamp = (ts: ITimestampStruct): types.JsonCodecTimestamp =>
  ts.sid === SESSION.SERVER ? ts.time : [ts.sid, ts.time];
 
/**
 * Encodes a patch into a JSON CRDT Patch "verbose" format.
 *
 * @param patch The {@link Patch} to encode.
 * @returns A JavaScript POJO object in JSON CRDT Patch "verbose" format.
 */
export const encode = (patch: Patch): types.JsonCodecPatch => {
  const id = patch.getId();
  Iif (!id) throw new Error('PATCH_EMPTY');
 
  const ops: types.JsonCodecPatch['ops'] = [];
  const res: types.JsonCodecPatch = {
    id: [id.sid, id.time],
    ops,
  };
 
  if (patch.meta !== undefined) res.meta = patch.meta;
 
  for (const op of patch.ops) {
    if (op instanceof operations.NewConOp) {
      const val = op.val;
      if (val instanceof Timestamp) {
        ops.push({op: 'new_con', timestamp: true, value: encodeTimestamp(val)});
      } else {
        ops.push({op: 'new_con', value: val});
      }
    } else if (op instanceof operations.NewObjOp) {
      ops.push({op: 'new_obj'});
    } else if (op instanceof operations.NewVecOp) {
      ops.push({op: 'new_vec'});
    } else if (op instanceof operations.NewArrOp) {
      ops.push({op: 'new_arr'});
    } else if (op instanceof operations.NewStrOp) {
      ops.push({op: 'new_str'});
    } else if (op instanceof operations.NewBinOp) {
      ops.push({op: 'new_bin'});
    } else if (op instanceof operations.NewValOp) {
      ops.push({op: 'new_val'});
    } else if (op instanceof operations.InsObjOp) {
      ops.push({
        op: 'ins_obj',
        obj: encodeTimestamp(op.obj),
        value: op.data.map(([key, value]) => [key, encodeTimestamp(value)]),
      });
    } else if (op instanceof operations.InsVecOp) {
      ops.push({
        op: 'ins_vec',
        obj: encodeTimestamp(op.obj),
        value: op.data.map(([key, value]) => [key, encodeTimestamp(value)]),
      });
    } else if (op instanceof operations.InsValOp) {
      ops.push({
        op: 'ins_val',
        obj: encodeTimestamp(op.obj),
        value: encodeTimestamp(op.val),
      });
    } else if (op instanceof operations.InsStrOp) {
      ops.push({
        op: 'ins_str',
        obj: encodeTimestamp(op.obj),
        after: encodeTimestamp(op.ref),
        value: op.data,
      });
    } else if (op instanceof operations.InsBinOp) {
      ops.push({
        op: 'ins_bin',
        obj: encodeTimestamp(op.obj),
        after: encodeTimestamp(op.ref),
        value: toBase64(op.data),
      });
    } else if (op instanceof operations.InsArrOp) {
      ops.push({
        op: 'ins_arr',
        obj: encodeTimestamp(op.obj),
        after: encodeTimestamp(op.ref),
        values: op.data.map(encodeTimestamp),
      });
    } else if (op instanceof operations.DelOp) {
      const encoded: types.JsonCodecDelOperation = {
        op: 'del',
        obj: encodeTimestamp(op.obj),
        what: op.what.map((span) => [span.sid, span.time, span.span] as types.JsonCodecTimespan),
      };
      ops.push(encoded);
    } else if (op instanceof operations.NopOp) {
      const encoded: types.JsonCodecNopOperation = {
        op: 'nop',
      };
      const length = op.len;
      if (length > 1) encoded.len = length;
      ops.push(encoded);
    }
  }
 
  return res;
};