json-joy
Version:
Collection of libraries for building collaborative editing apps.
193 lines (192 loc) • 5.72 kB
JavaScript
"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;