json-joy
Version:
Collection of libraries for building collaborative editing apps.
116 lines (115 loc) • 3.89 kB
JavaScript
import * as operations from '../../operations';
import { SESSION } from '../../constants';
import { toBase64 } from '@jsonjoy.com/base64/lib/toBase64';
import { Timestamp } from '../../clock';
const encodeTimestamp = (ts) => 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) => {
const id = patch.getId();
if (!id)
throw new Error('PATCH_EMPTY');
const ops = [];
const res = {
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 = {
op: 'del',
obj: encodeTimestamp(op.obj),
what: op.what.map((span) => [span.sid, span.time, span.span]),
};
ops.push(encoded);
}
else if (op instanceof operations.NopOp) {
const encoded = {
op: 'nop',
};
const length = op.len;
if (length > 1)
encoded.len = length;
ops.push(encoded);
}
}
return res;
};