UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

178 lines (177 loc) 4.89 kB
import * as nodes from '../../../nodes'; import { toBase64 } from '@jsonjoy.com/base64/lib/toBase64'; import { SESSION } from '../../../../json-crdt-patch/constants'; import { Timestamp } from '../../../../json-crdt-patch/clock'; export class Encoder { model; encode(model) { this.model = model; const clock = model.clock; const isServerClock = clock.sid === SESSION.SERVER; return { time: isServerClock ? clock.time : this.cClock(model.clock), root: this.cVal(model.root), }; } cClock(clock) { const data = []; const sessionId = clock.sid; const localTs = clock.peers.get(sessionId); if (!localTs) data.push([sessionId, clock.time]); for (const c of clock.peers.values()) data.push([c.sid, c.time]); return data; } cTs(ts) { return ts.sid === SESSION.SERVER ? ts.time : [ts.sid, ts.time]; } cNode(node) { if (node instanceof nodes.ObjNode) return this.cObj(node); else if (node instanceof nodes.ArrNode) return this.cArr(node); else if (node instanceof nodes.StrNode) return this.cStr(node); else if (node instanceof nodes.ValNode) return this.cVal(node); else if (node instanceof nodes.ConNode) return this.cCon(node); else if (node instanceof nodes.BinNode) return this.cBin(node); else if (node instanceof nodes.VecNode) return this.cVec(node); throw new Error('UNKNOWN_NODE'); } cObj(obj) { const map = {}; obj.nodes((node, key) => { map[key] = this.cNode(node); }); return { type: 'obj', id: this.cTs(obj.id), map, }; } cVec(obj) { const map = []; const elements = obj.elements; const length = elements.length; const index = this.model.index; for (let i = 0; i < length; i++) { const element = elements[i]; if (element === undefined) map.push(null); else map.push(this.cNode(index.get(element))); } return { type: 'vec', id: this.cTs(obj.id), map, }; } cArr(obj) { const chunks = []; const iterator = obj.iterator(); let chunk; while ((chunk = iterator())) chunks.push(this.cArrChunk(chunk)); return { type: 'arr', id: this.cTs(obj.id), chunks, }; } cArrChunk(chunk) { if (chunk.del) { const tombstone = { id: this.cTs(chunk.id), span: chunk.span, }; return tombstone; } const index = this.model.index; const res = { id: this.cTs(chunk.id), value: chunk.data.map((n) => this.cNode(index.get(n))), }; return res; } cStr(obj) { const chunks = []; const iterator = obj.iterator(); let chunk; while ((chunk = iterator())) chunks.push(this.cStrChunk(chunk)); return { type: 'str', id: this.cTs(obj.id), chunks, }; } cStrChunk(chunk) { if (chunk.del) { const tombstone = { id: this.cTs(chunk.id), span: chunk.span, }; return tombstone; } const res = { id: this.cTs(chunk.id), value: chunk.data, }; return res; } cBin(obj) { const chunks = []; const iterator = obj.iterator(); let chunk; while ((chunk = iterator())) chunks.push(this.cBinChunk(chunk)); return { type: 'bin', id: this.cTs(obj.id), chunks, }; } cBinChunk(chunk) { if (chunk.del) { const tombstone = { id: this.cTs(chunk.id), span: chunk.span, }; return tombstone; } const res = { id: this.cTs(chunk.id), value: toBase64(chunk.data), }; return res; } cVal(obj) { return { type: 'val', id: this.cTs(obj.id), value: this.cNode(obj.node()), }; } cCon(obj) { const node = { type: 'con', id: this.cTs(obj.id), }; const val = obj.val; if (val instanceof Timestamp) { node.timestamp = true; node.value = this.cTs(val); } else { if (val !== undefined) node.value = val; } return node; } }