UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

193 lines (192 loc) 5.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.VecNode = void 0; const ConNode_1 = require("../const/ConNode"); const constants_1 = require("../../constants"); const printTree_1 = require("tree-dump/lib/printTree"); const clock_1 = require("../../../json-crdt-patch/clock"); /** * Represents a `vec` JSON CRDT node, which is a LWW array. * * Vector is, usually a fixed length, last-write-wins array. Each element * in the array is a reference to another JSON CRDT node. The vector * can be extended by adding new elements to the end of the array. * * @category CRDT Node */ class VecNode { constructor( /** * @ignore */ doc, id) { this.doc = doc; this.id = id; /** * @ignore */ this.elements = []; // ----------------------------------------------------------------- extension /** * @ignore */ this.__extNode = undefined; /** * @ignore */ this._view = []; /** * @ignore */ this.api = undefined; } /** * Retrieves the ID of an element at the given index. * * @param index Index of the element to get. * @returns ID of the element at the given index, if any. */ val(index) { return this.elements[index]; } /** * Retrieves the JSON CRDT node at the given index. * * @param index Index of the element to get. * @returns JSON CRDT node at the given index, if any. */ get(index) { const id = this.elements[index]; if (!id) return undefined; return this.doc.index.get(id); } /** * @ignore */ put(index, id) { if (index > constants_1.CRDT_CONSTANTS.MAX_TUPLE_LENGTH) throw new Error('OUT_OF_BOUNDS'); const currentId = this.val(index); if (currentId && (0, clock_1.compare)(currentId, id) >= 0) return; if (index > this.elements.length) for (let i = this.elements.length; i < index; i++) this.elements.push(undefined); if (index < this.elements.length) this.elements[index] = id; else this.elements.push(id); return currentId; } /** * @ignore * @returns Returns the extension data node if this is an extension node, * otherwise `undefined`. The node is cached after the first access. */ ext() { if (this.__extNode) return this.__extNode; const extensionId = this.getExtId(); const isExtension = extensionId >= 0; if (!isExtension) return undefined; const extension = this.doc.ext.get(extensionId); if (!extension) return undefined; this.__extNode = new extension.Node(this.get(1)); return this.__extNode; } /** * @ignore */ isExt() { return !!this.ext(); } /** * @ignore * @returns Returns extension ID if this is an extension node, otherwise -1. */ getExtId() { if (this.elements.length !== 2) return -1; const type = this.get(0); if (!(type instanceof ConNode_1.ConNode)) return -1; const buf = type.val; const id = this.id; if (!(buf instanceof Uint8Array) || buf.length !== 3 || buf[1] !== id.sid % 256 || buf[2] !== id.time % 256) return -1; return buf[0]; } /** ------------------------------------------------------ {@link JsonNode} */ /** * @ignore */ child() { return this.ext(); } /** * @ignore */ container() { return this; } /** * @ignore */ children(callback) { const elements = this.elements; const length = elements.length; const index = this.doc.index; for (let i = 0; i < length; i++) { const id = elements[i]; if (!id) continue; const node = index.get(id); if (node) callback(node); } } /** * @ignore */ view() { const extNode = this.ext(); if (extNode) return extNode.view(); let useCache = true; const _view = this._view; const arr = []; const index = this.doc.index; const elements = this.elements; const length = elements.length; for (let i = 0; i < length; i++) { const id = elements[i]; const node = id ? index.get(id) : undefined; const value = node ? node.view() : undefined; if (_view[i] !== value) useCache = false; arr.push(value); } return useCache ? _view : (this._view = arr); } name() { return 'vec'; } /** ----------------------------------------------------- {@link Printable} */ toString(tab = '') { const extNode = this.ext(); const header = this.name() + ' ' + (0, clock_1.printTs)(this.id) + (extNode ? ` { extension = ${this.getExtId()} }` : ''); if (extNode) { return this.child().toString(tab, this.id); } const index = this.doc.index; return (header + (0, printTree_1.printTree)(tab, [ ...this.elements.map((id, i) => (tab) => `${i}: ${!id ? 'nil' : index.get(id) ? index.get(id).toString(tab + ' ' + ' '.repeat(('' + i).length)) : 'nil'}`), ...(extNode ? [(tab) => `${this.child().toString(tab)}`] : []), ])); } } exports.VecNode = VecNode;