UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

164 lines (163 loc) 5.56 kB
import * as nodes from '../../../nodes'; import { ClockDecoder } from '../../../../json-crdt-patch/codec/clock/ClockDecoder'; import { Timestamp } from '../../../../json-crdt-patch/clock'; import { Model, UNDEFINED } from '../../../model/Model'; import { JsonCrdtDataType, SESSION } from '../../../../json-crdt-patch/constants'; export class Decoder { time; clockDecoder; decode(doc) { const [time, root] = doc; const isServerTime = typeof time === 'number'; if (isServerTime) { this.time = time; } else { this.clockDecoder = ClockDecoder.fromArr(time); } const model = isServerTime ? Model.withServerClock(time) : Model.withLogicalClock(this.clockDecoder.clock); const val = root ? this.decNode(model, root) : UNDEFINED; model.root = new nodes.RootNode(model, val.id); return model; } ts(x) { if (typeof x === 'number') { return new Timestamp(SESSION.SERVER, this.time - x); } else { const [sid, time] = x; if (sid < 0) { return this.clockDecoder.decodeId(-sid, time); } else { return new Timestamp(sid, time); } } } decNode(model, node) { switch (node[0]) { case JsonCrdtDataType.con: return this.decCon(model, node); case JsonCrdtDataType.val: return this.decVal(model, node); case JsonCrdtDataType.obj: return this.decObj(model, node); case JsonCrdtDataType.vec: return this.decVec(model, node); case JsonCrdtDataType.str: return this.decStr(model, node); case JsonCrdtDataType.bin: return this.decBin(model, node); case JsonCrdtDataType.arr: return this.decArr(model, node); } throw new Error('UNKNOWN_NODE'); } decCon(doc, node) { const id = this.ts(node[1]); let data = node[2]; if (node.length > 3) { const specialData = node[3]; if (!specialData) data = undefined; else data = this.ts(specialData); } const obj = new nodes.ConNode(id, data); doc.index.set(id, obj); return obj; } decVal(doc, node) { const id = this.ts(node[1]); const child = this.decNode(doc, node[2]); const obj = new nodes.ValNode(doc, id, child.id); doc.index.set(id, obj); return obj; } decObj(model, node) { const id = this.ts(node[1]); const obj = new nodes.ObjNode(model, id); const map = node[2]; const keys = Object.keys(map); const length = keys.length; for (let i = 0; i < length; i++) { const key = keys[i]; const val = this.decNode(model, map[key]); obj.put(key, val.id); } model.index.set(id, obj); return obj; } decVec(model, node) { const id = this.ts(node[1]); const obj = new nodes.VecNode(model, id); const map = node[2]; const elements = obj.elements; const length = map.length; for (let i = 0; i < length; i++) { const item = map[i]; if (!item) elements.push(undefined); else { const child = this.decNode(model, item); elements.push(child.id); } } model.index.set(id, obj); return obj; } decStr(doc, node) { const id = this.ts(node[1]); const obj = new nodes.StrNode(id); const chunks = node[2]; const size = chunks.length; let i = 0; obj.ingest(size, () => { const chunk = chunks[i++]; const chunkId = this.ts(chunk[0]); const content = chunk[1]; if (typeof content === 'number') return new nodes.StrChunk(chunkId, content, ''); return new nodes.StrChunk(chunkId, content.length, content); }); doc.index.set(id, obj); return obj; } decBin(doc, node) { const id = this.ts(node[1]); const obj = new nodes.BinNode(id); const chunks = node[2]; const size = chunks.length; let i = 0; obj.ingest(size, () => { const chunk = chunks[i++]; const chunkId = this.ts(chunk[0]); const content = chunk[1]; if (typeof content === 'number') return new nodes.BinChunk(chunkId, content, undefined); return new nodes.BinChunk(chunkId, content.length, content); }); doc.index.set(id, obj); return obj; } decArr(doc, node) { const id = this.ts(node[1]); const obj = new nodes.ArrNode(doc, id); const chunks = node[2]; const size = chunks.length; let i = 0; obj.ingest(size, () => { const chunk = chunks[i++]; const chunkId = this.ts(chunk[0]); const content = chunk[1]; if (typeof content === 'number') return new nodes.ArrChunk(chunkId, content, undefined); const ids = content.map((c) => this.decNode(doc, c).id); return new nodes.ArrChunk(chunkId, content.length, ids); }); doc.index.set(id, obj); return obj; } }